Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
b3aa3b4
feat(api): api update
stainless-app[bot] Mar 6, 2026
41d32f3
chore(internal): codegen related update
stainless-app[bot] Mar 7, 2026
5caad8a
feat: support passing required body params through pipes
stainless-app[bot] Mar 7, 2026
cab651f
feat: add `--max-items` flag for paginated/streaming endpoints
stainless-app[bot] Mar 7, 2026
7898e52
fix: fix for encoding arrays with `any` type items
stainless-app[bot] Mar 7, 2026
5a4be3e
chore(ci): skip uploading artifacts on stainless-internal branches
stainless-app[bot] Mar 7, 2026
212d624
feat(api): api update
stainless-app[bot] Mar 8, 2026
427aaa8
codegen metadata
stainless-app[bot] Mar 9, 2026
5978d7a
feat(api): api update
stainless-app[bot] Mar 9, 2026
bee82d4
codegen metadata
stainless-app[bot] Mar 10, 2026
9af4dc1
codegen metadata
stainless-app[bot] Mar 10, 2026
96fe33f
codegen metadata
stainless-app[bot] Mar 12, 2026
d0fbf69
fix: fix for test cases with newlines in YAML and better error reporting
stainless-app[bot] Mar 12, 2026
6cfe6d4
fix: only set client options when the corresponding CLI flag or env v…
stainless-app[bot] Mar 14, 2026
0059fd5
codegen metadata
stainless-app[bot] Mar 16, 2026
eb3e18e
fix: improved workflow for developing on branches
stainless-app[bot] Mar 17, 2026
632158a
fix: better support passing client args in any position
stainless-app[bot] Mar 17, 2026
d82fd7d
fix: no longer require an API key when building on production repos
stainless-app[bot] Mar 17, 2026
6d81988
chore(internal): tweak CI branches
stainless-app[bot] Mar 17, 2026
32f2697
fix: avoid reading from stdin unless request body is form encoded or …
stainless-app[bot] Mar 18, 2026
151606e
codegen metadata
stainless-app[bot] Mar 18, 2026
922d06b
feat(api): api update
stainless-app[bot] Mar 18, 2026
7a56630
fix: improve linking behavior when developing on a branch not in the …
stainless-app[bot] Mar 19, 2026
93ec607
feat(api): api update
stainless-app[bot] Mar 19, 2026
b329a41
release: 0.7.2
stainless-app[bot] Mar 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .github/actions/setup-go/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Setup Go
description: 'Sets up Go environment with private modules'
inputs:
stainless-api-key:
required: false
description: the value of the STAINLESS_API_KEY secret
runs:
using: composite
steps:
- uses: stainless-api/retrieve-github-access-token@v1
if: github.repository == 'stainless-sdks/agentmail-cli'
id: get_token
with:
repo: stainless-sdks/agentmail-go
stainless-api-key: ${{ inputs.stainless-api-key }}

- name: Configure Git for access to the Go SDK's staging repo
if: github.repository == 'stainless-sdks/agentmail-cli'
shell: bash
run: git config --global url."https://x-access-token:${{ steps.get_token.outputs.github_access_token }}@github.com/stainless-sdks/agentmail-go".insteadOf "https://github.com/stainless-sdks/agentmail-go"

- name: Setup go
uses: actions/setup-go@v5
with:
go-version-file: ./go.mod

- name: Bootstrap
shell: bash
run: ./scripts/bootstrap
55 changes: 38 additions & 17 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
name: CI
on:
push:
branches-ignore:
- 'generated'
- 'codegen/**'
- 'integrated/**'
- 'stl-preview-head/**'
- 'stl-preview-base/**'
branches:
- '**'
- '!integrated/**'
- '!stl-preview-head/**'
- '!stl-preview-base/**'
- '!generated'
- '!codegen/**'
- 'codegen/stl/**'
pull_request:
branches-ignore:
- 'stl-preview-head/**'
- 'stl-preview-base/**'

env:
GOPRIVATE: github.com/agentmail-to/agentmail-go,github.com/stainless-sdks/agentmail-go

jobs:
lint:
timeout-minutes: 10
Expand All @@ -22,10 +27,14 @@ jobs:
steps:
- uses: actions/checkout@v6

- name: Setup go
uses: actions/setup-go@v5
- uses: ./.github/actions/setup-go
with:
go-version-file: ./go.mod
stainless-api-key: ${{ secrets.STAINLESS_API_KEY }}

- name: Link staging branch
if: github.repository == 'stainless-sdks/agentmail-cli'
run: |
./scripts/link 'github.com/stainless-sdks/agentmail-go@${{ github.ref_name }}' || true

- name: Bootstrap
run: ./scripts/bootstrap
Expand All @@ -44,10 +53,14 @@ jobs:
steps:
- uses: actions/checkout@v6

