diff --git a/.github/actions/setup-go/action.yml b/.github/actions/setup-go/action.yml
index 6c4c259..eb7b832 100644
--- a/.github/actions/setup-go/action.yml
+++ b/.github/actions/setup-go/action.yml
@@ -7,7 +7,7 @@ inputs:
runs:
using: composite
steps:
- - uses: stainless-api/retrieve-github-access-token@v1
+ - uses: stainless-api/retrieve-github-access-token@1f03f929b746c5b03dcdafa2bebbb18ca5672e1a # v1.0.0
if: github.repository == 'stainless-sdks/agentmail-cli'
id: get_token
with:
@@ -20,7 +20,7 @@ runs:
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
+ uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0
with:
go-version-file: ./go.mod
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 310fdcc..1556dd6 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -25,7 +25,7 @@ jobs:
if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata')
steps:
- - uses: actions/checkout@v6
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: ./.github/actions/setup-go
with:
@@ -51,7 +51,7 @@ jobs:
runs-on: ${{ github.repository == 'stainless-sdks/agentmail-cli' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata')
steps:
- - uses: actions/checkout@v6
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: ./.github/actions/setup-go
with:
@@ -66,7 +66,7 @@ jobs:
run: ./scripts/bootstrap
- name: Run goreleaser
- uses: goreleaser/goreleaser-action@v6.1.0
+ uses: goreleaser/goreleaser-action@9ed2f89a662bf1735a48bc8557fd212fa902bebf # v6.1.0
with:
version: latest
args: release --snapshot --clean --skip=publish
@@ -78,7 +78,7 @@ jobs:
github.repository == 'stainless-sdks/agentmail-cli' &&
!startsWith(github.ref, 'refs/heads/stl/')
id: github-oidc
- uses: actions/github-script@v8
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
script: core.setOutput('github_token', await core.getIDToken());
@@ -98,7 +98,7 @@ jobs:
runs-on: ${{ github.repository == 'stainless-sdks/agentmail-cli' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
steps:
- - uses: actions/checkout@v6
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: ./.github/actions/setup-go
with:
diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml
index ebb534a..0af93f4 100644
--- a/.github/workflows/publish-release.yml
+++ b/.github/workflows/publish-release.yml
@@ -10,20 +10,21 @@ on:
push:
tags:
- "v*"
+ workflow_dispatch: {}
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@v6
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
- name: Set up Go
- uses: actions/setup-go@v5
+ uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0
with:
go-version-file: "go.mod"
- name: Run GoReleaser
- uses: goreleaser/goreleaser-action@v6.1.0
+ uses: goreleaser/goreleaser-action@9ed2f89a662bf1735a48bc8557fd212fa902bebf # v6.1.0
with:
version: latest
args: release --clean
diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml
index 92e05a8..e672552 100644
--- a/.github/workflows/release-doctor.yml
+++ b/.github/workflows/release-doctor.yml
@@ -12,7 +12,7 @@ jobs:
if: github.repository == 'agentmail-to/agentmail-cli' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next')
steps:
- - uses: actions/checkout@v6
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Check release environment
run: |
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index de6d4ca..2daf110 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.7.11"
+ ".": "0.7.12"
}
\ No newline at end of file
diff --git a/.stats.yml b/.stats.yml
index d6b0c8a..e3c221d 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 94
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/agentmail%2Fagentmail-830c24d3ff39df5248fd06a0e32a64a57bdf6ed4f58bb33244cab98fa7c3476e.yml
-openapi_spec_hash: c71d36752561924eb2e11bce3f93c32f
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/agentmail/agentmail-ec4e26c46715d10a0de953666e890ab67f97e61f7355971ed728942f957100f8.yml
+openapi_spec_hash: 74a582c32cd3a4f6e2afb1e84eccbb21
config_hash: 8ab5eb1bfc282411f0283d386a319f23
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b640a72..faa61ab 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,42 @@
# Changelog
+## 0.7.12 (2026-05-14)
+
+Full Changelog: [v0.7.11...v0.7.12](https://github.com/agentmail-to/agentmail-cli/compare/v0.7.11...v0.7.12)
+
+### Features
+
+* **api:** api update ([2a05aba](https://github.com/agentmail-to/agentmail-cli/commit/2a05aba658ca45f0a782575b47c8f159ca67a186))
+* **api:** api update ([8daa4e3](https://github.com/agentmail-to/agentmail-cli/commit/8daa4e303af3d3e8b2543e1c294dbbac08dbbb2a))
+* **api:** api update ([42b296a](https://github.com/agentmail-to/agentmail-cli/commit/42b296accc273d1f43984b7a978500f5f4c890fd))
+* **cli:** add `--raw-output`/`-r` option to print raw (non-JSON) strings ([f9df419](https://github.com/agentmail-to/agentmail-cli/commit/f9df419dc3ba3b15a8a267db59207e81eea217f5))
+* **cli:** alias parameters in data with `x-stainless-cli-data-alias` ([ea26a8c](https://github.com/agentmail-to/agentmail-cli/commit/ea26a8c2a975386b3a00bf181d877e3eeb40c3f1))
+* **cli:** send filename and content type when reading input from files ([65a86ee](https://github.com/agentmail-to/agentmail-cli/commit/65a86ee9ddb38368eebc5a472380d9d85b4fb6c5))
+* support passing path and query params over stdin ([86c8aab](https://github.com/agentmail-to/agentmail-cli/commit/86c8aab82328e0362b0d699464f1a55a9421b4e7))
+
+
+### Bug Fixes
+
+* **cli:** correctly load zsh autocompletion ([1b982f7](https://github.com/agentmail-to/agentmail-cli/commit/1b982f78a9d150901b940791d7bc05fe1a396e49))
+* flags for nullable body scalar fields are strictly typed ([f105a97](https://github.com/agentmail-to/agentmail-cli/commit/f105a97104a8e6be911e6419733d9c7628577d84))
+
+
+### Chores
+
+* **ci:** support manually triggering release workflow ([0e60014](https://github.com/agentmail-to/agentmail-cli/commit/0e6001439c676f9b793176e39417f69ff94d01ac))
+* **cli:** fall back to JSON when using default "explore" with non-TTY ([fbfe2c1](https://github.com/agentmail-to/agentmail-cli/commit/fbfe2c1e0e56a779a885443c2d0d4743b512e242))
+* **cli:** switch long lists of positional args over to param structs ([d227554](https://github.com/agentmail-to/agentmail-cli/commit/d227554129ec4bddcf6ba84af9f08fe49247ea82))
+* **cli:** use `ShowJSONOpts` as argument to `formatJSON` instead of many positionals ([0e94446](https://github.com/agentmail-to/agentmail-cli/commit/0e944460e3f00ab8cb1040bb18e0380bb3574dbe))
+* **internal:** codegen related update ([aa96815](https://github.com/agentmail-to/agentmail-cli/commit/aa9681503fff5488e7b02db6e210c055f6699f19))
+* **internal:** codegen related update ([95968dd](https://github.com/agentmail-to/agentmail-cli/commit/95968dd8267ca302ca54e626fff6e71a58542cbf))
+* **internal:** codegen related update ([dbc4e39](https://github.com/agentmail-to/agentmail-cli/commit/dbc4e3921846cff6f687cd96b4c46163f8785baf))
+* **internal:** codegen related update ([5b36c53](https://github.com/agentmail-to/agentmail-cli/commit/5b36c5391637ffda46b95c9095acda6f6be532e9))
+* **internal:** codegen related update ([c7f1282](https://github.com/agentmail-to/agentmail-cli/commit/c7f1282aabb0958db3b8dd601bdca796e5dc0f57))
+* **internal:** codegen related update ([b25c8cb](https://github.com/agentmail-to/agentmail-cli/commit/b25c8cb53998ae20441273aa550a0b435b608bf3))
+* **internal:** more robust bootstrap script ([8b39e72](https://github.com/agentmail-to/agentmail-cli/commit/8b39e720cbe7e3c2c150bd7f2a563dd0feb298bf))
+* redact api-key headers in debug logs ([d3bae3e](https://github.com/agentmail-to/agentmail-cli/commit/d3bae3e70698a8869a12853ee029755a7654ff67))
+* update SDK settings ([a842755](https://github.com/agentmail-to/agentmail-cli/commit/a842755950fd6fa4ccbf2c890025e2d22ab48f97))
+
## 0.7.11 (2026-04-14)
Full Changelog: [v0.7.10...v0.7.11](https://github.com/agentmail-to/agentmail-cli/compare/v0.7.10...v0.7.11)
diff --git a/cmd/agentmail/main.go b/cmd/agentmail/main.go
index a8702f2..6cb588d 100644
--- a/cmd/agentmail/main.go
+++ b/cmd/agentmail/main.go
@@ -43,7 +43,12 @@ func main() {
fmt.Fprintf(os.Stderr, "%s %q: %d %s\n", apierr.Request.Method, apierr.Request.URL, apierr.Response.StatusCode, http.StatusText(apierr.Response.StatusCode))
format := app.String("format-error")
json := gjson.Parse(apierr.RawJSON())
- show_err := cmd.ShowJSON(os.Stdout, "Error", json, format, app.String("transform-error"))
+ show_err := cmd.ShowJSON(json, cmd.ShowJSONOpts{
+ ExplicitFormat: app.IsSet("format-error"),
+ Format: format,
+ Title: "Error",
+ Transform: app.String("transform-error"),
+ })
if show_err != nil {
// Just print the original error:
fmt.Fprintf(os.Stderr, "%s\n", err.Error())
diff --git a/go.mod b/go.mod
index 3183fac..daf7971 100644
--- a/go.mod
+++ b/go.mod
@@ -3,7 +3,7 @@ module github.com/agentmail-to/agentmail-cli
go 1.25
require (
- github.com/agentmail-to/agentmail-go v0.6.0
+ github.com/agentmail-to/agentmail-go v0.14.0
github.com/charmbracelet/bubbles v0.21.0
github.com/charmbracelet/bubbletea v1.3.6
github.com/charmbracelet/lipgloss v1.1.0
diff --git a/go.sum b/go.sum
index 550ec0a..d034339 100644
--- a/go.sum
+++ b/go.sum
@@ -1,5 +1,5 @@
-github.com/agentmail-to/agentmail-go v0.6.0 h1:CCQtBbYE97KASraV2v4IvSuaaJrjKDn97qH1rhMmJt8=
-github.com/agentmail-to/agentmail-go v0.6.0/go.mod h1:3NrKbeXLQKRgb9gj2bmCoN9WXDTy9y9yacV070xpvDU=
+github.com/agentmail-to/agentmail-go v0.14.0 h1:oEZQResNnsqY7aZl2zoXoQdAHYltsROr51Z+gNEHhS8=
+github.com/agentmail-to/agentmail-go v0.14.0/go.mod h1:3NrKbeXLQKRgb9gj2bmCoN9WXDTy9y9yacV070xpvDU=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8=
diff --git a/internal/autocomplete/shellscripts/zsh_autocomplete.zsh b/internal/autocomplete/shellscripts/zsh_autocomplete.zsh
index 4d4bdcd..d937171 100644
--- a/internal/autocomplete/shellscripts/zsh_autocomplete.zsh
+++ b/internal/autocomplete/shellscripts/zsh_autocomplete.zsh
@@ -1,5 +1,4 @@
-#!/bin/zsh
-compdef ____APPNAME___zsh_autocomplete __APPNAME__
+#compdef __APPNAME__
____APPNAME___zsh_autocomplete() {
@@ -44,3 +43,14 @@ ____APPNAME___zsh_autocomplete() {
;;
esac
}
+
+# When installed in fpath (e.g., via Homebrew's zsh_completion stanza), this file
+# is autoloaded as the function ___APPNAME__ and its body becomes that function's
+# body. Detect that case via funcstack and dispatch to the completion function.
+# When sourced (e.g., `source <(__APPNAME__ @completion zsh)`), register the
+# function with compdef instead.
+if [[ "${funcstack[1]}" = "___APPNAME__" ]]; then
+ ____APPNAME___zsh_autocomplete "$@"
+else
+ compdef ____APPNAME___zsh_autocomplete __APPNAME__
+fi
diff --git a/internal/debugmiddleware/debug_middleware.go b/internal/debugmiddleware/debug_middleware.go
index f07b93b..647f1de 100644
--- a/internal/debugmiddleware/debug_middleware.go
+++ b/internal/debugmiddleware/debug_middleware.go
@@ -21,7 +21,12 @@ const redactedPlaceholder = ""
// Headers known to contain sensitive information like an API key. Note that this exclude `Authorization`,
// which is handled specially in `redactRequest` below.
-var sensitiveHeaders = []string{}
+var sensitiveHeaders = []string{
+ "api-key",
+ "x-api-key",
+ "cookie",
+ "set-cookie",
+}
// RequestLogger is a middleware that logs HTTP requests and responses.
type RequestLogger struct {
diff --git a/internal/requestflag/innerflag.go b/internal/requestflag/innerflag.go
index 102624f..528915f 100644
--- a/internal/requestflag/innerflag.go
+++ b/internal/requestflag/innerflag.go
@@ -14,7 +14,8 @@ import (
type InnerFlag[
T []any | []map[string]any | []DateTimeValue | []DateValue | []TimeValue | []string |
[]float64 | []int64 | []bool | any | map[string]any | DateTimeValue | DateValue | TimeValue |
- string | float64 | int64 | bool,
+ string | float64 | int64 | bool |
+ *string | *float64 | *int64 | *bool | *DateTimeValue | *DateValue | *TimeValue,
] struct {
Name string // name of the flag
DefaultText string // default text of the flag for usage purposes
@@ -22,14 +23,35 @@ type InnerFlag[
Aliases []string // aliases that are allowed for this flag
Validator func(T) error // custom function to validate this flag value
- OuterFlag cli.Flag // The flag on which this inner flag will set values
- InnerField string // The inner field which this flag will set
+ OuterFlag cli.Flag // The flag on which this inner flag will set values
+ InnerField string // The inner field which this flag will set
+ DataAliases []string // alternate names recognized in YAML values passed as the outer flag
+
+ // OuterIsArrayOfObjects tells an untyped outer flag (Flag[any], used for nullable
+ // complex schemas) to seed its underlying value as []map[string]any rather than
+ // map[string]any before SetInnerField runs. The hint is ignored for typed outer
+ // flags whose zero value already carries a dispatchable reflect.Kind.
+ OuterIsArrayOfObjects bool
+}
+
+// GetDataAliases returns the aliases recognized when parsing inner field keys from piped or flag YAML.
+func (f *InnerFlag[T]) GetDataAliases() []string {
+ return f.DataAliases
+}
+
+// GetInnerField returns the API field name that this inner flag sets on its outer flag's value.
+// For example, the flag --parent.foo targeting a parameter whose OpenAPI property name is "foo"
+// would return "foo". This is distinct from the flag's CLI name and from any DataAliases entries.
+func (f *InnerFlag[T]) GetInnerField() string {
+ return f.InnerField
}
type HasOuterFlag interface {
cli.Flag
SetOuterFlag(cli.Flag)
GetOuterFlag() cli.Flag
+ GetInnerField() string
+ GetDataAliases() []string
}
func (f *InnerFlag[T]) SetOuterFlag(flag cli.Flag) {
@@ -61,6 +83,10 @@ func (f *InnerFlag[T]) Set(name string, rawVal string) error {
}
}
+ if seeder, ok := f.OuterFlag.(InnerFieldSeeder); ok {
+ seeder.SeedInnerCollection(f.OuterIsArrayOfObjects)
+ }
+
if settableInnerField, ok := f.OuterFlag.(SettableInnerField); ok {
settableInnerField.SetInnerField(f.InnerField, parsedValue)
} else {
@@ -121,6 +147,9 @@ func (f *InnerFlag[T]) TypeName() string {
if ty == nil {
return ""
}
+ if ty.Kind() == reflect.Pointer {
+ ty = ty.Elem()
+ }
// Get base type name with special handling for built-in types
getTypeName := func(t reflect.Type) string {
diff --git a/internal/requestflag/requestflag.go b/internal/requestflag/requestflag.go
index bdef64f..77c4f1f 100644
--- a/internal/requestflag/requestflag.go
+++ b/internal/requestflag/requestflag.go
@@ -1,6 +1,7 @@
package requestflag
import (
+ "encoding/json"
"fmt"
"reflect"
"strconv"
@@ -12,13 +13,38 @@ import (
"github.com/urfave/cli/v3"
)
+// formatForFlagSet converts a Go value parsed from YAML/JSON stdin data into a string
+// that flag.Set (and thus parseCLIArg) can parse correctly for each flag type.
+// Strings are returned as-is (parseCLIArg[string] assigns the raw value directly, so
+// JSON-quoting must be avoided). Scalars use %v. Complex types (maps, slices) are
+// JSON-encoded, which the yaml.Unmarshal default branch in parseCLIArg can parse.
+func formatForFlagSet(val any) (string, error) {
+ switch v := val.(type) {
+ case string:
+ return v, nil
+ case bool, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64:
+ return fmt.Sprintf("%v", val), nil
+ default:
+ b, err := json.Marshal(val)
+ if err != nil {
+ return "", fmt.Errorf("cannot format value %T for flag.Set: %w", val, err)
+ }
+ return string(b), nil
+ }
+}
+
// Flag [T] is a generic flag base which can be used to implement the most
// common interfaces used by urfave/cli. Additionally, it allows specifying
// where in an HTTP request the flag values should be placed (e.g. query, body, etc.).
+//
+// Pointer-to-primitive type parameters (e.g. *string) are used for flags whose underlying
+// schema is nullable. They give flags a tri-state: unset (excluded from the request),
+// set to the literal "null" (nil pointer → JSON null), or set to a value (*v → JSON value).
type Flag[
T []any | []map[string]any | []DateTimeValue | []DateValue | []TimeValue | []string |
[]float64 | []int64 | []bool | any | map[string]any | DateTimeValue | DateValue | TimeValue |
- string | float64 | int64 | bool,
+ string | float64 | int64 | bool |
+ *string | *float64 | *int64 | *bool | *DateTimeValue | *DateValue | *TimeValue,
] struct {
Name string // name of the flag
Category string // category of the flag, if any
@@ -36,6 +62,7 @@ type Flag[
HeaderPath string // location in the request header to put this flag's value
BodyPath string // location in the request body to put this flag's value
BodyRoot bool // if true, then use this value as the entire request body
+ PathParam string // name of the URL path parameter this flag's value maps to
// Const, when true, marks this flag as a constant. The flag's Default value is used as the fixed value
// and always included in the request (IsSet returns true). The user can still see and override the flag,
@@ -48,6 +75,10 @@ type Flag[
// binary` in the OpenAPI spec.
FileInput bool
+ // DataAliases is a list of alternate names for this parameter recognized when parsing piped YAML/JSON
+ // input. Values keyed by any alias are translated to the canonical API name before being sent.
+ DataAliases []string
+
// unexported fields for internal use
count int // number of times the flag has been set
hasBeenSet bool // whether the flag has been set from env or file
@@ -63,8 +94,10 @@ type InRequest interface {
GetQueryPath() string
GetHeaderPath() string
GetBodyPath() string
+ GetPathParam() string
IsBodyRoot() bool
IsFileInput() bool
+ GetDataAliases() []string
}
func (f Flag[T]) GetQueryPath() string {
@@ -79,6 +112,10 @@ func (f Flag[T]) GetBodyPath() string {
return f.BodyPath
}
+func (f Flag[T]) GetPathParam() string {
+ return f.PathParam
+}
+
func (f Flag[T]) IsBodyRoot() bool {
return f.BodyRoot
}
@@ -87,6 +124,10 @@ func (f Flag[T]) IsFileInput() bool {
return f.FileInput
}
+func (f Flag[T]) GetDataAliases() []string {
+ return f.DataAliases
+}
+
// The values that will be sent in different parts of a request.
type RequestContents struct {
Queries map[string]any
@@ -94,7 +135,91 @@ type RequestContents struct {
Body any
}
-// Extract query parameters, headers, and body values from command flags.
+// ApplyStdinDataToFlags sets flag values from a parsed stdin data map for flags that have not already been
+// set via the command line. This allows piped YAML/JSON data to satisfy path, query, and header parameters.
+// Body parameters are excluded: they are already handled by the maps.Copy merge in flagOptions.
+// For each unset flag, if the parsed data map contains a key matching the flag's QueryPath, HeaderPath, or
+// PathParam (or any of its DataAliases), the flag is set to that value via flag.Set.
+//
+// Inner flags (those with an outer flag) are also handled: if the outer flag's body path key exists in the
+// data map and contains a nested map with a key matching the inner flag's field (or aliases), the inner
+// flag is set from that nested value.
+func ApplyStdinDataToFlags(cmd *cli.Command, data map[string]any) error {
+ for _, flag := range cmd.Flags {
+ if flag.IsSet() {
+ continue
+ }
+
+ // Handle inner flags: look for their value nested under the outer flag's body path.
+ if inner, ok := flag.(HasOuterFlag); ok {
+ outer, outerOk := inner.GetOuterFlag().(InRequest)
+ if !outerOk || outer.GetBodyPath() == "" {
+ continue
+ }
+ nested, ok := data[outer.GetBodyPath()].(map[string]any)
+ if !ok {
+ continue
+ }
+ innerField := inner.GetInnerField()
+ val, found := nested[innerField]
+ if !found {
+ for _, alias := range inner.GetDataAliases() {
+ if alias != "" && alias != innerField {
+ if v, ok := nested[alias]; ok {
+ val, found = v, true
+ break
+ }
+ }
+ }
+ }
+ if !found {
+ continue
+ }
+ setVal, err := formatForFlagSet(val)
+ if err != nil {
+ return fmt.Errorf("cannot format piped value for flag %q: %w", flag.Names()[0], err)
+ }
+ if err := flag.Set(flag.Names()[0], setVal); err != nil {
+ return fmt.Errorf("cannot set flag %q from piped data: %w", flag.Names()[0], err)
+ }
+ continue
+ }
+
+ inReq, ok := flag.(InRequest)
+ if !ok {
+ continue
+ }
+
+ // Try each request location in turn, checking the canonical path key and all aliases.
+ // Body params are excluded: they are already handled by the maps.Copy merge in flagOptions.
+ for _, path := range []string{inReq.GetQueryPath(), inReq.GetHeaderPath(), inReq.GetPathParam()} {
+ if path == "" {
+ continue
+ }
+ var val any
+ var found bool
+ for _, key := range append([]string{path}, inReq.GetDataAliases()...) {
+ if v, ok := data[key]; ok {
+ val, found = v, true
+ break
+ }
+ }
+ if !found {
+ continue
+ }
+ setVal, err := formatForFlagSet(val)
+ if err != nil {
+ return fmt.Errorf("cannot format piped value for flag %q: %w", flag.Names()[0], err)
+ }
+ if err := flag.Set(flag.Names()[0], setVal); err != nil {
+ return fmt.Errorf("cannot set flag %q from piped data: %w", flag.Names()[0], err)
+ }
+ break
+ }
+ }
+ return nil
+}
+
func ExtractRequestContents(cmd *cli.Command) RequestContents {
bodyMap := make(map[string]any)
res := RequestContents{
@@ -277,7 +402,7 @@ func (f *Flag[T]) IsRequired() bool {
}
// Intentionally don't use `f.Required`, because request flags may be passed
// over stdin as well as by flag.
- if f.BodyPath != "" || f.BodyRoot {
+ if f.BodyPath != "" || f.BodyRoot || f.PathParam != "" || f.QueryPath != "" || f.HeaderPath != "" {
return false
}
return f.Required
@@ -332,6 +457,11 @@ func (f *Flag[T]) TypeName() string {
if ty == nil {
return ""
}
+ // Deref pointer-typed flags so --help surfaces the pointee kind (e.g. "string"), not
+ // Go's pointer syntax.
+ if ty.Kind() == reflect.Pointer {
+ ty = ty.Elem()
+ }
// Get base type name with special handling for built-in types
getTypeName := func(t reflect.Type) string {
@@ -387,6 +517,8 @@ func (f *Flag[T]) IsMultiValueFlag() bool {
}
func (f *Flag[T]) IsBoolFlag() bool {
+ // Flag[*bool] is deliberately not treated as a bool flag — the pointer form needs an
+ // explicit value (`--foo true`, `--foo null`) to disambiguate the tri-state.
_, isBool := any(f.Default).(bool)
return isBool
}
@@ -410,7 +542,8 @@ func (f Flag[T]) IsLocal() bool {
type cliValue[
T []any | []map[string]any | []DateTimeValue | []DateValue | []TimeValue | []string | []float64 |
[]int64 | []bool | any | map[string]any | DateTimeValue | DateValue | TimeValue | string |
- float64 | int64 | bool,
+ float64 | int64 | bool |
+ *string | *float64 | *int64 | *bool | *DateTimeValue | *DateValue | *TimeValue,
] struct {
value T
}
@@ -420,12 +553,27 @@ type cliValue[
func parseCLIArg[
T []any | []map[string]any | []DateTimeValue | []DateValue | []TimeValue | []string | []float64 |
[]int64 | []bool | any | map[string]any | DateTimeValue | DateValue | TimeValue | string |
- float64 | int64 | bool,
+ float64 | int64 | bool |
+ *string | *float64 | *int64 | *bool | *DateTimeValue | *DateValue | *TimeValue,
](value string) (T, error) {
var parsedValue any
var err error
var empty T
+
+ if value == "null" {
+ switch any(empty).(type) {
+ // Pointer-to-primitive: explicit nil gives the tri-state its "null" state
+ // (unset / null / value). Without this, numeric flags would fail to parse
+ // "null" and string flags would accept the literal word as a raw value.
+ case *string, *int64, *float64, *bool, *DateValue, *DateTimeValue, *TimeValue:
+ return empty, nil
+ // Maps marshal nil as JSON null natively; short-circuit avoids a YAML round-trip.
+ case map[string]any:
+ return empty, nil
+ }
+ }
+
switch any(empty).(type) {
case string:
parsedValue = value
@@ -456,6 +604,48 @@ func parseCLIArg[
parsedValue = t
}
+ // Pointer-to-primitive flags reach here only when `value != "null"`; we parse the
+ // pointee type and return its address so JSON marshaling emits the underlying value.
+ case *string:
+ v := value
+ parsedValue = &v
+ case *int64:
+ var v int64
+ v, err = strconv.ParseInt(value, 0, 64)
+ if err == nil {
+ parsedValue = &v
+ }
+ case *float64:
+ var v float64
+ v, err = strconv.ParseFloat(value, 64)
+ if err == nil {
+ parsedValue = &v
+ }
+ case *bool:
+ var v bool
+ v, err = strconv.ParseBool(value)
+ if err == nil {
+ parsedValue = &v
+ }
+ case *DateTimeValue:
+ var dt DateTimeValue
+ err = (&dt).Parse(value)
+ if err == nil {
+ parsedValue = &dt
+ }
+ case *DateValue:
+ var d DateValue
+ err = (&d).Parse(value)
+ if err == nil {
+ parsedValue = &d
+ }
+ case *TimeValue:
+ var t TimeValue
+ err = (&t).Parse(value)
+ if err == nil {
+ parsedValue = &t
+ }
+
default:
if strings.HasPrefix(value, "@") {
// File literals like @file.txt should work here
@@ -492,6 +682,13 @@ func parseCLIArg[
}
+// Ptr returns a pointer to its argument. It is used to initialize `Default` on pointer-typed
+// Flag values, since Go does not allow taking the address of a composite literal's element
+// or of an untyped constant.
+func Ptr[T any](v T) *T {
+ return &v
+}
+
// Assuming this string failed to parse as valid YAML, this function will
// return true for strings that can reasonably be interpreted as a string literal,
// like identifiers (`foo_bar`), UUIDs (`945b2f0c-8e89-487a-b02c-f851c69ea459`),
@@ -585,6 +782,15 @@ func (c *cliValue[T]) String() string {
// For basic types, use standard string representation
return fmt.Sprintf("%v", v)
+ case *string, *int64, *float64, *bool, *DateTimeValue, *DateValue, *TimeValue:
+ // Pointer-to-primitive: nil renders as "null" (the CLI literal that produces it);
+ // non-nil derefs to the pointee's standard representation.
+ rv := reflect.ValueOf(v)
+ if rv.IsNil() {
+ return "null"
+ }
+ return fmt.Sprintf("%v", rv.Elem().Interface())
+
default:
// For complex types, convert to YAML
yamlBytes, err := yaml.MarshalWithOptions(c.value, yaml.Flow(true))
@@ -696,6 +902,15 @@ type SettableInnerField interface {
SetInnerField(string, any)
}
+// InnerFieldSeeder lets an InnerFlag prepare its outer flag's underlying value
+// before dispatching SetInnerField. This is only meaningful for Flag[any] —
+// the codegen output for nullable complex schemas — whose untyped-nil zero
+// value would otherwise have no reflect.Kind for the inner-field switch to
+// dispatch on.
+type InnerFieldSeeder interface {
+ SeedInnerCollection(isArrayOfObjects bool)
+}
+
func (f *Flag[T]) SetInnerField(field string, val any) {
if f.value == nil {
f.value = &cliValue[T]{}
@@ -709,6 +924,33 @@ func (f *Flag[T]) SetInnerField(field string, val any) {
}
}
+// SeedInnerCollection initializes a Flag[any]'s underlying value as an empty
+// map[string]any or []map[string]any so subsequent SetInnerField calls have a
+// dispatchable reflect.Kind. For typed Flag[T] this is a no-op: the type
+// assertion fails and the existing reflect.Kind on the typed-nil zero value
+// already routes correctly.
+func (f *Flag[T]) SeedInnerCollection(isArrayOfObjects bool) {
+ if f.value == nil {
+ f.value = &cliValue[T]{}
+ }
+ cv, ok := f.value.(*cliValue[T])
+ if !ok {
+ return
+ }
+ if reflect.ValueOf(cv.value).Kind() != reflect.Invalid {
+ return
+ }
+ if isArrayOfObjects {
+ if seed, ok := any([]map[string]any{}).(T); ok {
+ cv.value = seed
+ }
+ return
+ }
+ if seed, ok := any(map[string]any{}).(T); ok {
+ cv.value = seed
+ }
+}
+
func (c *cliValue[T]) SetInnerField(field string, val any) {
flagVal := c.value
flagValReflect := reflect.ValueOf(flagVal)
diff --git a/internal/requestflag/requestflag_test.go b/internal/requestflag/requestflag_test.go
index 0e86e07..779bd57 100644
--- a/internal/requestflag/requestflag_test.go
+++ b/internal/requestflag/requestflag_test.go
@@ -1,6 +1,7 @@
package requestflag
import (
+ "encoding/json"
"fmt"
"testing"
"time"
@@ -616,6 +617,178 @@ func TestYamlHandling(t *testing.T) {
})
}
+// TestNullLiteralHandling pins how each Flag[T] type handles the literal value "null"
+// when passed via the CLI. Pointer-typed flags serialize nil as JSON null, which is how
+// nullable body fields (`anyOf: [T, null]` / `{nullable: true}`) let users clear a field
+// via `--foo null`. Non-pointer primitive flags treat "null" as a raw value — these are
+// non-nullable schemas where explicit null has no API semantics anyway.
+func TestNullLiteralHandling(t *testing.T) {
+ t.Parallel()
+
+ assertJSONBody := func(t *testing.T, value any, expected string) {
+ t.Helper()
+ body, err := json.Marshal(map[string]any{"foo": value})
+ assert.NoError(t, err)
+ assert.JSONEq(t, expected, string(body))
+ }
+
+ t.Run("Flag[any] null sends JSON null", func(t *testing.T) {
+ t.Parallel()
+ cv := &cliValue[any]{}
+ assert.NoError(t, cv.Set("null"))
+ assertJSONBody(t, cv.Get(), `{"foo":null}`)
+ })
+
+ t.Run("Flag[string] null is the raw string \"null\"", func(t *testing.T) {
+ t.Parallel()
+ cv := &cliValue[string]{}
+ assert.NoError(t, cv.Set("null"))
+ assertJSONBody(t, cv.Get(), `{"foo":"null"}`)
+ })
+
+ t.Run("Flag[int64] null errors", func(t *testing.T) {
+ t.Parallel()
+ cv := &cliValue[int64]{}
+ assert.Error(t, cv.Set("null"))
+ })
+
+ t.Run("Flag[*string] null sends JSON null", func(t *testing.T) {
+ t.Parallel()
+ cv := &cliValue[*string]{}
+ assert.NoError(t, cv.Set("null"))
+ assertJSONBody(t, cv.Get(), `{"foo":null}`)
+ })
+
+ t.Run("Flag[*string] value sends the string", func(t *testing.T) {
+ t.Parallel()
+ cv := &cliValue[*string]{}
+ assert.NoError(t, cv.Set("1.1"))
+ assertJSONBody(t, cv.Get(), `{"foo":"1.1"}`)
+ })
+
+ t.Run("Flag[*int64] null sends JSON null", func(t *testing.T) {
+ t.Parallel()
+ cv := &cliValue[*int64]{}
+ assert.NoError(t, cv.Set("null"))
+ assertJSONBody(t, cv.Get(), `{"foo":null}`)
+ })
+
+ t.Run("Flag[*int64] value sends the integer", func(t *testing.T) {
+ t.Parallel()
+ cv := &cliValue[*int64]{}
+ assert.NoError(t, cv.Set("42"))
+ assertJSONBody(t, cv.Get(), `{"foo":42}`)
+ })
+
+ t.Run("Flag[*int64] invalid value errors", func(t *testing.T) {
+ t.Parallel()
+ cv := &cliValue[*int64]{}
+ assert.Error(t, cv.Set("not-an-int"))
+ })
+
+ t.Run("Flag[*bool] null sends JSON null", func(t *testing.T) {
+ t.Parallel()
+ cv := &cliValue[*bool]{}
+ assert.NoError(t, cv.Set("null"))
+ assertJSONBody(t, cv.Get(), `{"foo":null}`)
+ })
+
+ t.Run("Flag[*bool] value sends the boolean", func(t *testing.T) {
+ t.Parallel()
+ cv := &cliValue[*bool]{}
+ assert.NoError(t, cv.Set("true"))
+ assertJSONBody(t, cv.Get(), `{"foo":true}`)
+ })
+
+ t.Run("Flag[*float64] null sends JSON null", func(t *testing.T) {
+ t.Parallel()
+ cv := &cliValue[*float64]{}
+ assert.NoError(t, cv.Set("null"))
+ assertJSONBody(t, cv.Get(), `{"foo":null}`)
+ })
+
+ t.Run("Flag[*float64] value sends the float", func(t *testing.T) {
+ t.Parallel()
+ cv := &cliValue[*float64]{}
+ assert.NoError(t, cv.Set("1.5"))
+ assertJSONBody(t, cv.Get(), `{"foo":1.5}`)
+ })
+
+ t.Run("Flag[*float64] invalid value errors", func(t *testing.T) {
+ t.Parallel()
+ cv := &cliValue[*float64]{}
+ assert.Error(t, cv.Set("not-a-float"))
+ })
+
+ t.Run("Flag[*DateValue] null sends JSON null", func(t *testing.T) {
+ t.Parallel()
+ cv := &cliValue[*DateValue]{}
+ assert.NoError(t, cv.Set("null"))
+ assertJSONBody(t, cv.Get(), `{"foo":null}`)
+ })
+
+ t.Run("Flag[*DateValue] value sends the date", func(t *testing.T) {
+ t.Parallel()
+ cv := &cliValue[*DateValue]{}
+ assert.NoError(t, cv.Set("2023-05-15"))
+ assertJSONBody(t, cv.Get(), `{"foo":"2023-05-15"}`)
+ })
+
+ t.Run("Flag[*DateValue] invalid value errors", func(t *testing.T) {
+ t.Parallel()
+ cv := &cliValue[*DateValue]{}
+ assert.Error(t, cv.Set("not-a-date"))
+ })
+
+ t.Run("Flag[*DateTimeValue] null sends JSON null", func(t *testing.T) {
+ t.Parallel()
+ cv := &cliValue[*DateTimeValue]{}
+ assert.NoError(t, cv.Set("null"))
+ assertJSONBody(t, cv.Get(), `{"foo":null}`)
+ })
+
+ t.Run("Flag[*DateTimeValue] value sends the datetime", func(t *testing.T) {
+ t.Parallel()
+ cv := &cliValue[*DateTimeValue]{}
+ assert.NoError(t, cv.Set("2023-05-15T14:30:45Z"))
+ assertJSONBody(t, cv.Get(), `{"foo":"2023-05-15T14:30:45Z"}`)
+ })
+
+ t.Run("Flag[*DateTimeValue] invalid value errors", func(t *testing.T) {
+ t.Parallel()
+ cv := &cliValue[*DateTimeValue]{}
+ assert.Error(t, cv.Set("not-a-datetime"))
+ })
+
+ t.Run("Flag[*TimeValue] null sends JSON null", func(t *testing.T) {
+ t.Parallel()
+ cv := &cliValue[*TimeValue]{}
+ assert.NoError(t, cv.Set("null"))
+ assertJSONBody(t, cv.Get(), `{"foo":null}`)
+ })
+
+ t.Run("Flag[*TimeValue] value sends the time", func(t *testing.T) {
+ t.Parallel()
+ cv := &cliValue[*TimeValue]{}
+ assert.NoError(t, cv.Set("14:30:45"))
+ assertJSONBody(t, cv.Get(), `{"foo":"14:30:45"}`)
+ })
+
+ t.Run("Flag[*TimeValue] invalid value errors", func(t *testing.T) {
+ t.Parallel()
+ cv := &cliValue[*TimeValue]{}
+ assert.Error(t, cv.Set("not-a-time"))
+ })
+
+ // Nullable maps don't need pointer wrapping — a nil map already marshals as JSON null.
+ t.Run("Flag[map[string]any] null sends JSON null", func(t *testing.T) {
+ t.Parallel()
+ cv := &cliValue[map[string]any]{}
+ assert.NoError(t, cv.Set("null"))
+ assertJSONBody(t, cv.Get(), `{"foo":null}`)
+ })
+}
+
func TestFlagTypeNames(t *testing.T) {
t.Parallel()
@@ -646,3 +819,409 @@ func TestFlagTypeNames(t *testing.T) {
})
}
}
+
+// TestInnerFlagDispatchOnUntypedFlag pins inner-flag behavior for `Flag[any]`,
+// which is the codegen output for nullable complex schemas (`anyOf: [T, null]`
+// or `{nullable: true}`). The untyped-nil zero value carries no reflect.Kind,
+// so SetInnerField has nowhere to dispatch the assignment — without explicit
+// help the inner-field value silently drops.
+func TestInnerFlagDispatchOnUntypedFlag(t *testing.T) {
+ t.Parallel()
+
+ t.Run("nullable array of objects appends element from inner flag", func(t *testing.T) {
+ t.Parallel()
+ outer := &Flag[any]{Name: "mcp-server"}
+ assert.NoError(t, outer.PreParse())
+
+ nameFlag := &InnerFlag[string]{
+ Name: "mcp-server.name", InnerField: "name",
+ OuterFlag: outer, OuterIsArrayOfObjects: true,
+ }
+ assert.NoError(t, nameFlag.Set("mcp-server.name", "first"))
+
+ body, err := json.Marshal(map[string]any{"foo": outer.Get()})
+ assert.NoError(t, err)
+ assert.JSONEq(t, `{"foo":[{"name":"first"}]}`, string(body))
+ })
+
+ t.Run("nullable object sets field from inner flag", func(t *testing.T) {
+ t.Parallel()
+ outer := &Flag[any]{Name: "metadata"}
+ assert.NoError(t, outer.PreParse())
+
+ keyFlag := &InnerFlag[string]{
+ Name: "metadata.key", InnerField: "key", OuterFlag: outer,
+ }
+ assert.NoError(t, keyFlag.Set("metadata.key", "value"))
+
+ body, err := json.Marshal(map[string]any{"foo": outer.Get()})
+ assert.NoError(t, err)
+ assert.JSONEq(t, `{"foo":{"key":"value"}}`, string(body))
+ })
+
+ t.Run("multiple inner flags merge into the trailing element", func(t *testing.T) {
+ t.Parallel()
+ outer := &Flag[any]{Name: "mcp-server"}
+ assert.NoError(t, outer.PreParse())
+
+ nameFlag := &InnerFlag[string]{
+ Name: "mcp-server.name", InnerField: "name",
+ OuterFlag: outer, OuterIsArrayOfObjects: true,
+ }
+ urlFlag := &InnerFlag[string]{
+ Name: "mcp-server.url", InnerField: "url",
+ OuterFlag: outer, OuterIsArrayOfObjects: true,
+ }
+ assert.NoError(t, nameFlag.Set("mcp-server.name", "first"))
+ assert.NoError(t, urlFlag.Set("mcp-server.url", "https://example.com"))
+
+ body, err := json.Marshal(map[string]any{"foo": outer.Get()})
+ assert.NoError(t, err)
+ assert.JSONEq(t, `{"foo":[{"name":"first","url":"https://example.com"}]}`, string(body))
+ })
+}
+
+func TestApplyStdinDataToFlags(t *testing.T) {
+ t.Parallel()
+
+ t.Run("sets query path flag from piped data", func(t *testing.T) {
+ t.Parallel()
+
+ flag := &Flag[string]{
+ Name: "account-id",
+ QueryPath: "account_id",
+ }
+ assert.NoError(t, flag.PreParse())
+
+ data := map[string]any{"account_id": "acct_123"}
+ cmd := &cli.Command{Flags: []cli.Flag{flag}}
+ assert.NoError(t, ApplyStdinDataToFlags(cmd, data))
+
+ assert.True(t, flag.IsSet())
+ assert.Equal(t, "acct_123", flag.Get())
+ })
+
+ t.Run("sets header path flag from piped data", func(t *testing.T) {
+ t.Parallel()
+
+ flag := &Flag[string]{
+ Name: "idempotency-key",
+ HeaderPath: "Idempotency-Key",
+ }
+ assert.NoError(t, flag.PreParse())
+
+ data := map[string]any{"Idempotency-Key": "key-xyz"}
+ cmd := &cli.Command{Flags: []cli.Flag{flag}}
+ assert.NoError(t, ApplyStdinDataToFlags(cmd, data))
+
+ assert.True(t, flag.IsSet())
+ assert.Equal(t, "key-xyz", flag.Get())
+ })
+
+ t.Run("does not set body path flag from piped data", func(t *testing.T) {
+ t.Parallel()
+
+ // Body params are handled by the maps.Copy merge in flagOptions, not by ApplyStdinDataToFlags.
+ flag := &Flag[string]{
+ Name: "message",
+ BodyPath: "message",
+ }
+ assert.NoError(t, flag.PreParse())
+
+ data := map[string]any{"message": "hello world"}
+ cmd := &cli.Command{Flags: []cli.Flag{flag}}
+ assert.NoError(t, ApplyStdinDataToFlags(cmd, data))
+
+ assert.False(t, flag.IsSet())
+ })
+
+ t.Run("does not override flag already set via CLI", func(t *testing.T) {
+ t.Parallel()
+
+ flag := &Flag[string]{
+ Name: "account-id",
+ QueryPath: "account_id",
+ }
+ assert.NoError(t, flag.PreParse())
+ assert.NoError(t, flag.Set("account-id", "explicit_value"))
+
+ data := map[string]any{"account_id": "piped_value"}
+ cmd := &cli.Command{Flags: []cli.Flag{flag}}
+ assert.NoError(t, ApplyStdinDataToFlags(cmd, data))
+
+ // The explicitly-set value should win.
+ assert.Equal(t, "explicit_value", flag.Get())
+ })
+
+ t.Run("sets integer query flag from piped data", func(t *testing.T) {
+ t.Parallel()
+
+ flag := &Flag[int64]{
+ Name: "page-size",
+ QueryPath: "page_size",
+ }
+ assert.NoError(t, flag.PreParse())
+
+ data := map[string]any{"page_size": int64(50)}
+ cmd := &cli.Command{Flags: []cli.Flag{flag}}
+ assert.NoError(t, ApplyStdinDataToFlags(cmd, data))
+
+ assert.True(t, flag.IsSet())
+ assert.Equal(t, int64(50), flag.Get())
+ })
+
+ t.Run("sets boolean query flag from piped data", func(t *testing.T) {
+ t.Parallel()
+
+ flag := &Flag[bool]{
+ Name: "include-deleted",
+ QueryPath: "include_deleted",
+ }
+ assert.NoError(t, flag.PreParse())
+
+ data := map[string]any{"include_deleted": true}
+ cmd := &cli.Command{Flags: []cli.Flag{flag}}
+ assert.NoError(t, ApplyStdinDataToFlags(cmd, data))
+
+ assert.True(t, flag.IsSet())
+ assert.Equal(t, true, flag.Get())
+ })
+
+ t.Run("resolves query path flag via data alias", func(t *testing.T) {
+ t.Parallel()
+
+ flag := &Flag[string]{
+ Name: "account-id",
+ QueryPath: "account_id",
+ DataAliases: []string{"accountId", "account"},
+ }
+ assert.NoError(t, flag.PreParse())
+
+ // Use one of the aliases as the key in piped data.
+ data := map[string]any{"accountId": "acct_alias"}
+ cmd := &cli.Command{Flags: []cli.Flag{flag}}
+ assert.NoError(t, ApplyStdinDataToFlags(cmd, data))
+
+ assert.True(t, flag.IsSet())
+ assert.Equal(t, "acct_alias", flag.Get())
+ })
+
+ t.Run("does not set body path flag via data alias", func(t *testing.T) {
+ t.Parallel()
+
+ // Body params are handled by the maps.Copy merge in flagOptions, not by ApplyStdinDataToFlags.
+ flag := &Flag[string]{
+ Name: "user-name",
+ BodyPath: "user_name",
+ DataAliases: []string{"userName", "username"},
+ }
+ assert.NoError(t, flag.PreParse())
+
+ data := map[string]any{"userName": "alice"}
+ cmd := &cli.Command{Flags: []cli.Flag{flag}}
+ assert.NoError(t, ApplyStdinDataToFlags(cmd, data))
+
+ assert.False(t, flag.IsSet())
+ })
+
+ t.Run("ignores flags with no matching key in piped data", func(t *testing.T) {
+ t.Parallel()
+
+ flag := &Flag[string]{
+ Name: "account-id",
+ QueryPath: "account_id",
+ }
+ assert.NoError(t, flag.PreParse())
+
+ data := map[string]any{"other_key": "value"}
+ cmd := &cli.Command{Flags: []cli.Flag{flag}}
+ assert.NoError(t, ApplyStdinDataToFlags(cmd, data))
+
+ assert.False(t, flag.IsSet())
+ })
+
+ t.Run("ignores flags with no path set", func(t *testing.T) {
+ t.Parallel()
+
+ flag := &Flag[string]{
+ Name: "some-flag",
+ // No QueryPath, HeaderPath, or BodyPath
+ }
+ assert.NoError(t, flag.PreParse())
+
+ data := map[string]any{"some-flag": "value"}
+ cmd := &cli.Command{Flags: []cli.Flag{flag}}
+ assert.NoError(t, ApplyStdinDataToFlags(cmd, data))
+
+ assert.False(t, flag.IsSet())
+ })
+
+ t.Run("handles multiple flags from piped data", func(t *testing.T) {
+ t.Parallel()
+
+ accountFlag := &Flag[string]{
+ Name: "account-id",
+ QueryPath: "account_id",
+ }
+ limitFlag := &Flag[int64]{
+ Name: "limit",
+ QueryPath: "limit",
+ }
+ assert.NoError(t, accountFlag.PreParse())
+ assert.NoError(t, limitFlag.PreParse())
+
+ data := map[string]any{
+ "account_id": "acct_abc",
+ "limit": int64(25),
+ }
+ cmd := &cli.Command{Flags: []cli.Flag{accountFlag, limitFlag}}
+ assert.NoError(t, ApplyStdinDataToFlags(cmd, data))
+
+ assert.True(t, accountFlag.IsSet())
+ assert.Equal(t, "acct_abc", accountFlag.Get())
+ assert.True(t, limitFlag.IsSet())
+ assert.Equal(t, int64(25), limitFlag.Get())
+ })
+
+ t.Run("sets inner flag from nested piped data under outer body path", func(t *testing.T) {
+ t.Parallel()
+
+ outer := &Flag[map[string]any]{
+ Name: "address",
+ BodyPath: "address",
+ }
+ assert.NoError(t, outer.PreParse())
+
+ cityInner := &InnerFlag[string]{
+ Name: "address.city",
+ InnerField: "city",
+ OuterFlag: outer,
+ }
+
+ data := map[string]any{
+ "address": map[string]any{"city": "San Francisco"},
+ }
+ cmd := &cli.Command{Flags: []cli.Flag{outer, cityInner}}
+ assert.NoError(t, ApplyStdinDataToFlags(cmd, data))
+
+ // InnerFlag.IsSet() is always false by design; verify the value was written
+ // into the outer flag's underlying map instead.
+ outerVal, ok := outer.Get().(map[string]any)
+ assert.True(t, ok, "expected outer flag value to be map[string]any, got %T", outer.Get())
+ assert.Equal(t, "San Francisco", outerVal["city"])
+ })
+
+ t.Run("sets inner flag via data alias in nested piped data", func(t *testing.T) {
+ t.Parallel()
+
+ outer := &Flag[map[string]any]{
+ Name: "address",
+ BodyPath: "address",
+ }
+ assert.NoError(t, outer.PreParse())
+
+ cityInner := &InnerFlag[string]{
+ Name: "address.city",
+ InnerField: "city",
+ DataAliases: []string{"cityName"},
+ OuterFlag: outer,
+ }
+
+ // Use the alias in piped data.
+ data := map[string]any{
+ "address": map[string]any{"cityName": "Portland"},
+ }
+ cmd := &cli.Command{Flags: []cli.Flag{outer, cityInner}}
+ assert.NoError(t, ApplyStdinDataToFlags(cmd, data))
+
+ // InnerFlag.IsSet() is always false by design; verify the value was written
+ // into the outer flag's underlying map instead.
+ outerVal, ok := outer.Get().(map[string]any)
+ assert.True(t, ok, "expected outer flag value to be map[string]any, got %T", outer.Get())
+ assert.Equal(t, "Portland", outerVal["city"])
+ })
+
+ t.Run("does not set inner flag when outer flag has no body path", func(t *testing.T) {
+ t.Parallel()
+
+ outer := &Flag[map[string]any]{
+ Name: "options",
+ // No BodyPath set
+ }
+ assert.NoError(t, outer.PreParse())
+
+ inner := &InnerFlag[string]{
+ Name: "options.key",
+ InnerField: "key",
+ OuterFlag: outer,
+ }
+
+ data := map[string]any{
+ "options": map[string]any{"key": "value"},
+ }
+ cmd := &cli.Command{Flags: []cli.Flag{outer, inner}}
+ assert.NoError(t, ApplyStdinDataToFlags(cmd, data))
+
+ assert.False(t, inner.IsSet())
+ })
+
+ t.Run("does not set inner flag when piped data has no nested map for outer path", func(t *testing.T) {
+ t.Parallel()
+
+ outer := &Flag[map[string]any]{
+ Name: "address",
+ BodyPath: "address",
+ }
+ assert.NoError(t, outer.PreParse())
+
+ inner := &InnerFlag[string]{
+ Name: "address.city",
+ InnerField: "city",
+ OuterFlag: outer,
+ }
+
+ // The outer body path key is missing from the piped data.
+ data := map[string]any{"other": "value"}
+ cmd := &cli.Command{Flags: []cli.Flag{outer, inner}}
+ assert.NoError(t, ApplyStdinDataToFlags(cmd, data))
+
+ assert.False(t, inner.IsSet())
+ })
+
+ t.Run("canonical path key takes precedence over alias when both are present", func(t *testing.T) {
+ t.Parallel()
+
+ flag := &Flag[string]{
+ Name: "account-id",
+ QueryPath: "account_id",
+ DataAliases: []string{"accountId"},
+ }
+ assert.NoError(t, flag.PreParse())
+
+ // Both canonical and alias present — canonical should win because it's checked first.
+ data := map[string]any{
+ "account_id": "canonical_value",
+ "accountId": "alias_value",
+ }
+ cmd := &cli.Command{Flags: []cli.Flag{flag}}
+ assert.NoError(t, ApplyStdinDataToFlags(cmd, data))
+
+ assert.True(t, flag.IsSet())
+ assert.Equal(t, "canonical_value", flag.Get())
+ })
+
+ t.Run("empty data map does not set any flags", func(t *testing.T) {
+ t.Parallel()
+
+ flag := &Flag[string]{
+ Name: "account-id",
+ QueryPath: "account_id",
+ }
+ assert.NoError(t, flag.PreParse())
+
+ cmd := &cli.Command{Flags: []cli.Flag{flag}}
+ assert.NoError(t, ApplyStdinDataToFlags(cmd, map[string]any{}))
+
+ assert.False(t, flag.IsSet())
+ })
+}
diff --git a/pkg/cmd/agent.go b/pkg/cmd/agent.go
index 03ee71b..bc75542 100644
--- a/pkg/cmd/agent.go
+++ b/pkg/cmd/agent.go
@@ -5,7 +5,6 @@ package cmd
import (
"context"
"fmt"
- "os"
"github.com/agentmail-to/agentmail-cli/internal/apiquery"
"github.com/agentmail-to/agentmail-cli/internal/requestflag"
@@ -32,6 +31,16 @@ var agentSignUp = cli.Command{
Required: true,
BodyPath: "username",
},
+ &requestflag.Flag[*string]{
+ Name: "referrer",
+ Usage: "The channel that drove this sign-up — where the agent or its developer discovered AgentMail\n(e.g. `agent.email`, a partner URL, a campaign tag). Answers \"where did this sign-up come from\".\nMax 2048 characters.",
+ BodyPath: "referrer",
+ },
+ &requestflag.Flag[*string]{
+ Name: "source",
+ Usage: "The SDK, framework, or platform issuing this sign-up (e.g. `agentmail-python`, `agentmail-cli`, `agentmail-mcp`).\nIdentifies the caller — answers \"who is signing up\".\nMax 2048 characters.",
+ BodyPath: "source",
+ },
},
Action: handleAgentSignUp,
HideHelpCommand: true,
@@ -61,8 +70,6 @@ func handleAgentSignUp(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.AgentSignUpParams{}
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -74,6 +81,8 @@ func handleAgentSignUp(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.AgentSignUpParams{}
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Agent.SignUp(ctx, params, options...)
@@ -83,8 +92,15 @@ func handleAgentSignUp(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "agent sign-up", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "agent sign-up",
+ Transform: transform,
+ })
}
func handleAgentVerify(ctx context.Context, cmd *cli.Command) error {
@@ -95,8 +111,6 @@ func handleAgentVerify(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.AgentVerifyParams{}
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -108,6 +122,8 @@ func handleAgentVerify(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.AgentVerifyParams{}
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Agent.Verify(ctx, params, options...)
@@ -117,6 +133,13 @@ func handleAgentVerify(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "agent verify", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "agent verify",
+ Transform: transform,
+ })
}
diff --git a/pkg/cmd/agent_test.go b/pkg/cmd/agent_test.go
index ecea87d..8f744a0 100644
--- a/pkg/cmd/agent_test.go
+++ b/pkg/cmd/agent_test.go
@@ -17,6 +17,8 @@ func TestAgentSignUp(t *testing.T) {
"agent", "sign-up",
"--human-email", "human_email",
"--username", "username",
+ "--referrer", "referrer",
+ "--source", "source",
)
})
@@ -24,7 +26,9 @@ func TestAgentSignUp(t *testing.T) {
// Test piping YAML data over stdin
pipeData := []byte("" +
"human_email: human_email\n" +
- "username: username\n")
+ "username: username\n" +
+ "referrer: referrer\n" +
+ "source: source\n")
mocktest.TestRunMockTestWithPipeAndFlags(
t, pipeData,
"--api-key", "string",
diff --git a/pkg/cmd/apikey.go b/pkg/cmd/apikey.go
index c93f3c8..1f75a1e 100644
--- a/pkg/cmd/apikey.go
+++ b/pkg/cmd/apikey.go
@@ -5,7 +5,6 @@ package cmd
import (
"context"
"fmt"
- "os"
"github.com/agentmail-to/agentmail-cli/internal/apiquery"
"github.com/agentmail-to/agentmail-cli/internal/requestflag"
@@ -20,7 +19,7 @@ var apiKeysCreate = requestflag.WithInnerFlags(cli.Command{
Usage: "**CLI:**",
Suggest: true,
Flags: []cli.Flag{
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "name",
Usage: "Name of api key.",
BodyPath: "name",
@@ -35,177 +34,177 @@ var apiKeysCreate = requestflag.WithInnerFlags(cli.Command{
HideHelpCommand: true,
}, map[string][]requestflag.HasOuterFlag{
"permissions": {
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.api-key-create",
Usage: "Create API keys.",
InnerField: "api_key_create",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.api-key-delete",
Usage: "Delete API keys.",
InnerField: "api_key_delete",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.api-key-read",
Usage: "Read API keys.",
InnerField: "api_key_read",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.domain-create",
Usage: "Create domains.",
InnerField: "domain_create",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.domain-delete",
Usage: "Delete domains.",
InnerField: "domain_delete",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.domain-read",
Usage: "Read domain details.",
InnerField: "domain_read",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.domain-update",
Usage: "Update domains.",
InnerField: "domain_update",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.draft-create",
Usage: "Create drafts.",
InnerField: "draft_create",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.draft-delete",
Usage: "Delete drafts.",
InnerField: "draft_delete",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.draft-read",
Usage: "Read drafts.",
InnerField: "draft_read",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.draft-send",
Usage: "Send drafts.",
InnerField: "draft_send",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.draft-update",
Usage: "Update drafts.",
InnerField: "draft_update",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.inbox-create",
Usage: "Create new inboxes.",
InnerField: "inbox_create",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.inbox-delete",
Usage: "Delete inboxes.",
InnerField: "inbox_delete",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.inbox-read",
Usage: "Read inbox details.",
InnerField: "inbox_read",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.inbox-update",
Usage: "Update inbox settings.",
InnerField: "inbox_update",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.label-blocked-read",
Usage: "Access messages labeled blocked.",
InnerField: "label_blocked_read",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.label-spam-read",
Usage: "Access messages labeled spam.",
InnerField: "label_spam_read",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.label-trash-read",
Usage: "Access messages labeled trash.",
InnerField: "label_trash_read",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.list-entry-create",
Usage: "Create list entries.",
InnerField: "list_entry_create",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.list-entry-delete",
Usage: "Delete list entries.",
InnerField: "list_entry_delete",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.list-entry-read",
Usage: "Read list entries.",
InnerField: "list_entry_read",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.message-read",
Usage: "Read messages.",
InnerField: "message_read",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.message-send",
Usage: "Send messages.",
InnerField: "message_send",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.message-update",
Usage: "Update message labels.",
InnerField: "message_update",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.metrics-read",
Usage: "Read metrics.",
InnerField: "metrics_read",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.pod-create",
Usage: "Create pods.",
InnerField: "pod_create",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.pod-delete",
Usage: "Delete pods.",
InnerField: "pod_delete",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.pod-read",
Usage: "Read pods.",
InnerField: "pod_read",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.thread-delete",
Usage: "Delete threads.",
InnerField: "thread_delete",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.thread-read",
Usage: "Read threads.",
InnerField: "thread_read",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.webhook-create",
Usage: "Create webhooks.",
InnerField: "webhook_create",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.webhook-delete",
Usage: "Delete webhooks.",
InnerField: "webhook_delete",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.webhook-read",
Usage: "Read webhook configurations.",
InnerField: "webhook_read",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.webhook-update",
Usage: "Update webhooks.",
InnerField: "webhook_update",
@@ -218,17 +217,17 @@ var apiKeysList = cli.Command{
Usage: "**CLI:**",
Suggest: true,
Flags: []cli.Flag{
- &requestflag.Flag[any]{
+ &requestflag.Flag[*bool]{
Name: "ascending",
Usage: "Sort in ascending temporal order.",
QueryPath: "ascending",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*int64]{
Name: "limit",
Usage: "Limit of number of items returned.",
QueryPath: "limit",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "page-token",
Usage: "Page token for pagination.",
QueryPath: "page_token",
@@ -244,9 +243,10 @@ var apiKeysDelete = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "api-key-id",
- Usage: "ID of api key.",
- Required: true,
+ Name: "api-key-id",
+ Usage: "ID of api key.",
+ Required: true,
+ PathParam: "api_key_id",
},
},
Action: handleAPIKeysDelete,
@@ -261,8 +261,6 @@ func handleAPIKeysCreate(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.APIKeyNewParams{}
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -274,6 +272,8 @@ func handleAPIKeysCreate(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.APIKeyNewParams{}
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.APIKeys.New(ctx, params, options...)
@@ -283,8 +283,15 @@ func handleAPIKeysCreate(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "api-keys create", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "api-keys create",
+ Transform: transform,
+ })
}
func handleAPIKeysList(ctx context.Context, cmd *cli.Command) error {
@@ -295,8 +302,6 @@ func handleAPIKeysList(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.APIKeyListParams{}
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -308,6 +313,8 @@ func handleAPIKeysList(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.APIKeyListParams{}
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.APIKeys.List(ctx, params, options...)
@@ -317,8 +324,15 @@ func handleAPIKeysList(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "api-keys list", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "api-keys list",
+ Transform: transform,
+ })
}
func handleAPIKeysDelete(ctx context.Context, cmd *cli.Command) error {
diff --git a/pkg/cmd/cmd.go b/pkg/cmd/cmd.go
index bede572..d89cb34 100644
--- a/pkg/cmd/cmd.go
+++ b/pkg/cmd/cmd.go
@@ -73,6 +73,11 @@ func init() {
Name: "transform-error",
Usage: "The GJSON transformation for errors.",
},
+ &cli.BoolFlag{
+ Name: "raw-output",
+ Aliases: []string{"r"},
+ Usage: "If the result is a string, print it without JSON quotes. This can be useful for making output transforms talk to non-JSON-based systems.",
+ },
&requestflag.Flag[string]{
Name: "api-key",
Sources: cli.EnvVars("AGENTMAIL_API_KEY"),
diff --git a/pkg/cmd/cmdutil.go b/pkg/cmd/cmdutil.go
index 75c8b90..644b807 100644
--- a/pkg/cmd/cmdutil.go
+++ b/pkg/cmd/cmdutil.go
@@ -323,21 +323,29 @@ func shouldUseColors(w io.Writer) bool {
return isTerminal(w)
}
-func formatJSON(expectedOutput *os.File, title string, res gjson.Result, format string, transform string) ([]byte, error) {
- if transform != "" {
- transformed := res.Get(transform)
+func formatJSON(res gjson.Result, opts ShowJSONOpts) ([]byte, error) {
+ if opts.Transform != "" {
+ transformed := res.Get(opts.Transform)
if transformed.Exists() {
res = transformed
}
}
- switch strings.ToLower(format) {
+ // Modeled after `jq -r` (`--raw-output`): if the result is a string, print it without JSON quotes so that
+ // it's easier to pipe into other programs.
+ if opts.RawOutput && res.Type == gjson.String {
+ return []byte(res.Str + "\n"), nil
+ }
+ switch strings.ToLower(opts.Format) {
case "auto":
- return formatJSON(expectedOutput, title, res, "json", "")
+ autoOpts := opts
+ autoOpts.Format = "json"
+ autoOpts.Transform = ""
+ return formatJSON(res, autoOpts)
case "pretty":
- return []byte(jsonview.RenderJSON(title, res) + "\n"), nil
+ return []byte(jsonview.RenderJSON(opts.Title, res) + "\n"), nil
case "json":
prettyJSON := pretty.Pretty([]byte(res.Raw))
- if shouldUseColors(expectedOutput) {
+ if shouldUseColors(opts.Stdout) {
return pretty.Color(prettyJSON, pretty.TerminalStyle), nil
} else {
return prettyJSON, nil
@@ -345,7 +353,7 @@ func formatJSON(expectedOutput *os.File, title string, res gjson.Result, format
case "jsonl":
// @ugly is gjson syntax for "no whitespace", so it fits on one line
oneLineJSON := res.Get("@ugly").Raw
- if shouldUseColors(expectedOutput) {
+ if shouldUseColors(opts.Stdout) {
bytes := append(pretty.Color([]byte(oneLineJSON), pretty.TerminalStyle), '\n')
return bytes, nil
} else {
@@ -359,34 +367,67 @@ func formatJSON(expectedOutput *os.File, title string, res gjson.Result, format
if err := json2yaml.Convert(&yaml, input); err != nil {
return nil, err
}
- _, err := expectedOutput.Write([]byte(yaml.String()))
+ _, err := opts.Stdout.Write([]byte(yaml.String()))
return nil, err
default:
- return nil, fmt.Errorf("Invalid format: %s, valid formats are: %s", format, strings.Join(OutputFormats, ", "))
+ return nil, fmt.Errorf("Invalid format: %s, valid formats are: %s", opts.Format, strings.Join(OutputFormats, ", "))
}
}
-// Display JSON to the user in various different formats
-func ShowJSON(out *os.File, title string, res gjson.Result, format string, transform string) error {
- if transform != "" {
- transformed := res.Get(transform)
- if transformed.Exists() {
- res = transformed
- }
+const warningExploreNotSupported = "Warning: Output format 'explore' not supported for non-terminal output; falling back to 'json'\n"
+
+// ShowJSONOpts configures how JSON output is displayed.
+type ShowJSONOpts struct {
+ ExplicitFormat bool // true if the user explicitly passed --format
+ Format string // output format (auto, explore, json, jsonl, pretty, raw, yaml)
+ RawOutput bool // like jq -r: print strings without JSON quotes
+ Stderr io.Writer // stderr for warnings; injectable for testing; defaults to os.Stderr
+ Stdout *os.File // stdout (or pager); injectable for testing; defaults to os.Stdout
+ Title string // display title
+ Transform string // GJSON path to extract before displaying
+}
+
+func (o *ShowJSONOpts) setDefaults() {
+ if o.Stderr == nil {
+ o.Stderr = os.Stderr
+ }
+ if o.Stdout == nil {
+ o.Stdout = os.Stdout
}
+}
+
+// ShowJSON displays a single JSON result to the user.
+func ShowJSON(res gjson.Result, opts ShowJSONOpts) error {
+ opts.setDefaults()
- switch strings.ToLower(format) {
+ switch strings.ToLower(opts.Format) {
case "auto":
- return ShowJSON(out, title, res, "json", "")
+ autoOpts := opts
+ autoOpts.Format = "json"
+ return ShowJSON(res, autoOpts)
case "explore":
- return jsonview.ExploreJSON(title, res)
+ if !isTerminal(opts.Stdout) {
+ if opts.ExplicitFormat {
+ fmt.Fprint(opts.Stderr, warningExploreNotSupported)
+ }
+ jsonOpts := opts
+ jsonOpts.Format = "json"
+ return ShowJSON(res, jsonOpts)
+ }
+ if opts.Transform != "" {
+ transformed := res.Get(opts.Transform)
+ if transformed.Exists() {
+ res = transformed
+ }
+ }
+ return jsonview.ExploreJSON(opts.Title, res)
default:
- bytes, err := formatJSON(out, title, res, format, transform)
+ bytes, err := formatJSON(res, opts)
if err != nil {
return err
}
- _, err = out.Write(bytes)
+ _, err = opts.Stdout.Write(bytes)
return err
}
}
@@ -400,12 +441,18 @@ type hasRawJSON interface {
RawJSON() string
}
-// For an iterator over different value types, display its values to the user in
-// different formats.
-// -1 is used to signal no limit of items to display
-func ShowJSONIterator[T any](stdout *os.File, title string, iter jsonview.Iterator[T], format string, transform string, itemsToDisplay int64) error {
- if format == "explore" {
- return jsonview.ExploreJSONStream(title, iter)
+// ShowJSONIterator displays an iterator of values to the user. Use itemsToDisplay = -1 for no limit.
+func ShowJSONIterator[T any](iter jsonview.Iterator[T], itemsToDisplay int64, opts ShowJSONOpts) error {
+ opts.setDefaults()
+
+ if opts.Format == "explore" {
+ if isTerminal(opts.Stdout) {
+ return jsonview.ExploreJSONStream(opts.Title, iter)
+ }
+ if opts.ExplicitFormat {
+ fmt.Fprint(opts.Stderr, warningExploreNotSupported)
+ }
+ opts.Format = "json"
}
terminalWidth, terminalHeight, err := term.GetSize(os.Stdout.Fd())
@@ -431,7 +478,7 @@ func ShowJSONIterator[T any](stdout *os.File, title string, iter jsonview.Iterat
}
obj = gjson.ParseBytes(jsonData)
}
- json, err := formatJSON(stdout, title, obj, format, transform)
+ json, err := formatJSON(obj, opts)
if err != nil {
return err
}
@@ -448,7 +495,7 @@ func ShowJSONIterator[T any](stdout *os.File, title string, iter jsonview.Iterat
}
if !usePager {
- _, err := stdout.Write(output)
+ _, err := opts.Stdout.Write(output)
if err != nil {
return err
}
@@ -456,13 +503,15 @@ func ShowJSONIterator[T any](stdout *os.File, title string, iter jsonview.Iterat
return iter.Err()
}
- return streamOutput(title, func(pager *os.File) error {
- // Write the output we used during the initial terminal size computation
+ return streamOutput(opts.Title, func(pager *os.File) error {
_, err := pager.Write(output)
if err != nil {
return err
}
+ pagerOpts := opts
+ pagerOpts.Stdout = pager
+
for iter.Next() {
if itemsToDisplay == 0 {
break
@@ -478,7 +527,7 @@ func ShowJSONIterator[T any](stdout *os.File, title string, iter jsonview.Iterat
}
obj = gjson.ParseBytes(jsonData)
}
- if err := ShowJSON(pager, title, obj, format, transform); err != nil {
+ if err := ShowJSON(obj, pagerOpts); err != nil {
return err
}
itemsToDisplay -= 1
diff --git a/pkg/cmd/cmdutil_test.go b/pkg/cmd/cmdutil_test.go
index 66e3b07..2388ce6 100644
--- a/pkg/cmd/cmdutil_test.go
+++ b/pkg/cmd/cmdutil_test.go
@@ -159,7 +159,7 @@ func TestFormatJSON(t *testing.T) {
t.Parallel()
res := gjson.Parse(`{"id":"abc123","name":"test"}`)
- formatted, err := formatJSON(os.Stdout, "test", res, "raw", "id")
+ formatted, err := formatJSON(res, ShowJSONOpts{Format: "raw", Stdout: os.Stdout, Transform: "id"})
require.NoError(t, err)
require.Equal(t, `"abc123"`+"\n", string(formatted))
})
@@ -168,7 +168,7 @@ func TestFormatJSON(t *testing.T) {
t.Parallel()
res := gjson.Parse(`{"id":"abc123","name":"test"}`)
- formatted, err := formatJSON(os.Stdout, "test", res, "raw", "")
+ formatted, err := formatJSON(res, ShowJSONOpts{Format: "raw", Stdout: os.Stdout})
require.NoError(t, err)
require.Equal(t, `{"id":"abc123","name":"test"}`+"\n", string(formatted))
})
@@ -177,7 +177,7 @@ func TestFormatJSON(t *testing.T) {
t.Parallel()
res := gjson.Parse(`{"data":{"items":[1,2,3]}}`)
- formatted, err := formatJSON(os.Stdout, "test", res, "raw", "data.items")
+ formatted, err := formatJSON(res, ShowJSONOpts{Format: "raw", Stdout: os.Stdout, Transform: "data.items"})
require.NoError(t, err)
require.Equal(t, "[1,2,3]\n", string(formatted))
})
@@ -186,11 +186,40 @@ func TestFormatJSON(t *testing.T) {
t.Parallel()
res := gjson.Parse(`{"id":"abc123"}`)
- formatted, err := formatJSON(os.Stdout, "test", res, "raw", "missing")
+ formatted, err := formatJSON(res, ShowJSONOpts{Format: "raw", Stdout: os.Stdout, Transform: "missing"})
require.NoError(t, err)
// Transform path doesn't exist, so original result is returned
require.Equal(t, `{"id":"abc123"}`+"\n", string(formatted))
})
+
+ t.Run("RawOutputString", func(t *testing.T) {
+ t.Parallel()
+
+ res := gjson.Parse(`{"id":"abc123","name":"test"}`)
+ formatted, err := formatJSON(res, ShowJSONOpts{Format: "json", Stdout: os.Stdout, Transform: "id", RawOutput: true})
+ require.NoError(t, err)
+ require.Equal(t, "abc123\n", string(formatted))
+ })
+
+ t.Run("RawOutputNonString", func(t *testing.T) {
+ t.Parallel()
+
+ // --raw-output has no effect on non-string values
+ res := gjson.Parse(`{"count":42}`)
+ formatted, err := formatJSON(res, ShowJSONOpts{Format: "raw", Stdout: os.Stdout, Transform: "count", RawOutput: true})
+ require.NoError(t, err)
+ require.Equal(t, "42\n", string(formatted))
+ })
+
+ t.Run("RawOutputObject", func(t *testing.T) {
+ t.Parallel()
+
+ // --raw-output has no effect on objects
+ res := gjson.Parse(`{"nested":{"a":1}}`)
+ formatted, err := formatJSON(res, ShowJSONOpts{Format: "raw", Stdout: os.Stdout, Transform: "nested", RawOutput: true})
+ require.NoError(t, err)
+ require.Equal(t, `{"a":1}`+"\n", string(formatted))
+ })
}
func TestShowJSONIterator(t *testing.T) {
@@ -231,6 +260,89 @@ func TestShowJSONIterator(t *testing.T) {
})
}
+func TestExploreFallback(t *testing.T) {
+ t.Parallel()
+
+ t.Run("ShowJSONFallsBackToJsonOnNonTTY", func(t *testing.T) {
+ t.Parallel()
+
+ // os.Pipe() produces a *os.File that isn't a terminal, so explore should fall back.
+ r, w, err := os.Pipe()
+ require.NoError(t, err)
+ defer r.Close()
+
+ var stderr bytes.Buffer
+ res := gjson.Parse(`{"id":"abc"}`)
+ err = ShowJSON(res, ShowJSONOpts{
+ Format: "explore",
+ Stderr: &stderr,
+ Stdout: w,
+ Title: "test",
+ })
+ w.Close()
+ require.NoError(t, err)
+
+ var buf bytes.Buffer
+ _, _ = buf.ReadFrom(r)
+ assert.Contains(t, buf.String(), `"id"`)
+ assert.Contains(t, buf.String(), `"abc"`)
+ })
+
+ t.Run("ShowJSONIteratorFallsBackToJsonOnNonTTY", func(t *testing.T) {
+ t.Parallel()
+
+ iter := &sliceIterator[map[string]any]{items: []map[string]any{
+ {"id": "abc"},
+ }}
+ captured := captureShowJSONIterator(t, iter, "explore", "", -1)
+ assert.Contains(t, captured, `"id"`)
+ assert.Contains(t, captured, `"abc"`)
+ })
+
+ t.Run("ShowJSONWarnsWhenExplicitFormatOnNonTTY", func(t *testing.T) {
+ t.Parallel()
+
+ r, w, err := os.Pipe()
+ require.NoError(t, err)
+ defer r.Close()
+
+ var stderr bytes.Buffer
+ res := gjson.Parse(`{"id":"abc"}`)
+ err = ShowJSON(res, ShowJSONOpts{
+ ExplicitFormat: true,
+ Format: "explore",
+ Stderr: &stderr,
+ Stdout: w,
+ Title: "test",
+ })
+ w.Close()
+ require.NoError(t, err)
+
+ assert.Equal(t, warningExploreNotSupported, stderr.String())
+ })
+
+ t.Run("ShowJSONSilentWhenDefaultFormatOnNonTTY", func(t *testing.T) {
+ t.Parallel()
+
+ r, w, err := os.Pipe()
+ require.NoError(t, err)
+ defer r.Close()
+
+ var stderr bytes.Buffer
+ res := gjson.Parse(`{"id":"abc"}`)
+ err = ShowJSON(res, ShowJSONOpts{
+ Format: "explore",
+ Stderr: &stderr,
+ Stdout: w,
+ Title: "test",
+ })
+ w.Close()
+ require.NoError(t, err)
+
+ assert.Empty(t, stderr.String(), "no warning expected when format was not explicit")
+ })
+}
+
// sliceIterator is a simple iterator over a slice for testing.
type sliceIterator[T any] struct {
index int
@@ -260,7 +372,13 @@ func captureShowJSONIterator[T any](t *testing.T, iter jsonview.Iterator[T], for
require.NoError(t, err)
defer r.Close()
- err = ShowJSONIterator(w, "test", iter, format, transform, itemsToDisplay)
+ err = ShowJSONIterator(iter, itemsToDisplay, ShowJSONOpts{
+ Format: format,
+ Stderr: io.Discard,
+ Stdout: w,
+ Title: "test",
+ Transform: transform,
+ })
w.Close()
require.NoError(t, err)
diff --git a/pkg/cmd/domain.go b/pkg/cmd/domain.go
index df45bb1..fe69bcd 100644
--- a/pkg/cmd/domain.go
+++ b/pkg/cmd/domain.go
@@ -5,7 +5,6 @@ package cmd
import (
"context"
"fmt"
- "os"
"github.com/agentmail-to/agentmail-cli/internal/apiquery"
"github.com/agentmail-to/agentmail-cli/internal/requestflag"
@@ -43,11 +42,12 @@ var domainsUpdate = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "domain-id",
- Usage: "The ID of the domain.",
- Required: true,
+ Name: "domain-id",
+ Usage: "The ID of the domain.",
+ Required: true,
+ PathParam: "domain_id",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*bool]{
Name: "feedback-enabled",
Usage: "Bounce and complaint notifications are sent to your inboxes.",
BodyPath: "feedback_enabled",
@@ -62,17 +62,17 @@ var domainsList = cli.Command{
Usage: "**CLI:**",
Suggest: true,
Flags: []cli.Flag{
- &requestflag.Flag[any]{
+ &requestflag.Flag[*bool]{
Name: "ascending",
Usage: "Sort in ascending temporal order.",
QueryPath: "ascending",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*int64]{
Name: "limit",
Usage: "Limit of number of items returned.",
QueryPath: "limit",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "page-token",
Usage: "Page token for pagination.",
QueryPath: "page_token",
@@ -88,9 +88,10 @@ var domainsDelete = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "domain-id",
- Usage: "The ID of the domain.",
- Required: true,
+ Name: "domain-id",
+ Usage: "The ID of the domain.",
+ Required: true,
+ PathParam: "domain_id",
},
},
Action: handleDomainsDelete,
@@ -103,9 +104,10 @@ var domainsGet = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "domain-id",
- Usage: "The ID of the domain.",
- Required: true,
+ Name: "domain-id",
+ Usage: "The ID of the domain.",
+ Required: true,
+ PathParam: "domain_id",
},
},
Action: handleDomainsGet,
@@ -118,9 +120,10 @@ var domainsGetZoneFile = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "domain-id",
- Usage: "The ID of the domain.",
- Required: true,
+ Name: "domain-id",
+ Usage: "The ID of the domain.",
+ Required: true,
+ PathParam: "domain_id",
},
},
Action: handleDomainsGetZoneFile,
@@ -133,9 +136,10 @@ var domainsVerify = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "domain-id",
- Usage: "The ID of the domain.",
- Required: true,
+ Name: "domain-id",
+ Usage: "The ID of the domain.",
+ Required: true,
+ PathParam: "domain_id",
},
},
Action: handleDomainsVerify,
@@ -150,8 +154,6 @@ func handleDomainsCreate(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.DomainNewParams{}
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -163,6 +165,8 @@ func handleDomainsCreate(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.DomainNewParams{}
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Domains.New(ctx, params, options...)
@@ -172,8 +176,15 @@ func handleDomainsCreate(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "domains create", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "domains create",
+ Transform: transform,
+ })
}
func handleDomainsUpdate(ctx context.Context, cmd *cli.Command) error {
@@ -187,8 +198,6 @@ func handleDomainsUpdate(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.DomainUpdateParams{}
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -200,6 +209,8 @@ func handleDomainsUpdate(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.DomainUpdateParams{}
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Domains.Update(
@@ -214,8 +225,15 @@ func handleDomainsUpdate(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "domains update", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "domains update",
+ Transform: transform,
+ })
}
func handleDomainsList(ctx context.Context, cmd *cli.Command) error {
@@ -226,8 +244,6 @@ func handleDomainsList(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.DomainListParams{}
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -239,6 +255,8 @@ func handleDomainsList(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.DomainListParams{}
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Domains.List(ctx, params, options...)
@@ -248,8 +266,15 @@ func handleDomainsList(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "domains list", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "domains list",
+ Transform: transform,
+ })
}
func handleDomainsDelete(ctx context.Context, cmd *cli.Command) error {
@@ -308,8 +333,15 @@ func handleDomainsGet(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "domains get", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "domains get",
+ Transform: transform,
+ })
}
func handleDomainsGetZoneFile(ctx context.Context, cmd *cli.Command) error {
diff --git a/pkg/cmd/draft.go b/pkg/cmd/draft.go
index e97079a..b105d8a 100644
--- a/pkg/cmd/draft.go
+++ b/pkg/cmd/draft.go
@@ -5,7 +5,6 @@ package cmd
import (
"context"
"fmt"
- "os"
"github.com/agentmail-to/agentmail-cli/internal/apiquery"
"github.com/agentmail-to/agentmail-cli/internal/requestflag"
@@ -25,7 +24,7 @@ var draftsList = cli.Command{
Usage: "Timestamp after which to filter by.",
QueryPath: "after",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*bool]{
Name: "ascending",
Usage: "Sort in ascending temporal order.",
QueryPath: "ascending",
@@ -40,12 +39,12 @@ var draftsList = cli.Command{
Usage: "Labels to filter by.",
QueryPath: "labels",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*int64]{
Name: "limit",
Usage: "Limit of number of items returned.",
QueryPath: "limit",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "page-token",
Usage: "Page token for pagination.",
QueryPath: "page_token",
@@ -61,9 +60,10 @@ var draftsGet = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "draft-id",
- Usage: "ID of draft.",
- Required: true,
+ Name: "draft-id",
+ Usage: "ID of draft.",
+ Required: true,
+ PathParam: "draft_id",
},
},
Action: handleDraftsGet,
@@ -76,14 +76,16 @@ var draftsGetAttachment = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "draft-id",
- Usage: "ID of draft.",
- Required: true,
+ Name: "draft-id",
+ Usage: "ID of draft.",
+ Required: true,
+ PathParam: "draft_id",
},
&requestflag.Flag[string]{
- Name: "attachment-id",
- Usage: "ID of attachment.",
- Required: true,
+ Name: "attachment-id",
+ Usage: "ID of attachment.",
+ Required: true,
+ PathParam: "attachment_id",
},
},
Action: handleDraftsGetAttachment,
@@ -98,8 +100,6 @@ func handleDraftsList(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.DraftListParams{}
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -111,6 +111,8 @@ func handleDraftsList(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.DraftListParams{}
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Drafts.List(ctx, params, options...)
@@ -120,8 +122,15 @@ func handleDraftsList(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "drafts list", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "drafts list",
+ Transform: transform,
+ })
}
func handleDraftsGet(ctx context.Context, cmd *cli.Command) error {
@@ -155,8 +164,15 @@ func handleDraftsGet(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "drafts get", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "drafts get",
+ Transform: transform,
+ })
}
func handleDraftsGetAttachment(ctx context.Context, cmd *cli.Command) error {
@@ -170,10 +186,6 @@ func handleDraftsGetAttachment(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.DraftGetAttachmentParams{
- DraftID: cmd.Value("draft-id").(string),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -185,6 +197,10 @@ func handleDraftsGetAttachment(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.DraftGetAttachmentParams{
+ DraftID: cmd.Value("draft-id").(string),
+ }
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Drafts.GetAttachment(
@@ -199,6 +215,13 @@ func handleDraftsGetAttachment(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "drafts get-attachment", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "drafts get-attachment",
+ Transform: transform,
+ })
}
diff --git a/pkg/cmd/flagoptions.go b/pkg/cmd/flagoptions.go
index 0d94ce4..db43a15 100644
--- a/pkg/cmd/flagoptions.go
+++ b/pkg/cmd/flagoptions.go
@@ -7,9 +7,11 @@ import (
"fmt"
"io"
"maps"
+ "mime"
"mime/multipart"
"net/http"
"os"
+ "path/filepath"
"reflect"
"strings"
"unicode/utf8"
@@ -36,7 +38,14 @@ const (
type FileEmbedStyle int
const (
+ // EmbedText reads referenced files fully into memory and substitutes the file's contents back into the
+ // value as a string. Binary files are base64-encoded. Used for JSON request bodies and for headers and
+ // query parameters, where the file contents need to be serialized inline.
EmbedText FileEmbedStyle = iota
+
+ // EmbedIOReader replaces file references with an io.Reader that streams the file's contents. Used for
+ // `multipart/form-data` and `application/octet-stream` request bodies, where files are uploaded as binary
+ // parts rather than embedded into a text value.
EmbedIOReader
)
@@ -142,6 +151,20 @@ func embedFilesValue(v reflect.Value, embedStyle FileEmbedStyle, stdin *onceStdi
if s == "" {
return v, nil
}
+ if embedStyle == EmbedIOReader {
+ if isStdinPath(s) {
+ r, err := stdin.read()
+ if err != nil {
+ return v, err
+ }
+ return reflect.ValueOf(io.NopCloser(r)), nil
+ }
+ upload, err := openFileUpload(s)
+ if err != nil {
+ return v, err
+ }
+ return reflect.ValueOf(upload), nil
+ }
if isStdinPath(s) {
content, err := stdin.readAll()
if err != nil {
@@ -250,7 +273,7 @@ func embedFilesValue(v reflect.Value, embedStyle FileEmbedStyle, stdin *onceStdi
return reflect.ValueOf(io.NopCloser(r)), nil
}
- file, err := os.Open(filename)
+ upload, err := openFileUpload(filename)
if err != nil {
if !expectsFile {
// For strings that start with "@" and don't look like a filename, return the string
@@ -258,7 +281,7 @@ func embedFilesValue(v reflect.Value, embedStyle FileEmbedStyle, stdin *onceStdi
}
return v, err
}
- return reflect.ValueOf(file), nil
+ return reflect.ValueOf(upload), nil
}
}
return v, nil
@@ -309,8 +332,14 @@ func flagOptions(
requestContents := requestflag.ExtractRequestContents(cmd)
+ // Translate inner-field aliases in YAML values that came from flags (e.g.
+ // `--parent '{"alias": val}'` resolving to the canonical inner field).
+ if bodyMap, ok := requestContents.Body.(map[string]any); ok {
+ applyDataAliases(cmd, bodyMap)
+ }
+
stdinConsumedByPipe := false
- if (bodyType == MultipartFormEncoded || bodyType == ApplicationJSON) && !ignoreStdin && isInputPiped() {
+ if bodyType != ApplicationOctetStream && !ignoreStdin && isInputPiped() {
pipeData, err := io.ReadAll(os.Stdin)
if err != nil {
return nil, err
@@ -323,16 +352,46 @@ func flagOptions(
return nil, fmt.Errorf("Failed to parse piped data as YAML/JSON:\n%w", err)
}
if bodyMap, ok := bodyData.(map[string]any); ok {
- if flagMap, ok := requestContents.Body.(map[string]any); ok {
- maps.Copy(bodyMap, flagMap)
- requestContents.Body = bodyMap
+ applyDataAliases(cmd, bodyMap)
+ // Apply any matching keys from the piped data to path, query, and header flags
+ // that have not already been set via the command line.
+ if err := requestflag.ApplyStdinDataToFlags(cmd, bodyMap); err != nil {
+ return nil, err
+ }
+ // Re-extract request contents now that flags may have been updated.
+ requestContents = requestflag.ExtractRequestContents(cmd)
+ // Remove keys that were consumed as query, header, or path params so they
+ // don't also leak into the request body via the maps.Copy merge below.
+ // We delete both the canonical key and any aliases since the user may have
+ // piped data using an alias name rather than the canonical API name.
+ for _, flag := range cmd.Flags {
+ inReq, ok := flag.(requestflag.InRequest)
+ if !ok || !flag.IsSet() {
+ continue
+ }
+ if inReq.GetQueryPath() != "" || inReq.GetHeaderPath() != "" || inReq.GetPathParam() != "" {
+ delete(bodyMap, inReq.GetQueryPath())
+ delete(bodyMap, inReq.GetHeaderPath())
+ delete(bodyMap, inReq.GetPathParam())
+ for _, alias := range inReq.GetDataAliases() {
+ delete(bodyMap, alias)
+ }
+ }
+ }
+ if bodyType != EmptyBody {
+ if flagMap, ok := requestContents.Body.(map[string]any); ok {
+ maps.Copy(bodyMap, flagMap)
+ requestContents.Body = bodyMap
+ } else {
+ bodyData = requestContents.Body
+ }
+ }
+ } else if bodyType != EmptyBody {
+ if flagMap, ok := requestContents.Body.(map[string]any); ok && len(flagMap) > 0 {
+ return nil, fmt.Errorf("Cannot merge flags with a body that is not a map: %v", bodyData)
} else {
- bodyData = requestContents.Body
+ requestContents.Body = bodyData
}
- } else if flagMap, ok := requestContents.Body.(map[string]any); ok && len(flagMap) > 0 {
- return nil, fmt.Errorf("Cannot merge flags with a body that is not a map: %v", bodyData)
- } else {
- requestContents.Body = bodyData
}
}
}
@@ -340,7 +399,6 @@ func flagOptions(
if missingFlags := requestflag.GetMissingRequiredFlags(cmd, requestContents.Body); len(missingFlags) > 0 {
if len(missingFlags) == 1 {
return nil, fmt.Errorf("Required flag %q not set\nRun '%s --help' for usage information", missingFlags[0].Names()[0], cmd.FullName())
-
} else {
names := []string{}
for _, flag := range missingFlags {
@@ -485,6 +543,84 @@ func flagOptions(
// as a file path without needing the "@" prefix.
type FilePathValue string
+// fileUpload wraps an io.Reader with filename and content-type metadata for
+// use as a multipart form part. The apiform encoder detects the Filename and
+// ContentType methods and uses them to populate the Content-Disposition
+// filename and the Content-Type header on the part.
+type fileUpload struct {
+ io.Reader // apiform checks for reader and reads its contents during encode
+ filename string
+ contentType string
+}
+
+func (f fileUpload) Filename() string { return f.filename }
+func (f fileUpload) ContentType() string { return f.contentType }
+func (f fileUpload) Close() error {
+ if c, ok := f.Reader.(io.Closer); ok {
+ return c.Close()
+ }
+ return nil
+}
+
+// openFileUpload opens the file at path and returns a fileUpload whose filename
+// is the path's basename and whose content type is derived from the file
+// extension (falling back to application/octet-stream when unknown).
+func openFileUpload(path string) (fileUpload, error) {
+ file, err := os.Open(path)
+ if err != nil {
+ return fileUpload{}, err
+ }
+ contentType := mime.TypeByExtension(filepath.Ext(path))
+ if contentType == "" {
+ contentType = "application/octet-stream"
+ }
+ return fileUpload{
+ Reader: file,
+ filename: filepath.Base(path),
+ contentType: contentType,
+ }, nil
+}
+
+// applyDataAliases rewrites keys in a body map based on flag `DataAliases` metadata. For top-level flags,
+// `{alias: value}` becomes `{canonical: value}`. For inner flags (those registered under an outer flag
+// via WithInnerFlags), the alias translation is also applied to the nested map under the outer flag's
+// body path, so values like `--parent '{"alias": val}'` resolve to the canonical inner field name.
+func applyDataAliases(cmd *cli.Command, bodyMap map[string]any) {
+ for _, flag := range cmd.Flags {
+ // Inner flags: rewrite aliases inside the nested map under the outer flag's body path.
+ if inner, ok := flag.(requestflag.HasOuterFlag); ok {
+ outer, outerOk := inner.GetOuterFlag().(requestflag.InRequest)
+ if !outerOk {
+ continue
+ }
+ if nested, ok := bodyMap[outer.GetBodyPath()].(map[string]any); ok && inner.GetInnerField() != "" {
+ rewriteAliases(nested, inner.GetInnerField(), inner.GetDataAliases())
+ }
+ continue
+ }
+ // Top-level flags: rewrite aliases in the body map.
+ if inReq, ok := flag.(requestflag.InRequest); ok && inReq.GetBodyPath() != "" {
+ rewriteAliases(bodyMap, inReq.GetBodyPath(), inReq.GetDataAliases())
+ }
+ }
+}
+
+// rewriteAliases replaces each alias key in m with the canonical key, preserving the value. The
+// "canonical" key is the name the API itself expects (the OpenAPI property/field name) — e.g. for
+// a top-level flag, the parameter's BodyPath; for an inner flag, the inner field name. Aliases are
+// the user-facing alternate names declared via x-stainless-cli-data-alias.
+func rewriteAliases(m map[string]any, canonical string, aliases []string) {
+ for _, alias := range aliases {
+ if alias == "" || alias == canonical {
+ continue
+ }
+ if val, exists := m[alias]; exists {
+ m[canonical] = val
+ delete(m, alias)
+ }
+ }
+}
+
// wrapFileInputValues replaces string values for FileInput flags (type: string, format: binary) with
// FilePathValue sentinel values. embedFilesValue recognizes FilePathValue and reads the file contents
// directly, so the user doesn't need to type the "@" prefix. This handles both values set via explicit
diff --git a/pkg/cmd/flagoptions_test.go b/pkg/cmd/flagoptions_test.go
index 039b9ff..00734ca 100644
--- a/pkg/cmd/flagoptions_test.go
+++ b/pkg/cmd/flagoptions_test.go
@@ -8,7 +8,6 @@ import (
"strings"
"testing"
- "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -31,7 +30,7 @@ func TestIsUTF8TextFile(t *testing.T) {
}
for _, tt := range tests {
- assert.Equal(t, tt.expected, isUTF8TextFile(tt.content))
+ require.Equal(t, tt.expected, isUTF8TextFile(tt.content))
}
}
@@ -226,10 +225,10 @@ func TestEmbedFiles(t *testing.T) {
got, err := embedFiles(tt.input, EmbedText, nil)
if tt.wantErr {
- assert.Error(t, err)
+ require.Error(t, err)
} else {
require.NoError(t, err)
- assert.Equal(t, tt.want, got)
+ require.Equal(t, tt.want, got)
}
})
@@ -238,7 +237,7 @@ func TestEmbedFiles(t *testing.T) {
_, err := embedFiles(tt.input, EmbedIOReader, nil)
if tt.wantErr {
- assert.Error(t, err)
+ require.Error(t, err)
} else {
require.NoError(t, err)
}
@@ -333,6 +332,56 @@ func TestEmbedFilesStdin(t *testing.T) {
})
}
+// TestEmbedFilesUploadMetadata verifies that EmbedIOReader mode wraps file readers with filename and
+// content-type metadata so the multipart encoder populates `Content-Disposition` and `Content-Type` headers.
+func TestEmbedFilesUploadMetadata(t *testing.T) {
+ t.Parallel()
+
+ tmpDir := t.TempDir()
+ writeTestFile(t, tmpDir, "hello.txt", "hi")
+ writeTestFile(t, tmpDir, "page.html", "")
+ writeTestFile(t, tmpDir, "blob.bin", "\x00\x01")
+
+ cases := []struct {
+ basename string
+ wantContentType string
+ }{
+ {"hello.txt", "text/plain; charset=utf-8"},
+ {"page.html", "text/html; charset=utf-8"},
+ {"blob.bin", "application/octet-stream"},
+ }
+
+ for _, tc := range cases {
+ t.Run("AtPrefix_"+tc.basename, func(t *testing.T) {
+ t.Parallel()
+
+ path := filepath.Join(tmpDir, tc.basename)
+ withEmbedded, err := embedFiles(map[string]any{"file": "@" + path}, EmbedIOReader, nil)
+ require.NoError(t, err)
+
+ upload, ok := withEmbedded.(map[string]any)["file"].(fileUpload)
+ require.True(t, ok, "expected fileUpload, got %T", withEmbedded.(map[string]any)["file"])
+ require.Equal(t, tc.basename, upload.Filename())
+ require.Equal(t, upload.ContentType(), tc.wantContentType)
+ require.NoError(t, upload.Close())
+ })
+
+ t.Run("FilePathValue_"+tc.basename, func(t *testing.T) {
+ t.Parallel()
+
+ path := filepath.Join(tmpDir, tc.basename)
+ withEmbedded, err := embedFiles(map[string]any{"file": FilePathValue(path)}, EmbedIOReader, nil)
+ require.NoError(t, err)
+
+ upload, ok := withEmbedded.(map[string]any)["file"].(fileUpload)
+ require.True(t, ok, "expected fileUpload, got %T", withEmbedded.(map[string]any)["file"])
+ require.Equal(t, tc.basename, upload.Filename())
+ require.Equal(t, upload.ContentType(), tc.wantContentType)
+ require.NoError(t, upload.Close())
+ })
+ }
+}
+
func writeTestFile(t *testing.T, dir, filename, content string) {
t.Helper()
diff --git a/pkg/cmd/inbox.go b/pkg/cmd/inbox.go
index 5ef6d17..c6956ad 100644
--- a/pkg/cmd/inbox.go
+++ b/pkg/cmd/inbox.go
@@ -5,7 +5,6 @@ package cmd
import (
"context"
"fmt"
- "os"
"github.com/agentmail-to/agentmail-cli/internal/apiquery"
"github.com/agentmail-to/agentmail-cli/internal/requestflag"
@@ -20,22 +19,22 @@ var inboxesCreate = cli.Command{
Usage: "**CLI:**",
Suggest: true,
Flags: []cli.Flag{
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "client-id",
Usage: "Client ID of inbox.",
BodyPath: "client_id",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "display-name",
Usage: "Display name: `Display Name `.",
BodyPath: "display_name",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "domain",
Usage: "Domain of address. Must be verified domain. Defaults to `agentmail.to`.",
BodyPath: "domain",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "username",
Usage: "Username of address. Randomly generated if not specified.",
BodyPath: "username",
@@ -51,9 +50,10 @@ var inboxesUpdate = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "inbox-id",
- Usage: "The ID of the inbox.",
- Required: true,
+ Name: "inbox-id",
+ Usage: "The ID of the inbox.",
+ Required: true,
+ PathParam: "inbox_id",
},
&requestflag.Flag[string]{
Name: "display-name",
@@ -71,17 +71,17 @@ var inboxesList = cli.Command{
Usage: "**CLI:**",
Suggest: true,
Flags: []cli.Flag{
- &requestflag.Flag[any]{
+ &requestflag.Flag[*bool]{
Name: "ascending",
Usage: "Sort in ascending temporal order.",
QueryPath: "ascending",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*int64]{
Name: "limit",
Usage: "Limit of number of items returned.",
QueryPath: "limit",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "page-token",
Usage: "Page token for pagination.",
QueryPath: "page_token",
@@ -97,9 +97,10 @@ var inboxesDelete = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "inbox-id",
- Usage: "The ID of the inbox.",
- Required: true,
+ Name: "inbox-id",
+ Usage: "The ID of the inbox.",
+ Required: true,
+ PathParam: "inbox_id",
},
},
Action: handleInboxesDelete,
@@ -112,9 +113,10 @@ var inboxesGet = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "inbox-id",
- Usage: "The ID of the inbox.",
- Required: true,
+ Name: "inbox-id",
+ Usage: "The ID of the inbox.",
+ Required: true,
+ PathParam: "inbox_id",
},
},
Action: handleInboxesGet,
@@ -127,11 +129,12 @@ var inboxesListMetrics = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "inbox-id",
- Usage: "The ID of the inbox.",
- Required: true,
+ Name: "inbox-id",
+ Usage: "The ID of the inbox.",
+ Required: true,
+ PathParam: "inbox_id",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*bool]{
Name: "descending",
Usage: "Sort in descending order.",
QueryPath: "descending",
@@ -146,12 +149,12 @@ var inboxesListMetrics = cli.Command{
Usage: "List of metric event types to query.",
QueryPath: "event_types",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*int64]{
Name: "limit",
Usage: "Limit on number of buckets to return.",
QueryPath: "limit",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "period",
Usage: "Period in number of seconds for the query.",
QueryPath: "period",
@@ -174,8 +177,6 @@ func handleInboxesCreate(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.InboxNewParams{}
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -187,6 +188,8 @@ func handleInboxesCreate(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.InboxNewParams{}
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Inboxes.New(ctx, params, options...)
@@ -196,8 +199,15 @@ func handleInboxesCreate(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "inboxes create", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "inboxes create",
+ Transform: transform,
+ })
}
func handleInboxesUpdate(ctx context.Context, cmd *cli.Command) error {
@@ -211,8 +221,6 @@ func handleInboxesUpdate(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.InboxUpdateParams{}
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -224,6 +232,8 @@ func handleInboxesUpdate(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.InboxUpdateParams{}
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Inboxes.Update(
@@ -238,8 +248,15 @@ func handleInboxesUpdate(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "inboxes update", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "inboxes update",
+ Transform: transform,
+ })
}
func handleInboxesList(ctx context.Context, cmd *cli.Command) error {
@@ -250,8 +267,6 @@ func handleInboxesList(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.InboxListParams{}
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -263,6 +278,8 @@ func handleInboxesList(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.InboxListParams{}
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Inboxes.List(ctx, params, options...)
@@ -272,8 +289,15 @@ func handleInboxesList(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "inboxes list", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "inboxes list",
+ Transform: transform,
+ })
}
func handleInboxesDelete(ctx context.Context, cmd *cli.Command) error {
@@ -332,8 +356,15 @@ func handleInboxesGet(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "inboxes get", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "inboxes get",
+ Transform: transform,
+ })
}
func handleInboxesListMetrics(ctx context.Context, cmd *cli.Command) error {
@@ -347,8 +378,6 @@ func handleInboxesListMetrics(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.InboxListMetricsParams{}
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -360,6 +389,8 @@ func handleInboxesListMetrics(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.InboxListMetricsParams{}
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Inboxes.ListMetrics(
@@ -374,6 +405,13 @@ func handleInboxesListMetrics(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "inboxes list-metrics", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "inboxes list-metrics",
+ Transform: transform,
+ })
}
diff --git a/pkg/cmd/inboxapikey.go b/pkg/cmd/inboxapikey.go
index eb44d6d..4478dd7 100644
--- a/pkg/cmd/inboxapikey.go
+++ b/pkg/cmd/inboxapikey.go
@@ -5,7 +5,6 @@ package cmd
import (
"context"
"fmt"
- "os"
"github.com/agentmail-to/agentmail-cli/internal/apiquery"
"github.com/agentmail-to/agentmail-cli/internal/requestflag"
@@ -21,11 +20,12 @@ var inboxesAPIKeysCreate = requestflag.WithInnerFlags(cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "inbox-id",
- Usage: "The ID of the inbox.",
- Required: true,
+ Name: "inbox-id",
+ Usage: "The ID of the inbox.",
+ Required: true,
+ PathParam: "inbox_id",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "name",
Usage: "Name of api key.",
BodyPath: "name",
@@ -40,177 +40,177 @@ var inboxesAPIKeysCreate = requestflag.WithInnerFlags(cli.Command{
HideHelpCommand: true,
}, map[string][]requestflag.HasOuterFlag{
"permissions": {
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.api-key-create",
Usage: "Create API keys.",
InnerField: "api_key_create",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.api-key-delete",
Usage: "Delete API keys.",
InnerField: "api_key_delete",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.api-key-read",
Usage: "Read API keys.",
InnerField: "api_key_read",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.domain-create",
Usage: "Create domains.",
InnerField: "domain_create",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.domain-delete",
Usage: "Delete domains.",
InnerField: "domain_delete",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.domain-read",
Usage: "Read domain details.",
InnerField: "domain_read",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.domain-update",
Usage: "Update domains.",
InnerField: "domain_update",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.draft-create",
Usage: "Create drafts.",
InnerField: "draft_create",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.draft-delete",
Usage: "Delete drafts.",
InnerField: "draft_delete",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.draft-read",
Usage: "Read drafts.",
InnerField: "draft_read",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.draft-send",
Usage: "Send drafts.",
InnerField: "draft_send",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.draft-update",
Usage: "Update drafts.",
InnerField: "draft_update",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.inbox-create",
Usage: "Create new inboxes.",
InnerField: "inbox_create",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.inbox-delete",
Usage: "Delete inboxes.",
InnerField: "inbox_delete",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.inbox-read",
Usage: "Read inbox details.",
InnerField: "inbox_read",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.inbox-update",
Usage: "Update inbox settings.",
InnerField: "inbox_update",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.label-blocked-read",
Usage: "Access messages labeled blocked.",
InnerField: "label_blocked_read",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.label-spam-read",
Usage: "Access messages labeled spam.",
InnerField: "label_spam_read",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.label-trash-read",
Usage: "Access messages labeled trash.",
InnerField: "label_trash_read",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.list-entry-create",
Usage: "Create list entries.",
InnerField: "list_entry_create",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.list-entry-delete",
Usage: "Delete list entries.",
InnerField: "list_entry_delete",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.list-entry-read",
Usage: "Read list entries.",
InnerField: "list_entry_read",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.message-read",
Usage: "Read messages.",
InnerField: "message_read",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.message-send",
Usage: "Send messages.",
InnerField: "message_send",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.message-update",
Usage: "Update message labels.",
InnerField: "message_update",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.metrics-read",
Usage: "Read metrics.",
InnerField: "metrics_read",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.pod-create",
Usage: "Create pods.",
InnerField: "pod_create",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.pod-delete",
Usage: "Delete pods.",
InnerField: "pod_delete",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.pod-read",
Usage: "Read pods.",
InnerField: "pod_read",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.thread-delete",
Usage: "Delete threads.",
InnerField: "thread_delete",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.thread-read",
Usage: "Read threads.",
InnerField: "thread_read",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.webhook-create",
Usage: "Create webhooks.",
InnerField: "webhook_create",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.webhook-delete",
Usage: "Delete webhooks.",
InnerField: "webhook_delete",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.webhook-read",
Usage: "Read webhook configurations.",
InnerField: "webhook_read",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.webhook-update",
Usage: "Update webhooks.",
InnerField: "webhook_update",
@@ -224,16 +224,17 @@ var inboxesAPIKeysList = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "inbox-id",
- Usage: "The ID of the inbox.",
- Required: true,
+ Name: "inbox-id",
+ Usage: "The ID of the inbox.",
+ Required: true,
+ PathParam: "inbox_id",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*int64]{
Name: "limit",
Usage: "Limit of number of items returned.",
QueryPath: "limit",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "page-token",
Usage: "Page token for pagination.",
QueryPath: "page_token",
@@ -249,14 +250,16 @@ var inboxesAPIKeysDelete = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "inbox-id",
- Usage: "The ID of the inbox.",
- Required: true,
+ Name: "inbox-id",
+ Usage: "The ID of the inbox.",
+ Required: true,
+ PathParam: "inbox_id",
},
&requestflag.Flag[string]{
- Name: "api-key-id",
- Usage: "ID of api key.",
- Required: true,
+ Name: "api-key-id",
+ Usage: "ID of api key.",
+ Required: true,
+ PathParam: "api_key_id",
},
},
Action: handleInboxesAPIKeysDelete,
@@ -274,8 +277,6 @@ func handleInboxesAPIKeysCreate(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.InboxAPIKeyNewParams{}
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -287,6 +288,8 @@ func handleInboxesAPIKeysCreate(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.InboxAPIKeyNewParams{}
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Inboxes.APIKeys.New(
@@ -301,8 +304,15 @@ func handleInboxesAPIKeysCreate(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "inboxes:api-keys create", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "inboxes:api-keys create",
+ Transform: transform,
+ })
}
func handleInboxesAPIKeysList(ctx context.Context, cmd *cli.Command) error {
@@ -316,8 +326,6 @@ func handleInboxesAPIKeysList(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.InboxAPIKeyListParams{}
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -329,6 +337,8 @@ func handleInboxesAPIKeysList(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.InboxAPIKeyListParams{}
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Inboxes.APIKeys.List(
@@ -343,8 +353,15 @@ func handleInboxesAPIKeysList(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "inboxes:api-keys list", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "inboxes:api-keys list",
+ Transform: transform,
+ })
}
func handleInboxesAPIKeysDelete(ctx context.Context, cmd *cli.Command) error {
@@ -358,10 +375,6 @@ func handleInboxesAPIKeysDelete(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.InboxAPIKeyDeleteParams{
- InboxID: cmd.Value("inbox-id").(string),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -373,6 +386,10 @@ func handleInboxesAPIKeysDelete(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.InboxAPIKeyDeleteParams{
+ InboxID: cmd.Value("inbox-id").(string),
+ }
+
return client.Inboxes.APIKeys.Delete(
ctx,
cmd.Value("api-key-id").(string),
diff --git a/pkg/cmd/inboxdraft.go b/pkg/cmd/inboxdraft.go
index a51e315..6aa1b99 100644
--- a/pkg/cmd/inboxdraft.go
+++ b/pkg/cmd/inboxdraft.go
@@ -5,7 +5,6 @@ package cmd
import (
"context"
"fmt"
- "os"
"github.com/agentmail-to/agentmail-cli/internal/apiquery"
"github.com/agentmail-to/agentmail-cli/internal/requestflag"
@@ -21,9 +20,10 @@ var inboxesDraftsCreate = requestflag.WithInnerFlags(cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "inbox-id",
- Usage: "The ID of the inbox.",
- Required: true,
+ Name: "inbox-id",
+ Usage: "The ID of the inbox.",
+ Required: true,
+ PathParam: "inbox_id",
},
&requestflag.Flag[any]{
Name: "attachment",
@@ -40,17 +40,17 @@ var inboxesDraftsCreate = requestflag.WithInnerFlags(cli.Command{
Usage: "Addresses of CC recipients. In format `username@domain.com` or `Display Name `.",
BodyPath: "cc",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "client-id",
Usage: "Client ID of draft.",
BodyPath: "client_id",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "html",
Usage: "HTML body of draft.",
BodyPath: "html",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "in-reply-to",
Usage: "ID of message being replied to.",
BodyPath: "in_reply_to",
@@ -70,12 +70,12 @@ var inboxesDraftsCreate = requestflag.WithInnerFlags(cli.Command{
Usage: "Time at which to schedule send draft.",
BodyPath: "send_at",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "subject",
Usage: "Subject of draft.",
BodyPath: "subject",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "text",
Usage: "Plain text body of draft.",
BodyPath: "text",
@@ -90,35 +90,41 @@ var inboxesDraftsCreate = requestflag.WithInnerFlags(cli.Command{
HideHelpCommand: true,
}, map[string][]requestflag.HasOuterFlag{
"attachment": {
- &requestflag.InnerFlag[any]{
- Name: "attachment.content",
- Usage: "Base64 encoded content of attachment.",
- InnerField: "content",
- },
- &requestflag.InnerFlag[string]{
- Name: "attachment.content-disposition",
- Usage: "Content disposition of attachment.",
- InnerField: "content_disposition",
- },
- &requestflag.InnerFlag[any]{
- Name: "attachment.content-id",
- Usage: "Content ID of attachment.",
- InnerField: "content_id",
- },
- &requestflag.InnerFlag[any]{
- Name: "attachment.content-type",
- Usage: "Content type of attachment.",
- InnerField: "content_type",
- },
- &requestflag.InnerFlag[any]{
- Name: "attachment.filename",
- Usage: "Filename of attachment.",
- InnerField: "filename",
- },
- &requestflag.InnerFlag[any]{
- Name: "attachment.url",
- Usage: "URL to the attachment.",
- InnerField: "url",
+ &requestflag.InnerFlag[*string]{
+ Name: "attachment.content",
+ Usage: "Base64 encoded content of attachment.",
+ InnerField: "content",
+ OuterIsArrayOfObjects: true,
+ },
+ &requestflag.InnerFlag[*string]{
+ Name: "attachment.content-disposition",
+ Usage: "Content disposition of attachment.",
+ InnerField: "content_disposition",
+ OuterIsArrayOfObjects: true,
+ },
+ &requestflag.InnerFlag[*string]{
+ Name: "attachment.content-id",
+ Usage: "Content ID of attachment.",
+ InnerField: "content_id",
+ OuterIsArrayOfObjects: true,
+ },
+ &requestflag.InnerFlag[*string]{
+ Name: "attachment.content-type",
+ Usage: "Content type of attachment.",
+ InnerField: "content_type",
+ OuterIsArrayOfObjects: true,
+ },
+ &requestflag.InnerFlag[*string]{
+ Name: "attachment.filename",
+ Usage: "Filename of attachment.",
+ InnerField: "filename",
+ OuterIsArrayOfObjects: true,
+ },
+ &requestflag.InnerFlag[*string]{
+ Name: "attachment.url",
+ Usage: "URL to the attachment.",
+ InnerField: "url",
+ OuterIsArrayOfObjects: true,
},
},
})
@@ -129,14 +135,16 @@ var inboxesDraftsUpdate = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "inbox-id",
- Usage: "The ID of the inbox.",
- Required: true,
+ Name: "inbox-id",
+ Usage: "The ID of the inbox.",
+ Required: true,
+ PathParam: "inbox_id",
},
&requestflag.Flag[string]{
- Name: "draft-id",
- Usage: "ID of draft.",
- Required: true,
+ Name: "draft-id",
+ Usage: "ID of draft.",
+ Required: true,
+ PathParam: "draft_id",
},
&requestflag.Flag[any]{
Name: "bcc",
@@ -148,7 +156,7 @@ var inboxesDraftsUpdate = cli.Command{
Usage: "Addresses of CC recipients. In format `username@domain.com` or `Display Name `.",
BodyPath: "cc",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "html",
Usage: "HTML body of draft.",
BodyPath: "html",
@@ -163,12 +171,12 @@ var inboxesDraftsUpdate = cli.Command{
Usage: "Time at which to schedule send draft.",
BodyPath: "send_at",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "subject",
Usage: "Subject of draft.",
BodyPath: "subject",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "text",
Usage: "Plain text body of draft.",
BodyPath: "text",
@@ -189,16 +197,17 @@ var inboxesDraftsList = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "inbox-id",
- Usage: "The ID of the inbox.",
- Required: true,
+ Name: "inbox-id",
+ Usage: "The ID of the inbox.",
+ Required: true,
+ PathParam: "inbox_id",
},
&requestflag.Flag[any]{
Name: "after",
Usage: "Timestamp after which to filter by.",
QueryPath: "after",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*bool]{
Name: "ascending",
Usage: "Sort in ascending temporal order.",
QueryPath: "ascending",
@@ -213,12 +222,12 @@ var inboxesDraftsList = cli.Command{
Usage: "Labels to filter by.",
QueryPath: "labels",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*int64]{
Name: "limit",
Usage: "Limit of number of items returned.",
QueryPath: "limit",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "page-token",
Usage: "Page token for pagination.",
QueryPath: "page_token",
@@ -234,14 +243,16 @@ var inboxesDraftsDelete = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "inbox-id",
- Usage: "The ID of the inbox.",
- Required: true,
+ Name: "inbox-id",
+ Usage: "The ID of the inbox.",
+ Required: true,
+ PathParam: "inbox_id",
},
&requestflag.Flag[string]{
- Name: "draft-id",
- Usage: "ID of draft.",
- Required: true,
+ Name: "draft-id",
+ Usage: "ID of draft.",
+ Required: true,
+ PathParam: "draft_id",
},
},
Action: handleInboxesDraftsDelete,
@@ -254,14 +265,16 @@ var inboxesDraftsGet = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "inbox-id",
- Usage: "The ID of the inbox.",
- Required: true,
+ Name: "inbox-id",
+ Usage: "The ID of the inbox.",
+ Required: true,
+ PathParam: "inbox_id",
},
&requestflag.Flag[string]{
- Name: "draft-id",
- Usage: "ID of draft.",
- Required: true,
+ Name: "draft-id",
+ Usage: "ID of draft.",
+ Required: true,
+ PathParam: "draft_id",
},
},
Action: handleInboxesDraftsGet,
@@ -274,19 +287,22 @@ var inboxesDraftsGetAttachment = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "inbox-id",
- Usage: "The ID of the inbox.",
- Required: true,
+ Name: "inbox-id",
+ Usage: "The ID of the inbox.",
+ Required: true,
+ PathParam: "inbox_id",
},
&requestflag.Flag[string]{
- Name: "draft-id",
- Usage: "ID of draft.",
- Required: true,
+ Name: "draft-id",
+ Usage: "ID of draft.",
+ Required: true,
+ PathParam: "draft_id",
},
&requestflag.Flag[string]{
- Name: "attachment-id",
- Usage: "ID of attachment.",
- Required: true,
+ Name: "attachment-id",
+ Usage: "ID of attachment.",
+ Required: true,
+ PathParam: "attachment_id",
},
},
Action: handleInboxesDraftsGetAttachment,
@@ -299,14 +315,16 @@ var inboxesDraftsSend = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "inbox-id",
- Usage: "The ID of the inbox.",
- Required: true,
+ Name: "inbox-id",
+ Usage: "The ID of the inbox.",
+ Required: true,
+ PathParam: "inbox_id",
},
&requestflag.Flag[string]{
- Name: "draft-id",
- Usage: "ID of draft.",
- Required: true,
+ Name: "draft-id",
+ Usage: "ID of draft.",
+ Required: true,
+ PathParam: "draft_id",
},
&requestflag.Flag[any]{
Name: "add-labels",
@@ -334,8 +352,6 @@ func handleInboxesDraftsCreate(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.InboxDraftNewParams{}
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -347,6 +363,8 @@ func handleInboxesDraftsCreate(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.InboxDraftNewParams{}
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Inboxes.Drafts.New(
@@ -361,8 +379,15 @@ func handleInboxesDraftsCreate(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "inboxes:drafts create", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "inboxes:drafts create",
+ Transform: transform,
+ })
}
func handleInboxesDraftsUpdate(ctx context.Context, cmd *cli.Command) error {
@@ -376,10 +401,6 @@ func handleInboxesDraftsUpdate(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.InboxDraftUpdateParams{
- InboxID: cmd.Value("inbox-id").(string),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -391,6 +412,10 @@ func handleInboxesDraftsUpdate(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.InboxDraftUpdateParams{
+ InboxID: cmd.Value("inbox-id").(string),
+ }
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Inboxes.Drafts.Update(
@@ -405,8 +430,15 @@ func handleInboxesDraftsUpdate(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "inboxes:drafts update", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "inboxes:drafts update",
+ Transform: transform,
+ })
}
func handleInboxesDraftsList(ctx context.Context, cmd *cli.Command) error {
@@ -420,8 +452,6 @@ func handleInboxesDraftsList(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.InboxDraftListParams{}
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -433,6 +463,8 @@ func handleInboxesDraftsList(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.InboxDraftListParams{}
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Inboxes.Drafts.List(
@@ -447,8 +479,15 @@ func handleInboxesDraftsList(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "inboxes:drafts list", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "inboxes:drafts list",
+ Transform: transform,
+ })
}
func handleInboxesDraftsDelete(ctx context.Context, cmd *cli.Command) error {
@@ -462,10 +501,6 @@ func handleInboxesDraftsDelete(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.InboxDraftDeleteParams{
- InboxID: cmd.Value("inbox-id").(string),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -477,6 +512,10 @@ func handleInboxesDraftsDelete(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.InboxDraftDeleteParams{
+ InboxID: cmd.Value("inbox-id").(string),
+ }
+
return client.Inboxes.Drafts.Delete(
ctx,
cmd.Value("draft-id").(string),
@@ -496,10 +535,6 @@ func handleInboxesDraftsGet(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.InboxDraftGetParams{
- InboxID: cmd.Value("inbox-id").(string),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -511,6 +546,10 @@ func handleInboxesDraftsGet(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.InboxDraftGetParams{
+ InboxID: cmd.Value("inbox-id").(string),
+ }
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Inboxes.Drafts.Get(
@@ -525,8 +564,15 @@ func handleInboxesDraftsGet(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "inboxes:drafts get", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "inboxes:drafts get",
+ Transform: transform,
+ })
}
func handleInboxesDraftsGetAttachment(ctx context.Context, cmd *cli.Command) error {
@@ -540,11 +586,6 @@ func handleInboxesDraftsGetAttachment(ctx context.Context, cmd *cli.Command) err
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.InboxDraftGetAttachmentParams{
- InboxID: cmd.Value("inbox-id").(string),
- DraftID: cmd.Value("draft-id").(string),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -556,6 +597,11 @@ func handleInboxesDraftsGetAttachment(ctx context.Context, cmd *cli.Command) err
return err
}
+ params := agentmail.InboxDraftGetAttachmentParams{
+ InboxID: cmd.Value("inbox-id").(string),
+ DraftID: cmd.Value("draft-id").(string),
+ }
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Inboxes.Drafts.GetAttachment(
@@ -570,8 +616,15 @@ func handleInboxesDraftsGetAttachment(ctx context.Context, cmd *cli.Command) err
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "inboxes:drafts get-attachment", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "inboxes:drafts get-attachment",
+ Transform: transform,
+ })
}
func handleInboxesDraftsSend(ctx context.Context, cmd *cli.Command) error {
@@ -585,10 +638,6 @@ func handleInboxesDraftsSend(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.InboxDraftSendParams{
- InboxID: cmd.Value("inbox-id").(string),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -600,6 +649,10 @@ func handleInboxesDraftsSend(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.InboxDraftSendParams{
+ InboxID: cmd.Value("inbox-id").(string),
+ }
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Inboxes.Drafts.Send(
@@ -614,6 +667,13 @@ func handleInboxesDraftsSend(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "inboxes:drafts send", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "inboxes:drafts send",
+ Transform: transform,
+ })
}
diff --git a/pkg/cmd/inboxlist.go b/pkg/cmd/inboxlist.go
index 9fe9be2..f926f49 100644
--- a/pkg/cmd/inboxlist.go
+++ b/pkg/cmd/inboxlist.go
@@ -5,7 +5,6 @@ package cmd
import (
"context"
"fmt"
- "os"
"github.com/agentmail-to/agentmail-cli/internal/apiquery"
"github.com/agentmail-to/agentmail-cli/internal/requestflag"
@@ -21,19 +20,22 @@ var inboxesListsCreate = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "inbox-id",
- Usage: "The ID of the inbox.",
- Required: true,
+ Name: "inbox-id",
+ Usage: "The ID of the inbox.",
+ Required: true,
+ PathParam: "inbox_id",
},
&requestflag.Flag[string]{
- Name: "direction",
- Usage: "Direction of list entry.",
- Required: true,
+ Name: "direction",
+ Usage: "Direction of list entry.",
+ Required: true,
+ PathParam: "direction",
},
&requestflag.Flag[string]{
- Name: "type",
- Usage: "Type of list entry.",
- Required: true,
+ Name: "type",
+ Usage: "Type of list entry.",
+ Required: true,
+ PathParam: "type",
},
&requestflag.Flag[string]{
Name: "entry",
@@ -41,7 +43,7 @@ var inboxesListsCreate = cli.Command{
Required: true,
BodyPath: "entry",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "reason",
Usage: "Reason for adding the entry.",
BodyPath: "reason",
@@ -57,26 +59,29 @@ var inboxesListsList = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "inbox-id",
- Usage: "The ID of the inbox.",
- Required: true,
+ Name: "inbox-id",
+ Usage: "The ID of the inbox.",
+ Required: true,
+ PathParam: "inbox_id",
},
&requestflag.Flag[string]{
- Name: "direction",
- Usage: "Direction of list entry.",
- Required: true,
+ Name: "direction",
+ Usage: "Direction of list entry.",
+ Required: true,
+ PathParam: "direction",
},
&requestflag.Flag[string]{
- Name: "type",
- Usage: "Type of list entry.",
- Required: true,
+ Name: "type",
+ Usage: "Type of list entry.",
+ Required: true,
+ PathParam: "type",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*int64]{
Name: "limit",
Usage: "Limit of number of items returned.",
QueryPath: "limit",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "page-token",
Usage: "Page token for pagination.",
QueryPath: "page_token",
@@ -92,23 +97,27 @@ var inboxesListsDelete = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "inbox-id",
- Usage: "The ID of the inbox.",
- Required: true,
+ Name: "inbox-id",
+ Usage: "The ID of the inbox.",
+ Required: true,
+ PathParam: "inbox_id",
},
&requestflag.Flag[string]{
- Name: "direction",
- Usage: "Direction of list entry.",
- Required: true,
+ Name: "direction",
+ Usage: "Direction of list entry.",
+ Required: true,
+ PathParam: "direction",
},
&requestflag.Flag[string]{
- Name: "type",
- Usage: "Type of list entry.",
- Required: true,
+ Name: "type",
+ Usage: "Type of list entry.",
+ Required: true,
+ PathParam: "type",
},
&requestflag.Flag[string]{
- Name: "entry",
- Required: true,
+ Name: "entry",
+ Required: true,
+ PathParam: "entry",
},
},
Action: handleInboxesListsDelete,
@@ -121,23 +130,27 @@ var inboxesListsGet = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "inbox-id",
- Usage: "The ID of the inbox.",
- Required: true,
+ Name: "inbox-id",
+ Usage: "The ID of the inbox.",
+ Required: true,
+ PathParam: "inbox_id",
},
&requestflag.Flag[string]{
- Name: "direction",
- Usage: "Direction of list entry.",
- Required: true,
+ Name: "direction",
+ Usage: "Direction of list entry.",
+ Required: true,
+ PathParam: "direction",
},
&requestflag.Flag[string]{
- Name: "type",
- Usage: "Type of list entry.",
- Required: true,
+ Name: "type",
+ Usage: "Type of list entry.",
+ Required: true,
+ PathParam: "type",
},
&requestflag.Flag[string]{
- Name: "entry",
- Required: true,
+ Name: "entry",
+ Required: true,
+ PathParam: "entry",
},
},
Action: handleInboxesListsGet,
@@ -155,11 +168,6 @@ func handleInboxesListsCreate(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.InboxListNewParams{
- InboxID: cmd.Value("inbox-id").(string),
- Direction: agentmail.InboxListNewParamsDirection(cmd.Value("direction").(string)),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -171,6 +179,11 @@ func handleInboxesListsCreate(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.InboxListNewParams{
+ InboxID: cmd.Value("inbox-id").(string),
+ Direction: agentmail.InboxListNewParamsDirection(cmd.Value("direction").(string)),
+ }
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Inboxes.Lists.New(
@@ -185,8 +198,15 @@ func handleInboxesListsCreate(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "inboxes:lists create", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "inboxes:lists create",
+ Transform: transform,
+ })
}
func handleInboxesListsList(ctx context.Context, cmd *cli.Command) error {
@@ -200,11 +220,6 @@ func handleInboxesListsList(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.InboxListListParams{
- InboxID: cmd.Value("inbox-id").(string),
- Direction: agentmail.InboxListListParamsDirection(cmd.Value("direction").(string)),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -216,6 +231,11 @@ func handleInboxesListsList(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.InboxListListParams{
+ InboxID: cmd.Value("inbox-id").(string),
+ Direction: agentmail.InboxListListParamsDirection(cmd.Value("direction").(string)),
+ }
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Inboxes.Lists.List(
@@ -230,8 +250,15 @@ func handleInboxesListsList(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "inboxes:lists list", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "inboxes:lists list",
+ Transform: transform,
+ })
}
func handleInboxesListsDelete(ctx context.Context, cmd *cli.Command) error {
@@ -245,12 +272,6 @@ func handleInboxesListsDelete(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.InboxListDeleteParams{
- InboxID: cmd.Value("inbox-id").(string),
- Direction: agentmail.InboxListDeleteParamsDirection(cmd.Value("direction").(string)),
- Type: agentmail.InboxListDeleteParamsType(cmd.Value("type").(string)),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -262,6 +283,12 @@ func handleInboxesListsDelete(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.InboxListDeleteParams{
+ InboxID: cmd.Value("inbox-id").(string),
+ Direction: agentmail.InboxListDeleteParamsDirection(cmd.Value("direction").(string)),
+ Type: agentmail.InboxListDeleteParamsType(cmd.Value("type").(string)),
+ }
+
return client.Inboxes.Lists.Delete(
ctx,
cmd.Value("entry").(string),
@@ -281,12 +308,6 @@ func handleInboxesListsGet(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.InboxListGetParams{
- InboxID: cmd.Value("inbox-id").(string),
- Direction: agentmail.InboxListGetParamsDirection(cmd.Value("direction").(string)),
- Type: agentmail.InboxListGetParamsType(cmd.Value("type").(string)),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -298,6 +319,12 @@ func handleInboxesListsGet(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.InboxListGetParams{
+ InboxID: cmd.Value("inbox-id").(string),
+ Direction: agentmail.InboxListGetParamsDirection(cmd.Value("direction").(string)),
+ Type: agentmail.InboxListGetParamsType(cmd.Value("type").(string)),
+ }
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Inboxes.Lists.Get(
@@ -312,6 +339,13 @@ func handleInboxesListsGet(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "inboxes:lists get", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "inboxes:lists get",
+ Transform: transform,
+ })
}
diff --git a/pkg/cmd/inboxmessage.go b/pkg/cmd/inboxmessage.go
index 7278ad5..16e482d 100644
--- a/pkg/cmd/inboxmessage.go
+++ b/pkg/cmd/inboxmessage.go
@@ -5,7 +5,6 @@ package cmd
import (
"context"
"fmt"
- "os"
"github.com/agentmail-to/agentmail-cli/internal/apiquery"
"github.com/agentmail-to/agentmail-cli/internal/requestflag"
@@ -21,14 +20,16 @@ var inboxesMessagesUpdate = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "inbox-id",
- Usage: "The ID of the inbox.",
- Required: true,
+ Name: "inbox-id",
+ Usage: "The ID of the inbox.",
+ Required: true,
+ PathParam: "inbox_id",
},
&requestflag.Flag[string]{
- Name: "message-id",
- Usage: "ID of message.",
- Required: true,
+ Name: "message-id",
+ Usage: "ID of message.",
+ Required: true,
+ PathParam: "message_id",
},
&requestflag.Flag[any]{
Name: "add-labels",
@@ -51,16 +52,17 @@ var inboxesMessagesList = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "inbox-id",
- Usage: "The ID of the inbox.",
- Required: true,
+ Name: "inbox-id",
+ Usage: "The ID of the inbox.",
+ Required: true,
+ PathParam: "inbox_id",
},
&requestflag.Flag[any]{
Name: "after",
Usage: "Timestamp after which to filter by.",
QueryPath: "after",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*bool]{
Name: "ascending",
Usage: "Sort in ascending temporal order.",
QueryPath: "ascending",
@@ -70,32 +72,37 @@ var inboxesMessagesList = cli.Command{
Usage: "Timestamp before which to filter by.",
QueryPath: "before",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*bool]{
Name: "include-blocked",
Usage: "Include blocked in results.",
QueryPath: "include_blocked",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*bool]{
Name: "include-spam",
Usage: "Include spam in results.",
QueryPath: "include_spam",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*bool]{
Name: "include-trash",
Usage: "Include trash in results.",
QueryPath: "include_trash",
},
+ &requestflag.Flag[*bool]{
+ Name: "include-unauthenticated",
+ Usage: "Include unauthenticated in results.",
+ QueryPath: "include_unauthenticated",
+ },
&requestflag.Flag[any]{
Name: "label",
Usage: "Labels to filter by.",
QueryPath: "labels",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*int64]{
Name: "limit",
Usage: "Limit of number of items returned.",
QueryPath: "limit",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "page-token",
Usage: "Page token for pagination.",
QueryPath: "page_token",
@@ -111,14 +118,16 @@ var inboxesMessagesForward = requestflag.WithInnerFlags(cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "inbox-id",
- Usage: "The ID of the inbox.",
- Required: true,
+ Name: "inbox-id",
+ Usage: "The ID of the inbox.",
+ Required: true,
+ PathParam: "inbox_id",
},
&requestflag.Flag[string]{
- Name: "message-id",
- Usage: "ID of message.",
- Required: true,
+ Name: "message-id",
+ Usage: "ID of message.",
+ Required: true,
+ PathParam: "message_id",
},
&requestflag.Flag[any]{
Name: "attachment",
@@ -138,7 +147,7 @@ var inboxesMessagesForward = requestflag.WithInnerFlags(cli.Command{
Usage: "Headers to include in message.",
BodyPath: "headers",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "html",
Usage: "HTML body of message.",
BodyPath: "html",
@@ -152,12 +161,12 @@ var inboxesMessagesForward = requestflag.WithInnerFlags(cli.Command{
Name: "reply-to",
BodyPath: "reply_to",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "subject",
Usage: "Subject of message.",
BodyPath: "subject",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "text",
Usage: "Plain text body of message.",
BodyPath: "text",
@@ -171,35 +180,41 @@ var inboxesMessagesForward = requestflag.WithInnerFlags(cli.Command{
HideHelpCommand: true,
}, map[string][]requestflag.HasOuterFlag{
"attachment": {
- &requestflag.InnerFlag[any]{
- Name: "attachment.content",
- Usage: "Base64 encoded content of attachment.",
- InnerField: "content",
- },
- &requestflag.InnerFlag[string]{
- Name: "attachment.content-disposition",
- Usage: "Content disposition of attachment.",
- InnerField: "content_disposition",
- },
- &requestflag.InnerFlag[any]{
- Name: "attachment.content-id",
- Usage: "Content ID of attachment.",
- InnerField: "content_id",
- },
- &requestflag.InnerFlag[any]{
- Name: "attachment.content-type",
- Usage: "Content type of attachment.",
- InnerField: "content_type",
- },
- &requestflag.InnerFlag[any]{
- Name: "attachment.filename",
- Usage: "Filename of attachment.",
- InnerField: "filename",
- },
- &requestflag.InnerFlag[any]{
- Name: "attachment.url",
- Usage: "URL to the attachment.",
- InnerField: "url",
+ &requestflag.InnerFlag[*string]{
+ Name: "attachment.content",
+ Usage: "Base64 encoded content of attachment.",
+ InnerField: "content",
+ OuterIsArrayOfObjects: true,
+ },
+ &requestflag.InnerFlag[*string]{
+ Name: "attachment.content-disposition",
+ Usage: "Content disposition of attachment.",
+ InnerField: "content_disposition",
+ OuterIsArrayOfObjects: true,
+ },
+ &requestflag.InnerFlag[*string]{
+ Name: "attachment.content-id",
+ Usage: "Content ID of attachment.",
+ InnerField: "content_id",
+ OuterIsArrayOfObjects: true,
+ },
+ &requestflag.InnerFlag[*string]{
+ Name: "attachment.content-type",
+ Usage: "Content type of attachment.",
+ InnerField: "content_type",
+ OuterIsArrayOfObjects: true,
+ },
+ &requestflag.InnerFlag[*string]{
+ Name: "attachment.filename",
+ Usage: "Filename of attachment.",
+ InnerField: "filename",
+ OuterIsArrayOfObjects: true,
+ },
+ &requestflag.InnerFlag[*string]{
+ Name: "attachment.url",
+ Usage: "URL to the attachment.",
+ InnerField: "url",
+ OuterIsArrayOfObjects: true,
},
},
})
@@ -210,14 +225,16 @@ var inboxesMessagesGet = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "inbox-id",
- Usage: "The ID of the inbox.",
- Required: true,
+ Name: "inbox-id",
+ Usage: "The ID of the inbox.",
+ Required: true,
+ PathParam: "inbox_id",
},
&requestflag.Flag[string]{
- Name: "message-id",
- Usage: "ID of message.",
- Required: true,
+ Name: "message-id",
+ Usage: "ID of message.",
+ Required: true,
+ PathParam: "message_id",
},
},
Action: handleInboxesMessagesGet,
@@ -230,19 +247,22 @@ var inboxesMessagesGetAttachment = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "inbox-id",
- Usage: "The ID of the inbox.",
- Required: true,
+ Name: "inbox-id",
+ Usage: "The ID of the inbox.",
+ Required: true,
+ PathParam: "inbox_id",
},
&requestflag.Flag[string]{
- Name: "message-id",
- Usage: "ID of message.",
- Required: true,
+ Name: "message-id",
+ Usage: "ID of message.",
+ Required: true,
+ PathParam: "message_id",
},
&requestflag.Flag[string]{
- Name: "attachment-id",
- Usage: "ID of attachment.",
- Required: true,
+ Name: "attachment-id",
+ Usage: "ID of attachment.",
+ Required: true,
+ PathParam: "attachment_id",
},
},
Action: handleInboxesMessagesGetAttachment,
@@ -255,14 +275,16 @@ var inboxesMessagesGetRaw = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "inbox-id",
- Usage: "The ID of the inbox.",
- Required: true,
+ Name: "inbox-id",
+ Usage: "The ID of the inbox.",
+ Required: true,
+ PathParam: "inbox_id",
},
&requestflag.Flag[string]{
- Name: "message-id",
- Usage: "ID of message.",
- Required: true,
+ Name: "message-id",
+ Usage: "ID of message.",
+ Required: true,
+ PathParam: "message_id",
},
},
Action: handleInboxesMessagesGetRaw,
@@ -275,14 +297,16 @@ var inboxesMessagesReply = requestflag.WithInnerFlags(cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "inbox-id",
- Usage: "The ID of the inbox.",
- Required: true,
+ Name: "inbox-id",
+ Usage: "The ID of the inbox.",
+ Required: true,
+ PathParam: "inbox_id",
},
&requestflag.Flag[string]{
- Name: "message-id",
- Usage: "ID of message.",
- Required: true,
+ Name: "message-id",
+ Usage: "ID of message.",
+ Required: true,
+ PathParam: "message_id",
},
&requestflag.Flag[any]{
Name: "attachment",
@@ -302,7 +326,7 @@ var inboxesMessagesReply = requestflag.WithInnerFlags(cli.Command{
Usage: "Headers to include in message.",
BodyPath: "headers",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "html",
Usage: "HTML body of message.",
BodyPath: "html",
@@ -312,7 +336,7 @@ var inboxesMessagesReply = requestflag.WithInnerFlags(cli.Command{
Usage: "Labels of message.",
BodyPath: "labels",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*bool]{
Name: "reply-all",
Usage: "Reply to all recipients of the original message.",
BodyPath: "reply_all",
@@ -321,7 +345,7 @@ var inboxesMessagesReply = requestflag.WithInnerFlags(cli.Command{
Name: "reply-to",
BodyPath: "reply_to",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "text",
Usage: "Plain text body of message.",
BodyPath: "text",
@@ -335,35 +359,41 @@ var inboxesMessagesReply = requestflag.WithInnerFlags(cli.Command{
HideHelpCommand: true,
}, map[string][]requestflag.HasOuterFlag{
"attachment": {
- &requestflag.InnerFlag[any]{
- Name: "attachment.content",
- Usage: "Base64 encoded content of attachment.",
- InnerField: "content",
- },
- &requestflag.InnerFlag[string]{
- Name: "attachment.content-disposition",
- Usage: "Content disposition of attachment.",
- InnerField: "content_disposition",
- },
- &requestflag.InnerFlag[any]{
- Name: "attachment.content-id",
- Usage: "Content ID of attachment.",
- InnerField: "content_id",
- },
- &requestflag.InnerFlag[any]{
- Name: "attachment.content-type",
- Usage: "Content type of attachment.",
- InnerField: "content_type",
- },
- &requestflag.InnerFlag[any]{
- Name: "attachment.filename",
- Usage: "Filename of attachment.",
- InnerField: "filename",
- },
- &requestflag.InnerFlag[any]{
- Name: "attachment.url",
- Usage: "URL to the attachment.",
- InnerField: "url",
+ &requestflag.InnerFlag[*string]{
+ Name: "attachment.content",
+ Usage: "Base64 encoded content of attachment.",
+ InnerField: "content",
+ OuterIsArrayOfObjects: true,
+ },
+ &requestflag.InnerFlag[*string]{
+ Name: "attachment.content-disposition",
+ Usage: "Content disposition of attachment.",
+ InnerField: "content_disposition",
+ OuterIsArrayOfObjects: true,
+ },
+ &requestflag.InnerFlag[*string]{
+ Name: "attachment.content-id",
+ Usage: "Content ID of attachment.",
+ InnerField: "content_id",
+ OuterIsArrayOfObjects: true,
+ },
+ &requestflag.InnerFlag[*string]{
+ Name: "attachment.content-type",
+ Usage: "Content type of attachment.",
+ InnerField: "content_type",
+ OuterIsArrayOfObjects: true,
+ },
+ &requestflag.InnerFlag[*string]{
+ Name: "attachment.filename",
+ Usage: "Filename of attachment.",
+ InnerField: "filename",
+ OuterIsArrayOfObjects: true,
+ },
+ &requestflag.InnerFlag[*string]{
+ Name: "attachment.url",
+ Usage: "URL to the attachment.",
+ InnerField: "url",
+ OuterIsArrayOfObjects: true,
},
},
})
@@ -374,14 +404,16 @@ var inboxesMessagesReplyAll = requestflag.WithInnerFlags(cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "inbox-id",
- Usage: "The ID of the inbox.",
- Required: true,
+ Name: "inbox-id",
+ Usage: "The ID of the inbox.",
+ Required: true,
+ PathParam: "inbox_id",
},
&requestflag.Flag[string]{
- Name: "message-id",
- Usage: "ID of message.",
- Required: true,
+ Name: "message-id",
+ Usage: "ID of message.",
+ Required: true,
+ PathParam: "message_id",
},
&requestflag.Flag[any]{
Name: "attachment",
@@ -393,7 +425,7 @@ var inboxesMessagesReplyAll = requestflag.WithInnerFlags(cli.Command{
Usage: "Headers to include in message.",
BodyPath: "headers",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "html",
Usage: "HTML body of message.",
BodyPath: "html",
@@ -407,7 +439,7 @@ var inboxesMessagesReplyAll = requestflag.WithInnerFlags(cli.Command{
Name: "reply-to",
BodyPath: "reply_to",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "text",
Usage: "Plain text body of message.",
BodyPath: "text",
@@ -417,35 +449,41 @@ var inboxesMessagesReplyAll = requestflag.WithInnerFlags(cli.Command{
HideHelpCommand: true,
}, map[string][]requestflag.HasOuterFlag{
"attachment": {
- &requestflag.InnerFlag[any]{
- Name: "attachment.content",
- Usage: "Base64 encoded content of attachment.",
- InnerField: "content",
- },
- &requestflag.InnerFlag[string]{
- Name: "attachment.content-disposition",
- Usage: "Content disposition of attachment.",
- InnerField: "content_disposition",
- },
- &requestflag.InnerFlag[any]{
- Name: "attachment.content-id",
- Usage: "Content ID of attachment.",
- InnerField: "content_id",
- },
- &requestflag.InnerFlag[any]{
- Name: "attachment.content-type",
- Usage: "Content type of attachment.",
- InnerField: "content_type",
- },
- &requestflag.InnerFlag[any]{
- Name: "attachment.filename",
- Usage: "Filename of attachment.",
- InnerField: "filename",
- },
- &requestflag.InnerFlag[any]{
- Name: "attachment.url",
- Usage: "URL to the attachment.",
- InnerField: "url",
+ &requestflag.InnerFlag[*string]{
+ Name: "attachment.content",
+ Usage: "Base64 encoded content of attachment.",
+ InnerField: "content",
+ OuterIsArrayOfObjects: true,
+ },
+ &requestflag.InnerFlag[*string]{
+ Name: "attachment.content-disposition",
+ Usage: "Content disposition of attachment.",
+ InnerField: "content_disposition",
+ OuterIsArrayOfObjects: true,
+ },
+ &requestflag.InnerFlag[*string]{
+ Name: "attachment.content-id",
+ Usage: "Content ID of attachment.",
+ InnerField: "content_id",
+ OuterIsArrayOfObjects: true,
+ },
+ &requestflag.InnerFlag[*string]{
+ Name: "attachment.content-type",
+ Usage: "Content type of attachment.",
+ InnerField: "content_type",
+ OuterIsArrayOfObjects: true,
+ },
+ &requestflag.InnerFlag[*string]{
+ Name: "attachment.filename",
+ Usage: "Filename of attachment.",
+ InnerField: "filename",
+ OuterIsArrayOfObjects: true,
+ },
+ &requestflag.InnerFlag[*string]{
+ Name: "attachment.url",
+ Usage: "URL to the attachment.",
+ InnerField: "url",
+ OuterIsArrayOfObjects: true,
},
},
})
@@ -456,9 +494,10 @@ var inboxesMessagesSend = requestflag.WithInnerFlags(cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "inbox-id",
- Usage: "The ID of the inbox.",
- Required: true,
+ Name: "inbox-id",
+ Usage: "The ID of the inbox.",
+ Required: true,
+ PathParam: "inbox_id",
},
&requestflag.Flag[any]{
Name: "attachment",
@@ -478,7 +517,7 @@ var inboxesMessagesSend = requestflag.WithInnerFlags(cli.Command{
Usage: "Headers to include in message.",
BodyPath: "headers",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "html",
Usage: "HTML body of message.",
BodyPath: "html",
@@ -492,12 +531,12 @@ var inboxesMessagesSend = requestflag.WithInnerFlags(cli.Command{
Name: "reply-to",
BodyPath: "reply_to",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "subject",
Usage: "Subject of message.",
BodyPath: "subject",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "text",
Usage: "Plain text body of message.",
BodyPath: "text",
@@ -511,35 +550,41 @@ var inboxesMessagesSend = requestflag.WithInnerFlags(cli.Command{
HideHelpCommand: true,
}, map[string][]requestflag.HasOuterFlag{
"attachment": {
- &requestflag.InnerFlag[any]{
- Name: "attachment.content",
- Usage: "Base64 encoded content of attachment.",
- InnerField: "content",
- },
- &requestflag.InnerFlag[string]{
- Name: "attachment.content-disposition",
- Usage: "Content disposition of attachment.",
- InnerField: "content_disposition",
- },
- &requestflag.InnerFlag[any]{
- Name: "attachment.content-id",
- Usage: "Content ID of attachment.",
- InnerField: "content_id",
- },
- &requestflag.InnerFlag[any]{
- Name: "attachment.content-type",
- Usage: "Content type of attachment.",
- InnerField: "content_type",
- },
- &requestflag.InnerFlag[any]{
- Name: "attachment.filename",
- Usage: "Filename of attachment.",
- InnerField: "filename",
- },
- &requestflag.InnerFlag[any]{
- Name: "attachment.url",
- Usage: "URL to the attachment.",
- InnerField: "url",
+ &requestflag.InnerFlag[*string]{
+ Name: "attachment.content",
+ Usage: "Base64 encoded content of attachment.",
+ InnerField: "content",
+ OuterIsArrayOfObjects: true,
+ },
+ &requestflag.InnerFlag[*string]{
+ Name: "attachment.content-disposition",
+ Usage: "Content disposition of attachment.",
+ InnerField: "content_disposition",
+ OuterIsArrayOfObjects: true,
+ },
+ &requestflag.InnerFlag[*string]{
+ Name: "attachment.content-id",
+ Usage: "Content ID of attachment.",
+ InnerField: "content_id",
+ OuterIsArrayOfObjects: true,
+ },
+ &requestflag.InnerFlag[*string]{
+ Name: "attachment.content-type",
+ Usage: "Content type of attachment.",
+ InnerField: "content_type",
+ OuterIsArrayOfObjects: true,
+ },
+ &requestflag.InnerFlag[*string]{
+ Name: "attachment.filename",
+ Usage: "Filename of attachment.",
+ InnerField: "filename",
+ OuterIsArrayOfObjects: true,
+ },
+ &requestflag.InnerFlag[*string]{
+ Name: "attachment.url",
+ Usage: "URL to the attachment.",
+ InnerField: "url",
+ OuterIsArrayOfObjects: true,
},
},
})
@@ -555,10 +600,6 @@ func handleInboxesMessagesUpdate(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.InboxMessageUpdateParams{
- InboxID: cmd.Value("inbox-id").(string),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -570,6 +611,10 @@ func handleInboxesMessagesUpdate(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.InboxMessageUpdateParams{
+ InboxID: cmd.Value("inbox-id").(string),
+ }
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Inboxes.Messages.Update(
@@ -584,8 +629,15 @@ func handleInboxesMessagesUpdate(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "inboxes:messages update", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "inboxes:messages update",
+ Transform: transform,
+ })
}
func handleInboxesMessagesList(ctx context.Context, cmd *cli.Command) error {
@@ -599,8 +651,6 @@ func handleInboxesMessagesList(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.InboxMessageListParams{}
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -612,6 +662,8 @@ func handleInboxesMessagesList(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.InboxMessageListParams{}
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Inboxes.Messages.List(
@@ -626,8 +678,15 @@ func handleInboxesMessagesList(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "inboxes:messages list", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "inboxes:messages list",
+ Transform: transform,
+ })
}
func handleInboxesMessagesForward(ctx context.Context, cmd *cli.Command) error {
@@ -641,10 +700,6 @@ func handleInboxesMessagesForward(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.InboxMessageForwardParams{
- InboxID: cmd.Value("inbox-id").(string),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -656,6 +711,10 @@ func handleInboxesMessagesForward(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.InboxMessageForwardParams{
+ InboxID: cmd.Value("inbox-id").(string),
+ }
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Inboxes.Messages.Forward(
@@ -670,8 +729,15 @@ func handleInboxesMessagesForward(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "inboxes:messages forward", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "inboxes:messages forward",
+ Transform: transform,
+ })
}
func handleInboxesMessagesGet(ctx context.Context, cmd *cli.Command) error {
@@ -685,10 +751,6 @@ func handleInboxesMessagesGet(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.InboxMessageGetParams{
- InboxID: cmd.Value("inbox-id").(string),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -700,6 +762,10 @@ func handleInboxesMessagesGet(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.InboxMessageGetParams{
+ InboxID: cmd.Value("inbox-id").(string),
+ }
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Inboxes.Messages.Get(
@@ -714,8 +780,15 @@ func handleInboxesMessagesGet(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "inboxes:messages get", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "inboxes:messages get",
+ Transform: transform,
+ })
}
func handleInboxesMessagesGetAttachment(ctx context.Context, cmd *cli.Command) error {
@@ -729,11 +802,6 @@ func handleInboxesMessagesGetAttachment(ctx context.Context, cmd *cli.Command) e
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.InboxMessageGetAttachmentParams{
- InboxID: cmd.Value("inbox-id").(string),
- MessageID: cmd.Value("message-id").(string),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -745,6 +813,11 @@ func handleInboxesMessagesGetAttachment(ctx context.Context, cmd *cli.Command) e
return err
}
+ params := agentmail.InboxMessageGetAttachmentParams{
+ InboxID: cmd.Value("inbox-id").(string),
+ MessageID: cmd.Value("message-id").(string),
+ }
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Inboxes.Messages.GetAttachment(
@@ -759,8 +832,15 @@ func handleInboxesMessagesGetAttachment(ctx context.Context, cmd *cli.Command) e
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "inboxes:messages get-attachment", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "inboxes:messages get-attachment",
+ Transform: transform,
+ })
}
func handleInboxesMessagesGetRaw(ctx context.Context, cmd *cli.Command) error {
@@ -774,10 +854,6 @@ func handleInboxesMessagesGetRaw(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.InboxMessageGetRawParams{
- InboxID: cmd.Value("inbox-id").(string),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -789,6 +865,10 @@ func handleInboxesMessagesGetRaw(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.InboxMessageGetRawParams{
+ InboxID: cmd.Value("inbox-id").(string),
+ }
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Inboxes.Messages.GetRaw(
@@ -803,8 +883,15 @@ func handleInboxesMessagesGetRaw(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "inboxes:messages get-raw", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "inboxes:messages get-raw",
+ Transform: transform,
+ })
}
func handleInboxesMessagesReply(ctx context.Context, cmd *cli.Command) error {
@@ -818,10 +905,6 @@ func handleInboxesMessagesReply(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.InboxMessageReplyParams{
- InboxID: cmd.Value("inbox-id").(string),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -833,6 +916,10 @@ func handleInboxesMessagesReply(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.InboxMessageReplyParams{
+ InboxID: cmd.Value("inbox-id").(string),
+ }
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Inboxes.Messages.Reply(
@@ -847,8 +934,15 @@ func handleInboxesMessagesReply(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "inboxes:messages reply", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "inboxes:messages reply",
+ Transform: transform,
+ })
}
func handleInboxesMessagesReplyAll(ctx context.Context, cmd *cli.Command) error {
@@ -862,10 +956,6 @@ func handleInboxesMessagesReplyAll(ctx context.Context, cmd *cli.Command) error
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.InboxMessageReplyAllParams{
- InboxID: cmd.Value("inbox-id").(string),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -877,6 +967,10 @@ func handleInboxesMessagesReplyAll(ctx context.Context, cmd *cli.Command) error
return err
}
+ params := agentmail.InboxMessageReplyAllParams{
+ InboxID: cmd.Value("inbox-id").(string),
+ }
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Inboxes.Messages.ReplyAll(
@@ -891,8 +985,15 @@ func handleInboxesMessagesReplyAll(ctx context.Context, cmd *cli.Command) error
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "inboxes:messages reply-all", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "inboxes:messages reply-all",
+ Transform: transform,
+ })
}
func handleInboxesMessagesSend(ctx context.Context, cmd *cli.Command) error {
@@ -906,8 +1007,6 @@ func handleInboxesMessagesSend(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.InboxMessageSendParams{}
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -919,6 +1018,8 @@ func handleInboxesMessagesSend(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.InboxMessageSendParams{}
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Inboxes.Messages.Send(
@@ -933,6 +1034,13 @@ func handleInboxesMessagesSend(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "inboxes:messages send", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "inboxes:messages send",
+ Transform: transform,
+ })
}
diff --git a/pkg/cmd/inboxmessage_test.go b/pkg/cmd/inboxmessage_test.go
index 2fbd4d9..d21c0e9 100644
--- a/pkg/cmd/inboxmessage_test.go
+++ b/pkg/cmd/inboxmessage_test.go
@@ -52,6 +52,7 @@ func TestInboxesMessagesList(t *testing.T) {
"--include-blocked=true",
"--include-spam=true",
"--include-trash=true",
+ "--include-unauthenticated=true",
"--label", "[string]",
"--limit", "0",
"--page-token", "page_token",
diff --git a/pkg/cmd/inboxthread.go b/pkg/cmd/inboxthread.go
index d679a57..201208b 100644
--- a/pkg/cmd/inboxthread.go
+++ b/pkg/cmd/inboxthread.go
@@ -5,7 +5,6 @@ package cmd
import (
"context"
"fmt"
- "os"
"github.com/agentmail-to/agentmail-cli/internal/apiquery"
"github.com/agentmail-to/agentmail-cli/internal/requestflag"
@@ -21,16 +20,17 @@ var inboxesThreadsList = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "inbox-id",
- Usage: "The ID of the inbox.",
- Required: true,
+ Name: "inbox-id",
+ Usage: "The ID of the inbox.",
+ Required: true,
+ PathParam: "inbox_id",
},
&requestflag.Flag[any]{
Name: "after",
Usage: "Timestamp after which to filter by.",
QueryPath: "after",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*bool]{
Name: "ascending",
Usage: "Sort in ascending temporal order.",
QueryPath: "ascending",
@@ -40,32 +40,37 @@ var inboxesThreadsList = cli.Command{
Usage: "Timestamp before which to filter by.",
QueryPath: "before",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*bool]{
Name: "include-blocked",
Usage: "Include blocked in results.",
QueryPath: "include_blocked",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*bool]{
Name: "include-spam",
Usage: "Include spam in results.",
QueryPath: "include_spam",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*bool]{
Name: "include-trash",
Usage: "Include trash in results.",
QueryPath: "include_trash",
},
+ &requestflag.Flag[*bool]{
+ Name: "include-unauthenticated",
+ Usage: "Include unauthenticated in results.",
+ QueryPath: "include_unauthenticated",
+ },
&requestflag.Flag[any]{
Name: "label",
Usage: "Labels to filter by.",
QueryPath: "labels",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*int64]{
Name: "limit",
Usage: "Limit of number of items returned.",
QueryPath: "limit",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "page-token",
Usage: "Page token for pagination.",
QueryPath: "page_token",
@@ -81,16 +86,18 @@ var inboxesThreadsDelete = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "inbox-id",
- Usage: "The ID of the inbox.",
- Required: true,
+ Name: "inbox-id",
+ Usage: "The ID of the inbox.",
+ Required: true,
+ PathParam: "inbox_id",
},
&requestflag.Flag[string]{
- Name: "thread-id",
- Usage: "ID of thread.",
- Required: true,
+ Name: "thread-id",
+ Usage: "ID of thread.",
+ Required: true,
+ PathParam: "thread_id",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*bool]{
Name: "permanent",
Usage: "If true, permanently delete the thread instead of moving to trash.",
QueryPath: "permanent",
@@ -106,14 +113,16 @@ var inboxesThreadsGet = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "inbox-id",
- Usage: "The ID of the inbox.",
- Required: true,
+ Name: "inbox-id",
+ Usage: "The ID of the inbox.",
+ Required: true,
+ PathParam: "inbox_id",
},
&requestflag.Flag[string]{
- Name: "thread-id",
- Usage: "ID of thread.",
- Required: true,
+ Name: "thread-id",
+ Usage: "ID of thread.",
+ Required: true,
+ PathParam: "thread_id",
},
},
Action: handleInboxesThreadsGet,
@@ -126,19 +135,22 @@ var inboxesThreadsGetAttachment = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "inbox-id",
- Usage: "The ID of the inbox.",
- Required: true,
+ Name: "inbox-id",
+ Usage: "The ID of the inbox.",
+ Required: true,
+ PathParam: "inbox_id",
},
&requestflag.Flag[string]{
- Name: "thread-id",
- Usage: "ID of thread.",
- Required: true,
+ Name: "thread-id",
+ Usage: "ID of thread.",
+ Required: true,
+ PathParam: "thread_id",
},
&requestflag.Flag[string]{
- Name: "attachment-id",
- Usage: "ID of attachment.",
- Required: true,
+ Name: "attachment-id",
+ Usage: "ID of attachment.",
+ Required: true,
+ PathParam: "attachment_id",
},
},
Action: handleInboxesThreadsGetAttachment,
@@ -156,8 +168,6 @@ func handleInboxesThreadsList(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.InboxThreadListParams{}
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -169,6 +179,8 @@ func handleInboxesThreadsList(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.InboxThreadListParams{}
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Inboxes.Threads.List(
@@ -183,8 +195,15 @@ func handleInboxesThreadsList(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "inboxes:threads list", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "inboxes:threads list",
+ Transform: transform,
+ })
}
func handleInboxesThreadsDelete(ctx context.Context, cmd *cli.Command) error {
@@ -198,10 +217,6 @@ func handleInboxesThreadsDelete(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.InboxThreadDeleteParams{
- InboxID: cmd.Value("inbox-id").(string),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -213,6 +228,10 @@ func handleInboxesThreadsDelete(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.InboxThreadDeleteParams{
+ InboxID: cmd.Value("inbox-id").(string),
+ }
+
return client.Inboxes.Threads.Delete(
ctx,
cmd.Value("thread-id").(string),
@@ -232,10 +251,6 @@ func handleInboxesThreadsGet(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.InboxThreadGetParams{
- InboxID: cmd.Value("inbox-id").(string),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -247,6 +262,10 @@ func handleInboxesThreadsGet(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.InboxThreadGetParams{
+ InboxID: cmd.Value("inbox-id").(string),
+ }
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Inboxes.Threads.Get(
@@ -261,8 +280,15 @@ func handleInboxesThreadsGet(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "inboxes:threads get", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "inboxes:threads get",
+ Transform: transform,
+ })
}
func handleInboxesThreadsGetAttachment(ctx context.Context, cmd *cli.Command) error {
@@ -276,11 +302,6 @@ func handleInboxesThreadsGetAttachment(ctx context.Context, cmd *cli.Command) er
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.InboxThreadGetAttachmentParams{
- InboxID: cmd.Value("inbox-id").(string),
- ThreadID: cmd.Value("thread-id").(string),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -292,6 +313,11 @@ func handleInboxesThreadsGetAttachment(ctx context.Context, cmd *cli.Command) er
return err
}
+ params := agentmail.InboxThreadGetAttachmentParams{
+ InboxID: cmd.Value("inbox-id").(string),
+ ThreadID: cmd.Value("thread-id").(string),
+ }
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Inboxes.Threads.GetAttachment(
@@ -306,6 +332,13 @@ func handleInboxesThreadsGetAttachment(ctx context.Context, cmd *cli.Command) er
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "inboxes:threads get-attachment", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "inboxes:threads get-attachment",
+ Transform: transform,
+ })
}
diff --git a/pkg/cmd/inboxthread_test.go b/pkg/cmd/inboxthread_test.go
index 73b0e25..71c86ab 100644
--- a/pkg/cmd/inboxthread_test.go
+++ b/pkg/cmd/inboxthread_test.go
@@ -22,6 +22,7 @@ func TestInboxesThreadsList(t *testing.T) {
"--include-blocked=true",
"--include-spam=true",
"--include-trash=true",
+ "--include-unauthenticated=true",
"--label", "[string]",
"--limit", "0",
"--page-token", "page_token",
diff --git a/pkg/cmd/list.go b/pkg/cmd/list.go
index 8442109..3431bfd 100644
--- a/pkg/cmd/list.go
+++ b/pkg/cmd/list.go
@@ -5,7 +5,6 @@ package cmd
import (
"context"
"fmt"
- "os"
"github.com/agentmail-to/agentmail-cli/internal/apiquery"
"github.com/agentmail-to/agentmail-cli/internal/requestflag"
@@ -21,14 +20,16 @@ var listsCreate = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "direction",
- Usage: "Direction of list entry.",
- Required: true,
+ Name: "direction",
+ Usage: "Direction of list entry.",
+ Required: true,
+ PathParam: "direction",
},
&requestflag.Flag[string]{
- Name: "type",
- Usage: "Type of list entry.",
- Required: true,
+ Name: "type",
+ Usage: "Type of list entry.",
+ Required: true,
+ PathParam: "type",
},
&requestflag.Flag[string]{
Name: "entry",
@@ -36,7 +37,7 @@ var listsCreate = cli.Command{
Required: true,
BodyPath: "entry",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "reason",
Usage: "Reason for adding the entry.",
BodyPath: "reason",
@@ -52,21 +53,23 @@ var listsList = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "direction",
- Usage: "Direction of list entry.",
- Required: true,
+ Name: "direction",
+ Usage: "Direction of list entry.",
+ Required: true,
+ PathParam: "direction",
},
&requestflag.Flag[string]{
- Name: "type",
- Usage: "Type of list entry.",
- Required: true,
+ Name: "type",
+ Usage: "Type of list entry.",
+ Required: true,
+ PathParam: "type",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*int64]{
Name: "limit",
Usage: "Limit of number of items returned.",
QueryPath: "limit",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "page-token",
Usage: "Page token for pagination.",
QueryPath: "page_token",
@@ -82,18 +85,21 @@ var listsDelete = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "direction",
- Usage: "Direction of list entry.",
- Required: true,
+ Name: "direction",
+ Usage: "Direction of list entry.",
+ Required: true,
+ PathParam: "direction",
},
&requestflag.Flag[string]{
- Name: "type",
- Usage: "Type of list entry.",
- Required: true,
+ Name: "type",
+ Usage: "Type of list entry.",
+ Required: true,
+ PathParam: "type",
},
&requestflag.Flag[string]{
- Name: "entry",
- Required: true,
+ Name: "entry",
+ Required: true,
+ PathParam: "entry",
},
},
Action: handleListsDelete,
@@ -106,18 +112,21 @@ var listsGet = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "direction",
- Usage: "Direction of list entry.",
- Required: true,
+ Name: "direction",
+ Usage: "Direction of list entry.",
+ Required: true,
+ PathParam: "direction",
},
&requestflag.Flag[string]{
- Name: "type",
- Usage: "Type of list entry.",
- Required: true,
+ Name: "type",
+ Usage: "Type of list entry.",
+ Required: true,
+ PathParam: "type",
},
&requestflag.Flag[string]{
- Name: "entry",
- Required: true,
+ Name: "entry",
+ Required: true,
+ PathParam: "entry",
},
},
Action: handleListsGet,
@@ -135,10 +144,6 @@ func handleListsCreate(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.ListNewParams{
- Direction: agentmail.ListNewParamsDirection(cmd.Value("direction").(string)),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -150,6 +155,10 @@ func handleListsCreate(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.ListNewParams{
+ Direction: agentmail.ListNewParamsDirection(cmd.Value("direction").(string)),
+ }
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Lists.New(
@@ -164,8 +173,15 @@ func handleListsCreate(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "lists create", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "lists create",
+ Transform: transform,
+ })
}
func handleListsList(ctx context.Context, cmd *cli.Command) error {
@@ -179,10 +195,6 @@ func handleListsList(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.ListListParams{
- Direction: agentmail.ListListParamsDirection(cmd.Value("direction").(string)),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -194,6 +206,10 @@ func handleListsList(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.ListListParams{
+ Direction: agentmail.ListListParamsDirection(cmd.Value("direction").(string)),
+ }
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Lists.List(
@@ -208,8 +224,15 @@ func handleListsList(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "lists list", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "lists list",
+ Transform: transform,
+ })
}
func handleListsDelete(ctx context.Context, cmd *cli.Command) error {
@@ -223,11 +246,6 @@ func handleListsDelete(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.ListDeleteParams{
- Direction: agentmail.ListDeleteParamsDirection(cmd.Value("direction").(string)),
- Type: agentmail.ListDeleteParamsType(cmd.Value("type").(string)),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -239,6 +257,11 @@ func handleListsDelete(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.ListDeleteParams{
+ Direction: agentmail.ListDeleteParamsDirection(cmd.Value("direction").(string)),
+ Type: agentmail.ListDeleteParamsType(cmd.Value("type").(string)),
+ }
+
return client.Lists.Delete(
ctx,
cmd.Value("entry").(string),
@@ -258,11 +281,6 @@ func handleListsGet(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.ListGetParams{
- Direction: agentmail.ListGetParamsDirection(cmd.Value("direction").(string)),
- Type: agentmail.ListGetParamsType(cmd.Value("type").(string)),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -274,6 +292,11 @@ func handleListsGet(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.ListGetParams{
+ Direction: agentmail.ListGetParamsDirection(cmd.Value("direction").(string)),
+ Type: agentmail.ListGetParamsType(cmd.Value("type").(string)),
+ }
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Lists.Get(
@@ -288,6 +311,13 @@ func handleListsGet(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "lists get", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "lists get",
+ Transform: transform,
+ })
}
diff --git a/pkg/cmd/metric.go b/pkg/cmd/metric.go
index 7b1768b..267ee13 100644
--- a/pkg/cmd/metric.go
+++ b/pkg/cmd/metric.go
@@ -5,7 +5,6 @@ package cmd
import (
"context"
"fmt"
- "os"
"github.com/agentmail-to/agentmail-cli/internal/apiquery"
"github.com/agentmail-to/agentmail-cli/internal/requestflag"
@@ -20,7 +19,7 @@ var metricsList = cli.Command{
Usage: "**CLI:**",
Suggest: true,
Flags: []cli.Flag{
- &requestflag.Flag[any]{
+ &requestflag.Flag[*bool]{
Name: "descending",
Usage: "Sort in descending order.",
QueryPath: "descending",
@@ -35,12 +34,12 @@ var metricsList = cli.Command{
Usage: "List of metric event types to query.",
QueryPath: "event_types",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*int64]{
Name: "limit",
Usage: "Limit on number of buckets to return.",
QueryPath: "limit",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "period",
Usage: "Period in number of seconds for the query.",
QueryPath: "period",
@@ -63,8 +62,6 @@ func handleMetricsList(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.MetricListParams{}
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -76,6 +73,8 @@ func handleMetricsList(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.MetricListParams{}
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Metrics.List(ctx, params, options...)
@@ -85,6 +84,13 @@ func handleMetricsList(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "metrics list", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "metrics list",
+ Transform: transform,
+ })
}
diff --git a/pkg/cmd/organization.go b/pkg/cmd/organization.go
index d2aea0f..1af9601 100644
--- a/pkg/cmd/organization.go
+++ b/pkg/cmd/organization.go
@@ -5,7 +5,6 @@ package cmd
import (
"context"
"fmt"
- "os"
"github.com/agentmail-to/agentmail-cli/internal/apiquery"
"github.com/agentmail-to/agentmail-go"
@@ -51,6 +50,13 @@ func handleOrganizationsGet(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "organizations get", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "organizations get",
+ Transform: transform,
+ })
}
diff --git a/pkg/cmd/pod.go b/pkg/cmd/pod.go
index 64243e3..59e8f7b 100644
--- a/pkg/cmd/pod.go
+++ b/pkg/cmd/pod.go
@@ -5,7 +5,6 @@ package cmd
import (
"context"
"fmt"
- "os"
"github.com/agentmail-to/agentmail-cli/internal/apiquery"
"github.com/agentmail-to/agentmail-cli/internal/requestflag"
@@ -20,12 +19,12 @@ var podsCreate = cli.Command{
Usage: "**CLI:**",
Suggest: true,
Flags: []cli.Flag{
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "client-id",
Usage: "Client ID of pod.",
BodyPath: "client_id",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "name",
Usage: "Name of pod.",
BodyPath: "name",
@@ -40,17 +39,17 @@ var podsList = cli.Command{
Usage: "**CLI:**",
Suggest: true,
Flags: []cli.Flag{
- &requestflag.Flag[any]{
+ &requestflag.Flag[*bool]{
Name: "ascending",
Usage: "Sort in ascending temporal order.",
QueryPath: "ascending",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*int64]{
Name: "limit",
Usage: "Limit of number of items returned.",
QueryPath: "limit",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "page-token",
Usage: "Page token for pagination.",
QueryPath: "page_token",
@@ -66,9 +65,10 @@ var podsDelete = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "pod-id",
- Usage: "ID of pod.",
- Required: true,
+ Name: "pod-id",
+ Usage: "ID of pod.",
+ Required: true,
+ PathParam: "pod_id",
},
},
Action: handlePodsDelete,
@@ -81,9 +81,10 @@ var podsGet = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "pod-id",
- Usage: "ID of pod.",
- Required: true,
+ Name: "pod-id",
+ Usage: "ID of pod.",
+ Required: true,
+ PathParam: "pod_id",
},
},
Action: handlePodsGet,
@@ -98,8 +99,6 @@ func handlePodsCreate(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.PodNewParams{}
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -111,6 +110,8 @@ func handlePodsCreate(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.PodNewParams{}
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Pods.New(ctx, params, options...)
@@ -120,8 +121,15 @@ func handlePodsCreate(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "pods create", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "pods create",
+ Transform: transform,
+ })
}
func handlePodsList(ctx context.Context, cmd *cli.Command) error {
@@ -132,8 +140,6 @@ func handlePodsList(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.PodListParams{}
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -145,6 +151,8 @@ func handlePodsList(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.PodListParams{}
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Pods.List(ctx, params, options...)
@@ -154,8 +162,15 @@ func handlePodsList(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "pods list", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "pods list",
+ Transform: transform,
+ })
}
func handlePodsDelete(ctx context.Context, cmd *cli.Command) error {
@@ -214,6 +229,13 @@ func handlePodsGet(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "pods get", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "pods get",
+ Transform: transform,
+ })
}
diff --git a/pkg/cmd/podapikey.go b/pkg/cmd/podapikey.go
index 3b47191..ff85ae4 100644
--- a/pkg/cmd/podapikey.go
+++ b/pkg/cmd/podapikey.go
@@ -5,7 +5,6 @@ package cmd
import (
"context"
"fmt"
- "os"
"github.com/agentmail-to/agentmail-cli/internal/apiquery"
"github.com/agentmail-to/agentmail-cli/internal/requestflag"
@@ -21,11 +20,12 @@ var podsAPIKeysCreate = requestflag.WithInnerFlags(cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "pod-id",
- Usage: "ID of pod.",
- Required: true,
+ Name: "pod-id",
+ Usage: "ID of pod.",
+ Required: true,
+ PathParam: "pod_id",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "name",
Usage: "Name of api key.",
BodyPath: "name",
@@ -40,177 +40,177 @@ var podsAPIKeysCreate = requestflag.WithInnerFlags(cli.Command{
HideHelpCommand: true,
}, map[string][]requestflag.HasOuterFlag{
"permissions": {
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.api-key-create",
Usage: "Create API keys.",
InnerField: "api_key_create",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.api-key-delete",
Usage: "Delete API keys.",
InnerField: "api_key_delete",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.api-key-read",
Usage: "Read API keys.",
InnerField: "api_key_read",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.domain-create",
Usage: "Create domains.",
InnerField: "domain_create",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.domain-delete",
Usage: "Delete domains.",
InnerField: "domain_delete",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.domain-read",
Usage: "Read domain details.",
InnerField: "domain_read",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.domain-update",
Usage: "Update domains.",
InnerField: "domain_update",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.draft-create",
Usage: "Create drafts.",
InnerField: "draft_create",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.draft-delete",
Usage: "Delete drafts.",
InnerField: "draft_delete",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.draft-read",
Usage: "Read drafts.",
InnerField: "draft_read",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.draft-send",
Usage: "Send drafts.",
InnerField: "draft_send",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.draft-update",
Usage: "Update drafts.",
InnerField: "draft_update",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.inbox-create",
Usage: "Create new inboxes.",
InnerField: "inbox_create",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.inbox-delete",
Usage: "Delete inboxes.",
InnerField: "inbox_delete",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.inbox-read",
Usage: "Read inbox details.",
InnerField: "inbox_read",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.inbox-update",
Usage: "Update inbox settings.",
InnerField: "inbox_update",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.label-blocked-read",
Usage: "Access messages labeled blocked.",
InnerField: "label_blocked_read",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.label-spam-read",
Usage: "Access messages labeled spam.",
InnerField: "label_spam_read",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.label-trash-read",
Usage: "Access messages labeled trash.",
InnerField: "label_trash_read",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.list-entry-create",
Usage: "Create list entries.",
InnerField: "list_entry_create",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.list-entry-delete",
Usage: "Delete list entries.",
InnerField: "list_entry_delete",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.list-entry-read",
Usage: "Read list entries.",
InnerField: "list_entry_read",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.message-read",
Usage: "Read messages.",
InnerField: "message_read",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.message-send",
Usage: "Send messages.",
InnerField: "message_send",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.message-update",
Usage: "Update message labels.",
InnerField: "message_update",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.metrics-read",
Usage: "Read metrics.",
InnerField: "metrics_read",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.pod-create",
Usage: "Create pods.",
InnerField: "pod_create",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.pod-delete",
Usage: "Delete pods.",
InnerField: "pod_delete",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.pod-read",
Usage: "Read pods.",
InnerField: "pod_read",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.thread-delete",
Usage: "Delete threads.",
InnerField: "thread_delete",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.thread-read",
Usage: "Read threads.",
InnerField: "thread_read",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.webhook-create",
Usage: "Create webhooks.",
InnerField: "webhook_create",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.webhook-delete",
Usage: "Delete webhooks.",
InnerField: "webhook_delete",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.webhook-read",
Usage: "Read webhook configurations.",
InnerField: "webhook_read",
},
- &requestflag.InnerFlag[any]{
+ &requestflag.InnerFlag[*bool]{
Name: "permissions.webhook-update",
Usage: "Update webhooks.",
InnerField: "webhook_update",
@@ -224,16 +224,17 @@ var podsAPIKeysList = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "pod-id",
- Usage: "ID of pod.",
- Required: true,
+ Name: "pod-id",
+ Usage: "ID of pod.",
+ Required: true,
+ PathParam: "pod_id",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*int64]{
Name: "limit",
Usage: "Limit of number of items returned.",
QueryPath: "limit",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "page-token",
Usage: "Page token for pagination.",
QueryPath: "page_token",
@@ -249,14 +250,16 @@ var podsAPIKeysDelete = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "pod-id",
- Usage: "ID of pod.",
- Required: true,
+ Name: "pod-id",
+ Usage: "ID of pod.",
+ Required: true,
+ PathParam: "pod_id",
},
&requestflag.Flag[string]{
- Name: "api-key-id",
- Usage: "ID of api key.",
- Required: true,
+ Name: "api-key-id",
+ Usage: "ID of api key.",
+ Required: true,
+ PathParam: "api_key_id",
},
},
Action: handlePodsAPIKeysDelete,
@@ -274,8 +277,6 @@ func handlePodsAPIKeysCreate(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.PodAPIKeyNewParams{}
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -287,6 +288,8 @@ func handlePodsAPIKeysCreate(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.PodAPIKeyNewParams{}
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Pods.APIKeys.New(
@@ -301,8 +304,15 @@ func handlePodsAPIKeysCreate(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "pods:api-keys create", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "pods:api-keys create",
+ Transform: transform,
+ })
}
func handlePodsAPIKeysList(ctx context.Context, cmd *cli.Command) error {
@@ -316,8 +326,6 @@ func handlePodsAPIKeysList(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.PodAPIKeyListParams{}
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -329,6 +337,8 @@ func handlePodsAPIKeysList(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.PodAPIKeyListParams{}
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Pods.APIKeys.List(
@@ -343,8 +353,15 @@ func handlePodsAPIKeysList(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "pods:api-keys list", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "pods:api-keys list",
+ Transform: transform,
+ })
}
func handlePodsAPIKeysDelete(ctx context.Context, cmd *cli.Command) error {
@@ -358,10 +375,6 @@ func handlePodsAPIKeysDelete(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.PodAPIKeyDeleteParams{
- PodID: cmd.Value("pod-id").(string),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -373,6 +386,10 @@ func handlePodsAPIKeysDelete(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.PodAPIKeyDeleteParams{
+ PodID: cmd.Value("pod-id").(string),
+ }
+
return client.Pods.APIKeys.Delete(
ctx,
cmd.Value("api-key-id").(string),
diff --git a/pkg/cmd/poddomain.go b/pkg/cmd/poddomain.go
index 92b31bd..61083cd 100644
--- a/pkg/cmd/poddomain.go
+++ b/pkg/cmd/poddomain.go
@@ -5,7 +5,6 @@ package cmd
import (
"context"
"fmt"
- "os"
"github.com/agentmail-to/agentmail-cli/internal/apiquery"
"github.com/agentmail-to/agentmail-cli/internal/requestflag"
@@ -21,9 +20,10 @@ var podsDomainsCreate = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "pod-id",
- Usage: "ID of pod.",
- Required: true,
+ Name: "pod-id",
+ Usage: "ID of pod.",
+ Required: true,
+ PathParam: "pod_id",
},
&requestflag.Flag[string]{
Name: "domain",
@@ -48,16 +48,18 @@ var podsDomainsUpdate = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "pod-id",
- Usage: "ID of pod.",
- Required: true,
+ Name: "pod-id",
+ Usage: "ID of pod.",
+ Required: true,
+ PathParam: "pod_id",
},
&requestflag.Flag[string]{
- Name: "domain-id",
- Usage: "The ID of the domain.",
- Required: true,
+ Name: "domain-id",
+ Usage: "The ID of the domain.",
+ Required: true,
+ PathParam: "domain_id",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*bool]{
Name: "feedback-enabled",
Usage: "Bounce and complaint notifications are sent to your inboxes.",
BodyPath: "feedback_enabled",
@@ -73,21 +75,22 @@ var podsDomainsList = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "pod-id",
- Usage: "ID of pod.",
- Required: true,
+ Name: "pod-id",
+ Usage: "ID of pod.",
+ Required: true,
+ PathParam: "pod_id",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*bool]{
Name: "ascending",
Usage: "Sort in ascending temporal order.",
QueryPath: "ascending",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*int64]{
Name: "limit",
Usage: "Limit of number of items returned.",
QueryPath: "limit",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "page-token",
Usage: "Page token for pagination.",
QueryPath: "page_token",
@@ -103,14 +106,16 @@ var podsDomainsDelete = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "pod-id",
- Usage: "ID of pod.",
- Required: true,
+ Name: "pod-id",
+ Usage: "ID of pod.",
+ Required: true,
+ PathParam: "pod_id",
},
&requestflag.Flag[string]{
- Name: "domain-id",
- Usage: "The ID of the domain.",
- Required: true,
+ Name: "domain-id",
+ Usage: "The ID of the domain.",
+ Required: true,
+ PathParam: "domain_id",
},
},
Action: handlePodsDomainsDelete,
@@ -123,14 +128,16 @@ var podsDomainsGet = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "pod-id",
- Usage: "ID of pod.",
- Required: true,
+ Name: "pod-id",
+ Usage: "ID of pod.",
+ Required: true,
+ PathParam: "pod_id",
},
&requestflag.Flag[string]{
- Name: "domain-id",
- Usage: "The ID of the domain.",
- Required: true,
+ Name: "domain-id",
+ Usage: "The ID of the domain.",
+ Required: true,
+ PathParam: "domain_id",
},
},
Action: handlePodsDomainsGet,
@@ -143,14 +150,16 @@ var podsDomainsGetZoneFile = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "pod-id",
- Usage: "ID of pod.",
- Required: true,
+ Name: "pod-id",
+ Usage: "ID of pod.",
+ Required: true,
+ PathParam: "pod_id",
},
&requestflag.Flag[string]{
- Name: "domain-id",
- Usage: "The ID of the domain.",
- Required: true,
+ Name: "domain-id",
+ Usage: "The ID of the domain.",
+ Required: true,
+ PathParam: "domain_id",
},
},
Action: handlePodsDomainsGetZoneFile,
@@ -163,14 +172,16 @@ var podsDomainsVerify = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "pod-id",
- Usage: "ID of pod.",
- Required: true,
+ Name: "pod-id",
+ Usage: "ID of pod.",
+ Required: true,
+ PathParam: "pod_id",
},
&requestflag.Flag[string]{
- Name: "domain-id",
- Usage: "The ID of the domain.",
- Required: true,
+ Name: "domain-id",
+ Usage: "The ID of the domain.",
+ Required: true,
+ PathParam: "domain_id",
},
},
Action: handlePodsDomainsVerify,
@@ -188,8 +199,6 @@ func handlePodsDomainsCreate(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.PodDomainNewParams{}
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -201,6 +210,8 @@ func handlePodsDomainsCreate(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.PodDomainNewParams{}
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Pods.Domains.New(
@@ -215,8 +226,15 @@ func handlePodsDomainsCreate(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "pods:domains create", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "pods:domains create",
+ Transform: transform,
+ })
}
func handlePodsDomainsUpdate(ctx context.Context, cmd *cli.Command) error {
@@ -230,10 +248,6 @@ func handlePodsDomainsUpdate(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.PodDomainUpdateParams{
- PodID: cmd.Value("pod-id").(string),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -245,6 +259,10 @@ func handlePodsDomainsUpdate(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.PodDomainUpdateParams{
+ PodID: cmd.Value("pod-id").(string),
+ }
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Pods.Domains.Update(
@@ -259,8 +277,15 @@ func handlePodsDomainsUpdate(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "pods:domains update", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "pods:domains update",
+ Transform: transform,
+ })
}
func handlePodsDomainsList(ctx context.Context, cmd *cli.Command) error {
@@ -274,8 +299,6 @@ func handlePodsDomainsList(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.PodDomainListParams{}
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -287,6 +310,8 @@ func handlePodsDomainsList(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.PodDomainListParams{}
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Pods.Domains.List(
@@ -301,8 +326,15 @@ func handlePodsDomainsList(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "pods:domains list", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "pods:domains list",
+ Transform: transform,
+ })
}
func handlePodsDomainsDelete(ctx context.Context, cmd *cli.Command) error {
@@ -316,10 +348,6 @@ func handlePodsDomainsDelete(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.PodDomainDeleteParams{
- PodID: cmd.Value("pod-id").(string),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -331,6 +359,10 @@ func handlePodsDomainsDelete(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.PodDomainDeleteParams{
+ PodID: cmd.Value("pod-id").(string),
+ }
+
return client.Pods.Domains.Delete(
ctx,
cmd.Value("domain-id").(string),
@@ -350,10 +382,6 @@ func handlePodsDomainsGet(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.PodDomainGetParams{
- PodID: cmd.Value("pod-id").(string),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -365,6 +393,10 @@ func handlePodsDomainsGet(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.PodDomainGetParams{
+ PodID: cmd.Value("pod-id").(string),
+ }
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Pods.Domains.Get(
@@ -379,8 +411,15 @@ func handlePodsDomainsGet(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "pods:domains get", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "pods:domains get",
+ Transform: transform,
+ })
}
func handlePodsDomainsGetZoneFile(ctx context.Context, cmd *cli.Command) error {
@@ -394,10 +433,6 @@ func handlePodsDomainsGetZoneFile(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.PodDomainGetZoneFileParams{
- PodID: cmd.Value("pod-id").(string),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -409,6 +444,10 @@ func handlePodsDomainsGetZoneFile(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.PodDomainGetZoneFileParams{
+ PodID: cmd.Value("pod-id").(string),
+ }
+
return client.Pods.Domains.GetZoneFile(
ctx,
cmd.Value("domain-id").(string),
@@ -428,10 +467,6 @@ func handlePodsDomainsVerify(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.PodDomainVerifyParams{
- PodID: cmd.Value("pod-id").(string),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -443,6 +478,10 @@ func handlePodsDomainsVerify(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.PodDomainVerifyParams{
+ PodID: cmd.Value("pod-id").(string),
+ }
+
return client.Pods.Domains.Verify(
ctx,
cmd.Value("domain-id").(string),
diff --git a/pkg/cmd/poddraft.go b/pkg/cmd/poddraft.go
index 1ecf2bc..ae61c90 100644
--- a/pkg/cmd/poddraft.go
+++ b/pkg/cmd/poddraft.go
@@ -5,7 +5,6 @@ package cmd
import (
"context"
"fmt"
- "os"
"github.com/agentmail-to/agentmail-cli/internal/apiquery"
"github.com/agentmail-to/agentmail-cli/internal/requestflag"
@@ -21,16 +20,17 @@ var podsDraftsList = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "pod-id",
- Usage: "ID of pod.",
- Required: true,
+ Name: "pod-id",
+ Usage: "ID of pod.",
+ Required: true,
+ PathParam: "pod_id",
},
&requestflag.Flag[any]{
Name: "after",
Usage: "Timestamp after which to filter by.",
QueryPath: "after",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*bool]{
Name: "ascending",
Usage: "Sort in ascending temporal order.",
QueryPath: "ascending",
@@ -45,12 +45,12 @@ var podsDraftsList = cli.Command{
Usage: "Labels to filter by.",
QueryPath: "labels",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*int64]{
Name: "limit",
Usage: "Limit of number of items returned.",
QueryPath: "limit",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "page-token",
Usage: "Page token for pagination.",
QueryPath: "page_token",
@@ -66,14 +66,16 @@ var podsDraftsGet = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "pod-id",
- Usage: "ID of pod.",
- Required: true,
+ Name: "pod-id",
+ Usage: "ID of pod.",
+ Required: true,
+ PathParam: "pod_id",
},
&requestflag.Flag[string]{
- Name: "draft-id",
- Usage: "ID of draft.",
- Required: true,
+ Name: "draft-id",
+ Usage: "ID of draft.",
+ Required: true,
+ PathParam: "draft_id",
},
},
Action: handlePodsDraftsGet,
@@ -86,19 +88,22 @@ var podsDraftsGetAttachment = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "pod-id",
- Usage: "ID of pod.",
- Required: true,
+ Name: "pod-id",
+ Usage: "ID of pod.",
+ Required: true,
+ PathParam: "pod_id",
},
&requestflag.Flag[string]{
- Name: "draft-id",
- Usage: "ID of draft.",
- Required: true,
+ Name: "draft-id",
+ Usage: "ID of draft.",
+ Required: true,
+ PathParam: "draft_id",
},
&requestflag.Flag[string]{
- Name: "attachment-id",
- Usage: "ID of attachment.",
- Required: true,
+ Name: "attachment-id",
+ Usage: "ID of attachment.",
+ Required: true,
+ PathParam: "attachment_id",
},
},
Action: handlePodsDraftsGetAttachment,
@@ -116,8 +121,6 @@ func handlePodsDraftsList(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.PodDraftListParams{}
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -129,6 +132,8 @@ func handlePodsDraftsList(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.PodDraftListParams{}
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Pods.Drafts.List(
@@ -143,8 +148,15 @@ func handlePodsDraftsList(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "pods:drafts list", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "pods:drafts list",
+ Transform: transform,
+ })
}
func handlePodsDraftsGet(ctx context.Context, cmd *cli.Command) error {
@@ -158,10 +170,6 @@ func handlePodsDraftsGet(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.PodDraftGetParams{
- PodID: cmd.Value("pod-id").(string),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -173,6 +181,10 @@ func handlePodsDraftsGet(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.PodDraftGetParams{
+ PodID: cmd.Value("pod-id").(string),
+ }
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Pods.Drafts.Get(
@@ -187,8 +199,15 @@ func handlePodsDraftsGet(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "pods:drafts get", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "pods:drafts get",
+ Transform: transform,
+ })
}
func handlePodsDraftsGetAttachment(ctx context.Context, cmd *cli.Command) error {
@@ -202,11 +221,6 @@ func handlePodsDraftsGetAttachment(ctx context.Context, cmd *cli.Command) error
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.PodDraftGetAttachmentParams{
- PodID: cmd.Value("pod-id").(string),
- DraftID: cmd.Value("draft-id").(string),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -218,6 +232,11 @@ func handlePodsDraftsGetAttachment(ctx context.Context, cmd *cli.Command) error
return err
}
+ params := agentmail.PodDraftGetAttachmentParams{
+ PodID: cmd.Value("pod-id").(string),
+ DraftID: cmd.Value("draft-id").(string),
+ }
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Pods.Drafts.GetAttachment(
@@ -232,6 +251,13 @@ func handlePodsDraftsGetAttachment(ctx context.Context, cmd *cli.Command) error
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "pods:drafts get-attachment", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "pods:drafts get-attachment",
+ Transform: transform,
+ })
}
diff --git a/pkg/cmd/podinbox.go b/pkg/cmd/podinbox.go
index aa0729b..e7c797d 100644
--- a/pkg/cmd/podinbox.go
+++ b/pkg/cmd/podinbox.go
@@ -5,7 +5,6 @@ package cmd
import (
"context"
"fmt"
- "os"
"github.com/agentmail-to/agentmail-cli/internal/apiquery"
"github.com/agentmail-to/agentmail-cli/internal/requestflag"
@@ -21,26 +20,27 @@ var podsInboxesCreate = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "pod-id",
- Usage: "ID of pod.",
- Required: true,
+ Name: "pod-id",
+ Usage: "ID of pod.",
+ Required: true,
+ PathParam: "pod_id",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "client-id",
Usage: "Client ID of inbox.",
BodyPath: "client_id",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "display-name",
Usage: "Display name: `Display Name `.",
BodyPath: "display_name",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "domain",
Usage: "Domain of address. Must be verified domain. Defaults to `agentmail.to`.",
BodyPath: "domain",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "username",
Usage: "Username of address. Randomly generated if not specified.",
BodyPath: "username",
@@ -56,14 +56,16 @@ var podsInboxesUpdate = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "pod-id",
- Usage: "ID of pod.",
- Required: true,
+ Name: "pod-id",
+ Usage: "ID of pod.",
+ Required: true,
+ PathParam: "pod_id",
},
&requestflag.Flag[string]{
- Name: "inbox-id",
- Usage: "The ID of the inbox.",
- Required: true,
+ Name: "inbox-id",
+ Usage: "The ID of the inbox.",
+ Required: true,
+ PathParam: "inbox_id",
},
&requestflag.Flag[string]{
Name: "display-name",
@@ -82,21 +84,22 @@ var podsInboxesList = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "pod-id",
- Usage: "ID of pod.",
- Required: true,
+ Name: "pod-id",
+ Usage: "ID of pod.",
+ Required: true,
+ PathParam: "pod_id",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*bool]{
Name: "ascending",
Usage: "Sort in ascending temporal order.",
QueryPath: "ascending",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*int64]{
Name: "limit",
Usage: "Limit of number of items returned.",
QueryPath: "limit",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "page-token",
Usage: "Page token for pagination.",
QueryPath: "page_token",
@@ -112,14 +115,16 @@ var podsInboxesDelete = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "pod-id",
- Usage: "ID of pod.",
- Required: true,
+ Name: "pod-id",
+ Usage: "ID of pod.",
+ Required: true,
+ PathParam: "pod_id",
},
&requestflag.Flag[string]{
- Name: "inbox-id",
- Usage: "The ID of the inbox.",
- Required: true,
+ Name: "inbox-id",
+ Usage: "The ID of the inbox.",
+ Required: true,
+ PathParam: "inbox_id",
},
},
Action: handlePodsInboxesDelete,
@@ -132,14 +137,16 @@ var podsInboxesGet = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "pod-id",
- Usage: "ID of pod.",
- Required: true,
+ Name: "pod-id",
+ Usage: "ID of pod.",
+ Required: true,
+ PathParam: "pod_id",
},
&requestflag.Flag[string]{
- Name: "inbox-id",
- Usage: "The ID of the inbox.",
- Required: true,
+ Name: "inbox-id",
+ Usage: "The ID of the inbox.",
+ Required: true,
+ PathParam: "inbox_id",
},
},
Action: handlePodsInboxesGet,
@@ -157,8 +164,6 @@ func handlePodsInboxesCreate(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.PodInboxNewParams{}
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -170,6 +175,8 @@ func handlePodsInboxesCreate(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.PodInboxNewParams{}
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Pods.Inboxes.New(
@@ -184,8 +191,15 @@ func handlePodsInboxesCreate(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "pods:inboxes create", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "pods:inboxes create",
+ Transform: transform,
+ })
}
func handlePodsInboxesUpdate(ctx context.Context, cmd *cli.Command) error {
@@ -199,10 +213,6 @@ func handlePodsInboxesUpdate(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.PodInboxUpdateParams{
- PodID: cmd.Value("pod-id").(string),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -214,6 +224,10 @@ func handlePodsInboxesUpdate(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.PodInboxUpdateParams{
+ PodID: cmd.Value("pod-id").(string),
+ }
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Pods.Inboxes.Update(
@@ -228,8 +242,15 @@ func handlePodsInboxesUpdate(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "pods:inboxes update", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "pods:inboxes update",
+ Transform: transform,
+ })
}
func handlePodsInboxesList(ctx context.Context, cmd *cli.Command) error {
@@ -243,8 +264,6 @@ func handlePodsInboxesList(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.PodInboxListParams{}
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -256,6 +275,8 @@ func handlePodsInboxesList(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.PodInboxListParams{}
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Pods.Inboxes.List(
@@ -270,8 +291,15 @@ func handlePodsInboxesList(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "pods:inboxes list", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "pods:inboxes list",
+ Transform: transform,
+ })
}
func handlePodsInboxesDelete(ctx context.Context, cmd *cli.Command) error {
@@ -285,10 +313,6 @@ func handlePodsInboxesDelete(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.PodInboxDeleteParams{
- PodID: cmd.Value("pod-id").(string),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -300,6 +324,10 @@ func handlePodsInboxesDelete(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.PodInboxDeleteParams{
+ PodID: cmd.Value("pod-id").(string),
+ }
+
return client.Pods.Inboxes.Delete(
ctx,
cmd.Value("inbox-id").(string),
@@ -319,10 +347,6 @@ func handlePodsInboxesGet(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.PodInboxGetParams{
- PodID: cmd.Value("pod-id").(string),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -334,6 +358,10 @@ func handlePodsInboxesGet(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.PodInboxGetParams{
+ PodID: cmd.Value("pod-id").(string),
+ }
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Pods.Inboxes.Get(
@@ -348,6 +376,13 @@ func handlePodsInboxesGet(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "pods:inboxes get", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "pods:inboxes get",
+ Transform: transform,
+ })
}
diff --git a/pkg/cmd/podlist.go b/pkg/cmd/podlist.go
index 10c0a14..c720b84 100644
--- a/pkg/cmd/podlist.go
+++ b/pkg/cmd/podlist.go
@@ -5,7 +5,6 @@ package cmd
import (
"context"
"fmt"
- "os"
"github.com/agentmail-to/agentmail-cli/internal/apiquery"
"github.com/agentmail-to/agentmail-cli/internal/requestflag"
@@ -21,19 +20,22 @@ var podsListsCreate = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "pod-id",
- Usage: "ID of pod.",
- Required: true,
+ Name: "pod-id",
+ Usage: "ID of pod.",
+ Required: true,
+ PathParam: "pod_id",
},
&requestflag.Flag[string]{
- Name: "direction",
- Usage: "Direction of list entry.",
- Required: true,
+ Name: "direction",
+ Usage: "Direction of list entry.",
+ Required: true,
+ PathParam: "direction",
},
&requestflag.Flag[string]{
- Name: "type",
- Usage: "Type of list entry.",
- Required: true,
+ Name: "type",
+ Usage: "Type of list entry.",
+ Required: true,
+ PathParam: "type",
},
&requestflag.Flag[string]{
Name: "entry",
@@ -41,7 +43,7 @@ var podsListsCreate = cli.Command{
Required: true,
BodyPath: "entry",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "reason",
Usage: "Reason for adding the entry.",
BodyPath: "reason",
@@ -57,26 +59,29 @@ var podsListsList = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "pod-id",
- Usage: "ID of pod.",
- Required: true,
+ Name: "pod-id",
+ Usage: "ID of pod.",
+ Required: true,
+ PathParam: "pod_id",
},
&requestflag.Flag[string]{
- Name: "direction",
- Usage: "Direction of list entry.",
- Required: true,
+ Name: "direction",
+ Usage: "Direction of list entry.",
+ Required: true,
+ PathParam: "direction",
},
&requestflag.Flag[string]{
- Name: "type",
- Usage: "Type of list entry.",
- Required: true,
+ Name: "type",
+ Usage: "Type of list entry.",
+ Required: true,
+ PathParam: "type",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*int64]{
Name: "limit",
Usage: "Limit of number of items returned.",
QueryPath: "limit",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "page-token",
Usage: "Page token for pagination.",
QueryPath: "page_token",
@@ -92,23 +97,27 @@ var podsListsDelete = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "pod-id",
- Usage: "ID of pod.",
- Required: true,
+ Name: "pod-id",
+ Usage: "ID of pod.",
+ Required: true,
+ PathParam: "pod_id",
},
&requestflag.Flag[string]{
- Name: "direction",
- Usage: "Direction of list entry.",
- Required: true,
+ Name: "direction",
+ Usage: "Direction of list entry.",
+ Required: true,
+ PathParam: "direction",
},
&requestflag.Flag[string]{
- Name: "type",
- Usage: "Type of list entry.",
- Required: true,
+ Name: "type",
+ Usage: "Type of list entry.",
+ Required: true,
+ PathParam: "type",
},
&requestflag.Flag[string]{
- Name: "entry",
- Required: true,
+ Name: "entry",
+ Required: true,
+ PathParam: "entry",
},
},
Action: handlePodsListsDelete,
@@ -121,23 +130,27 @@ var podsListsGet = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "pod-id",
- Usage: "ID of pod.",
- Required: true,
+ Name: "pod-id",
+ Usage: "ID of pod.",
+ Required: true,
+ PathParam: "pod_id",
},
&requestflag.Flag[string]{
- Name: "direction",
- Usage: "Direction of list entry.",
- Required: true,
+ Name: "direction",
+ Usage: "Direction of list entry.",
+ Required: true,
+ PathParam: "direction",
},
&requestflag.Flag[string]{
- Name: "type",
- Usage: "Type of list entry.",
- Required: true,
+ Name: "type",
+ Usage: "Type of list entry.",
+ Required: true,
+ PathParam: "type",
},
&requestflag.Flag[string]{
- Name: "entry",
- Required: true,
+ Name: "entry",
+ Required: true,
+ PathParam: "entry",
},
},
Action: handlePodsListsGet,
@@ -155,11 +168,6 @@ func handlePodsListsCreate(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.PodListNewParams{
- PodID: cmd.Value("pod-id").(string),
- Direction: agentmail.PodListNewParamsDirection(cmd.Value("direction").(string)),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -171,6 +179,11 @@ func handlePodsListsCreate(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.PodListNewParams{
+ PodID: cmd.Value("pod-id").(string),
+ Direction: agentmail.PodListNewParamsDirection(cmd.Value("direction").(string)),
+ }
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Pods.Lists.New(
@@ -185,8 +198,15 @@ func handlePodsListsCreate(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "pods:lists create", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "pods:lists create",
+ Transform: transform,
+ })
}
func handlePodsListsList(ctx context.Context, cmd *cli.Command) error {
@@ -200,11 +220,6 @@ func handlePodsListsList(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.PodListListParams{
- PodID: cmd.Value("pod-id").(string),
- Direction: agentmail.PodListListParamsDirection(cmd.Value("direction").(string)),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -216,6 +231,11 @@ func handlePodsListsList(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.PodListListParams{
+ PodID: cmd.Value("pod-id").(string),
+ Direction: agentmail.PodListListParamsDirection(cmd.Value("direction").(string)),
+ }
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Pods.Lists.List(
@@ -230,8 +250,15 @@ func handlePodsListsList(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "pods:lists list", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "pods:lists list",
+ Transform: transform,
+ })
}
func handlePodsListsDelete(ctx context.Context, cmd *cli.Command) error {
@@ -245,12 +272,6 @@ func handlePodsListsDelete(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.PodListDeleteParams{
- PodID: cmd.Value("pod-id").(string),
- Direction: agentmail.PodListDeleteParamsDirection(cmd.Value("direction").(string)),
- Type: agentmail.PodListDeleteParamsType(cmd.Value("type").(string)),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -262,6 +283,12 @@ func handlePodsListsDelete(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.PodListDeleteParams{
+ PodID: cmd.Value("pod-id").(string),
+ Direction: agentmail.PodListDeleteParamsDirection(cmd.Value("direction").(string)),
+ Type: agentmail.PodListDeleteParamsType(cmd.Value("type").(string)),
+ }
+
return client.Pods.Lists.Delete(
ctx,
cmd.Value("entry").(string),
@@ -281,12 +308,6 @@ func handlePodsListsGet(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.PodListGetParams{
- PodID: cmd.Value("pod-id").(string),
- Direction: agentmail.PodListGetParamsDirection(cmd.Value("direction").(string)),
- Type: agentmail.PodListGetParamsType(cmd.Value("type").(string)),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -298,6 +319,12 @@ func handlePodsListsGet(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.PodListGetParams{
+ PodID: cmd.Value("pod-id").(string),
+ Direction: agentmail.PodListGetParamsDirection(cmd.Value("direction").(string)),
+ Type: agentmail.PodListGetParamsType(cmd.Value("type").(string)),
+ }
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Pods.Lists.Get(
@@ -312,6 +339,13 @@ func handlePodsListsGet(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "pods:lists get", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "pods:lists get",
+ Transform: transform,
+ })
}
diff --git a/pkg/cmd/podmetric.go b/pkg/cmd/podmetric.go
index 88579b1..99c857d 100644
--- a/pkg/cmd/podmetric.go
+++ b/pkg/cmd/podmetric.go
@@ -5,7 +5,6 @@ package cmd
import (
"context"
"fmt"
- "os"
"github.com/agentmail-to/agentmail-cli/internal/apiquery"
"github.com/agentmail-to/agentmail-cli/internal/requestflag"
@@ -21,11 +20,12 @@ var podsMetricsQuery = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "pod-id",
- Usage: "ID of pod.",
- Required: true,
+ Name: "pod-id",
+ Usage: "ID of pod.",
+ Required: true,
+ PathParam: "pod_id",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*bool]{
Name: "descending",
Usage: "Sort in descending order.",
QueryPath: "descending",
@@ -40,12 +40,12 @@ var podsMetricsQuery = cli.Command{
Usage: "List of metric event types to query.",
QueryPath: "event_types",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*int64]{
Name: "limit",
Usage: "Limit on number of buckets to return.",
QueryPath: "limit",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "period",
Usage: "Period in number of seconds for the query.",
QueryPath: "period",
@@ -71,8 +71,6 @@ func handlePodsMetricsQuery(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.PodMetricQueryParams{}
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -84,6 +82,8 @@ func handlePodsMetricsQuery(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.PodMetricQueryParams{}
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Pods.Metrics.Query(
@@ -98,6 +98,13 @@ func handlePodsMetricsQuery(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "pods:metrics query", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "pods:metrics query",
+ Transform: transform,
+ })
}
diff --git a/pkg/cmd/podthread.go b/pkg/cmd/podthread.go
index 92ce97d..084222e 100644
--- a/pkg/cmd/podthread.go
+++ b/pkg/cmd/podthread.go
@@ -5,7 +5,6 @@ package cmd
import (
"context"
"fmt"
- "os"
"github.com/agentmail-to/agentmail-cli/internal/apiquery"
"github.com/agentmail-to/agentmail-cli/internal/requestflag"
@@ -21,16 +20,17 @@ var podsThreadsList = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "pod-id",
- Usage: "ID of pod.",
- Required: true,
+ Name: "pod-id",
+ Usage: "ID of pod.",
+ Required: true,
+ PathParam: "pod_id",
},
&requestflag.Flag[any]{
Name: "after",
Usage: "Timestamp after which to filter by.",
QueryPath: "after",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*bool]{
Name: "ascending",
Usage: "Sort in ascending temporal order.",
QueryPath: "ascending",
@@ -40,32 +40,37 @@ var podsThreadsList = cli.Command{
Usage: "Timestamp before which to filter by.",
QueryPath: "before",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*bool]{
Name: "include-blocked",
Usage: "Include blocked in results.",
QueryPath: "include_blocked",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*bool]{
Name: "include-spam",
Usage: "Include spam in results.",
QueryPath: "include_spam",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*bool]{
Name: "include-trash",
Usage: "Include trash in results.",
QueryPath: "include_trash",
},
+ &requestflag.Flag[*bool]{
+ Name: "include-unauthenticated",
+ Usage: "Include unauthenticated in results.",
+ QueryPath: "include_unauthenticated",
+ },
&requestflag.Flag[any]{
Name: "label",
Usage: "Labels to filter by.",
QueryPath: "labels",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*int64]{
Name: "limit",
Usage: "Limit of number of items returned.",
QueryPath: "limit",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "page-token",
Usage: "Page token for pagination.",
QueryPath: "page_token",
@@ -81,16 +86,18 @@ var podsThreadsDelete = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "pod-id",
- Usage: "ID of pod.",
- Required: true,
+ Name: "pod-id",
+ Usage: "ID of pod.",
+ Required: true,
+ PathParam: "pod_id",
},
&requestflag.Flag[string]{
- Name: "thread-id",
- Usage: "ID of thread.",
- Required: true,
+ Name: "thread-id",
+ Usage: "ID of thread.",
+ Required: true,
+ PathParam: "thread_id",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*bool]{
Name: "permanent",
Usage: "If true, permanently delete the thread instead of moving to trash.",
QueryPath: "permanent",
@@ -106,14 +113,16 @@ var podsThreadsGet = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "pod-id",
- Usage: "ID of pod.",
- Required: true,
+ Name: "pod-id",
+ Usage: "ID of pod.",
+ Required: true,
+ PathParam: "pod_id",
},
&requestflag.Flag[string]{
- Name: "thread-id",
- Usage: "ID of thread.",
- Required: true,
+ Name: "thread-id",
+ Usage: "ID of thread.",
+ Required: true,
+ PathParam: "thread_id",
},
},
Action: handlePodsThreadsGet,
@@ -126,19 +135,22 @@ var podsThreadsGetAttachment = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "pod-id",
- Usage: "ID of pod.",
- Required: true,
+ Name: "pod-id",
+ Usage: "ID of pod.",
+ Required: true,
+ PathParam: "pod_id",
},
&requestflag.Flag[string]{
- Name: "thread-id",
- Usage: "ID of thread.",
- Required: true,
+ Name: "thread-id",
+ Usage: "ID of thread.",
+ Required: true,
+ PathParam: "thread_id",
},
&requestflag.Flag[string]{
- Name: "attachment-id",
- Usage: "ID of attachment.",
- Required: true,
+ Name: "attachment-id",
+ Usage: "ID of attachment.",
+ Required: true,
+ PathParam: "attachment_id",
},
},
Action: handlePodsThreadsGetAttachment,
@@ -156,8 +168,6 @@ func handlePodsThreadsList(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.PodThreadListParams{}
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -169,6 +179,8 @@ func handlePodsThreadsList(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.PodThreadListParams{}
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Pods.Threads.List(
@@ -183,8 +195,15 @@ func handlePodsThreadsList(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "pods:threads list", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "pods:threads list",
+ Transform: transform,
+ })
}
func handlePodsThreadsDelete(ctx context.Context, cmd *cli.Command) error {
@@ -198,10 +217,6 @@ func handlePodsThreadsDelete(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.PodThreadDeleteParams{
- PodID: cmd.Value("pod-id").(string),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -213,6 +228,10 @@ func handlePodsThreadsDelete(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.PodThreadDeleteParams{
+ PodID: cmd.Value("pod-id").(string),
+ }
+
return client.Pods.Threads.Delete(
ctx,
cmd.Value("thread-id").(string),
@@ -232,10 +251,6 @@ func handlePodsThreadsGet(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.PodThreadGetParams{
- PodID: cmd.Value("pod-id").(string),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -247,6 +262,10 @@ func handlePodsThreadsGet(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.PodThreadGetParams{
+ PodID: cmd.Value("pod-id").(string),
+ }
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Pods.Threads.Get(
@@ -261,8 +280,15 @@ func handlePodsThreadsGet(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "pods:threads get", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "pods:threads get",
+ Transform: transform,
+ })
}
func handlePodsThreadsGetAttachment(ctx context.Context, cmd *cli.Command) error {
@@ -276,11 +302,6 @@ func handlePodsThreadsGetAttachment(ctx context.Context, cmd *cli.Command) error
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.PodThreadGetAttachmentParams{
- PodID: cmd.Value("pod-id").(string),
- ThreadID: cmd.Value("thread-id").(string),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -292,6 +313,11 @@ func handlePodsThreadsGetAttachment(ctx context.Context, cmd *cli.Command) error
return err
}
+ params := agentmail.PodThreadGetAttachmentParams{
+ PodID: cmd.Value("pod-id").(string),
+ ThreadID: cmd.Value("thread-id").(string),
+ }
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Pods.Threads.GetAttachment(
@@ -306,6 +332,13 @@ func handlePodsThreadsGetAttachment(ctx context.Context, cmd *cli.Command) error
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "pods:threads get-attachment", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "pods:threads get-attachment",
+ Transform: transform,
+ })
}
diff --git a/pkg/cmd/podthread_test.go b/pkg/cmd/podthread_test.go
index fe7bab7..f2c674a 100644
--- a/pkg/cmd/podthread_test.go
+++ b/pkg/cmd/podthread_test.go
@@ -22,6 +22,7 @@ func TestPodsThreadsList(t *testing.T) {
"--include-blocked=true",
"--include-spam=true",
"--include-trash=true",
+ "--include-unauthenticated=true",
"--label", "[string]",
"--limit", "0",
"--page-token", "page_token",
diff --git a/pkg/cmd/thread.go b/pkg/cmd/thread.go
index c41f595..b9a345d 100644
--- a/pkg/cmd/thread.go
+++ b/pkg/cmd/thread.go
@@ -5,7 +5,6 @@ package cmd
import (
"context"
"fmt"
- "os"
"github.com/agentmail-to/agentmail-cli/internal/apiquery"
"github.com/agentmail-to/agentmail-cli/internal/requestflag"
@@ -25,7 +24,7 @@ var threadsList = cli.Command{
Usage: "Timestamp after which to filter by.",
QueryPath: "after",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*bool]{
Name: "ascending",
Usage: "Sort in ascending temporal order.",
QueryPath: "ascending",
@@ -35,32 +34,37 @@ var threadsList = cli.Command{
Usage: "Timestamp before which to filter by.",
QueryPath: "before",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*bool]{
Name: "include-blocked",
Usage: "Include blocked in results.",
QueryPath: "include_blocked",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*bool]{
Name: "include-spam",
Usage: "Include spam in results.",
QueryPath: "include_spam",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*bool]{
Name: "include-trash",
Usage: "Include trash in results.",
QueryPath: "include_trash",
},
+ &requestflag.Flag[*bool]{
+ Name: "include-unauthenticated",
+ Usage: "Include unauthenticated in results.",
+ QueryPath: "include_unauthenticated",
+ },
&requestflag.Flag[any]{
Name: "label",
Usage: "Labels to filter by.",
QueryPath: "labels",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*int64]{
Name: "limit",
Usage: "Limit of number of items returned.",
QueryPath: "limit",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "page-token",
Usage: "Page token for pagination.",
QueryPath: "page_token",
@@ -76,11 +80,12 @@ var threadsDelete = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "thread-id",
- Usage: "ID of thread.",
- Required: true,
+ Name: "thread-id",
+ Usage: "ID of thread.",
+ Required: true,
+ PathParam: "thread_id",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*bool]{
Name: "permanent",
Usage: "If true, permanently delete the thread instead of moving to trash.",
QueryPath: "permanent",
@@ -96,9 +101,10 @@ var threadsGet = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "thread-id",
- Usage: "ID of thread.",
- Required: true,
+ Name: "thread-id",
+ Usage: "ID of thread.",
+ Required: true,
+ PathParam: "thread_id",
},
},
Action: handleThreadsGet,
@@ -111,14 +117,16 @@ var threadsGetAttachment = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "thread-id",
- Usage: "ID of thread.",
- Required: true,
+ Name: "thread-id",
+ Usage: "ID of thread.",
+ Required: true,
+ PathParam: "thread_id",
},
&requestflag.Flag[string]{
- Name: "attachment-id",
- Usage: "ID of attachment.",
- Required: true,
+ Name: "attachment-id",
+ Usage: "ID of attachment.",
+ Required: true,
+ PathParam: "attachment_id",
},
},
Action: handleThreadsGetAttachment,
@@ -133,8 +141,6 @@ func handleThreadsList(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.ThreadListParams{}
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -146,6 +152,8 @@ func handleThreadsList(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.ThreadListParams{}
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Threads.List(ctx, params, options...)
@@ -155,8 +163,15 @@ func handleThreadsList(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "threads list", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "threads list",
+ Transform: transform,
+ })
}
func handleThreadsDelete(ctx context.Context, cmd *cli.Command) error {
@@ -170,8 +185,6 @@ func handleThreadsDelete(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.ThreadDeleteParams{}
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -183,6 +196,8 @@ func handleThreadsDelete(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.ThreadDeleteParams{}
+
return client.Threads.Delete(
ctx,
cmd.Value("thread-id").(string),
@@ -222,8 +237,15 @@ func handleThreadsGet(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "threads get", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "threads get",
+ Transform: transform,
+ })
}
func handleThreadsGetAttachment(ctx context.Context, cmd *cli.Command) error {
@@ -237,10 +259,6 @@ func handleThreadsGetAttachment(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.ThreadGetAttachmentParams{
- ThreadID: cmd.Value("thread-id").(string),
- }
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -252,6 +270,10 @@ func handleThreadsGetAttachment(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.ThreadGetAttachmentParams{
+ ThreadID: cmd.Value("thread-id").(string),
+ }
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Threads.GetAttachment(
@@ -266,6 +288,13 @@ func handleThreadsGetAttachment(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "threads get-attachment", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "threads get-attachment",
+ Transform: transform,
+ })
}
diff --git a/pkg/cmd/thread_test.go b/pkg/cmd/thread_test.go
index 9835b56..b9d55f9 100644
--- a/pkg/cmd/thread_test.go
+++ b/pkg/cmd/thread_test.go
@@ -21,6 +21,7 @@ func TestThreadsList(t *testing.T) {
"--include-blocked=true",
"--include-spam=true",
"--include-trash=true",
+ "--include-unauthenticated=true",
"--label", "[string]",
"--limit", "0",
"--page-token", "page_token",
diff --git a/pkg/cmd/version.go b/pkg/cmd/version.go
index 1830802..d0a0ee8 100644
--- a/pkg/cmd/version.go
+++ b/pkg/cmd/version.go
@@ -2,4 +2,4 @@
package cmd
-const Version = "0.7.11" // x-release-please-version
+const Version = "0.7.12" // x-release-please-version
diff --git a/pkg/cmd/webhook.go b/pkg/cmd/webhook.go
index afff2aa..0294e1d 100644
--- a/pkg/cmd/webhook.go
+++ b/pkg/cmd/webhook.go
@@ -5,7 +5,6 @@ package cmd
import (
"context"
"fmt"
- "os"
"github.com/agentmail-to/agentmail-cli/internal/apiquery"
"github.com/agentmail-to/agentmail-cli/internal/requestflag"
@@ -22,7 +21,7 @@ var webhooksCreate = cli.Command{
Flags: []cli.Flag{
&requestflag.Flag[[]string]{
Name: "event-type",
- Usage: "Event types for which to send events.",
+ Usage: "Full list of event types this webhook should receive. At least one type is required. Send every type you\nwant in this array (not incremental). See [Webhooks overview](https://docs.agentmail.to/webhooks-overview)\nfor spam, blocked, and unauthenticated events and required permissions.",
Required: true,
BodyPath: "event_types",
},
@@ -32,7 +31,7 @@ var webhooksCreate = cli.Command{
Required: true,
BodyPath: "url",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "client-id",
Usage: "Client ID of webhook.",
BodyPath: "client_id",
@@ -54,13 +53,14 @@ var webhooksCreate = cli.Command{
var webhooksUpdate = cli.Command{
Name: "update",
- Usage: "**CLI:**",
+ Usage: "Update inbox or pod subscriptions, or replace the webhook's `event_types` in\nfull when you pass a non-empty `event_types` array (see request field docs).\nInbox and pod changes use add/remove lists.",
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "webhook-id",
- Usage: "ID of webhook.",
- Required: true,
+ Name: "webhook-id",
+ Usage: "ID of webhook.",
+ Required: true,
+ PathParam: "webhook_id",
},
&requestflag.Flag[any]{
Name: "add-inbox-id",
@@ -72,6 +72,11 @@ var webhooksUpdate = cli.Command{
Usage: "Pod IDs to subscribe to the webhook.",
BodyPath: "add_pod_ids",
},
+ &requestflag.Flag[any]{
+ Name: "event-type",
+ Usage: "When you send a non-empty list, it replaces the webhook's subscribed event types in full (the same\n\"set the list\" behavior as create). It is not a merge or diff: include every event type you want after\nthe update. Sending a one-element array means the webhook will only receive that one type afterward.\nOmit this field or send an empty array to leave event types unchanged. Clearing all types with an empty\nlist is not supported. Subscribing to `message.received.spam`, `message.received.blocked`, or\n`message.received.unauthenticated` requires the matching label permission on the API key.",
+ BodyPath: "event_types",
+ },
&requestflag.Flag[any]{
Name: "remove-inbox-id",
Usage: "Inbox IDs to unsubscribe from the webhook.",
@@ -92,17 +97,17 @@ var webhooksList = cli.Command{
Usage: "**CLI:**",
Suggest: true,
Flags: []cli.Flag{
- &requestflag.Flag[any]{
+ &requestflag.Flag[*bool]{
Name: "ascending",
Usage: "Sort in ascending temporal order.",
QueryPath: "ascending",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*int64]{
Name: "limit",
Usage: "Limit of number of items returned.",
QueryPath: "limit",
},
- &requestflag.Flag[any]{
+ &requestflag.Flag[*string]{
Name: "page-token",
Usage: "Page token for pagination.",
QueryPath: "page_token",
@@ -118,9 +123,10 @@ var webhooksDelete = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "webhook-id",
- Usage: "ID of webhook.",
- Required: true,
+ Name: "webhook-id",
+ Usage: "ID of webhook.",
+ Required: true,
+ PathParam: "webhook_id",
},
},
Action: handleWebhooksDelete,
@@ -133,9 +139,10 @@ var webhooksGet = cli.Command{
Suggest: true,
Flags: []cli.Flag{
&requestflag.Flag[string]{
- Name: "webhook-id",
- Usage: "ID of webhook.",
- Required: true,
+ Name: "webhook-id",
+ Usage: "ID of webhook.",
+ Required: true,
+ PathParam: "webhook_id",
},
},
Action: handleWebhooksGet,
@@ -150,8 +157,6 @@ func handleWebhooksCreate(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.WebhookNewParams{}
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -163,6 +168,8 @@ func handleWebhooksCreate(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.WebhookNewParams{}
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Webhooks.New(ctx, params, options...)
@@ -172,8 +179,15 @@ func handleWebhooksCreate(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "webhooks create", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "webhooks create",
+ Transform: transform,
+ })
}
func handleWebhooksUpdate(ctx context.Context, cmd *cli.Command) error {
@@ -187,8 +201,6 @@ func handleWebhooksUpdate(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.WebhookUpdateParams{}
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -200,6 +212,8 @@ func handleWebhooksUpdate(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.WebhookUpdateParams{}
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Webhooks.Update(
@@ -214,8 +228,15 @@ func handleWebhooksUpdate(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "webhooks update", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "webhooks update",
+ Transform: transform,
+ })
}
func handleWebhooksList(ctx context.Context, cmd *cli.Command) error {
@@ -226,8 +247,6 @@ func handleWebhooksList(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
}
- params := agentmail.WebhookListParams{}
-
options, err := flagOptions(
cmd,
apiquery.NestedQueryFormatBrackets,
@@ -239,6 +258,8 @@ func handleWebhooksList(ctx context.Context, cmd *cli.Command) error {
return err
}
+ params := agentmail.WebhookListParams{}
+
var res []byte
options = append(options, option.WithResponseBodyInto(&res))
_, err = client.Webhooks.List(ctx, params, options...)
@@ -248,8 +269,15 @@ func handleWebhooksList(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "webhooks list", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "webhooks list",
+ Transform: transform,
+ })
}
func handleWebhooksDelete(ctx context.Context, cmd *cli.Command) error {
@@ -308,6 +336,13 @@ func handleWebhooksGet(ctx context.Context, cmd *cli.Command) error {
obj := gjson.ParseBytes(res)
format := cmd.Root().String("format")
+ explicitFormat := cmd.Root().IsSet("format")
transform := cmd.Root().String("transform")
- return ShowJSON(os.Stdout, "webhooks get", obj, format, transform)
+ return ShowJSON(obj, ShowJSONOpts{
+ ExplicitFormat: explicitFormat,
+ Format: format,
+ RawOutput: cmd.Root().Bool("raw-output"),
+ Title: "webhooks get",
+ Transform: transform,
+ })
}
diff --git a/pkg/cmd/webhook_test.go b/pkg/cmd/webhook_test.go
index 03d11cb..c5c929b 100644
--- a/pkg/cmd/webhook_test.go
+++ b/pkg/cmd/webhook_test.go
@@ -52,6 +52,7 @@ func TestWebhooksUpdate(t *testing.T) {
"--webhook-id", "webhook_id",
"--add-inbox-id", "[string]",
"--add-pod-id", "[string]",
+ "--event-type", "[message.received]",
"--remove-inbox-id", "[string]",
"--remove-pod-id", "[string]",
)
@@ -64,6 +65,8 @@ func TestWebhooksUpdate(t *testing.T) {
" - string\n" +
"add_pod_ids:\n" +
" - string\n" +
+ "event_types:\n" +
+ " - message.received\n" +
"remove_inbox_ids:\n" +
" - string\n" +
"remove_pod_ids:\n" +
diff --git a/scripts/bootstrap b/scripts/bootstrap
index 9ebb7d3..bbc786d 100755
--- a/scripts/bootstrap
+++ b/scripts/bootstrap
@@ -4,7 +4,7 @@ set -e
cd "$(dirname "$0")/.."
-if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "$SKIP_BREW" != "1" ] && [ -t 0 ]; then
+if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "${SKIP_BREW:-}" != "1" ] && [ -t 0 ]; then
brew bundle check >/dev/null 2>&1 || {
echo -n "==> Install Homebrew dependencies? (y/N): "
read -r response