diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml index df3058e..9c8076e 100644 --- a/.github/workflows/release-doctor.yml +++ b/.github/workflows/release-doctor.yml @@ -9,7 +9,7 @@ jobs: release_doctor: name: release doctor runs-on: ubuntu-latest - if: github.repository == 'stainless-sdks/cerca-cli' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next') + if: github.repository == 'matrices/cerca-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 diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 1332969..3d2ac0b 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.0.1" + ".": "0.1.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 0287a32..36f5c54 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 66 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/calvinfo-o4h6u5%2Fcerca-1ca6943fcf7d9c2630357db20c3d1eb4495d4ecab907f4e20f62508175b8ae5c.yml -openapi_spec_hash: 9c2f51f5495c1b592e96a9e0c9969f46 -config_hash: 9105f134a32978d1d1bfd7c6b30c2a3b +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/calvinfo-o4h6u5/cerca-6338f21488a9e712e2f5cf43b2687c51c88c4b11c05239bc1aa86db643417b74.yml +openapi_spec_hash: e8825a4209dfbf533407eef182184acf +config_hash: d55df93567c0e09fea6827066eeb10c1 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..0732400 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,24 @@ +# Changelog + +## 0.1.0 (2026-04-30) + +Full Changelog: [v0.0.1...v0.1.0](https://github.com/matrices/cerca-cli/compare/v0.0.1...v0.1.0) + +### Features + +* **api:** manual updates ([7806afd](https://github.com/matrices/cerca-cli/commit/7806afd2cb58f1438f6aca04476ac95ae07daf39)) +* support passing path and query params over stdin ([67a8c65](https://github.com/matrices/cerca-cli/commit/67a8c655fe8e124855d5c73332063d477a627cad)) + + +### Bug Fixes + +* **cli:** correctly load zsh autocompletion ([b20f691](https://github.com/matrices/cerca-cli/commit/b20f6914f3ad282d91a18bf7557a693e5944a5d5)) +* flags for nullable body scalar fields are strictly typed ([88408b9](https://github.com/matrices/cerca-cli/commit/88408b9360bfaab9ef94198b1c6bf81897c86aec)) + + +### Chores + +* configure new SDK language ([ee21394](https://github.com/matrices/cerca-cli/commit/ee2139491a83fa2aec8d66305839ec8fd274de5d)) +* configure new SDK language ([a5f666b](https://github.com/matrices/cerca-cli/commit/a5f666bd240a8f0471b410c17e5ec7a93ba9b428)) +* configure new SDK language ([8a2d277](https://github.com/matrices/cerca-cli/commit/8a2d277426b9b83d9d4b0ab6ce7c76d9447d5f7d)) +* update SDK settings ([8254e9b](https://github.com/matrices/cerca-cli/commit/8254e9b96718b206c4b067528e93365fdb79f89a)) diff --git a/README.md b/README.md index c5d3aa9..3f224a2 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ The official CLI for the [Cerca REST API](https://docs.cerca.dev). It is generated with [Stainless](https://www.stainless.com/). + + ## Installation ### Installing with Go @@ -11,7 +13,7 @@ It is generated with [Stainless](https://www.stainless.com/). To test or install the CLI locally, you need [Go](https://go.dev/doc/install) version 1.22 or later installed. ```sh -go install 'github.com/stainless-sdks/cerca-cli/cmd/cerca@latest' +go install 'github.com/matrices/cerca-cli/cmd/cerca@latest' ``` Once you have run `go install`, the binary is placed in your Go bin directory: @@ -26,6 +28,8 @@ If commands aren't found after installation, add the Go bin directory to your PA export PATH="$PATH:$(go env GOPATH)/bin" ``` + + ### Running Locally After cloning the git repository for this project, you can use the diff --git a/cmd/cerca/main.go b/cmd/cerca/main.go index a5db262..5dc606e 100644 --- a/cmd/cerca/main.go +++ b/cmd/cerca/main.go @@ -10,8 +10,8 @@ import ( "os" "slices" + "github.com/matrices/cerca-cli/pkg/cmd" "github.com/matrices/cerca-go" - "github.com/stainless-sdks/cerca-cli/pkg/cmd" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" ) diff --git a/go.mod b/go.mod index ee93a84..dc60d5a 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/stainless-sdks/cerca-cli +module github.com/matrices/cerca-cli go 1.25 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/requestflag/innerflag.go b/internal/requestflag/innerflag.go index eeeb8bc..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 @@ -25,6 +26,12 @@ type InnerFlag[ 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. @@ -76,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 { @@ -136,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 bfaf064..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, @@ -67,6 +94,7 @@ type InRequest interface { GetQueryPath() string GetHeaderPath() string GetBodyPath() string + GetPathParam() string IsBodyRoot() bool IsFileInput() bool GetDataAliases() []string @@ -84,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 } @@ -103,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{ @@ -286,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 @@ -341,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 { @@ -396,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 } @@ -419,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 } @@ -429,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 @@ -465,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 @@ -501,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`), @@ -594,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)) @@ -705,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]{} @@ -718,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 2a87754..4c6c5c7 100644 --- a/pkg/cmd/agent.go +++ b/pkg/cmd/agent.go @@ -6,10 +6,10 @@ import ( "context" "fmt" + "github.com/matrices/cerca-cli/internal/apiquery" + "github.com/matrices/cerca-cli/internal/requestflag" "github.com/matrices/cerca-go" "github.com/matrices/cerca-go/option" - "github.com/stainless-sdks/cerca-cli/internal/apiquery" - "github.com/stainless-sdks/cerca-cli/internal/requestflag" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" ) @@ -19,11 +19,6 @@ var agentsCreate = requestflag.WithInnerFlags(cli.Command{ Usage: "Perform create operation", Suggest: true, Flags: []cli.Flag{ - &requestflag.Flag[string]{ - Name: "user-id", - Required: true, - BodyPath: "userId", - }, &requestflag.Flag[map[string]any]{ Name: "configuration", BodyPath: "configuration", @@ -37,6 +32,10 @@ var agentsCreate = requestflag.WithInnerFlags(cli.Command{ Usage: "Arbitrary string metadata stored on an agent. Runtime enforces maximum key and value sizes.", BodyPath: "metadata", }, + &requestflag.Flag[string]{ + Name: "user-id", + BodyPath: "userId", + }, }, Action: handleAgentsCreate, HideHelpCommand: true, @@ -67,8 +66,9 @@ var agentsRetrieve = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, }, Action: handleAgentsRetrieve, @@ -81,8 +81,9 @@ var agentsUpdate = requestflag.WithInnerFlags(cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, &requestflag.Flag[map[string]any]{ Name: "configuration", @@ -153,8 +154,9 @@ var agentsDelete = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, }, Action: handleAgentsDelete, @@ -167,8 +169,9 @@ var agentsRetrieveConfig = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, }, Action: handleAgentsRetrieveConfig, @@ -181,8 +184,9 @@ var agentsUpdateMetadata = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, &requestflag.Flag[map[string]any]{ Name: "metadata", @@ -203,8 +207,6 @@ func handleAgentsCreate(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := cercago.AgentNewParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -216,6 +218,8 @@ func handleAgentsCreate(ctx context.Context, cmd *cli.Command) error { return err } + params := cercago.AgentNewParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Agents.New(ctx, params, options...) @@ -289,8 +293,6 @@ func handleAgentsUpdate(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := cercago.AgentUpdateParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -302,6 +304,8 @@ func handleAgentsUpdate(ctx context.Context, cmd *cli.Command) error { return err } + params := cercago.AgentUpdateParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Agents.Update( @@ -335,8 +339,6 @@ func handleAgentsList(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := cercago.AgentListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -348,6 +350,8 @@ func handleAgentsList(ctx context.Context, cmd *cli.Command) error { return err } + params := cercago.AgentListParams{} + format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") @@ -477,8 +481,6 @@ func handleAgentsUpdateMetadata(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := cercago.AgentUpdateMetadataParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -490,6 +492,8 @@ func handleAgentsUpdateMetadata(ctx context.Context, cmd *cli.Command) error { return err } + params := cercago.AgentUpdateMetadataParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Agents.UpdateMetadata( diff --git a/pkg/cmd/agent_test.go b/pkg/cmd/agent_test.go index 76dbc04..30ab14b 100644 --- a/pkg/cmd/agent_test.go +++ b/pkg/cmd/agent_test.go @@ -5,8 +5,8 @@ package cmd import ( "testing" - "github.com/stainless-sdks/cerca-cli/internal/mocktest" - "github.com/stainless-sdks/cerca-cli/internal/requestflag" + "github.com/matrices/cerca-cli/internal/mocktest" + "github.com/matrices/cerca-cli/internal/requestflag" ) func TestAgentsCreate(t *testing.T) { @@ -15,10 +15,10 @@ func TestAgentsCreate(t *testing.T) { t, "--api-key", "string", "agents", "create", - "--user-id", "userId", "--configuration", "{approvals: {timeoutMs: 0, tools: {foo: always}}, defaultModel: defaultModel, instructions: instructions, tools: [sandbox.*]}", "--fleet-id", "fleetId", "--metadata", "{project: alpha}", + "--user-id", "userId", ) }) @@ -31,20 +31,19 @@ func TestAgentsCreate(t *testing.T) { t, "--api-key", "string", "agents", "create", - "--user-id", "userId", "--configuration.approvals", "{timeoutMs: 0, tools: {foo: always}}", "--configuration.default-model", "defaultModel", "--configuration.instructions", "instructions", "--configuration.tools", "[sandbox.*]", "--fleet-id", "fleetId", "--metadata", "{project: alpha}", + "--user-id", "userId", ) }) t.Run("piping data", func(t *testing.T) { // Test piping YAML data over stdin pipeData := []byte("" + - "userId: userId\n" + "configuration:\n" + " approvals:\n" + " timeoutMs: 0\n" + @@ -56,7 +55,8 @@ func TestAgentsCreate(t *testing.T) { " - sandbox.*\n" + "fleetId: fleetId\n" + "metadata:\n" + - " project: alpha\n") + " project: alpha\n" + + "userId: userId\n") mocktest.TestRunMockTestWithPipeAndFlags( t, pipeData, "--api-key", "string", diff --git a/pkg/cmd/approval.go b/pkg/cmd/approval.go index 1380afd..a51f20d 100644 --- a/pkg/cmd/approval.go +++ b/pkg/cmd/approval.go @@ -6,10 +6,10 @@ import ( "context" "fmt" + "github.com/matrices/cerca-cli/internal/apiquery" + "github.com/matrices/cerca-cli/internal/requestflag" "github.com/matrices/cerca-go" "github.com/matrices/cerca-go/option" - "github.com/stainless-sdks/cerca-cli/internal/apiquery" - "github.com/stainless-sdks/cerca-cli/internal/requestflag" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" ) @@ -20,8 +20,9 @@ var approvalsList = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, &requestflag.Flag[string]{ Name: "cursor", @@ -53,16 +54,19 @@ var approvalsResolve = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, &requestflag.Flag[string]{ - Name: "thread-id", - Required: true, + Name: "thread-id", + Required: true, + PathParam: "threadId", }, &requestflag.Flag[string]{ - Name: "approval-id", - Required: true, + Name: "approval-id", + Required: true, + PathParam: "approvalId", }, &requestflag.Flag[string]{ Name: "decision", @@ -91,8 +95,6 @@ func handleApprovalsList(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := cercago.ApprovalListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -104,6 +106,8 @@ func handleApprovalsList(ctx context.Context, cmd *cli.Command) error { return err } + params := cercago.ApprovalListParams{} + format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") @@ -167,8 +171,6 @@ func handleApprovalsResolve(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := cercago.ApprovalResolveParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -180,6 +182,8 @@ func handleApprovalsResolve(ctx context.Context, cmd *cli.Command) error { return err } + params := cercago.ApprovalResolveParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Approvals.Resolve( diff --git a/pkg/cmd/approval_test.go b/pkg/cmd/approval_test.go index 2ce029e..b0295e5 100644 --- a/pkg/cmd/approval_test.go +++ b/pkg/cmd/approval_test.go @@ -5,7 +5,7 @@ package cmd import ( "testing" - "github.com/stainless-sdks/cerca-cli/internal/mocktest" + "github.com/matrices/cerca-cli/internal/mocktest" ) func TestApprovalsList(t *testing.T) { diff --git a/pkg/cmd/approvalgrant.go b/pkg/cmd/approvalgrant.go index d36d16c..a91ab26 100644 --- a/pkg/cmd/approvalgrant.go +++ b/pkg/cmd/approvalgrant.go @@ -6,10 +6,10 @@ import ( "context" "fmt" + "github.com/matrices/cerca-cli/internal/apiquery" + "github.com/matrices/cerca-cli/internal/requestflag" "github.com/matrices/cerca-go" "github.com/matrices/cerca-go/option" - "github.com/stainless-sdks/cerca-cli/internal/apiquery" - "github.com/stainless-sdks/cerca-cli/internal/requestflag" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" ) @@ -20,8 +20,9 @@ var approvalGrantsList = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, &requestflag.Flag[string]{ Name: "cursor", @@ -48,12 +49,14 @@ var approvalGrantsDelete = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, &requestflag.Flag[string]{ - Name: "grant-id", - Required: true, + Name: "grant-id", + Required: true, + PathParam: "grantId", }, }, Action: handleApprovalGrantsDelete, @@ -66,16 +69,19 @@ var approvalGrantsDeleteForThread = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, &requestflag.Flag[string]{ - Name: "thread-id", - Required: true, + Name: "thread-id", + Required: true, + PathParam: "threadId", }, &requestflag.Flag[string]{ - Name: "grant-id", - Required: true, + Name: "grant-id", + Required: true, + PathParam: "grantId", }, }, Action: handleApprovalGrantsDeleteForThread, @@ -88,12 +94,14 @@ var approvalGrantsListForThread = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, &requestflag.Flag[string]{ - Name: "thread-id", - Required: true, + Name: "thread-id", + Required: true, + PathParam: "threadId", }, }, Action: handleApprovalGrantsListForThread, @@ -111,8 +119,6 @@ func handleApprovalGrantsList(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := cercago.ApprovalGrantListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -124,6 +130,8 @@ func handleApprovalGrantsList(ctx context.Context, cmd *cli.Command) error { return err } + params := cercago.ApprovalGrantListParams{} + format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") diff --git a/pkg/cmd/approvalgrant_test.go b/pkg/cmd/approvalgrant_test.go index 6ac425a..e721f9c 100644 --- a/pkg/cmd/approvalgrant_test.go +++ b/pkg/cmd/approvalgrant_test.go @@ -5,7 +5,7 @@ package cmd import ( "testing" - "github.com/stainless-sdks/cerca-cli/internal/mocktest" + "github.com/matrices/cerca-cli/internal/mocktest" ) func TestApprovalGrantsList(t *testing.T) { diff --git a/pkg/cmd/auth.go b/pkg/cmd/auth.go index 5176459..fd75732 100644 --- a/pkg/cmd/auth.go +++ b/pkg/cmd/auth.go @@ -6,10 +6,10 @@ import ( "context" "fmt" + "github.com/matrices/cerca-cli/internal/apiquery" + "github.com/matrices/cerca-cli/internal/requestflag" "github.com/matrices/cerca-go" "github.com/matrices/cerca-go/option" - "github.com/stainless-sdks/cerca-cli/internal/apiquery" - "github.com/stainless-sdks/cerca-cli/internal/requestflag" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" ) @@ -94,8 +94,6 @@ func handleAuthListFleets(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := cercago.AuthListFleetsParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -107,6 +105,8 @@ func handleAuthListFleets(ctx context.Context, cmd *cli.Command) error { return err } + params := cercago.AuthListFleetsParams{} + format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") diff --git a/pkg/cmd/auth_test.go b/pkg/cmd/auth_test.go index 959672b..3bcbedb 100644 --- a/pkg/cmd/auth_test.go +++ b/pkg/cmd/auth_test.go @@ -5,7 +5,7 @@ package cmd import ( "testing" - "github.com/stainless-sdks/cerca-cli/internal/mocktest" + "github.com/matrices/cerca-cli/internal/mocktest" ) func TestAuthContext(t *testing.T) { diff --git a/pkg/cmd/cmd.go b/pkg/cmd/cmd.go index 66b9c1b..6b1d611 100644 --- a/pkg/cmd/cmd.go +++ b/pkg/cmd/cmd.go @@ -12,8 +12,8 @@ import ( "slices" "strings" - "github.com/stainless-sdks/cerca-cli/internal/autocomplete" - "github.com/stainless-sdks/cerca-cli/internal/requestflag" + "github.com/matrices/cerca-cli/internal/autocomplete" + "github.com/matrices/cerca-cli/internal/requestflag" docs "github.com/urfave/cli-docs/v3" "github.com/urfave/cli/v3" ) diff --git a/pkg/cmd/cmdutil.go b/pkg/cmd/cmdutil.go index 88c9724..de25c95 100644 --- a/pkg/cmd/cmdutil.go +++ b/pkg/cmd/cmdutil.go @@ -16,8 +16,8 @@ import ( "strings" "syscall" + "github.com/matrices/cerca-cli/internal/jsonview" "github.com/matrices/cerca-go/option" - "github.com/stainless-sdks/cerca-cli/internal/jsonview" "github.com/charmbracelet/x/term" "github.com/itchyny/json2yaml" diff --git a/pkg/cmd/cmdutil_test.go b/pkg/cmd/cmdutil_test.go index 13b8256..e38f193 100644 --- a/pkg/cmd/cmdutil_test.go +++ b/pkg/cmd/cmdutil_test.go @@ -12,7 +12,7 @@ import ( "github.com/stretchr/testify/require" "github.com/tidwall/gjson" - "github.com/stainless-sdks/cerca-cli/internal/jsonview" + "github.com/matrices/cerca-cli/internal/jsonview" ) func TestStreamOutput(t *testing.T) { diff --git a/pkg/cmd/connection.go b/pkg/cmd/connection.go index 9021027..ec3c9af 100644 --- a/pkg/cmd/connection.go +++ b/pkg/cmd/connection.go @@ -6,10 +6,10 @@ import ( "context" "fmt" + "github.com/matrices/cerca-cli/internal/apiquery" + "github.com/matrices/cerca-cli/internal/requestflag" "github.com/matrices/cerca-go" "github.com/matrices/cerca-go/option" - "github.com/stainless-sdks/cerca-cli/internal/apiquery" - "github.com/stainless-sdks/cerca-cli/internal/requestflag" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" ) @@ -20,8 +20,9 @@ var connectionsList = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, }, Action: handleConnectionsList, @@ -34,8 +35,9 @@ var connectionsAttach = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, &requestflag.Flag[string]{ Name: "connection-id", @@ -57,12 +59,14 @@ var connectionsDetach = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, &requestflag.Flag[string]{ - Name: "connection-id", - Required: true, + Name: "connection-id", + Required: true, + PathParam: "connectionId", }, }, Action: handleConnectionsDetach, @@ -122,8 +126,6 @@ func handleConnectionsAttach(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := cercago.ConnectionAttachParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -135,6 +137,8 @@ func handleConnectionsAttach(ctx context.Context, cmd *cli.Command) error { return err } + params := cercago.ConnectionAttachParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Connections.Attach( diff --git a/pkg/cmd/connection_test.go b/pkg/cmd/connection_test.go index b7c06b1..8d38939 100644 --- a/pkg/cmd/connection_test.go +++ b/pkg/cmd/connection_test.go @@ -5,7 +5,7 @@ package cmd import ( "testing" - "github.com/stainless-sdks/cerca-cli/internal/mocktest" + "github.com/matrices/cerca-cli/internal/mocktest" ) func TestConnectionsList(t *testing.T) { diff --git a/pkg/cmd/context.go b/pkg/cmd/context.go index 9015c92..9281aab 100644 --- a/pkg/cmd/context.go +++ b/pkg/cmd/context.go @@ -6,10 +6,10 @@ import ( "context" "fmt" + "github.com/matrices/cerca-cli/internal/apiquery" + "github.com/matrices/cerca-cli/internal/requestflag" "github.com/matrices/cerca-go" "github.com/matrices/cerca-go/option" - "github.com/stainless-sdks/cerca-cli/internal/apiquery" - "github.com/stainless-sdks/cerca-cli/internal/requestflag" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" ) @@ -20,8 +20,9 @@ var contextRetrieve = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, &requestflag.Flag[string]{ Name: "key", @@ -40,8 +41,9 @@ var contextList = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, &requestflag.Flag[string]{ Name: "cursor", @@ -73,8 +75,9 @@ var contextDelete = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, &requestflag.Flag[string]{ Name: "key", @@ -93,8 +96,9 @@ var contextSearch = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, &requestflag.Flag[string]{ Name: "q", @@ -132,8 +136,9 @@ var contextWrite = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, &requestflag.Flag[string]{ Name: "content", @@ -169,8 +174,6 @@ func handleContextRetrieve(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := cercago.ContextGetParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -182,6 +185,8 @@ func handleContextRetrieve(ctx context.Context, cmd *cli.Command) error { return err } + params := cercago.ContextGetParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Context.Get( @@ -218,8 +223,6 @@ func handleContextList(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := cercago.ContextListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -231,6 +234,8 @@ func handleContextList(ctx context.Context, cmd *cli.Command) error { return err } + params := cercago.ContextListParams{} + format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") @@ -286,8 +291,6 @@ func handleContextDelete(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := cercago.ContextDeleteParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -299,6 +302,8 @@ func handleContextDelete(ctx context.Context, cmd *cli.Command) error { return err } + params := cercago.ContextDeleteParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Context.Delete( @@ -335,8 +340,6 @@ func handleContextSearch(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := cercago.ContextSearchParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -348,6 +351,8 @@ func handleContextSearch(ctx context.Context, cmd *cli.Command) error { return err } + params := cercago.ContextSearchParams{} + format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") @@ -403,8 +408,6 @@ func handleContextWrite(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := cercago.ContextWriteParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -416,6 +419,8 @@ func handleContextWrite(ctx context.Context, cmd *cli.Command) error { return err } + params := cercago.ContextWriteParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Context.Write( diff --git a/pkg/cmd/context_test.go b/pkg/cmd/context_test.go index 964297a..0ef2bf9 100644 --- a/pkg/cmd/context_test.go +++ b/pkg/cmd/context_test.go @@ -5,7 +5,7 @@ package cmd import ( "testing" - "github.com/stainless-sdks/cerca-cli/internal/mocktest" + "github.com/matrices/cerca-cli/internal/mocktest" ) func TestContextRetrieve(t *testing.T) { diff --git a/pkg/cmd/credential.go b/pkg/cmd/credential.go index f764124..4fff457 100644 --- a/pkg/cmd/credential.go +++ b/pkg/cmd/credential.go @@ -6,10 +6,10 @@ import ( "context" "fmt" + "github.com/matrices/cerca-cli/internal/apiquery" + "github.com/matrices/cerca-cli/internal/requestflag" "github.com/matrices/cerca-go" "github.com/matrices/cerca-go/option" - "github.com/stainless-sdks/cerca-cli/internal/apiquery" - "github.com/stainless-sdks/cerca-cli/internal/requestflag" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" ) @@ -20,8 +20,9 @@ var credentialsList = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "scope", - Required: true, + Name: "scope", + Required: true, + PathParam: "scope", }, &requestflag.Flag[string]{ Name: "cursor", @@ -48,12 +49,14 @@ var credentialsDelete = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "scope", - Required: true, + Name: "scope", + Required: true, + PathParam: "scope", }, &requestflag.Flag[string]{ - Name: "connection-id", - Required: true, + Name: "connection-id", + Required: true, + PathParam: "connectionId", }, }, Action: handleCredentialsDelete, @@ -66,8 +69,9 @@ var credentialsCreateAPIKey = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "scope", - Required: true, + Name: "scope", + Required: true, + PathParam: "scope", }, &requestflag.Flag[string]{ Name: "api-key", @@ -102,8 +106,6 @@ func handleCredentialsList(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := cercago.CredentialListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -115,6 +117,8 @@ func handleCredentialsList(ctx context.Context, cmd *cli.Command) error { return err } + params := cercago.CredentialListParams{} + format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") @@ -221,8 +225,6 @@ func handleCredentialsCreateAPIKey(ctx context.Context, cmd *cli.Command) error return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := cercago.CredentialNewAPIKeyParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -234,6 +236,8 @@ func handleCredentialsCreateAPIKey(ctx context.Context, cmd *cli.Command) error return err } + params := cercago.CredentialNewAPIKeyParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Credentials.NewAPIKey( diff --git a/pkg/cmd/credential_test.go b/pkg/cmd/credential_test.go index 0a6051d..f1f5a1e 100644 --- a/pkg/cmd/credential_test.go +++ b/pkg/cmd/credential_test.go @@ -5,7 +5,7 @@ package cmd import ( "testing" - "github.com/stainless-sdks/cerca-cli/internal/mocktest" + "github.com/matrices/cerca-cli/internal/mocktest" ) func TestCredentialsList(t *testing.T) { diff --git a/pkg/cmd/event.go b/pkg/cmd/event.go index 5b50df3..0f03c72 100644 --- a/pkg/cmd/event.go +++ b/pkg/cmd/event.go @@ -6,10 +6,10 @@ import ( "context" "fmt" + "github.com/matrices/cerca-cli/internal/apiquery" + "github.com/matrices/cerca-cli/internal/requestflag" "github.com/matrices/cerca-go" "github.com/matrices/cerca-go/option" - "github.com/stainless-sdks/cerca-cli/internal/apiquery" - "github.com/stainless-sdks/cerca-cli/internal/requestflag" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" ) @@ -20,8 +20,9 @@ var eventsListForAgent = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, &requestflag.Flag[string]{ Name: "cursor", @@ -58,8 +59,9 @@ var eventsListForFleet = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "fleet-id", - Required: true, + Name: "fleet-id", + Required: true, + PathParam: "fleetId", }, &requestflag.Flag[string]{ Name: "cursor", @@ -96,12 +98,14 @@ var eventsListForThread = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, &requestflag.Flag[string]{ - Name: "thread-id", - Required: true, + Name: "thread-id", + Required: true, + PathParam: "threadId", }, &requestflag.Flag[string]{ Name: "cursor", @@ -138,8 +142,9 @@ var eventsSubscribeAgent = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, }, Action: handleEventsSubscribeAgent, @@ -152,8 +157,9 @@ var eventsSubscribeFleet = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "fleet-id", - Required: true, + Name: "fleet-id", + Required: true, + PathParam: "fleetId", }, }, Action: handleEventsSubscribeFleet, @@ -166,12 +172,14 @@ var eventsSubscribeThread = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, &requestflag.Flag[string]{ - Name: "thread-id", - Required: true, + Name: "thread-id", + Required: true, + PathParam: "threadId", }, }, Action: handleEventsSubscribeThread, @@ -189,8 +197,6 @@ func handleEventsListForAgent(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := cercago.EventListForAgentParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -202,6 +208,8 @@ func handleEventsListForAgent(ctx context.Context, cmd *cli.Command) error { return err } + params := cercago.EventListForAgentParams{} + format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") @@ -257,8 +265,6 @@ func handleEventsListForFleet(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := cercago.EventListForFleetParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -270,6 +276,8 @@ func handleEventsListForFleet(ctx context.Context, cmd *cli.Command) error { return err } + params := cercago.EventListForFleetParams{} + format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") @@ -329,8 +337,6 @@ func handleEventsListForThread(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := cercago.EventListForThreadParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -342,6 +348,8 @@ func handleEventsListForThread(ctx context.Context, cmd *cli.Command) error { return err } + params := cercago.EventListForThreadParams{} + format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") diff --git a/pkg/cmd/event_test.go b/pkg/cmd/event_test.go index d4f7521..d3919d9 100644 --- a/pkg/cmd/event_test.go +++ b/pkg/cmd/event_test.go @@ -5,7 +5,7 @@ package cmd import ( "testing" - "github.com/stainless-sdks/cerca-cli/internal/mocktest" + "github.com/matrices/cerca-cli/internal/mocktest" ) func TestEventsListForAgent(t *testing.T) { diff --git a/pkg/cmd/flagoptions.go b/pkg/cmd/flagoptions.go index aba8d70..5ba3e7a 100644 --- a/pkg/cmd/flagoptions.go +++ b/pkg/cmd/flagoptions.go @@ -16,11 +16,11 @@ import ( "strings" "unicode/utf8" + "github.com/matrices/cerca-cli/internal/apiform" + "github.com/matrices/cerca-cli/internal/apiquery" + "github.com/matrices/cerca-cli/internal/debugmiddleware" + "github.com/matrices/cerca-cli/internal/requestflag" "github.com/matrices/cerca-go/option" - "github.com/stainless-sdks/cerca-cli/internal/apiform" - "github.com/stainless-sdks/cerca-cli/internal/apiquery" - "github.com/stainless-sdks/cerca-cli/internal/debugmiddleware" - "github.com/stainless-sdks/cerca-cli/internal/requestflag" "github.com/goccy/go-yaml" "github.com/urfave/cli/v3" @@ -339,7 +339,7 @@ func flagOptions( } 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 @@ -353,16 +353,45 @@ func flagOptions( } if bodyMap, ok := bodyData.(map[string]any); ok { applyDataAliases(cmd, bodyMap) - if flagMap, ok := requestContents.Body.(map[string]any); ok { - maps.Copy(bodyMap, flagMap) - requestContents.Body = 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 } } } @@ -370,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 { diff --git a/pkg/cmd/log.go b/pkg/cmd/log.go index 43cd751..05fea40 100644 --- a/pkg/cmd/log.go +++ b/pkg/cmd/log.go @@ -6,10 +6,10 @@ import ( "context" "fmt" + "github.com/matrices/cerca-cli/internal/apiquery" + "github.com/matrices/cerca-cli/internal/requestflag" "github.com/matrices/cerca-go" "github.com/matrices/cerca-go/option" - "github.com/stainless-sdks/cerca-cli/internal/apiquery" - "github.com/stainless-sdks/cerca-cli/internal/requestflag" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" ) @@ -20,8 +20,9 @@ var logsListForAgent = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, &requestflag.Flag[string]{ Name: "cursor", @@ -48,12 +49,14 @@ var logsListForThread = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, &requestflag.Flag[string]{ - Name: "thread-id", - Required: true, + Name: "thread-id", + Required: true, + PathParam: "threadId", }, &requestflag.Flag[string]{ Name: "cursor", @@ -85,8 +88,6 @@ func handleLogsListForAgent(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := cercago.LogListForAgentParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -98,6 +99,8 @@ func handleLogsListForAgent(ctx context.Context, cmd *cli.Command) error { return err } + params := cercago.LogListForAgentParams{} + format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") @@ -157,8 +160,6 @@ func handleLogsListForThread(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := cercago.LogListForThreadParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -170,6 +171,8 @@ func handleLogsListForThread(ctx context.Context, cmd *cli.Command) error { return err } + params := cercago.LogListForThreadParams{} + format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") diff --git a/pkg/cmd/log_test.go b/pkg/cmd/log_test.go index 733d4c3..d9a02b9 100644 --- a/pkg/cmd/log_test.go +++ b/pkg/cmd/log_test.go @@ -5,7 +5,7 @@ package cmd import ( "testing" - "github.com/stainless-sdks/cerca-cli/internal/mocktest" + "github.com/matrices/cerca-cli/internal/mocktest" ) func TestLogsListForAgent(t *testing.T) { diff --git a/pkg/cmd/model.go b/pkg/cmd/model.go index 2256bbd..b1c0a50 100644 --- a/pkg/cmd/model.go +++ b/pkg/cmd/model.go @@ -6,9 +6,9 @@ import ( "context" "fmt" + "github.com/matrices/cerca-cli/internal/apiquery" "github.com/matrices/cerca-go" "github.com/matrices/cerca-go/option" - "github.com/stainless-sdks/cerca-cli/internal/apiquery" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" ) diff --git a/pkg/cmd/model_test.go b/pkg/cmd/model_test.go index 06dd28f..0c4ad10 100644 --- a/pkg/cmd/model_test.go +++ b/pkg/cmd/model_test.go @@ -5,7 +5,7 @@ package cmd import ( "testing" - "github.com/stainless-sdks/cerca-cli/internal/mocktest" + "github.com/matrices/cerca-cli/internal/mocktest" ) func TestModelsList(t *testing.T) { diff --git a/pkg/cmd/oauth.go b/pkg/cmd/oauth.go index 6c38c85..40a8009 100644 --- a/pkg/cmd/oauth.go +++ b/pkg/cmd/oauth.go @@ -6,10 +6,10 @@ import ( "context" "fmt" + "github.com/matrices/cerca-cli/internal/apiquery" + "github.com/matrices/cerca-cli/internal/requestflag" "github.com/matrices/cerca-go" "github.com/matrices/cerca-go/option" - "github.com/stainless-sdks/cerca-cli/internal/apiquery" - "github.com/stainless-sdks/cerca-cli/internal/requestflag" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" ) @@ -20,8 +20,9 @@ var oauthConnect = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "provider", - Required: true, + Name: "provider", + Required: true, + PathParam: "provider", }, &requestflag.Flag[string]{ Name: "return-origin", @@ -56,8 +57,6 @@ func handleOAuthConnect(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := cercago.OAuthConnectParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -69,6 +68,8 @@ func handleOAuthConnect(ctx context.Context, cmd *cli.Command) error { return err } + params := cercago.OAuthConnectParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.OAuth.Connect( diff --git a/pkg/cmd/oauth_test.go b/pkg/cmd/oauth_test.go index 8091f94..a28a6d3 100644 --- a/pkg/cmd/oauth_test.go +++ b/pkg/cmd/oauth_test.go @@ -5,7 +5,7 @@ package cmd import ( "testing" - "github.com/stainless-sdks/cerca-cli/internal/mocktest" + "github.com/matrices/cerca-cli/internal/mocktest" ) func TestOAuthConnect(t *testing.T) { diff --git a/pkg/cmd/sandbox.go b/pkg/cmd/sandbox.go index ab7cac1..8f07840 100644 --- a/pkg/cmd/sandbox.go +++ b/pkg/cmd/sandbox.go @@ -6,10 +6,10 @@ import ( "context" "fmt" + "github.com/matrices/cerca-cli/internal/apiquery" + "github.com/matrices/cerca-cli/internal/requestflag" "github.com/matrices/cerca-go" "github.com/matrices/cerca-go/option" - "github.com/stainless-sdks/cerca-cli/internal/apiquery" - "github.com/stainless-sdks/cerca-cli/internal/requestflag" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" ) @@ -20,8 +20,9 @@ var sandboxExec = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, &requestflag.Flag[string]{ Name: "command", @@ -37,7 +38,7 @@ var sandboxExec = cli.Command{ Usage: "Timeout in seconds. Runtime converts this to milliseconds.", BodyPath: "timeout", }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "workdir", Usage: "Optional sandbox working directory.", BodyPath: "workdir", @@ -53,8 +54,9 @@ var sandboxRead = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, &requestflag.Flag[string]{ Name: "path", @@ -80,8 +82,9 @@ var sandboxWrite = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, &requestflag.Flag[string]{ Name: "content", @@ -109,8 +112,6 @@ func handleSandboxExec(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := cercago.SandboxExecParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -122,6 +123,8 @@ func handleSandboxExec(ctx context.Context, cmd *cli.Command) error { return err } + params := cercago.SandboxExecParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Sandbox.Exec( @@ -158,8 +161,6 @@ func handleSandboxRead(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := cercago.SandboxReadParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -171,6 +172,8 @@ func handleSandboxRead(ctx context.Context, cmd *cli.Command) error { return err } + params := cercago.SandboxReadParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Sandbox.Read( @@ -207,8 +210,6 @@ func handleSandboxWrite(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := cercago.SandboxWriteParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -220,6 +221,8 @@ func handleSandboxWrite(ctx context.Context, cmd *cli.Command) error { return err } + params := cercago.SandboxWriteParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Sandbox.Write( diff --git a/pkg/cmd/sandbox_test.go b/pkg/cmd/sandbox_test.go index f8afa61..a5b313f 100644 --- a/pkg/cmd/sandbox_test.go +++ b/pkg/cmd/sandbox_test.go @@ -5,7 +5,7 @@ package cmd import ( "testing" - "github.com/stainless-sdks/cerca-cli/internal/mocktest" + "github.com/matrices/cerca-cli/internal/mocktest" ) func TestSandboxExec(t *testing.T) { diff --git a/pkg/cmd/schedule.go b/pkg/cmd/schedule.go index 0eb24aa..13d02ed 100644 --- a/pkg/cmd/schedule.go +++ b/pkg/cmd/schedule.go @@ -6,10 +6,10 @@ import ( "context" "fmt" + "github.com/matrices/cerca-cli/internal/apiquery" + "github.com/matrices/cerca-cli/internal/requestflag" "github.com/matrices/cerca-go" "github.com/matrices/cerca-go/option" - "github.com/stainless-sdks/cerca-cli/internal/apiquery" - "github.com/stainless-sdks/cerca-cli/internal/requestflag" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" ) @@ -20,13 +20,9 @@ var schedulesCreate = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, - }, - &requestflag.Flag[string]{ - Name: "cron", - Required: true, - BodyPath: "cron", + Name: "agent-id", + Required: true, + PathParam: "agentId", }, &requestflag.Flag[string]{ Name: "name", @@ -38,6 +34,10 @@ var schedulesCreate = cli.Command{ Required: true, BodyPath: "prompt", }, + &requestflag.Flag[string]{ + Name: "cron", + BodyPath: "cron", + }, &requestflag.Flag[string]{ Name: "instructions", BodyPath: "instructions", @@ -46,6 +46,10 @@ var schedulesCreate = cli.Command{ Name: "model", BodyPath: "model", }, + &requestflag.Flag[any]{ + Name: "run-at", + BodyPath: "runAt", + }, &requestflag.Flag[string]{ Name: "timezone", BodyPath: "timezone", @@ -65,12 +69,14 @@ var schedulesUpdate = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, &requestflag.Flag[string]{ - Name: "schedule-id", - Required: true, + Name: "schedule-id", + Required: true, + PathParam: "scheduleId", }, &requestflag.Flag[string]{ Name: "cron", @@ -96,6 +102,10 @@ var schedulesUpdate = cli.Command{ Name: "prompt", BodyPath: "prompt", }, + &requestflag.Flag[any]{ + Name: "run-at", + BodyPath: "runAt", + }, &requestflag.Flag[string]{ Name: "timezone", BodyPath: "timezone", @@ -115,8 +125,9 @@ var schedulesList = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, }, Action: handleSchedulesList, @@ -129,12 +140,14 @@ var schedulesDelete = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, &requestflag.Flag[string]{ - Name: "schedule-id", - Required: true, + Name: "schedule-id", + Required: true, + PathParam: "scheduleId", }, }, Action: handleSchedulesDelete, @@ -147,12 +160,14 @@ var schedulesTrigger = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, &requestflag.Flag[string]{ - Name: "schedule-id", - Required: true, + Name: "schedule-id", + Required: true, + PathParam: "scheduleId", }, }, Action: handleSchedulesTrigger, @@ -170,8 +185,6 @@ func handleSchedulesCreate(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := cercago.ScheduleNewParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -183,6 +196,8 @@ func handleSchedulesCreate(ctx context.Context, cmd *cli.Command) error { return err } + params := cercago.ScheduleNewParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Schedules.New( @@ -223,8 +238,6 @@ func handleSchedulesUpdate(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := cercago.ScheduleUpdateParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -236,6 +249,8 @@ func handleSchedulesUpdate(ctx context.Context, cmd *cli.Command) error { return err } + params := cercago.ScheduleUpdateParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Schedules.Update( diff --git a/pkg/cmd/schedule_test.go b/pkg/cmd/schedule_test.go index 552f569..4e276f0 100644 --- a/pkg/cmd/schedule_test.go +++ b/pkg/cmd/schedule_test.go @@ -5,7 +5,7 @@ package cmd import ( "testing" - "github.com/stainless-sdks/cerca-cli/internal/mocktest" + "github.com/matrices/cerca-cli/internal/mocktest" ) func TestSchedulesCreate(t *testing.T) { @@ -15,11 +15,12 @@ func TestSchedulesCreate(t *testing.T) { "--api-key", "string", "schedules", "create", "--agent-id", "agent_abc123", - "--cron", "cron", "--name", "name", "--prompt", "prompt", + "--cron", "cron", "--instructions", "instructions", "--model", "model", + "--run-at", "'2019-12-27T18:11:19.117Z'", "--timezone", "timezone", "--tool", "sandbox.*", ) @@ -28,11 +29,12 @@ func TestSchedulesCreate(t *testing.T) { t.Run("piping data", func(t *testing.T) { // Test piping YAML data over stdin pipeData := []byte("" + - "cron: cron\n" + "name: name\n" + "prompt: prompt\n" + + "cron: cron\n" + "instructions: instructions\n" + "model: model\n" + + "runAt: '2019-12-27T18:11:19.117Z'\n" + "timezone: timezone\n" + "tools:\n" + " - sandbox.*\n") @@ -59,6 +61,7 @@ func TestSchedulesUpdate(t *testing.T) { "--model", "model", "--name", "name", "--prompt", "prompt", + "--run-at", "'2019-12-27T18:11:19.117Z'", "--timezone", "timezone", "--tool", "sandbox.*", ) @@ -73,6 +76,7 @@ func TestSchedulesUpdate(t *testing.T) { "model: model\n" + "name: name\n" + "prompt: prompt\n" + + "runAt: '2019-12-27T18:11:19.117Z'\n" + "timezone: timezone\n" + "tools:\n" + " - sandbox.*\n") diff --git a/pkg/cmd/thread.go b/pkg/cmd/thread.go index 4f17576..5314c24 100644 --- a/pkg/cmd/thread.go +++ b/pkg/cmd/thread.go @@ -6,10 +6,10 @@ import ( "context" "fmt" + "github.com/matrices/cerca-cli/internal/apiquery" + "github.com/matrices/cerca-cli/internal/requestflag" "github.com/matrices/cerca-go" "github.com/matrices/cerca-go/option" - "github.com/stainless-sdks/cerca-cli/internal/apiquery" - "github.com/stainless-sdks/cerca-cli/internal/requestflag" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" ) @@ -20,8 +20,9 @@ var threadsCreate = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, &requestflag.Flag[string]{ Name: "instructions", @@ -55,12 +56,24 @@ var threadsRetrieve = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, &requestflag.Flag[string]{ - Name: "thread-id", - Required: true, + Name: "thread-id", + Required: true, + PathParam: "threadId", + }, + &requestflag.Flag[string]{ + Name: "after-seq", + Usage: "Return messages newer than this sequence number. Mutually exclusive with `beforeSeq`.", + QueryPath: "afterSeq", + }, + &requestflag.Flag[string]{ + Name: "before-seq", + Usage: "Return messages older than this sequence number. Mutually exclusive with `afterSeq`.", + QueryPath: "beforeSeq", }, &requestflag.Flag[string]{ Name: "debug", @@ -72,6 +85,11 @@ var threadsRetrieve = cli.Command{ Usage: "When true, includes message content in the thread detail.", QueryPath: "includeMessages", }, + &requestflag.Flag[string]{ + Name: "message-limit", + Usage: "Maximum number of messages to include. Set to 0 to return metadata without messages.", + QueryPath: "messageLimit", + }, }, Action: handleThreadsRetrieve, HideHelpCommand: true, @@ -83,8 +101,9 @@ var threadsList = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, &requestflag.Flag[string]{ Name: "cursor", @@ -121,12 +140,14 @@ var threadsCancel = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, &requestflag.Flag[string]{ - Name: "thread-id", - Required: true, + Name: "thread-id", + Required: true, + PathParam: "threadId", }, }, Action: handleThreadsCancel, @@ -139,12 +160,14 @@ var threadsClose = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, &requestflag.Flag[string]{ - Name: "thread-id", - Required: true, + Name: "thread-id", + Required: true, + PathParam: "threadId", }, }, Action: handleThreadsClose, @@ -157,12 +180,14 @@ var threadsCompact = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, &requestflag.Flag[string]{ - Name: "thread-id", - Required: true, + Name: "thread-id", + Required: true, + PathParam: "threadId", }, }, Action: handleThreadsCompact, @@ -175,12 +200,14 @@ var threadsStartTurn = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, &requestflag.Flag[string]{ - Name: "thread-id", - Required: true, + Name: "thread-id", + Required: true, + PathParam: "threadId", }, &requestflag.Flag[string]{ Name: "user-message", @@ -206,12 +233,14 @@ var threadsSteer = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, &requestflag.Flag[string]{ - Name: "thread-id", - Required: true, + Name: "thread-id", + Required: true, + PathParam: "threadId", }, &requestflag.Flag[string]{ Name: "message", @@ -234,8 +263,6 @@ func handleThreadsCreate(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := cercago.ThreadNewParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -247,6 +274,8 @@ func handleThreadsCreate(ctx context.Context, cmd *cli.Command) error { return err } + params := cercago.ThreadNewParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Threads.New( @@ -287,8 +316,6 @@ func handleThreadsRetrieve(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := cercago.ThreadGetParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -300,6 +327,8 @@ func handleThreadsRetrieve(ctx context.Context, cmd *cli.Command) error { return err } + params := cercago.ThreadGetParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Threads.Get( @@ -337,8 +366,6 @@ func handleThreadsList(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := cercago.ThreadListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -350,6 +377,8 @@ func handleThreadsList(ctx context.Context, cmd *cli.Command) error { return err } + params := cercago.ThreadListParams{} + format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") @@ -562,8 +591,6 @@ func handleThreadsStartTurn(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := cercago.ThreadStartTurnParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -575,6 +602,8 @@ func handleThreadsStartTurn(ctx context.Context, cmd *cli.Command) error { return err } + params := cercago.ThreadStartTurnParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Threads.StartTurn( @@ -616,8 +645,6 @@ func handleThreadsSteer(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := cercago.ThreadSteerParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -629,6 +656,8 @@ func handleThreadsSteer(ctx context.Context, cmd *cli.Command) error { return err } + params := cercago.ThreadSteerParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Threads.Steer( diff --git a/pkg/cmd/thread_test.go b/pkg/cmd/thread_test.go index 8cc1ea1..4df88bb 100644 --- a/pkg/cmd/thread_test.go +++ b/pkg/cmd/thread_test.go @@ -5,7 +5,7 @@ package cmd import ( "testing" - "github.com/stainless-sdks/cerca-cli/internal/mocktest" + "github.com/matrices/cerca-cli/internal/mocktest" ) func TestThreadsCreate(t *testing.T) { @@ -49,8 +49,11 @@ func TestThreadsRetrieve(t *testing.T) { "threads", "retrieve", "--agent-id", "agent_abc123", "--thread-id", "thread_abc123", + "--after-seq", "42", + "--before-seq", "42", "--debug", "false", "--include-messages", "true", + "--message-limit", "50", ) }) } diff --git a/pkg/cmd/tool.go b/pkg/cmd/tool.go index 0cf5de7..0f4cd68 100644 --- a/pkg/cmd/tool.go +++ b/pkg/cmd/tool.go @@ -6,10 +6,10 @@ import ( "context" "fmt" + "github.com/matrices/cerca-cli/internal/apiquery" + "github.com/matrices/cerca-cli/internal/requestflag" "github.com/matrices/cerca-go" "github.com/matrices/cerca-go/option" - "github.com/stainless-sdks/cerca-cli/internal/apiquery" - "github.com/stainless-sdks/cerca-cli/internal/requestflag" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" ) @@ -20,8 +20,9 @@ var toolsListForAgent = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, }, Action: handleToolsListForAgent, diff --git a/pkg/cmd/tool_test.go b/pkg/cmd/tool_test.go index 2106d31..6f2867d 100644 --- a/pkg/cmd/tool_test.go +++ b/pkg/cmd/tool_test.go @@ -5,7 +5,7 @@ package cmd import ( "testing" - "github.com/stainless-sdks/cerca-cli/internal/mocktest" + "github.com/matrices/cerca-cli/internal/mocktest" ) func TestToolsListForAgent(t *testing.T) { diff --git a/pkg/cmd/toolsource.go b/pkg/cmd/toolsource.go index a339121..45dbf57 100644 --- a/pkg/cmd/toolsource.go +++ b/pkg/cmd/toolsource.go @@ -6,10 +6,10 @@ import ( "context" "fmt" + "github.com/matrices/cerca-cli/internal/apiquery" + "github.com/matrices/cerca-cli/internal/requestflag" "github.com/matrices/cerca-go" "github.com/matrices/cerca-go/option" - "github.com/stainless-sdks/cerca-cli/internal/apiquery" - "github.com/stainless-sdks/cerca-cli/internal/requestflag" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" ) @@ -20,8 +20,9 @@ var toolSourcesCreate = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "fleet-id", - Required: true, + Name: "fleet-id", + Required: true, + PathParam: "fleetId", }, &requestflag.Flag[map[string]any]{ Name: "source", @@ -39,12 +40,14 @@ var toolSourcesRetrieve = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "fleet-id", - Required: true, + Name: "fleet-id", + Required: true, + PathParam: "fleetId", }, &requestflag.Flag[string]{ - Name: "source-id", - Required: true, + Name: "source-id", + Required: true, + PathParam: "sourceId", }, }, Action: handleToolSourcesRetrieve, @@ -57,12 +60,14 @@ var toolSourcesUpdate = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "fleet-id", - Required: true, + Name: "fleet-id", + Required: true, + PathParam: "fleetId", }, &requestflag.Flag[string]{ - Name: "source-id", - Required: true, + Name: "source-id", + Required: true, + PathParam: "sourceId", }, &requestflag.Flag[map[string]any]{ Name: "source", @@ -80,8 +85,9 @@ var toolSourcesList = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "fleet-id", - Required: true, + Name: "fleet-id", + Required: true, + PathParam: "fleetId", }, &requestflag.Flag[string]{ Name: "cursor", @@ -108,12 +114,14 @@ var toolSourcesDelete = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "fleet-id", - Required: true, + Name: "fleet-id", + Required: true, + PathParam: "fleetId", }, &requestflag.Flag[string]{ - Name: "source-id", - Required: true, + Name: "source-id", + Required: true, + PathParam: "sourceId", }, }, Action: handleToolSourcesDelete, @@ -126,8 +134,9 @@ var toolSourcesListForAgent = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "agent-id", - Required: true, + Name: "agent-id", + Required: true, + PathParam: "agentId", }, }, Action: handleToolSourcesListForAgent, @@ -145,8 +154,6 @@ func handleToolSourcesCreate(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := cercago.ToolSourceNewParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -158,6 +165,8 @@ func handleToolSourcesCreate(ctx context.Context, cmd *cli.Command) error { return err } + params := cercago.ToolSourceNewParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.ToolSources.New( @@ -249,8 +258,6 @@ func handleToolSourcesUpdate(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := cercago.ToolSourceUpdateParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -262,6 +269,8 @@ func handleToolSourcesUpdate(ctx context.Context, cmd *cli.Command) error { return err } + params := cercago.ToolSourceUpdateParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.ToolSources.Update( @@ -299,8 +308,6 @@ func handleToolSourcesList(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := cercago.ToolSourceListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -312,6 +319,8 @@ func handleToolSourcesList(ctx context.Context, cmd *cli.Command) error { return err } + params := cercago.ToolSourceListParams{} + format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") diff --git a/pkg/cmd/toolsource_test.go b/pkg/cmd/toolsource_test.go index a5888f7..39d6925 100644 --- a/pkg/cmd/toolsource_test.go +++ b/pkg/cmd/toolsource_test.go @@ -5,7 +5,7 @@ package cmd import ( "testing" - "github.com/stainless-sdks/cerca-cli/internal/mocktest" + "github.com/matrices/cerca-cli/internal/mocktest" ) func TestToolSourcesCreate(t *testing.T) { diff --git a/pkg/cmd/version.go b/pkg/cmd/version.go index ad586f4..9bb8168 100644 --- a/pkg/cmd/version.go +++ b/pkg/cmd/version.go @@ -2,4 +2,4 @@ package cmd -const Version = "0.0.1" +const Version = "0.1.0" // x-release-please-version diff --git a/pkg/cmd/webhook.go b/pkg/cmd/webhook.go index b0af311..f2e16d7 100644 --- a/pkg/cmd/webhook.go +++ b/pkg/cmd/webhook.go @@ -6,10 +6,10 @@ import ( "context" "fmt" + "github.com/matrices/cerca-cli/internal/apiquery" + "github.com/matrices/cerca-cli/internal/requestflag" "github.com/matrices/cerca-go" "github.com/matrices/cerca-go/option" - "github.com/stainless-sdks/cerca-cli/internal/apiquery" - "github.com/stainless-sdks/cerca-cli/internal/requestflag" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" ) @@ -20,8 +20,9 @@ var webhooksCreate = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "fleet-id", - Required: true, + Name: "fleet-id", + Required: true, + PathParam: "fleetId", }, &requestflag.Flag[string]{ Name: "url", @@ -45,12 +46,14 @@ var webhooksRetrieve = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "fleet-id", - Required: true, + Name: "fleet-id", + Required: true, + PathParam: "fleetId", }, &requestflag.Flag[string]{ - Name: "webhook-id", - Required: true, + Name: "webhook-id", + Required: true, + PathParam: "webhookId", }, }, Action: handleWebhooksRetrieve, @@ -63,12 +66,14 @@ var webhooksUpdate = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "fleet-id", - Required: true, + Name: "fleet-id", + Required: true, + PathParam: "fleetId", }, &requestflag.Flag[string]{ - Name: "webhook-id", - Required: true, + Name: "webhook-id", + Required: true, + PathParam: "webhookId", }, &requestflag.Flag[bool]{ Name: "enabled", @@ -96,8 +101,9 @@ var webhooksList = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "fleet-id", - Required: true, + Name: "fleet-id", + Required: true, + PathParam: "fleetId", }, &requestflag.Flag[string]{ Name: "cursor", @@ -124,12 +130,14 @@ var webhooksDelete = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "fleet-id", - Required: true, + Name: "fleet-id", + Required: true, + PathParam: "fleetId", }, &requestflag.Flag[string]{ - Name: "webhook-id", - Required: true, + Name: "webhook-id", + Required: true, + PathParam: "webhookId", }, }, Action: handleWebhooksDelete, @@ -142,12 +150,14 @@ var webhooksRotate = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "fleet-id", - Required: true, + Name: "fleet-id", + Required: true, + PathParam: "fleetId", }, &requestflag.Flag[string]{ - Name: "webhook-id", - Required: true, + Name: "webhook-id", + Required: true, + PathParam: "webhookId", }, }, Action: handleWebhooksRotate, @@ -160,12 +170,14 @@ var webhooksTest = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "fleet-id", - Required: true, + Name: "fleet-id", + Required: true, + PathParam: "fleetId", }, &requestflag.Flag[string]{ - Name: "webhook-id", - Required: true, + Name: "webhook-id", + Required: true, + PathParam: "webhookId", }, }, Action: handleWebhooksTest, @@ -183,8 +195,6 @@ func handleWebhooksCreate(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := cercago.WebhookNewParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -196,6 +206,8 @@ func handleWebhooksCreate(ctx context.Context, cmd *cli.Command) error { return err } + params := cercago.WebhookNewParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Webhooks.New( @@ -287,8 +299,6 @@ func handleWebhooksUpdate(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := cercago.WebhookUpdateParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -300,6 +310,8 @@ func handleWebhooksUpdate(ctx context.Context, cmd *cli.Command) error { return err } + params := cercago.WebhookUpdateParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Webhooks.Update( @@ -337,8 +349,6 @@ func handleWebhooksList(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := cercago.WebhookListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -350,6 +360,8 @@ func handleWebhooksList(ctx context.Context, cmd *cli.Command) error { return err } + params := cercago.WebhookListParams{} + format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") diff --git a/pkg/cmd/webhook_test.go b/pkg/cmd/webhook_test.go index a06ef44..9a45b17 100644 --- a/pkg/cmd/webhook_test.go +++ b/pkg/cmd/webhook_test.go @@ -5,7 +5,7 @@ package cmd import ( "testing" - "github.com/stainless-sdks/cerca-cli/internal/mocktest" + "github.com/matrices/cerca-cli/internal/mocktest" ) func TestWebhooksCreate(t *testing.T) {