- name: Setup go
uses: actions/setup-go@v5
- uses: ./.github/actions/setup-go
with:
go-version-file: ./go.mod
stainless-api-key: ${{ secrets.STAINLESS_API_KEY }}

- name: Link staging branch
if: github.repository == 'stainless-sdks/agentmail-cli'
run: |
./scripts/link 'github.com/stainless-sdks/agentmail-go@${{ github.ref_name }}' || true

- name: Bootstrap
run: ./scripts/bootstrap
Expand All @@ -61,14 +74,18 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Get GitHub OIDC Token
if: github.repository == 'stainless-sdks/agentmail-cli'
if: |-
github.repository == 'stainless-sdks/agentmail-cli' &&
!startsWith(github.ref, 'refs/heads/stl/')
id: github-oidc
uses: actions/github-script@v8
with:
script: core.setOutput('github_token', await core.getIDToken());

- name: Upload tarball
if: github.repository == 'stainless-sdks/agentmail-cli'
if: |-
github.repository == 'stainless-sdks/agentmail-cli' &&
!startsWith(github.ref, 'refs/heads/stl/')
env:
URL: https://pkg.stainless.com/s
AUTH: ${{ steps.github-oidc.outputs.github_token }}
Expand All @@ -83,10 +100,14 @@ jobs:
steps:
- uses: actions/checkout@v6

- name: Setup go
uses: actions/setup-go@v5
- uses: ./.github/actions/setup-go
with:
go-version-file: ./go.mod
stainless-api-key: ${{ secrets.STAINLESS_API_KEY }}

- name: Link staging branch
if: github.repository == 'stainless-sdks/agentmail-cli'
run: |
./scripts/link 'github.com/stainless-sdks/agentmail-go@${{ github.ref_name }}' || true

- name: Bootstrap
run: ./scripts/bootstrap
Expand Down
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "0.7.1"
".": "0.7.2"
}
6 changes: 3 additions & 3 deletions .stats.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
configured_endpoints: 62
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/agentmail%2Fagentmail-161e9f5d786cbd431b293059ed3202a30936b4ec47aa100dd2344b6cc4b77ec0.yml
openapi_spec_hash: b4e026c4a83dab7c8766a9179377674d
configured_endpoints: 61
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/agentmail%2Fagentmail-7fdcd6b4651f31badd1999d492b21d2c53bc6f3a5f57ca0f6c354136b389bf1c.yml
openapi_spec_hash: bdd107c27a5e1e30b43bff0ae7195a10
config_hash: 2595ffb55fb009abe18a6566962fbf0a
33 changes: 33 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,38 @@
# Changelog

## 0.7.2 (2026-03-19)

Full Changelog: [v0.7.1...v0.7.2](https://github.com/agentmail-to/agentmail-cli/compare/v0.7.1...v0.7.2)

### Features

* add `--max-items` flag for paginated/streaming endpoints ([cab651f](https://github.com/agentmail-to/agentmail-cli/commit/cab651f16b51cc0b8fd8197307bd9a2a3149097f))
* **api:** api update ([93ec607](https://github.com/agentmail-to/agentmail-cli/commit/93ec607579dc3c74a00c1559876f19159f17bf18))
* **api:** api update ([922d06b](https://github.com/agentmail-to/agentmail-cli/commit/922d06ba6370e908121be1ce2a3dcb60a025a919))
* **api:** api update ([5978d7a](https://github.com/agentmail-to/agentmail-cli/commit/5978d7ab85c90ec624165671208606c5d3bb9825))
* **api:** api update ([212d624](https://github.com/agentmail-to/agentmail-cli/commit/212d6248a314de915377c0fa35c346e7b9fe3549))
* **api:** api update ([b3aa3b4](https://github.com/agentmail-to/agentmail-cli/commit/b3aa3b49b6b1077ba92aa4146f7860dff0b05920))
* support passing required body params through pipes ([5caad8a](https://github.com/agentmail-to/agentmail-cli/commit/5caad8a5272b56a28394d040333c420a9f853b56))


### Bug Fixes

* avoid reading from stdin unless request body is form encoded or json ([32f2697](https://github.com/agentmail-to/agentmail-cli/commit/32f26970e29bc6c1b02d2e543bda76b38d018d5a))
* better support passing client args in any position ([632158a](https://github.com/agentmail-to/agentmail-cli/commit/632158a4431957bc86e8f0bc1b33adefb7067e59))
* fix for encoding arrays with `any` type items ([7898e52](https://github.com/agentmail-to/agentmail-cli/commit/7898e52f357d2232cb5db0c22791692dabc13d30))
* fix for test cases with newlines in YAML and better error reporting ([d0fbf69](https://github.com/agentmail-to/agentmail-cli/commit/d0fbf69778cfba589c91162a01bc111c19d03054))
* improve linking behavior when developing on a branch not in the Go SDK ([7a56630](https://github.com/agentmail-to/agentmail-cli/commit/7a566301e20873025326c753da2fe601e455879b))
* improved workflow for developing on branches ([eb3e18e](https://github.com/agentmail-to/agentmail-cli/commit/eb3e18ec65da5b7b9e43ba20993af7943807be89))
* no longer require an API key when building on production repos ([d82fd7d](https://github.com/agentmail-to/agentmail-cli/commit/d82fd7d528fa133a93bf7d94165382d37fe873df))
* only set client options when the corresponding CLI flag or env var is explicitly set ([6cfe6d4](https://github.com/agentmail-to/agentmail-cli/commit/6cfe6d4f1d49f539dde18233414d3c6132ec5dc9))


### Chores

* **ci:** skip uploading artifacts on stainless-internal branches ([5a4be3e](https://github.com/agentmail-to/agentmail-cli/commit/5a4be3e1c740c2b1f89d116f6cd9348fd81a5dae))
* **internal:** codegen related update ([41d32f3](https://github.com/agentmail-to/agentmail-cli/commit/41d32f3b7bcb7c421d033e0d18c4cf70f7343dd2))
* **internal:** tweak CI branches ([6d81988](https://github.com/agentmail-to/agentmail-cli/commit/6d8198801ce6a24c892d313650ef37cc7e3920b1))

## 0.7.1 (2026-03-06)

Full Changelog: [v0.7.0...v0.7.1](https://github.com/agentmail-to/agentmail-cli/compare/v0.7.0...v0.7.1)
Expand Down
9 changes: 9 additions & 0 deletions internal/apiform/encoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,15 @@ func (e *encoder) encodeArray(key string, val reflect.Value, writer *multipart.W
var values []string
for i := 0; i < val.Len(); i++ {
item := val.Index(i)
if (item.Kind() == reflect.Pointer || item.Kind() == reflect.Interface) && item.IsNil() {
// Null values are sent as an empty string
values = append(values, "")
continue
}
// If item is an interface, reduce it to the concrete type
if item.Kind() == reflect.Interface {
item = item.Elem()
}
var strValue string
switch item.Kind() {
case reflect.String:
Expand Down
2 changes: 1 addition & 1 deletion internal/binaryparam/binary_param.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func FileOrStdin(stdin io.ReadCloser, path string) (io.ReadCloser, bool, error)
// When the special glyph "-" is used, read from stdin. Although probably less necessary, also support
// special Unix files that refer to stdin.
switch path {
case stdinGlyph, "/dev/fd/0", "/dev/stdin":
case "", stdinGlyph, "/dev/fd/0", "/dev/stdin":
return stdin, true, nil
}

Expand Down
81 changes: 28 additions & 53 deletions internal/binaryparam/binary_param_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,57 +28,32 @@ func TestFileOrStdin(t *testing.T) {
require.False(t, stdinInUse)
})

t.Run("WithStdinGlyph", func(t *testing.T) {
tempFile := t.TempDir() + "/test_file.txt"
require.NoError(t, os.WriteFile(tempFile, []byte(expectedContents), 0600))

stubStdin, err := os.Open(tempFile)
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, stubStdin.Close()) })

readCloser, stdinInUse, err := FileOrStdin(stubStdin, "-")
require.NoError(t, err)

actualContents, err := io.ReadAll(readCloser)
require.NoError(t, err)
require.Equal(t, expectedContents, string(actualContents))

require.True(t, stdinInUse)
})

t.Run("WithDevFD0File", func(t *testing.T) {
tempFile := t.TempDir() + "/dev_fd_0"
require.NoError(t, os.WriteFile(tempFile, []byte(expectedContents), 0600))

stubStdin, err := os.Open(tempFile)
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, stubStdin.Close()) })

readCloser, stdinInUse, err := FileOrStdin(stubStdin, "/dev/fd/0")
require.NoError(t, err)

actualContents, err := io.ReadAll(readCloser)
require.NoError(t, err)
require.Equal(t, expectedContents, string(actualContents))

require.True(t, stdinInUse)
})

t.Run("WithDevStdinFile", func(t *testing.T) {
tempFile := t.TempDir() + "/dev_stdin"
require.NoError(t, os.WriteFile(tempFile, []byte(expectedContents), 0600))

stubStdin, err := os.Open(tempFile)
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, stubStdin.Close()) })

readCloser, stdinInUse, err := FileOrStdin(stubStdin, "/dev/stdin")
require.NoError(t, err)

actualContents, err := io.ReadAll(readCloser)
require.NoError(t, err)
require.Equal(t, expectedContents, string(actualContents))

require.True(t, stdinInUse)
})
stdinTests := []struct {
testName string
path string
}{
{"TestEmptyString", ""},
{"TestDash", "-"},
{"TestDevStdin", "/dev/stdin"},
{"TestDevFD0", "/dev/fd/0"},
}
for _, test := range stdinTests {
t.Run(test.testName, func(t *testing.T) {
tempFile := t.TempDir() + "/test_file.txt"
require.NoError(t, os.WriteFile(tempFile, []byte(expectedContents), 0600))

stubStdin, err := os.Open(tempFile)
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, stubStdin.Close()) })

readCloser, stdinInUse, err := FileOrStdin(stubStdin, test.path)
require.NoError(t, err)

actualContents, err := io.ReadAll(readCloser)
require.NoError(t, err)
require.Equal(t, expectedContents, string(actualContents))

require.True(t, stdinInUse)
})
}
}
70 changes: 22 additions & 48 deletions internal/mocktest/mocktest.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package mocktest

import (
"bytes"
"context"
"fmt"
"net"
Expand All @@ -14,6 +15,7 @@ import (
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -54,8 +56,14 @@ func restoreNetwork(origClient, origDefault http.RoundTripper) {
}

// TestRunMockTestWithFlags runs a test against a mock server with the provided
// CLI flags and ensures it succeeds
func TestRunMockTestWithFlags(t *testing.T, flags ...string) {
// CLI args and ensures it succeeds
func TestRunMockTestWithFlags(t *testing.T, args ...string) {
TestRunMockTestWithPipeAndFlags(t, nil, args...)
}

// TestRunMockTestWithPipeAndFlags runs a test against a mock server with the provided
// data piped over stdin and CLI args and ensures it succeeds
func TestRunMockTestWithPipeAndFlags(t *testing.T, pipeData []byte, args ...string) {
origClient, origDefault := blockNetworkExceptMockServer()
defer restoreNetwork(origClient, origDefault)

Expand All @@ -71,52 +79,18 @@ func TestRunMockTestWithFlags(t *testing.T, flags ...string) {
_, filename, _, ok := runtime.Caller(0)
require.True(t, ok, "Could not get current file path")
dirPath := filepath.Dir(filename)
project := filepath.Join(dirPath, "..", "..", "cmd", "...")

args := []string{"run", project, "--base-url", mockServerURL.String()}
args = append(args, flags...)

t.Logf("Testing command: agentmail %s", strings.Join(args[4:], " "))

cliCmd := exec.Command("go", args...)

// Pipe the CLI tool's output into `head` so it doesn't hang when simulating
// paginated or streamed endpoints. 100 lines of output should be enough to
// test that the API endpoint worked, or report back a meaningful amount of
// data if something went wrong.
headCmd := exec.Command("head", "-n", "100")
pipe, err := cliCmd.StdoutPipe()
require.NoError(t, err, "Failed to create pipe for CLI command")
headCmd.Stdin = pipe

// Capture `head` output and CLI command stderr outputs:
var output strings.Builder
headCmd.Stdout = &output
headCmd.Stderr = &output
cliCmd.Stderr = &output

// First start `head`, so it's ready for data to come in:
err = headCmd.Start()
require.NoError(t, err, "Failed to start `head` command")

// Next start the CLI command so it can pipe data to `head` without
// buffering any data in advance:
err = cliCmd.Start()
require.NoError(t, err, "Failed to start CLI command")

// Ensure that the stdout pipe is closed as soon as `head` exits, to let the
// CLI tool know that no more output is needed and it can stop streaming
// test data for streaming/paginated endpoints. This needs to happen before
// calling `cliCmd.Wait()`, otherwise there will be a deadlock.
err = headCmd.Wait()
pipe.Close()
require.NoError(t, err, "`head` command finished with an error")

// Finally, wait for the CLI tool to finish up:
err = cliCmd.Wait()
require.NoError(t, err, "CLI command failed\n%s", output.String())

t.Logf("Test passed successfully\nOutput:\n%s", output.String())
project := filepath.Join(dirPath, "..", "..", "cmd", "agentmail")

args = append([]string{"run", project, "--base-url", mockServerURL.String()}, args...)

t.Logf("Testing command: go run ./cmd/agentmail %s", strings.Join(args[2:], " "))

cmd := exec.Command("go", args...)
cmd.Stdin = bytes.NewReader(pipeData)
output, err := cmd.CombinedOutput()
assert.NoError(t, err, "Test failed\nError: %v\nOutput: %s", err, output)

t.Logf("Test passed successfully\nOutput:\n%s", string(output))
}

func TestFile(t *testing.T, contents string) string {
Expand Down
Loading
Loading