From df7e71b731758877a22074668862d865d71603eb Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 14 Apr 2026 05:22:32 +0000 Subject: [PATCH 01/56] chore: add documentation for ./scripts/link --- README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/README.md b/README.md index 5afe42f..2e0ae2f 100644 --- a/README.md +++ b/README.md @@ -119,3 +119,23 @@ base64-encoding). Note that absolute paths will begin with `@file://` or ```bash zavudev --arg @data://file.txt ``` + +## Linking different Go SDK versions + +You can link the CLI against a different version of the Zavudev Go SDK +for development purposes using the `./scripts/link` script. + +To link to a specific version from a repository (version can be a branch, +git tag, or commit hash): + +```bash +./scripts/link github.com/org/repo@version +``` + +To link to a local copy of the SDK: + +```bash +./scripts/link ../path/to/zavudev-go +``` + +If you run the link script without any arguments, it will default to `../zavudev-go`. From bcb63ef6b1b4e3f5fad06d07650c34f8bd3450d3 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 14 Apr 2026 17:45:19 +0000 Subject: [PATCH 02/56] feat(api): api update --- .stats.yml | 4 ++-- pkg/cmd/template.go | 15 +++++++++++++++ pkg/cmd/template_test.go | 9 +++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index aad1a14..7a3566b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 77 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu%2Fzavudev-d264cd6c8b053a313afd2c81d2a85d5c0053889ec50e81acd8c4f0db93b9e0d3.yml -openapi_spec_hash: 721c392cd496ecce25354e9f8220638e +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu%2Fzavudev-8bebbc269d62102c587a3a2ed99f065e0336599697b55d21e30080aa9c160672.yml +openapi_spec_hash: 1e701bc39fb0673ddef95c579da4a54b config_hash: 866b537cf536cf8cb91b44e437fa812d diff --git a/pkg/cmd/template.go b/pkg/cmd/template.go index 02b9b20..d2a2274 100644 --- a/pkg/cmd/template.go +++ b/pkg/cmd/template.go @@ -52,6 +52,21 @@ var templatesCreate = requestflag.WithInnerFlags(cli.Command{ Usage: "Code expiration time in minutes. Only for AUTHENTICATION templates.", BodyPath: "codeExpirationMinutes", }, + &requestflag.Flag[string]{ + Name: "footer", + Usage: "Footer text for the template.", + BodyPath: "footer", + }, + &requestflag.Flag[string]{ + Name: "header-content", + Usage: "Header content (text string or media URL).", + BodyPath: "headerContent", + }, + &requestflag.Flag[string]{ + Name: "header-type", + Usage: "Type of header for the template.", + BodyPath: "headerType", + }, &requestflag.Flag[string]{ Name: "instagram-body", Usage: "Channel-specific body for Instagram. Falls back to `body` if not set.", diff --git a/pkg/cmd/template_test.go b/pkg/cmd/template_test.go index a3c5661..78bbf24 100644 --- a/pkg/cmd/template_test.go +++ b/pkg/cmd/template_test.go @@ -22,6 +22,9 @@ func TestTemplatesCreate(t *testing.T) { "--add-security-recommendation=true", "--button", "{text: text, type: quick_reply, otpType: COPY_CODE, packageName: packageName, phoneNumber: phoneNumber, signatureHash: signatureHash, url: https://example.com}", "--code-expiration-minutes", "1", + "--footer", "footer", + "--header-content", "headerContent", + "--header-type", "text", "--instagram-body", "instagramBody", "--sms-body", "smsBody", "--telegram-body", "telegramBody", @@ -52,6 +55,9 @@ func TestTemplatesCreate(t *testing.T) { "--button.signature-hash", "signatureHash", "--button.url", "https://example.com", "--code-expiration-minutes", "1", + "--footer", "footer", + "--header-content", "headerContent", + "--header-type", "text", "--instagram-body", "instagramBody", "--sms-body", "smsBody", "--telegram-body", "telegramBody", @@ -77,6 +83,9 @@ func TestTemplatesCreate(t *testing.T) { " signatureHash: signatureHash\n" + " url: https://example.com\n" + "codeExpirationMinutes: 1\n" + + "footer: footer\n" + + "headerContent: headerContent\n" + + "headerType: text\n" + "instagramBody: instagramBody\n" + "smsBody: smsBody\n" + "telegramBody: telegramBody\n" + From c40c4cb409948540bb7feef81b0ae11de056e7f5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 14 Apr 2026 17:56:23 +0000 Subject: [PATCH 03/56] feat(api): manual updates --- .stats.yml | 4 +- pkg/cmd/balance.go | 56 ++ pkg/cmd/balance_test.go | 20 + pkg/cmd/broadcast.go | 98 +++ pkg/cmd/broadcast_test.go | 24 + pkg/cmd/cmd.go | 143 +++++ pkg/cmd/contact.go | 189 ++++++ pkg/cmd/contact_test.go | 89 +++ pkg/cmd/contactchannel.go | 290 +++++++++ pkg/cmd/contactchannel_test.go | 100 ++++ pkg/cmd/export.go | 195 ++++++ pkg/cmd/export_test.go | 66 ++ pkg/cmd/invitation.go | 259 ++++++++ pkg/cmd/invitation_test.go | 84 +++ pkg/cmd/number10dlcbrand.go | 562 ++++++++++++++++++ pkg/cmd/number10dlcbrand_test.go | 194 ++++++ pkg/cmd/number10dlccampaign.go | 492 +++++++++++++++ pkg/cmd/number10dlccampaign_test.go | 179 ++++++ pkg/cmd/number10dlccampaignphonenumber.go | 179 ++++++ .../number10dlccampaignphonenumber_test.go | 58 ++ pkg/cmd/plan.go | 56 ++ pkg/cmd/plan_test.go | 20 + pkg/cmd/senderwhatsappsync.go | 163 +++++ pkg/cmd/senderwhatsappsync_test.go | 45 ++ pkg/cmd/subaccount.go | 369 ++++++++++++ pkg/cmd/subaccount_test.go | 123 ++++ pkg/cmd/subaccountapikey.go | 188 ++++++ pkg/cmd/subaccountapikey_test.go | 64 ++ pkg/cmd/url.go | 185 ++++++ pkg/cmd/url_test.go | 58 ++ pkg/cmd/usage.go | 56 ++ pkg/cmd/usage_test.go | 20 + 32 files changed, 4626 insertions(+), 2 deletions(-) create mode 100644 pkg/cmd/balance.go create mode 100644 pkg/cmd/balance_test.go create mode 100644 pkg/cmd/contactchannel.go create mode 100644 pkg/cmd/contactchannel_test.go create mode 100644 pkg/cmd/export.go create mode 100644 pkg/cmd/export_test.go create mode 100644 pkg/cmd/invitation.go create mode 100644 pkg/cmd/invitation_test.go create mode 100644 pkg/cmd/number10dlcbrand.go create mode 100644 pkg/cmd/number10dlcbrand_test.go create mode 100644 pkg/cmd/number10dlccampaign.go create mode 100644 pkg/cmd/number10dlccampaign_test.go create mode 100644 pkg/cmd/number10dlccampaignphonenumber.go create mode 100644 pkg/cmd/number10dlccampaignphonenumber_test.go create mode 100644 pkg/cmd/plan.go create mode 100644 pkg/cmd/plan_test.go create mode 100644 pkg/cmd/senderwhatsappsync.go create mode 100644 pkg/cmd/senderwhatsappsync_test.go create mode 100644 pkg/cmd/subaccount.go create mode 100644 pkg/cmd/subaccount_test.go create mode 100644 pkg/cmd/subaccountapikey.go create mode 100644 pkg/cmd/subaccountapikey_test.go create mode 100644 pkg/cmd/url.go create mode 100644 pkg/cmd/url_test.go create mode 100644 pkg/cmd/usage.go create mode 100644 pkg/cmd/usage_test.go diff --git a/.stats.yml b/.stats.yml index 7a3566b..9ed46fe 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 77 +configured_endpoints: 129 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu%2Fzavudev-8bebbc269d62102c587a3a2ed99f065e0336599697b55d21e30080aa9c160672.yml openapi_spec_hash: 1e701bc39fb0673ddef95c579da4a54b -config_hash: 866b537cf536cf8cb91b44e437fa812d +config_hash: fd9bfe2e122ab75779fb52fed46d1d2e diff --git a/pkg/cmd/balance.go b/pkg/cmd/balance.go new file mode 100644 index 0000000..a27b43c --- /dev/null +++ b/pkg/cmd/balance.go @@ -0,0 +1,56 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "context" + "fmt" + "os" + + "github.com/tidwall/gjson" + "github.com/urfave/cli/v3" + "github.com/zavudev/cli/internal/apiquery" + "github.com/zavudev/sdk-go" + "github.com/zavudev/sdk-go/option" +) + +var balanceRetrieve = cli.Command{ + Name: "retrieve", + Usage: "Get balance for the API key's team. If the API key belongs to a sub-account,\nalso includes the sub-account's total spending and credit limit.", + Suggest: true, + Flags: []cli.Flag{}, + Action: handleBalanceRetrieve, + HideHelpCommand: true, +} + +func handleBalanceRetrieve(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Balance.Get(ctx, options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + return ShowJSON(os.Stdout, "balance retrieve", obj, format, transform) +} diff --git a/pkg/cmd/balance_test.go b/pkg/cmd/balance_test.go new file mode 100644 index 0000000..12ef8a7 --- /dev/null +++ b/pkg/cmd/balance_test.go @@ -0,0 +1,20 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "testing" + + "github.com/zavudev/cli/internal/mocktest" +) + +func TestBalanceRetrieve(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "balance", "retrieve", + ) + }) +} diff --git a/pkg/cmd/broadcast.go b/pkg/cmd/broadcast.go index 78e04b1..6f64c9f 100644 --- a/pkg/cmd/broadcast.go +++ b/pkg/cmd/broadcast.go @@ -256,6 +256,20 @@ var broadcastsCancel = cli.Command{ HideHelpCommand: true, } +var broadcastsEscalateReview = cli.Command{ + Name: "escalate-review", + Usage: "Request manual review by the Zavu team for a rejected broadcast. Use this after\nautomated review rejection if you believe the content is legitimate.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "broadcast-id", + Required: true, + }, + }, + Action: handleBroadcastsEscalateReview, + HideHelpCommand: true, +} + var broadcastsProgress = cli.Command{ Name: "progress", Usage: "Get real-time progress of a broadcast including delivery counts and estimated\ncompletion time.", @@ -290,6 +304,20 @@ var broadcastsReschedule = cli.Command{ HideHelpCommand: true, } +var broadcastsRetryReview = cli.Command{ + Name: "retry-review", + Usage: "Resubmit a rejected broadcast for AI review after editing content. Maximum 3\nreview attempts allowed per broadcast.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "broadcast-id", + Required: true, + }, + }, + Action: handleBroadcastsRetryReview, + HideHelpCommand: true, +} + var broadcastsSend = cli.Command{ Name: "send", Usage: "Start sending the broadcast immediately or schedule for later. Broadcasts go\nthrough automated AI content review before sending. If the review passes, the\nbroadcast proceeds. If rejected, use PATCH to edit content, then call POST\n/retry-review. Reserves the estimated cost from your balance.", @@ -522,6 +550,41 @@ func handleBroadcastsCancel(ctx context.Context, cmd *cli.Command) error { return ShowJSON(os.Stdout, "broadcasts cancel", obj, format, transform) } +func handleBroadcastsEscalateReview(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("broadcast-id") && len(unusedArgs) > 0 { + cmd.Set("broadcast-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Broadcasts.EscalateReview(ctx, cmd.Value("broadcast-id").(string), options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + return ShowJSON(os.Stdout, "broadcasts escalate-review", obj, format, transform) +} + func handleBroadcastsProgress(ctx context.Context, cmd *cli.Command) error { client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) unusedArgs := cmd.Args().Slice() @@ -599,6 +662,41 @@ func handleBroadcastsReschedule(ctx context.Context, cmd *cli.Command) error { return ShowJSON(os.Stdout, "broadcasts reschedule", obj, format, transform) } +func handleBroadcastsRetryReview(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("broadcast-id") && len(unusedArgs) > 0 { + cmd.Set("broadcast-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Broadcasts.RetryReview(ctx, cmd.Value("broadcast-id").(string), options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + return ShowJSON(os.Stdout, "broadcasts retry-review", obj, format, transform) +} + func handleBroadcastsSend(ctx context.Context, cmd *cli.Command) error { client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) unusedArgs := cmd.Args().Slice() diff --git a/pkg/cmd/broadcast_test.go b/pkg/cmd/broadcast_test.go index 8a9c39b..819ea40 100644 --- a/pkg/cmd/broadcast_test.go +++ b/pkg/cmd/broadcast_test.go @@ -206,6 +206,18 @@ func TestBroadcastsCancel(t *testing.T) { }) } +func TestBroadcastsEscalateReview(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "broadcasts", "escalate-review", + "--broadcast-id", "broadcastId", + ) + }) +} + func TestBroadcastsProgress(t *testing.T) { t.Skip("Mock server tests are disabled") t.Run("regular flags", func(t *testing.T) { @@ -242,6 +254,18 @@ func TestBroadcastsReschedule(t *testing.T) { }) } +func TestBroadcastsRetryReview(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "broadcasts", "retry-review", + "--broadcast-id", "broadcastId", + ) + }) +} + func TestBroadcastsSend(t *testing.T) { t.Skip("Mock server tests are disabled") t.Run("regular flags", func(t *testing.T) { diff --git a/pkg/cmd/cmd.go b/pkg/cmd/cmd.go index 69f46c9..93facec 100644 --- a/pkg/cmd/cmd.go +++ b/pkg/cmd/cmd.go @@ -186,17 +186,41 @@ func init() { &sendersAgentKnowledgeBasesDocumentsDelete, }, }, + { + Name: "senders:whatsapp-sync", + Category: "API RESOURCE", + Suggest: true, + Commands: []*cli.Command{ + &sendersWhatsappSyncRetrieve, + &sendersWhatsappSyncStartContactsSync, + &sendersWhatsappSyncStartHistorySync, + }, + }, { Name: "contacts", Category: "API RESOURCE", Suggest: true, Commands: []*cli.Command{ + &contactsCreate, &contactsRetrieve, &contactsUpdate, &contactsList, + &contactsDismissMergeSuggestion, + &contactsMerge, &contactsRetrieveByPhone, }, }, + { + Name: "contacts:channels", + Category: "API RESOURCE", + Suggest: true, + Commands: []*cli.Command{ + &contactsChannelsUpdate, + &contactsChannelsAdd, + &contactsChannelsRemove, + &contactsChannelsSetPrimary, + }, + }, { Name: "broadcasts", Category: "API RESOURCE", @@ -208,8 +232,10 @@ func init() { &broadcastsList, &broadcastsDelete, &broadcastsCancel, + &broadcastsEscalateReview, &broadcastsProgress, &broadcastsReschedule, + &broadcastsRetryReview, &broadcastsSend, }, }, @@ -268,6 +294,123 @@ func init() { ®ulatoryDocumentsUploadURL, }, }, + { + Name: "invitations", + Category: "API RESOURCE", + Suggest: true, + Commands: []*cli.Command{ + &invitationsCreate, + &invitationsRetrieve, + &invitationsList, + &invitationsCancel, + }, + }, + { + Name: "exports", + Category: "API RESOURCE", + Suggest: true, + Commands: []*cli.Command{ + &exportsCreate, + &exportsRetrieve, + &exportsList, + }, + }, + { + Name: "urls", + Category: "API RESOURCE", + Suggest: true, + Commands: []*cli.Command{ + &urlsListVerified, + &urlsRetrieveDetails, + &urlsSubmitForVerification, + }, + }, + { + Name: "balance", + Category: "API RESOURCE", + Suggest: true, + Commands: []*cli.Command{ + &balanceRetrieve, + }, + }, + { + Name: "plan", + Category: "API RESOURCE", + Suggest: true, + Commands: []*cli.Command{ + &planRetrieve, + }, + }, + { + Name: "usage", + Category: "API RESOURCE", + Suggest: true, + Commands: []*cli.Command{ + &usageRetrieve, + }, + }, + { + Name: "sub-accounts", + Category: "API RESOURCE", + Suggest: true, + Commands: []*cli.Command{ + &subAccountsCreate, + &subAccountsRetrieve, + &subAccountsUpdate, + &subAccountsList, + &subAccountsDeactivate, + &subAccountsGetBalance, + }, + }, + { + Name: "sub-accounts:api-keys", + Category: "API RESOURCE", + Suggest: true, + Commands: []*cli.Command{ + &subAccountsAPIKeysCreate, + &subAccountsAPIKeysList, + &subAccountsAPIKeysRevoke, + }, + }, + { + Name: "number-10dlc:brands", + Category: "API RESOURCE", + Suggest: true, + Commands: []*cli.Command{ + &number10dlcBrandsCreate, + &number10dlcBrandsRetrieve, + &number10dlcBrandsUpdate, + &number10dlcBrandsList, + &number10dlcBrandsDelete, + &number10dlcBrandsListUseCases, + &number10dlcBrandsSubmit, + &number10dlcBrandsSyncStatus, + }, + }, + { + Name: "number-10dlc:campaigns", + Category: "API RESOURCE", + Suggest: true, + Commands: []*cli.Command{ + &number10dlcCampaignsCreate, + &number10dlcCampaignsRetrieve, + &number10dlcCampaignsUpdate, + &number10dlcCampaignsList, + &number10dlcCampaignsDelete, + &number10dlcCampaignsSubmit, + &number10dlcCampaignsSyncStatus, + }, + }, + { + Name: "number-10dlc:campaigns:phone-numbers", + Category: "API RESOURCE", + Suggest: true, + Commands: []*cli.Command{ + &number10dlcCampaignsPhoneNumbersList, + &number10dlcCampaignsPhoneNumbersAssign, + &number10dlcCampaignsPhoneNumbersUnassign, + }, + }, { Name: "@manpages", Usage: "Generate documentation for 'man'", diff --git a/pkg/cmd/contact.go b/pkg/cmd/contact.go index 9cd8f98..efc7451 100644 --- a/pkg/cmd/contact.go +++ b/pkg/cmd/contact.go @@ -15,6 +15,60 @@ import ( "github.com/zavudev/sdk-go/option" ) +var contactsCreate = requestflag.WithInnerFlags(cli.Command{ + Name: "create", + Usage: "Create a new contact with one or more communication channels.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[[]map[string]any]{ + Name: "channel", + Usage: "Communication channels for the contact.", + Required: true, + BodyPath: "channels", + }, + &requestflag.Flag[string]{ + Name: "display-name", + Usage: "Display name for the contact.", + BodyPath: "displayName", + }, + &requestflag.Flag[map[string]any]{ + Name: "metadata", + Usage: "Arbitrary metadata to associate with the contact.", + BodyPath: "metadata", + }, + }, + Action: handleContactsCreate, + HideHelpCommand: true, +}, map[string][]requestflag.HasOuterFlag{ + "channel": { + &requestflag.InnerFlag[string]{ + Name: "channel.channel", + Usage: "Channel type.", + InnerField: "channel", + }, + &requestflag.InnerFlag[string]{ + Name: "channel.identifier", + Usage: "Channel identifier (phone number in E.164 format or email address).", + InnerField: "identifier", + }, + &requestflag.InnerFlag[string]{ + Name: "channel.country-code", + Usage: "ISO country code for phone numbers.", + InnerField: "countryCode", + }, + &requestflag.InnerFlag[bool]{ + Name: "channel.is-primary", + Usage: "Whether this should be the primary channel for its type.", + InnerField: "isPrimary", + }, + &requestflag.InnerFlag[string]{ + Name: "channel.label", + Usage: "Optional label for the channel.", + InnerField: "label", + }, + }, +}) + var contactsRetrieve = cli.Command{ Name: "retrieve", Usage: "Get contact", @@ -79,6 +133,40 @@ var contactsList = cli.Command{ HideHelpCommand: true, } +var contactsDismissMergeSuggestion = cli.Command{ + Name: "dismiss-merge-suggestion", + Usage: "Dismiss the merge suggestion for a contact.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "contact-id", + Required: true, + }, + }, + Action: handleContactsDismissMergeSuggestion, + HideHelpCommand: true, +} + +var contactsMerge = cli.Command{ + Name: "merge", + Usage: "Merge a source contact into this contact. All channels from the source contact\nwill be moved to the target contact, and the source contact will be marked as\nmerged.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "contact-id", + Required: true, + }, + &requestflag.Flag[string]{ + Name: "source-contact-id", + Usage: "ID of the contact to merge into the target contact. The source contact will be marked as merged.", + Required: true, + BodyPath: "sourceContactId", + }, + }, + Action: handleContactsMerge, + HideHelpCommand: true, +} + var contactsRetrieveByPhone = cli.Command{ Name: "retrieve-by-phone", Usage: "Get contact by phone number", @@ -93,6 +181,40 @@ var contactsRetrieveByPhone = cli.Command{ HideHelpCommand: true, } +func handleContactsCreate(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + params := zavudev.ContactNewParams{} + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Contacts.New(ctx, params, options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + return ShowJSON(os.Stdout, "contacts create", obj, format, transform) +} + func handleContactsRetrieve(ctx context.Context, cmd *cli.Command) error { client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) unusedArgs := cmd.Args().Slice() @@ -212,6 +334,73 @@ func handleContactsList(ctx context.Context, cmd *cli.Command) error { } } +func handleContactsDismissMergeSuggestion(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("contact-id") && len(unusedArgs) > 0 { + cmd.Set("contact-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + return client.Contacts.DismissMergeSuggestion(ctx, cmd.Value("contact-id").(string), options...) +} + +func handleContactsMerge(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("contact-id") && len(unusedArgs) > 0 { + cmd.Set("contact-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + params := zavudev.ContactMergeParams{} + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Contacts.Merge( + ctx, + cmd.Value("contact-id").(string), + params, + options..., + ) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + return ShowJSON(os.Stdout, "contacts merge", obj, format, transform) +} + func handleContactsRetrieveByPhone(ctx context.Context, cmd *cli.Command) error { client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) unusedArgs := cmd.Args().Slice() diff --git a/pkg/cmd/contact_test.go b/pkg/cmd/contact_test.go index b11eb87..24fc89e 100644 --- a/pkg/cmd/contact_test.go +++ b/pkg/cmd/contact_test.go @@ -6,8 +6,61 @@ import ( "testing" "github.com/zavudev/cli/internal/mocktest" + "github.com/zavudev/cli/internal/requestflag" ) +func TestContactsCreate(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "contacts", "create", + "--channel", "{channel: sms, identifier: '+14155551234', countryCode: US, isPrimary: true, label: work}", + "--display-name", "John Doe", + "--metadata", "{foo: string}", + ) + }) + + t.Run("inner flags", func(t *testing.T) { + // Check that inner flags have been set up correctly + requestflag.CheckInnerFlags(contactsCreate) + + // Alternative argument passing style using inner flags + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "contacts", "create", + "--channel.channel", "sms", + "--channel.identifier", "+14155551234", + "--channel.country-code", "US", + "--channel.is-primary=true", + "--channel.label", "work", + "--display-name", "John Doe", + "--metadata", "{foo: string}", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("" + + "channels:\n" + + " - channel: sms\n" + + " identifier: '+14155551234'\n" + + " countryCode: US\n" + + " isPrimary: true\n" + + " label: work\n" + + "displayName: John Doe\n" + + "metadata:\n" + + " foo: string\n") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "contacts", "create", + ) + }) +} + func TestContactsRetrieve(t *testing.T) { t.Skip("Mock server tests are disabled") t.Run("regular flags", func(t *testing.T) { @@ -63,6 +116,42 @@ func TestContactsList(t *testing.T) { }) } +func TestContactsDismissMergeSuggestion(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "contacts", "dismiss-merge-suggestion", + "--contact-id", "contactId", + ) + }) +} + +func TestContactsMerge(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "contacts", "merge", + "--contact-id", "contactId", + "--source-contact-id", "jx7xyz789", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("sourceContactId: jx7xyz789") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "contacts", "merge", + "--contact-id", "contactId", + ) + }) +} + func TestContactsRetrieveByPhone(t *testing.T) { t.Skip("Mock server tests are disabled") t.Run("regular flags", func(t *testing.T) { diff --git a/pkg/cmd/contactchannel.go b/pkg/cmd/contactchannel.go new file mode 100644 index 0000000..9cd520a --- /dev/null +++ b/pkg/cmd/contactchannel.go @@ -0,0 +1,290 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "context" + "fmt" + "os" + + "github.com/tidwall/gjson" + "github.com/urfave/cli/v3" + "github.com/zavudev/cli/internal/apiquery" + "github.com/zavudev/cli/internal/requestflag" + "github.com/zavudev/sdk-go" + "github.com/zavudev/sdk-go/option" +) + +var contactsChannelsUpdate = cli.Command{ + Name: "update", + Usage: "Update a contact's channel properties.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "contact-id", + Required: true, + }, + &requestflag.Flag[string]{ + Name: "channel-id", + Required: true, + }, + &requestflag.Flag[any]{ + Name: "label", + Usage: "Optional label for the channel. Set to null to clear.", + BodyPath: "label", + }, + &requestflag.Flag[map[string]any]{ + Name: "metadata", + BodyPath: "metadata", + }, + &requestflag.Flag[bool]{ + Name: "verified", + Usage: "Whether the channel is verified.", + BodyPath: "verified", + }, + }, + Action: handleContactsChannelsUpdate, + HideHelpCommand: true, +} + +var contactsChannelsAdd = cli.Command{ + Name: "add", + Usage: "Add a new communication channel to an existing contact.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "contact-id", + Required: true, + }, + &requestflag.Flag[string]{ + Name: "channel", + Usage: "Channel type.", + Required: true, + BodyPath: "channel", + }, + &requestflag.Flag[string]{ + Name: "identifier", + Usage: "Channel identifier (phone number in E.164 format or email address).", + Required: true, + BodyPath: "identifier", + }, + &requestflag.Flag[string]{ + Name: "country-code", + Usage: "ISO country code for phone numbers.", + BodyPath: "countryCode", + }, + &requestflag.Flag[bool]{ + Name: "is-primary", + Usage: "Whether this should be the primary channel for its type.", + Default: false, + BodyPath: "isPrimary", + }, + &requestflag.Flag[string]{ + Name: "label", + Usage: "Optional label for the channel.", + BodyPath: "label", + }, + }, + Action: handleContactsChannelsAdd, + HideHelpCommand: true, +} + +var contactsChannelsRemove = cli.Command{ + Name: "remove", + Usage: "Remove a communication channel from a contact. Cannot remove the last channel.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "contact-id", + Required: true, + }, + &requestflag.Flag[string]{ + Name: "channel-id", + Required: true, + }, + }, + Action: handleContactsChannelsRemove, + HideHelpCommand: true, +} + +var contactsChannelsSetPrimary = cli.Command{ + Name: "set-primary", + Usage: "Set a channel as the primary channel for its type.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "contact-id", + Required: true, + }, + &requestflag.Flag[string]{ + Name: "channel-id", + Required: true, + }, + }, + Action: handleContactsChannelsSetPrimary, + HideHelpCommand: true, +} + +func handleContactsChannelsUpdate(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("channel-id") && len(unusedArgs) > 0 { + cmd.Set("channel-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + params := zavudev.ContactChannelUpdateParams{ + ContactID: cmd.Value("contact-id").(string), + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Contacts.Channels.Update( + ctx, + cmd.Value("channel-id").(string), + params, + options..., + ) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + return ShowJSON(os.Stdout, "contacts:channels update", obj, format, transform) +} + +func handleContactsChannelsAdd(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("contact-id") && len(unusedArgs) > 0 { + cmd.Set("contact-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + params := zavudev.ContactChannelAddParams{} + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Contacts.Channels.Add( + ctx, + cmd.Value("contact-id").(string), + params, + options..., + ) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + return ShowJSON(os.Stdout, "contacts:channels add", obj, format, transform) +} + +func handleContactsChannelsRemove(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("channel-id") && len(unusedArgs) > 0 { + cmd.Set("channel-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + params := zavudev.ContactChannelRemoveParams{ + ContactID: cmd.Value("contact-id").(string), + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + return client.Contacts.Channels.Remove( + ctx, + cmd.Value("channel-id").(string), + params, + options..., + ) +} + +func handleContactsChannelsSetPrimary(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("channel-id") && len(unusedArgs) > 0 { + cmd.Set("channel-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + params := zavudev.ContactChannelSetPrimaryParams{ + ContactID: cmd.Value("contact-id").(string), + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Contacts.Channels.SetPrimary( + ctx, + cmd.Value("channel-id").(string), + params, + options..., + ) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + return ShowJSON(os.Stdout, "contacts:channels set-primary", obj, format, transform) +} diff --git a/pkg/cmd/contactchannel_test.go b/pkg/cmd/contactchannel_test.go new file mode 100644 index 0000000..6ebf814 --- /dev/null +++ b/pkg/cmd/contactchannel_test.go @@ -0,0 +1,100 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "testing" + + "github.com/zavudev/cli/internal/mocktest" +) + +func TestContactsChannelsUpdate(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "contacts:channels", "update", + "--contact-id", "contactId", + "--channel-id", "channelId", + "--label", "label", + "--metadata", "{foo: string}", + "--verified=true", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("" + + "label: label\n" + + "metadata:\n" + + " foo: string\n" + + "verified: true\n") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "contacts:channels", "update", + "--contact-id", "contactId", + "--channel-id", "channelId", + ) + }) +} + +func TestContactsChannelsAdd(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "contacts:channels", "add", + "--contact-id", "contactId", + "--channel", "email", + "--identifier", "john.work@company.com", + "--country-code", "US", + "--is-primary=true", + "--label", "work", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("" + + "channel: email\n" + + "identifier: john.work@company.com\n" + + "countryCode: US\n" + + "isPrimary: true\n" + + "label: work\n") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "contacts:channels", "add", + "--contact-id", "contactId", + ) + }) +} + +func TestContactsChannelsRemove(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "contacts:channels", "remove", + "--contact-id", "contactId", + "--channel-id", "channelId", + ) + }) +} + +func TestContactsChannelsSetPrimary(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "contacts:channels", "set-primary", + "--contact-id", "contactId", + "--channel-id", "channelId", + ) + }) +} diff --git a/pkg/cmd/export.go b/pkg/cmd/export.go new file mode 100644 index 0000000..4ebec17 --- /dev/null +++ b/pkg/cmd/export.go @@ -0,0 +1,195 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "context" + "fmt" + "os" + + "github.com/tidwall/gjson" + "github.com/urfave/cli/v3" + "github.com/zavudev/cli/internal/apiquery" + "github.com/zavudev/cli/internal/requestflag" + "github.com/zavudev/sdk-go" + "github.com/zavudev/sdk-go/option" +) + +var exportsCreate = cli.Command{ + Name: "create", + Usage: "Create a new data export job. The export will be processed asynchronously and\nthe download URL will be available when status is 'completed'. Export links\nexpire after 24 hours.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[[]string]{ + Name: "data-type", + Usage: "List of data types to include in the export.", + Required: true, + BodyPath: "dataTypes", + }, + &requestflag.Flag[any]{ + Name: "date-from", + Usage: "Start date for data to export (inclusive).", + BodyPath: "dateFrom", + }, + &requestflag.Flag[any]{ + Name: "date-to", + Usage: "End date for data to export (inclusive).", + BodyPath: "dateTo", + }, + }, + Action: handleExportsCreate, + HideHelpCommand: true, +} + +var exportsRetrieve = cli.Command{ + Name: "retrieve", + Usage: "Get details of a specific data export, including download URL when completed.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "export-id", + Required: true, + }, + }, + Action: handleExportsRetrieve, + HideHelpCommand: true, +} + +var exportsList = cli.Command{ + Name: "list", + Usage: "List data exports for this project.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "cursor", + QueryPath: "cursor", + }, + &requestflag.Flag[int64]{ + Name: "limit", + Default: 50, + QueryPath: "limit", + }, + &requestflag.Flag[string]{ + Name: "status", + Usage: "Status of a data export job.", + QueryPath: "status", + }, + &requestflag.Flag[int64]{ + Name: "max-items", + Usage: "The maximum number of items to return (use -1 for unlimited).", + }, + }, + Action: handleExportsList, + HideHelpCommand: true, +} + +func handleExportsCreate(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + params := zavudev.ExportNewParams{} + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Exports.New(ctx, params, options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + return ShowJSON(os.Stdout, "exports create", obj, format, transform) +} + +func handleExportsRetrieve(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("export-id") && len(unusedArgs) > 0 { + cmd.Set("export-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Exports.Get(ctx, cmd.Value("export-id").(string), options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + return ShowJSON(os.Stdout, "exports retrieve", obj, format, transform) +} + +func handleExportsList(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + params := zavudev.ExportListParams{} + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + if format == "raw" { + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Exports.List(ctx, params, options...) + if err != nil { + return err + } + obj := gjson.ParseBytes(res) + return ShowJSON(os.Stdout, "exports list", obj, format, transform) + } else { + iter := client.Exports.ListAutoPaging(ctx, params, options...) + maxItems := int64(-1) + if cmd.IsSet("max-items") { + maxItems = cmd.Value("max-items").(int64) + } + return ShowJSONIterator(os.Stdout, "exports list", iter, format, transform, maxItems) + } +} diff --git a/pkg/cmd/export_test.go b/pkg/cmd/export_test.go new file mode 100644 index 0000000..71041f6 --- /dev/null +++ b/pkg/cmd/export_test.go @@ -0,0 +1,66 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "testing" + + "github.com/zavudev/cli/internal/mocktest" +) + +func TestExportsCreate(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "exports", "create", + "--data-type", "messages", + "--data-type", "conversations", + "--date-from", "'2024-01-01T00:00:00Z'", + "--date-to", "'2024-12-31T23:59:59Z'", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("" + + "dataTypes:\n" + + " - messages\n" + + " - conversations\n" + + "dateFrom: '2024-01-01T00:00:00Z'\n" + + "dateTo: '2024-12-31T23:59:59Z'\n") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "exports", "create", + ) + }) +} + +func TestExportsRetrieve(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "exports", "retrieve", + "--export-id", "exportId", + ) + }) +} + +func TestExportsList(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "exports", "list", + "--max-items", "10", + "--cursor", "cursor", + "--limit", "100", + "--status", "pending", + ) + }) +} diff --git a/pkg/cmd/invitation.go b/pkg/cmd/invitation.go new file mode 100644 index 0000000..12cec7a --- /dev/null +++ b/pkg/cmd/invitation.go @@ -0,0 +1,259 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "context" + "fmt" + "os" + + "github.com/tidwall/gjson" + "github.com/urfave/cli/v3" + "github.com/zavudev/cli/internal/apiquery" + "github.com/zavudev/cli/internal/requestflag" + "github.com/zavudev/sdk-go" + "github.com/zavudev/sdk-go/option" +) + +var invitationsCreate = cli.Command{ + Name: "create", + Usage: "Create a partner invitation link for a client to connect their WhatsApp Business\naccount. The client will complete Meta's embedded signup flow and the resulting\nsender will be created in your project.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[[]string]{ + Name: "allowed-phone-country", + Usage: "ISO country codes for allowed phone numbers.", + BodyPath: "allowedPhoneCountries", + }, + &requestflag.Flag[string]{ + Name: "client-email", + Usage: "Email of the client being invited.", + BodyPath: "clientEmail", + }, + &requestflag.Flag[string]{ + Name: "client-name", + Usage: "Name of the client being invited.", + BodyPath: "clientName", + }, + &requestflag.Flag[string]{ + Name: "client-phone", + Usage: "Phone number of the client in E.164 format.", + BodyPath: "clientPhone", + }, + &requestflag.Flag[int64]{ + Name: "expires-in-days", + Usage: "Number of days until the invitation expires.", + Default: 7, + BodyPath: "expiresInDays", + }, + &requestflag.Flag[string]{ + Name: "phone-number-id", + Usage: "ID of a Zavu phone number to pre-assign for WhatsApp registration. If provided, the client will use this number instead of their own.", + BodyPath: "phoneNumberId", + }, + }, + Action: handleInvitationsCreate, + HideHelpCommand: true, +} + +var invitationsRetrieve = cli.Command{ + Name: "retrieve", + Usage: "Get invitation", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "invitation-id", + Required: true, + }, + }, + Action: handleInvitationsRetrieve, + HideHelpCommand: true, +} + +var invitationsList = cli.Command{ + Name: "list", + Usage: "List partner invitations for this project.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "cursor", + QueryPath: "cursor", + }, + &requestflag.Flag[int64]{ + Name: "limit", + Default: 50, + QueryPath: "limit", + }, + &requestflag.Flag[string]{ + Name: "status", + Usage: "Current status of the partner invitation.", + QueryPath: "status", + }, + &requestflag.Flag[int64]{ + Name: "max-items", + Usage: "The maximum number of items to return (use -1 for unlimited).", + }, + }, + Action: handleInvitationsList, + HideHelpCommand: true, +} + +var invitationsCancel = cli.Command{ + Name: "cancel", + Usage: "Cancel an active invitation. The client will no longer be able to use the\ninvitation link.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "invitation-id", + Required: true, + }, + }, + Action: handleInvitationsCancel, + HideHelpCommand: true, +} + +func handleInvitationsCreate(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + params := zavudev.InvitationNewParams{} + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Invitations.New(ctx, params, options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + return ShowJSON(os.Stdout, "invitations create", obj, format, transform) +} + +func handleInvitationsRetrieve(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("invitation-id") && len(unusedArgs) > 0 { + cmd.Set("invitation-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Invitations.Get(ctx, cmd.Value("invitation-id").(string), options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + return ShowJSON(os.Stdout, "invitations retrieve", obj, format, transform) +} + +func handleInvitationsList(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + params := zavudev.InvitationListParams{} + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + if format == "raw" { + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Invitations.List(ctx, params, options...) + if err != nil { + return err + } + obj := gjson.ParseBytes(res) + return ShowJSON(os.Stdout, "invitations list", obj, format, transform) + } else { + iter := client.Invitations.ListAutoPaging(ctx, params, options...) + maxItems := int64(-1) + if cmd.IsSet("max-items") { + maxItems = cmd.Value("max-items").(int64) + } + return ShowJSONIterator(os.Stdout, "invitations list", iter, format, transform, maxItems) + } +} + +func handleInvitationsCancel(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("invitation-id") && len(unusedArgs) > 0 { + cmd.Set("invitation-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Invitations.Cancel(ctx, cmd.Value("invitation-id").(string), options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + return ShowJSON(os.Stdout, "invitations cancel", obj, format, transform) +} diff --git a/pkg/cmd/invitation_test.go b/pkg/cmd/invitation_test.go new file mode 100644 index 0000000..2a09733 --- /dev/null +++ b/pkg/cmd/invitation_test.go @@ -0,0 +1,84 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "testing" + + "github.com/zavudev/cli/internal/mocktest" +) + +func TestInvitationsCreate(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "invitations", "create", + "--allowed-phone-country", "US", + "--allowed-phone-country", "MX", + "--client-email", "contact@acme.com", + "--client-name", "Acme Corp", + "--client-phone", "+14155551234", + "--expires-in-days", "1", + "--phone-number-id", "pn_abc123", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("" + + "allowedPhoneCountries:\n" + + " - US\n" + + " - MX\n" + + "clientEmail: contact@acme.com\n" + + "clientName: Acme Corp\n" + + "clientPhone: '+14155551234'\n" + + "expiresInDays: 1\n" + + "phoneNumberId: pn_abc123\n") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "invitations", "create", + ) + }) +} + +func TestInvitationsRetrieve(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "invitations", "retrieve", + "--invitation-id", "invitationId", + ) + }) +} + +func TestInvitationsList(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "invitations", "list", + "--max-items", "10", + "--cursor", "cursor", + "--limit", "100", + "--status", "pending", + ) + }) +} + +func TestInvitationsCancel(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "invitations", "cancel", + "--invitation-id", "invitationId", + ) + }) +} diff --git a/pkg/cmd/number10dlcbrand.go b/pkg/cmd/number10dlcbrand.go new file mode 100644 index 0000000..9dc2c00 --- /dev/null +++ b/pkg/cmd/number10dlcbrand.go @@ -0,0 +1,562 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "context" + "fmt" + "os" + + "github.com/tidwall/gjson" + "github.com/urfave/cli/v3" + "github.com/zavudev/cli/internal/apiquery" + "github.com/zavudev/cli/internal/requestflag" + "github.com/zavudev/sdk-go" + "github.com/zavudev/sdk-go/option" +) + +var number10dlcBrandsCreate = cli.Command{ + Name: "create", + Usage: "Create a 10DLC brand registration. The brand starts in draft status. Submit it\nfor review using the submit endpoint.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "city", + Required: true, + BodyPath: "city", + }, + &requestflag.Flag[string]{ + Name: "country", + Usage: "Two-letter ISO country code.", + Required: true, + BodyPath: "country", + }, + &requestflag.Flag[string]{ + Name: "display-name", + Usage: "Display name of the brand.", + Required: true, + BodyPath: "displayName", + }, + &requestflag.Flag[string]{ + Name: "email", + Required: true, + BodyPath: "email", + }, + &requestflag.Flag[string]{ + Name: "entity-type", + Usage: "Business entity type for 10DLC brand registration.", + Required: true, + BodyPath: "entityType", + }, + &requestflag.Flag[string]{ + Name: "phone", + Usage: "Contact phone in E.164 format.", + Required: true, + BodyPath: "phone", + }, + &requestflag.Flag[string]{ + Name: "postal-code", + Required: true, + BodyPath: "postalCode", + }, + &requestflag.Flag[string]{ + Name: "state", + Required: true, + BodyPath: "state", + }, + &requestflag.Flag[string]{ + Name: "street", + Required: true, + BodyPath: "street", + }, + &requestflag.Flag[string]{ + Name: "vertical", + Usage: "Industry vertical.", + Required: true, + BodyPath: "vertical", + }, + &requestflag.Flag[string]{ + Name: "company-name", + Usage: "Legal company name.", + BodyPath: "companyName", + }, + &requestflag.Flag[string]{ + Name: "ein", + Usage: "Employer Identification Number (format: XX-XXXXXXX).", + BodyPath: "ein", + }, + &requestflag.Flag[string]{ + Name: "first-name", + BodyPath: "firstName", + }, + &requestflag.Flag[string]{ + Name: "last-name", + BodyPath: "lastName", + }, + &requestflag.Flag[string]{ + Name: "stock-exchange", + BodyPath: "stockExchange", + }, + &requestflag.Flag[string]{ + Name: "stock-symbol", + BodyPath: "stockSymbol", + }, + &requestflag.Flag[string]{ + Name: "website", + BodyPath: "website", + }, + }, + Action: handleNumber10dlcBrandsCreate, + HideHelpCommand: true, +} + +var number10dlcBrandsRetrieve = cli.Command{ + Name: "retrieve", + Usage: "Get 10DLC brand", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "brand-id", + Required: true, + }, + }, + Action: handleNumber10dlcBrandsRetrieve, + HideHelpCommand: true, +} + +var number10dlcBrandsUpdate = cli.Command{ + Name: "update", + Usage: "Update a 10DLC brand in draft status. Cannot update after submission.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "brand-id", + Required: true, + }, + &requestflag.Flag[string]{ + Name: "city", + BodyPath: "city", + }, + &requestflag.Flag[string]{ + Name: "company-name", + BodyPath: "companyName", + }, + &requestflag.Flag[string]{ + Name: "country", + BodyPath: "country", + }, + &requestflag.Flag[string]{ + Name: "display-name", + BodyPath: "displayName", + }, + &requestflag.Flag[string]{ + Name: "ein", + BodyPath: "ein", + }, + &requestflag.Flag[string]{ + Name: "email", + BodyPath: "email", + }, + &requestflag.Flag[string]{ + Name: "entity-type", + Usage: "Business entity type for 10DLC brand registration.", + BodyPath: "entityType", + }, + &requestflag.Flag[string]{ + Name: "first-name", + BodyPath: "firstName", + }, + &requestflag.Flag[string]{ + Name: "last-name", + BodyPath: "lastName", + }, + &requestflag.Flag[string]{ + Name: "phone", + BodyPath: "phone", + }, + &requestflag.Flag[string]{ + Name: "postal-code", + BodyPath: "postalCode", + }, + &requestflag.Flag[string]{ + Name: "state", + BodyPath: "state", + }, + &requestflag.Flag[string]{ + Name: "stock-exchange", + BodyPath: "stockExchange", + }, + &requestflag.Flag[string]{ + Name: "stock-symbol", + BodyPath: "stockSymbol", + }, + &requestflag.Flag[string]{ + Name: "street", + BodyPath: "street", + }, + &requestflag.Flag[string]{ + Name: "vertical", + BodyPath: "vertical", + }, + &requestflag.Flag[string]{ + Name: "website", + BodyPath: "website", + }, + }, + Action: handleNumber10dlcBrandsUpdate, + HideHelpCommand: true, +} + +var number10dlcBrandsList = cli.Command{ + Name: "list", + Usage: "List 10DLC brand registrations for this project.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "cursor", + QueryPath: "cursor", + }, + &requestflag.Flag[int64]{ + Name: "limit", + Default: 50, + QueryPath: "limit", + }, + &requestflag.Flag[int64]{ + Name: "max-items", + Usage: "The maximum number of items to return (use -1 for unlimited).", + }, + }, + Action: handleNumber10dlcBrandsList, + HideHelpCommand: true, +} + +var number10dlcBrandsDelete = cli.Command{ + Name: "delete", + Usage: "Delete 10DLC brand", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "brand-id", + Required: true, + }, + }, + Action: handleNumber10dlcBrandsDelete, + HideHelpCommand: true, +} + +var number10dlcBrandsListUseCases = cli.Command{ + Name: "list-use-cases", + Usage: "List available use cases for 10DLC campaign registration.", + Suggest: true, + Flags: []cli.Flag{}, + Action: handleNumber10dlcBrandsListUseCases, + HideHelpCommand: true, +} + +var number10dlcBrandsSubmit = cli.Command{ + Name: "submit", + Usage: "Submit a draft brand to The Campaign Registry (TCR) for vetting. The brand must\nbe in draft status. A $35 registration fee is charged from your balance.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "brand-id", + Required: true, + }, + }, + Action: handleNumber10dlcBrandsSubmit, + HideHelpCommand: true, +} + +var number10dlcBrandsSyncStatus = cli.Command{ + Name: "sync-status", + Usage: "Sync the brand status with the registration provider. Use this to check for\napproval updates after submission.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "brand-id", + Required: true, + }, + }, + Action: handleNumber10dlcBrandsSyncStatus, + HideHelpCommand: true, +} + +func handleNumber10dlcBrandsCreate(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + params := zavudev.Number10dlcBrandNewParams{} + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Number10dlc.Brands.New(ctx, params, options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + return ShowJSON(os.Stdout, "number-10dlc:brands create", obj, format, transform) +} + +func handleNumber10dlcBrandsRetrieve(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("brand-id") && len(unusedArgs) > 0 { + cmd.Set("brand-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Number10dlc.Brands.Get(ctx, cmd.Value("brand-id").(string), options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + return ShowJSON(os.Stdout, "number-10dlc:brands retrieve", obj, format, transform) +} + +func handleNumber10dlcBrandsUpdate(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("brand-id") && len(unusedArgs) > 0 { + cmd.Set("brand-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + params := zavudev.Number10dlcBrandUpdateParams{} + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Number10dlc.Brands.Update( + ctx, + cmd.Value("brand-id").(string), + params, + options..., + ) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + return ShowJSON(os.Stdout, "number-10dlc:brands update", obj, format, transform) +} + +func handleNumber10dlcBrandsList(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + params := zavudev.Number10dlcBrandListParams{} + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + if format == "raw" { + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Number10dlc.Brands.List(ctx, params, options...) + if err != nil { + return err + } + obj := gjson.ParseBytes(res) + return ShowJSON(os.Stdout, "number-10dlc:brands list", obj, format, transform) + } else { + iter := client.Number10dlc.Brands.ListAutoPaging(ctx, params, options...) + maxItems := int64(-1) + if cmd.IsSet("max-items") { + maxItems = cmd.Value("max-items").(int64) + } + return ShowJSONIterator(os.Stdout, "number-10dlc:brands list", iter, format, transform, maxItems) + } +} + +func handleNumber10dlcBrandsDelete(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("brand-id") && len(unusedArgs) > 0 { + cmd.Set("brand-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + return client.Number10dlc.Brands.Delete(ctx, cmd.Value("brand-id").(string), options...) +} + +func handleNumber10dlcBrandsListUseCases(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Number10dlc.Brands.ListUseCases(ctx, options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + return ShowJSON(os.Stdout, "number-10dlc:brands list-use-cases", obj, format, transform) +} + +func handleNumber10dlcBrandsSubmit(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("brand-id") && len(unusedArgs) > 0 { + cmd.Set("brand-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Number10dlc.Brands.Submit(ctx, cmd.Value("brand-id").(string), options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + return ShowJSON(os.Stdout, "number-10dlc:brands submit", obj, format, transform) +} + +func handleNumber10dlcBrandsSyncStatus(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("brand-id") && len(unusedArgs) > 0 { + cmd.Set("brand-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Number10dlc.Brands.SyncStatus(ctx, cmd.Value("brand-id").(string), options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + return ShowJSON(os.Stdout, "number-10dlc:brands sync-status", obj, format, transform) +} diff --git a/pkg/cmd/number10dlcbrand_test.go b/pkg/cmd/number10dlcbrand_test.go new file mode 100644 index 0000000..48c5f53 --- /dev/null +++ b/pkg/cmd/number10dlcbrand_test.go @@ -0,0 +1,194 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "testing" + + "github.com/zavudev/cli/internal/mocktest" +) + +func TestNumber10dlcBrandsCreate(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "number-10dlc:brands", "create", + "--city", "San Francisco", + "--country", "US", + "--display-name", "Acme Corp", + "--email", "compliance@acme.com", + "--entity-type", "PRIVATE_PROFIT", + "--phone", "+14155551234", + "--postal-code", "94102", + "--state", "CA", + "--street", "123 Main St", + "--vertical", "Technology", + "--company-name", "Acme Corporation", + "--ein", "12-3456789", + "--first-name", "firstName", + "--last-name", "lastName", + "--stock-exchange", "stockExchange", + "--stock-symbol", "stockSymbol", + "--website", "https://acme.com", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("" + + "city: San Francisco\n" + + "country: US\n" + + "displayName: Acme Corp\n" + + "email: compliance@acme.com\n" + + "entityType: PRIVATE_PROFIT\n" + + "phone: '+14155551234'\n" + + "postalCode: '94102'\n" + + "state: CA\n" + + "street: 123 Main St\n" + + "vertical: Technology\n" + + "companyName: Acme Corporation\n" + + "ein: 12-3456789\n" + + "firstName: firstName\n" + + "lastName: lastName\n" + + "stockExchange: stockExchange\n" + + "stockSymbol: stockSymbol\n" + + "website: https://acme.com\n") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "number-10dlc:brands", "create", + ) + }) +} + +func TestNumber10dlcBrandsRetrieve(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "number-10dlc:brands", "retrieve", + "--brand-id", "brandId", + ) + }) +} + +func TestNumber10dlcBrandsUpdate(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "number-10dlc:brands", "update", + "--brand-id", "brandId", + "--city", "city", + "--company-name", "companyName", + "--country", "xx", + "--display-name", "displayName", + "--ein", "ein", + "--email", "dev@stainless.com", + "--entity-type", "PRIVATE_PROFIT", + "--first-name", "firstName", + "--last-name", "lastName", + "--phone", "phone", + "--postal-code", "postalCode", + "--state", "state", + "--stock-exchange", "stockExchange", + "--stock-symbol", "stockSymbol", + "--street", "street", + "--vertical", "vertical", + "--website", "https://example.com", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("" + + "city: city\n" + + "companyName: companyName\n" + + "country: xx\n" + + "displayName: displayName\n" + + "ein: ein\n" + + "email: dev@stainless.com\n" + + "entityType: PRIVATE_PROFIT\n" + + "firstName: firstName\n" + + "lastName: lastName\n" + + "phone: phone\n" + + "postalCode: postalCode\n" + + "state: state\n" + + "stockExchange: stockExchange\n" + + "stockSymbol: stockSymbol\n" + + "street: street\n" + + "vertical: vertical\n" + + "website: https://example.com\n") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "number-10dlc:brands", "update", + "--brand-id", "brandId", + ) + }) +} + +func TestNumber10dlcBrandsList(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "number-10dlc:brands", "list", + "--max-items", "10", + "--cursor", "cursor", + "--limit", "100", + ) + }) +} + +func TestNumber10dlcBrandsDelete(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "number-10dlc:brands", "delete", + "--brand-id", "brandId", + ) + }) +} + +func TestNumber10dlcBrandsListUseCases(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "number-10dlc:brands", "list-use-cases", + ) + }) +} + +func TestNumber10dlcBrandsSubmit(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "number-10dlc:brands", "submit", + "--brand-id", "brandId", + ) + }) +} + +func TestNumber10dlcBrandsSyncStatus(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "number-10dlc:brands", "sync-status", + "--brand-id", "brandId", + ) + }) +} diff --git a/pkg/cmd/number10dlccampaign.go b/pkg/cmd/number10dlccampaign.go new file mode 100644 index 0000000..5796bd0 --- /dev/null +++ b/pkg/cmd/number10dlccampaign.go @@ -0,0 +1,492 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "context" + "fmt" + "os" + + "github.com/tidwall/gjson" + "github.com/urfave/cli/v3" + "github.com/zavudev/cli/internal/apiquery" + "github.com/zavudev/cli/internal/requestflag" + "github.com/zavudev/sdk-go" + "github.com/zavudev/sdk-go/option" +) + +var number10dlcCampaignsCreate = cli.Command{ + Name: "create", + Usage: "Create a 10DLC campaign under an existing brand. The campaign starts in draft\nstatus. Submit it for carrier review using the submit endpoint.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[bool]{ + Name: "affiliate-marketing", + Required: true, + BodyPath: "affiliateMarketing", + }, + &requestflag.Flag[bool]{ + Name: "age-gated", + Required: true, + BodyPath: "ageGated", + }, + &requestflag.Flag[string]{ + Name: "brand-id", + Usage: "ID of the brand to create this campaign under.", + Required: true, + BodyPath: "brandId", + }, + &requestflag.Flag[string]{ + Name: "description", + Required: true, + BodyPath: "description", + }, + &requestflag.Flag[bool]{ + Name: "direct-lending", + Required: true, + BodyPath: "directLending", + }, + &requestflag.Flag[bool]{ + Name: "embedded-link", + Required: true, + BodyPath: "embeddedLink", + }, + &requestflag.Flag[bool]{ + Name: "embedded-phone", + Required: true, + BodyPath: "embeddedPhone", + }, + &requestflag.Flag[string]{ + Name: "name", + Required: true, + BodyPath: "name", + }, + &requestflag.Flag[bool]{ + Name: "number-pooling", + Required: true, + BodyPath: "numberPooling", + }, + &requestflag.Flag[[]string]{ + Name: "sample-message", + Required: true, + BodyPath: "sampleMessages", + }, + &requestflag.Flag[bool]{ + Name: "subscriber-help", + Required: true, + BodyPath: "subscriberHelp", + }, + &requestflag.Flag[bool]{ + Name: "subscriber-opt-in", + Required: true, + BodyPath: "subscriberOptIn", + }, + &requestflag.Flag[bool]{ + Name: "subscriber-opt-out", + Required: true, + BodyPath: "subscriberOptOut", + }, + &requestflag.Flag[string]{ + Name: "use-case", + Usage: "Campaign use case (e.g., ACCOUNT_NOTIFICATION, MARKETING, 2FA).", + Required: true, + BodyPath: "useCase", + }, + &requestflag.Flag[string]{ + Name: "help-message", + BodyPath: "helpMessage", + }, + &requestflag.Flag[string]{ + Name: "message-flow", + BodyPath: "messageFlow", + }, + &requestflag.Flag[[]string]{ + Name: "opt-in-keyword", + BodyPath: "optInKeywords", + }, + &requestflag.Flag[[]string]{ + Name: "opt-out-keyword", + BodyPath: "optOutKeywords", + }, + &requestflag.Flag[[]string]{ + Name: "sub-use-case", + BodyPath: "subUseCases", + }, + }, + Action: handleNumber10dlcCampaignsCreate, + HideHelpCommand: true, +} + +var number10dlcCampaignsRetrieve = cli.Command{ + Name: "retrieve", + Usage: "Get 10DLC campaign", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "campaign-id", + Required: true, + }, + }, + Action: handleNumber10dlcCampaignsRetrieve, + HideHelpCommand: true, +} + +var number10dlcCampaignsUpdate = cli.Command{ + Name: "update", + Usage: "Update a 10DLC campaign in draft status. Cannot update after submission.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "campaign-id", + Required: true, + }, + &requestflag.Flag[string]{ + Name: "description", + BodyPath: "description", + }, + &requestflag.Flag[string]{ + Name: "help-message", + BodyPath: "helpMessage", + }, + &requestflag.Flag[string]{ + Name: "message-flow", + BodyPath: "messageFlow", + }, + &requestflag.Flag[string]{ + Name: "name", + BodyPath: "name", + }, + &requestflag.Flag[[]string]{ + Name: "opt-in-keyword", + BodyPath: "optInKeywords", + }, + &requestflag.Flag[[]string]{ + Name: "opt-out-keyword", + BodyPath: "optOutKeywords", + }, + &requestflag.Flag[[]string]{ + Name: "sample-message", + BodyPath: "sampleMessages", + }, + }, + Action: handleNumber10dlcCampaignsUpdate, + HideHelpCommand: true, +} + +var number10dlcCampaignsList = cli.Command{ + Name: "list", + Usage: "List 10DLC campaign registrations for this project.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "brand-id", + Usage: "Filter campaigns by brand ID.", + QueryPath: "brandId", + }, + &requestflag.Flag[string]{ + Name: "cursor", + QueryPath: "cursor", + }, + &requestflag.Flag[int64]{ + Name: "limit", + Default: 50, + QueryPath: "limit", + }, + &requestflag.Flag[int64]{ + Name: "max-items", + Usage: "The maximum number of items to return (use -1 for unlimited).", + }, + }, + Action: handleNumber10dlcCampaignsList, + HideHelpCommand: true, +} + +var number10dlcCampaignsDelete = cli.Command{ + Name: "delete", + Usage: "Delete 10DLC campaign", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "campaign-id", + Required: true, + }, + }, + Action: handleNumber10dlcCampaignsDelete, + HideHelpCommand: true, +} + +var number10dlcCampaignsSubmit = cli.Command{ + Name: "submit", + Usage: "Submit a draft campaign for carrier review. The campaign must be in draft status\nand its brand must be verified.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "campaign-id", + Required: true, + }, + }, + Action: handleNumber10dlcCampaignsSubmit, + HideHelpCommand: true, +} + +var number10dlcCampaignsSyncStatus = cli.Command{ + Name: "sync-status", + Usage: "Sync the campaign status with the registration provider. Use this to check for\napproval updates after submission.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "campaign-id", + Required: true, + }, + }, + Action: handleNumber10dlcCampaignsSyncStatus, + HideHelpCommand: true, +} + +func handleNumber10dlcCampaignsCreate(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + params := zavudev.Number10dlcCampaignNewParams{} + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Number10dlc.Campaigns.New(ctx, params, options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + return ShowJSON(os.Stdout, "number-10dlc:campaigns create", obj, format, transform) +} + +func handleNumber10dlcCampaignsRetrieve(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("campaign-id") && len(unusedArgs) > 0 { + cmd.Set("campaign-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Number10dlc.Campaigns.Get(ctx, cmd.Value("campaign-id").(string), options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + return ShowJSON(os.Stdout, "number-10dlc:campaigns retrieve", obj, format, transform) +} + +func handleNumber10dlcCampaignsUpdate(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("campaign-id") && len(unusedArgs) > 0 { + cmd.Set("campaign-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + params := zavudev.Number10dlcCampaignUpdateParams{} + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Number10dlc.Campaigns.Update( + ctx, + cmd.Value("campaign-id").(string), + params, + options..., + ) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + return ShowJSON(os.Stdout, "number-10dlc:campaigns update", obj, format, transform) +} + +func handleNumber10dlcCampaignsList(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + params := zavudev.Number10dlcCampaignListParams{} + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + if format == "raw" { + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Number10dlc.Campaigns.List(ctx, params, options...) + if err != nil { + return err + } + obj := gjson.ParseBytes(res) + return ShowJSON(os.Stdout, "number-10dlc:campaigns list", obj, format, transform) + } else { + iter := client.Number10dlc.Campaigns.ListAutoPaging(ctx, params, options...) + maxItems := int64(-1) + if cmd.IsSet("max-items") { + maxItems = cmd.Value("max-items").(int64) + } + return ShowJSONIterator(os.Stdout, "number-10dlc:campaigns list", iter, format, transform, maxItems) + } +} + +func handleNumber10dlcCampaignsDelete(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("campaign-id") && len(unusedArgs) > 0 { + cmd.Set("campaign-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + return client.Number10dlc.Campaigns.Delete(ctx, cmd.Value("campaign-id").(string), options...) +} + +func handleNumber10dlcCampaignsSubmit(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("campaign-id") && len(unusedArgs) > 0 { + cmd.Set("campaign-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Number10dlc.Campaigns.Submit(ctx, cmd.Value("campaign-id").(string), options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + return ShowJSON(os.Stdout, "number-10dlc:campaigns submit", obj, format, transform) +} + +func handleNumber10dlcCampaignsSyncStatus(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("campaign-id") && len(unusedArgs) > 0 { + cmd.Set("campaign-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Number10dlc.Campaigns.SyncStatus(ctx, cmd.Value("campaign-id").(string), options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + return ShowJSON(os.Stdout, "number-10dlc:campaigns sync-status", obj, format, transform) +} diff --git a/pkg/cmd/number10dlccampaign_test.go b/pkg/cmd/number10dlccampaign_test.go new file mode 100644 index 0000000..39fd758 --- /dev/null +++ b/pkg/cmd/number10dlccampaign_test.go @@ -0,0 +1,179 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "testing" + + "github.com/zavudev/cli/internal/mocktest" +) + +func TestNumber10dlcCampaignsCreate(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "number-10dlc:campaigns", "create", + "--affiliate-marketing=false", + "--age-gated=false", + "--brand-id", "brand_abc123", + "--description", "Send order status updates and shipping notifications to customers who opted in.", + "--direct-lending=false", + "--embedded-link=true", + "--embedded-phone=false", + "--name", "Order Notifications", + "--number-pooling=false", + "--sample-message", "Hi {{name}}, your order #{{order_id}} has shipped! Track it at {{url}}", + "--sample-message", "Your order #{{order_id}} has been delivered. Thank you for your purchase!", + "--subscriber-help=true", + "--subscriber-opt-in=true", + "--subscriber-opt-out=true", + "--use-case", "ACCOUNT_NOTIFICATION", + "--help-message", "helpMessage", + "--message-flow", "messageFlow", + "--opt-in-keyword", "string", + "--opt-out-keyword", "string", + "--sub-use-case", "string", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("" + + "affiliateMarketing: false\n" + + "ageGated: false\n" + + "brandId: brand_abc123\n" + + "description: >-\n" + + " Send order status updates and shipping notifications to customers who opted\n" + + " in.\n" + + "directLending: false\n" + + "embeddedLink: true\n" + + "embeddedPhone: false\n" + + "name: Order Notifications\n" + + "numberPooling: false\n" + + "sampleMessages:\n" + + " - 'Hi {{name}}, your order #{{order_id}} has shipped! Track it at {{url}}'\n" + + " - 'Your order #{{order_id}} has been delivered. Thank you for your purchase!'\n" + + "subscriberHelp: true\n" + + "subscriberOptIn: true\n" + + "subscriberOptOut: true\n" + + "useCase: ACCOUNT_NOTIFICATION\n" + + "helpMessage: helpMessage\n" + + "messageFlow: messageFlow\n" + + "optInKeywords:\n" + + " - string\n" + + "optOutKeywords:\n" + + " - string\n" + + "subUseCases:\n" + + " - string\n") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "number-10dlc:campaigns", "create", + ) + }) +} + +func TestNumber10dlcCampaignsRetrieve(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "number-10dlc:campaigns", "retrieve", + "--campaign-id", "campaignId", + ) + }) +} + +func TestNumber10dlcCampaignsUpdate(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "number-10dlc:campaigns", "update", + "--campaign-id", "campaignId", + "--description", "description", + "--help-message", "helpMessage", + "--message-flow", "messageFlow", + "--name", "name", + "--opt-in-keyword", "string", + "--opt-out-keyword", "string", + "--sample-message", "string", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("" + + "description: description\n" + + "helpMessage: helpMessage\n" + + "messageFlow: messageFlow\n" + + "name: name\n" + + "optInKeywords:\n" + + " - string\n" + + "optOutKeywords:\n" + + " - string\n" + + "sampleMessages:\n" + + " - string\n") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "number-10dlc:campaigns", "update", + "--campaign-id", "campaignId", + ) + }) +} + +func TestNumber10dlcCampaignsList(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "number-10dlc:campaigns", "list", + "--max-items", "10", + "--brand-id", "brandId", + "--cursor", "cursor", + "--limit", "100", + ) + }) +} + +func TestNumber10dlcCampaignsDelete(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "number-10dlc:campaigns", "delete", + "--campaign-id", "campaignId", + ) + }) +} + +func TestNumber10dlcCampaignsSubmit(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "number-10dlc:campaigns", "submit", + "--campaign-id", "campaignId", + ) + }) +} + +func TestNumber10dlcCampaignsSyncStatus(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "number-10dlc:campaigns", "sync-status", + "--campaign-id", "campaignId", + ) + }) +} diff --git a/pkg/cmd/number10dlccampaignphonenumber.go b/pkg/cmd/number10dlccampaignphonenumber.go new file mode 100644 index 0000000..ae1874b --- /dev/null +++ b/pkg/cmd/number10dlccampaignphonenumber.go @@ -0,0 +1,179 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "context" + "fmt" + "os" + + "github.com/tidwall/gjson" + "github.com/urfave/cli/v3" + "github.com/zavudev/cli/internal/apiquery" + "github.com/zavudev/cli/internal/requestflag" + "github.com/zavudev/sdk-go" + "github.com/zavudev/sdk-go/option" +) + +var number10dlcCampaignsPhoneNumbersList = cli.Command{ + Name: "list", + Usage: "List phone numbers assigned to a 10DLC campaign.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "campaign-id", + Required: true, + }, + }, + Action: handleNumber10dlcCampaignsPhoneNumbersList, + HideHelpCommand: true, +} + +var number10dlcCampaignsPhoneNumbersAssign = cli.Command{ + Name: "assign", + Usage: "Assign a US phone number to an approved 10DLC campaign. The campaign must be in\napproved status.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "campaign-id", + Required: true, + }, + &requestflag.Flag[string]{ + Name: "phone-number-id", + Usage: "ID of the phone number to assign.", + Required: true, + BodyPath: "phoneNumberId", + }, + }, + Action: handleNumber10dlcCampaignsPhoneNumbersAssign, + HideHelpCommand: true, +} + +var number10dlcCampaignsPhoneNumbersUnassign = cli.Command{ + Name: "unassign", + Usage: "Remove a phone number assignment from a 10DLC campaign.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "campaign-id", + Required: true, + }, + &requestflag.Flag[string]{ + Name: "assignment-id", + Required: true, + }, + }, + Action: handleNumber10dlcCampaignsPhoneNumbersUnassign, + HideHelpCommand: true, +} + +func handleNumber10dlcCampaignsPhoneNumbersList(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("campaign-id") && len(unusedArgs) > 0 { + cmd.Set("campaign-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Number10dlc.Campaigns.PhoneNumbers.List(ctx, cmd.Value("campaign-id").(string), options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + return ShowJSON(os.Stdout, "number-10dlc:campaigns:phone-numbers list", obj, format, transform) +} + +func handleNumber10dlcCampaignsPhoneNumbersAssign(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("campaign-id") && len(unusedArgs) > 0 { + cmd.Set("campaign-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + params := zavudev.Number10dlcCampaignPhoneNumberAssignParams{} + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Number10dlc.Campaigns.PhoneNumbers.Assign( + ctx, + cmd.Value("campaign-id").(string), + params, + options..., + ) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + return ShowJSON(os.Stdout, "number-10dlc:campaigns:phone-numbers assign", obj, format, transform) +} + +func handleNumber10dlcCampaignsPhoneNumbersUnassign(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("assignment-id") && len(unusedArgs) > 0 { + cmd.Set("assignment-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + params := zavudev.Number10dlcCampaignPhoneNumberUnassignParams{ + CampaignID: cmd.Value("campaign-id").(string), + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + return client.Number10dlc.Campaigns.PhoneNumbers.Unassign( + ctx, + cmd.Value("assignment-id").(string), + params, + options..., + ) +} diff --git a/pkg/cmd/number10dlccampaignphonenumber_test.go b/pkg/cmd/number10dlccampaignphonenumber_test.go new file mode 100644 index 0000000..f447958 --- /dev/null +++ b/pkg/cmd/number10dlccampaignphonenumber_test.go @@ -0,0 +1,58 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "testing" + + "github.com/zavudev/cli/internal/mocktest" +) + +func TestNumber10dlcCampaignsPhoneNumbersList(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "number-10dlc:campaigns:phone-numbers", "list", + "--campaign-id", "campaignId", + ) + }) +} + +func TestNumber10dlcCampaignsPhoneNumbersAssign(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "number-10dlc:campaigns:phone-numbers", "assign", + "--campaign-id", "campaignId", + "--phone-number-id", "pn_abc123", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("phoneNumberId: pn_abc123") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "number-10dlc:campaigns:phone-numbers", "assign", + "--campaign-id", "campaignId", + ) + }) +} + +func TestNumber10dlcCampaignsPhoneNumbersUnassign(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "number-10dlc:campaigns:phone-numbers", "unassign", + "--campaign-id", "campaignId", + "--assignment-id", "assignmentId", + ) + }) +} diff --git a/pkg/cmd/plan.go b/pkg/cmd/plan.go new file mode 100644 index 0000000..5382a40 --- /dev/null +++ b/pkg/cmd/plan.go @@ -0,0 +1,56 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "context" + "fmt" + "os" + + "github.com/tidwall/gjson" + "github.com/urfave/cli/v3" + "github.com/zavudev/cli/internal/apiquery" + "github.com/zavudev/sdk-go" + "github.com/zavudev/sdk-go/option" +) + +var planRetrieve = cli.Command{ + Name: "retrieve", + Usage: "Get the current subscription plan for the API key's team, including tier,\nbilling interval, and period dates.", + Suggest: true, + Flags: []cli.Flag{}, + Action: handlePlanRetrieve, + HideHelpCommand: true, +} + +func handlePlanRetrieve(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Plan.Get(ctx, options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + return ShowJSON(os.Stdout, "plan retrieve", obj, format, transform) +} diff --git a/pkg/cmd/plan_test.go b/pkg/cmd/plan_test.go new file mode 100644 index 0000000..57fb0ef --- /dev/null +++ b/pkg/cmd/plan_test.go @@ -0,0 +1,20 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "testing" + + "github.com/zavudev/cli/internal/mocktest" +) + +func TestPlanRetrieve(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "plan", "retrieve", + ) + }) +} diff --git a/pkg/cmd/senderwhatsappsync.go b/pkg/cmd/senderwhatsappsync.go new file mode 100644 index 0000000..3a7bb1c --- /dev/null +++ b/pkg/cmd/senderwhatsappsync.go @@ -0,0 +1,163 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "context" + "fmt" + "os" + + "github.com/tidwall/gjson" + "github.com/urfave/cli/v3" + "github.com/zavudev/cli/internal/apiquery" + "github.com/zavudev/cli/internal/requestflag" + "github.com/zavudev/sdk-go" + "github.com/zavudev/sdk-go/option" +) + +var sendersWhatsappSyncRetrieve = cli.Command{ + Name: "retrieve", + Usage: "Get the current sync status for a sender's WhatsApp coexistence account. Only\navailable for senders connected in coexistence mode (WhatsApp Business App +\nCloud API).", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "sender-id", + Required: true, + }, + }, + Action: handleSendersWhatsappSyncRetrieve, + HideHelpCommand: true, +} + +var sendersWhatsappSyncStartContactsSync = cli.Command{ + Name: "start-contacts-sync", + Usage: "Initiate contact names sync from the WhatsApp Business App. This imports contact\nnames stored in the app to Zavu. Only available for coexistence accounts with\nactive status.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "sender-id", + Required: true, + }, + }, + Action: handleSendersWhatsappSyncStartContactsSync, + HideHelpCommand: true, +} + +var sendersWhatsappSyncStartHistorySync = cli.Command{ + Name: "start-history-sync", + Usage: "Initiate message history sync from the WhatsApp Business App. This sends a\nrequest to the account owner to approve sharing their conversation history. Only\navailable for coexistence accounts with active status.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "sender-id", + Required: true, + }, + }, + Action: handleSendersWhatsappSyncStartHistorySync, + HideHelpCommand: true, +} + +func handleSendersWhatsappSyncRetrieve(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("sender-id") && len(unusedArgs) > 0 { + cmd.Set("sender-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Senders.WhatsappSync.Get(ctx, cmd.Value("sender-id").(string), options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + return ShowJSON(os.Stdout, "senders:whatsapp-sync retrieve", obj, format, transform) +} + +func handleSendersWhatsappSyncStartContactsSync(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("sender-id") && len(unusedArgs) > 0 { + cmd.Set("sender-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Senders.WhatsappSync.StartContactsSync(ctx, cmd.Value("sender-id").(string), options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + return ShowJSON(os.Stdout, "senders:whatsapp-sync start-contacts-sync", obj, format, transform) +} + +func handleSendersWhatsappSyncStartHistorySync(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("sender-id") && len(unusedArgs) > 0 { + cmd.Set("sender-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Senders.WhatsappSync.StartHistorySync(ctx, cmd.Value("sender-id").(string), options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + return ShowJSON(os.Stdout, "senders:whatsapp-sync start-history-sync", obj, format, transform) +} diff --git a/pkg/cmd/senderwhatsappsync_test.go b/pkg/cmd/senderwhatsappsync_test.go new file mode 100644 index 0000000..7f7edb1 --- /dev/null +++ b/pkg/cmd/senderwhatsappsync_test.go @@ -0,0 +1,45 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "testing" + + "github.com/zavudev/cli/internal/mocktest" +) + +func TestSendersWhatsappSyncRetrieve(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "senders:whatsapp-sync", "retrieve", + "--sender-id", "senderId", + ) + }) +} + +func TestSendersWhatsappSyncStartContactsSync(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "senders:whatsapp-sync", "start-contacts-sync", + "--sender-id", "senderId", + ) + }) +} + +func TestSendersWhatsappSyncStartHistorySync(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "senders:whatsapp-sync", "start-history-sync", + "--sender-id", "senderId", + ) + }) +} diff --git a/pkg/cmd/subaccount.go b/pkg/cmd/subaccount.go new file mode 100644 index 0000000..622ce7a --- /dev/null +++ b/pkg/cmd/subaccount.go @@ -0,0 +1,369 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "context" + "fmt" + "os" + + "github.com/tidwall/gjson" + "github.com/urfave/cli/v3" + "github.com/zavudev/cli/internal/apiquery" + "github.com/zavudev/cli/internal/requestflag" + "github.com/zavudev/sdk-go" + "github.com/zavudev/sdk-go/option" +) + +var subAccountsCreate = cli.Command{ + Name: "create", + Usage: "Create a new sub-account (project) with its own API key. All charges are billed\nto the parent team's balance. Use creditLimit to set a spending cap. The\nsub-account's API key is returned only in the creation response.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "name", + Usage: "Name of the sub-account.", + Required: true, + BodyPath: "name", + }, + &requestflag.Flag[int64]{ + Name: "credit-limit", + Usage: "Spending cap in cents. When reached, messages from this sub-account will be blocked. Omit or set to 0 for no limit.", + BodyPath: "creditLimit", + }, + &requestflag.Flag[string]{ + Name: "external-id", + Usage: "External reference ID for your own tracking.", + BodyPath: "externalId", + }, + &requestflag.Flag[map[string]any]{ + Name: "metadata", + BodyPath: "metadata", + }, + }, + Action: handleSubAccountsCreate, + HideHelpCommand: true, +} + +var subAccountsRetrieve = cli.Command{ + Name: "retrieve", + Usage: "Get sub-account", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "id", + Required: true, + }, + }, + Action: handleSubAccountsRetrieve, + HideHelpCommand: true, +} + +var subAccountsUpdate = cli.Command{ + Name: "update", + Usage: "Update sub-account", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "id", + Required: true, + }, + &requestflag.Flag[any]{ + Name: "credit-limit", + BodyPath: "creditLimit", + }, + &requestflag.Flag[string]{ + Name: "external-id", + BodyPath: "externalId", + }, + &requestflag.Flag[map[string]any]{ + Name: "metadata", + BodyPath: "metadata", + }, + &requestflag.Flag[string]{ + Name: "name", + BodyPath: "name", + }, + &requestflag.Flag[string]{ + Name: "status", + Usage: `Allowed values: "active", "inactive".`, + BodyPath: "status", + }, + }, + Action: handleSubAccountsUpdate, + HideHelpCommand: true, +} + +var subAccountsList = cli.Command{ + Name: "list", + Usage: "List sub-accounts for this team.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "cursor", + QueryPath: "cursor", + }, + &requestflag.Flag[int64]{ + Name: "limit", + Default: 50, + QueryPath: "limit", + }, + &requestflag.Flag[int64]{ + Name: "max-items", + Usage: "The maximum number of items to return (use -1 for unlimited).", + }, + }, + Action: handleSubAccountsList, + HideHelpCommand: true, +} + +var subAccountsDeactivate = cli.Command{ + Name: "deactivate", + Usage: "Deactivate a sub-account. Remaining balance is returned to the parent team and\nall API keys are revoked.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "id", + Required: true, + }, + }, + Action: handleSubAccountsDeactivate, + HideHelpCommand: true, +} + +var subAccountsGetBalance = cli.Command{ + Name: "get-balance", + Usage: "Get spending information for a sub-account. Returns the parent team's balance,\nthe sub-account's total spending, and its credit limit (spending cap).", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "id", + Required: true, + }, + }, + Action: handleSubAccountsGetBalance, + HideHelpCommand: true, +} + +func handleSubAccountsCreate(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + params := zavudev.SubAccountNewParams{} + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.SubAccounts.New(ctx, params, options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + return ShowJSON(os.Stdout, "sub-accounts create", obj, format, transform) +} + +func handleSubAccountsRetrieve(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("id") && len(unusedArgs) > 0 { + cmd.Set("id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.SubAccounts.Get(ctx, cmd.Value("id").(string), options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + return ShowJSON(os.Stdout, "sub-accounts retrieve", obj, format, transform) +} + +func handleSubAccountsUpdate(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("id") && len(unusedArgs) > 0 { + cmd.Set("id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + params := zavudev.SubAccountUpdateParams{} + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.SubAccounts.Update( + ctx, + cmd.Value("id").(string), + params, + options..., + ) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + return ShowJSON(os.Stdout, "sub-accounts update", obj, format, transform) +} + +func handleSubAccountsList(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + params := zavudev.SubAccountListParams{} + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + if format == "raw" { + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.SubAccounts.List(ctx, params, options...) + if err != nil { + return err + } + obj := gjson.ParseBytes(res) + return ShowJSON(os.Stdout, "sub-accounts list", obj, format, transform) + } else { + iter := client.SubAccounts.ListAutoPaging(ctx, params, options...) + maxItems := int64(-1) + if cmd.IsSet("max-items") { + maxItems = cmd.Value("max-items").(int64) + } + return ShowJSONIterator(os.Stdout, "sub-accounts list", iter, format, transform, maxItems) + } +} + +func handleSubAccountsDeactivate(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("id") && len(unusedArgs) > 0 { + cmd.Set("id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.SubAccounts.Deactivate(ctx, cmd.Value("id").(string), options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + return ShowJSON(os.Stdout, "sub-accounts deactivate", obj, format, transform) +} + +func handleSubAccountsGetBalance(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("id") && len(unusedArgs) > 0 { + cmd.Set("id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.SubAccounts.GetBalance(ctx, cmd.Value("id").(string), options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + return ShowJSON(os.Stdout, "sub-accounts get-balance", obj, format, transform) +} diff --git a/pkg/cmd/subaccount_test.go b/pkg/cmd/subaccount_test.go new file mode 100644 index 0000000..97de900 --- /dev/null +++ b/pkg/cmd/subaccount_test.go @@ -0,0 +1,123 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "testing" + + "github.com/zavudev/cli/internal/mocktest" +) + +func TestSubAccountsCreate(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "sub-accounts", "create", + "--name", "Client ABC", + "--credit-limit", "0", + "--external-id", "externalId", + "--metadata", "{foo: bar}", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("" + + "name: Client ABC\n" + + "creditLimit: 0\n" + + "externalId: externalId\n" + + "metadata:\n" + + " foo: bar\n") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "sub-accounts", "create", + ) + }) +} + +func TestSubAccountsRetrieve(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "sub-accounts", "retrieve", + "--id", "id", + ) + }) +} + +func TestSubAccountsUpdate(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "sub-accounts", "update", + "--id", "id", + "--credit-limit", "0", + "--external-id", "externalId", + "--metadata", "{foo: bar}", + "--name", "name", + "--status", "active", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("" + + "creditLimit: 0\n" + + "externalId: externalId\n" + + "metadata:\n" + + " foo: bar\n" + + "name: name\n" + + "status: active\n") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "sub-accounts", "update", + "--id", "id", + ) + }) +} + +func TestSubAccountsList(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "sub-accounts", "list", + "--max-items", "10", + "--cursor", "cursor", + "--limit", "100", + ) + }) +} + +func TestSubAccountsDeactivate(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "sub-accounts", "deactivate", + "--id", "id", + ) + }) +} + +func TestSubAccountsGetBalance(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "sub-accounts", "get-balance", + "--id", "id", + ) + }) +} diff --git a/pkg/cmd/subaccountapikey.go b/pkg/cmd/subaccountapikey.go new file mode 100644 index 0000000..90af413 --- /dev/null +++ b/pkg/cmd/subaccountapikey.go @@ -0,0 +1,188 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "context" + "fmt" + "os" + + "github.com/tidwall/gjson" + "github.com/urfave/cli/v3" + "github.com/zavudev/cli/internal/apiquery" + "github.com/zavudev/cli/internal/requestflag" + "github.com/zavudev/sdk-go" + "github.com/zavudev/sdk-go/option" +) + +var subAccountsAPIKeysCreate = cli.Command{ + Name: "create", + Usage: "Create sub-account API key", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "id", + Required: true, + }, + &requestflag.Flag[string]{ + Name: "name", + Required: true, + BodyPath: "name", + }, + &requestflag.Flag[string]{ + Name: "environment", + Usage: `Allowed values: "live", "test".`, + Default: "live", + BodyPath: "environment", + }, + &requestflag.Flag[[]string]{ + Name: "permission", + BodyPath: "permissions", + }, + }, + Action: handleSubAccountsAPIKeysCreate, + HideHelpCommand: true, +} + +var subAccountsAPIKeysList = cli.Command{ + Name: "list", + Usage: "List sub-account API keys", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "id", + Required: true, + }, + }, + Action: handleSubAccountsAPIKeysList, + HideHelpCommand: true, +} + +var subAccountsAPIKeysRevoke = cli.Command{ + Name: "revoke", + Usage: "Revoke sub-account API key", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "id", + Required: true, + }, + &requestflag.Flag[string]{ + Name: "key-id", + Required: true, + }, + }, + Action: handleSubAccountsAPIKeysRevoke, + HideHelpCommand: true, +} + +func handleSubAccountsAPIKeysCreate(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("id") && len(unusedArgs) > 0 { + cmd.Set("id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + params := zavudev.SubAccountAPIKeyNewParams{} + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.SubAccounts.APIKeys.New( + ctx, + cmd.Value("id").(string), + params, + options..., + ) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + return ShowJSON(os.Stdout, "sub-accounts:api-keys create", obj, format, transform) +} + +func handleSubAccountsAPIKeysList(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("id") && len(unusedArgs) > 0 { + cmd.Set("id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.SubAccounts.APIKeys.List(ctx, cmd.Value("id").(string), options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + return ShowJSON(os.Stdout, "sub-accounts:api-keys list", obj, format, transform) +} + +func handleSubAccountsAPIKeysRevoke(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("key-id") && len(unusedArgs) > 0 { + cmd.Set("key-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + params := zavudev.SubAccountAPIKeyRevokeParams{ + ID: cmd.Value("id").(string), + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + return client.SubAccounts.APIKeys.Revoke( + ctx, + cmd.Value("key-id").(string), + params, + options..., + ) +} diff --git a/pkg/cmd/subaccountapikey_test.go b/pkg/cmd/subaccountapikey_test.go new file mode 100644 index 0000000..f0f37f1 --- /dev/null +++ b/pkg/cmd/subaccountapikey_test.go @@ -0,0 +1,64 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "testing" + + "github.com/zavudev/cli/internal/mocktest" +) + +func TestSubAccountsAPIKeysCreate(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "sub-accounts:api-keys", "create", + "--id", "id", + "--name", "Production Key", + "--environment", "live", + "--permission", "string", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("" + + "name: Production Key\n" + + "environment: live\n" + + "permissions:\n" + + " - string\n") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "sub-accounts:api-keys", "create", + "--id", "id", + ) + }) +} + +func TestSubAccountsAPIKeysList(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "sub-accounts:api-keys", "list", + "--id", "id", + ) + }) +} + +func TestSubAccountsAPIKeysRevoke(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "sub-accounts:api-keys", "revoke", + "--id", "id", + "--key-id", "keyId", + ) + }) +} diff --git a/pkg/cmd/url.go b/pkg/cmd/url.go new file mode 100644 index 0000000..0bb8bef --- /dev/null +++ b/pkg/cmd/url.go @@ -0,0 +1,185 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "context" + "fmt" + "os" + + "github.com/tidwall/gjson" + "github.com/urfave/cli/v3" + "github.com/zavudev/cli/internal/apiquery" + "github.com/zavudev/cli/internal/requestflag" + "github.com/zavudev/sdk-go" + "github.com/zavudev/sdk-go/option" +) + +var urlsListVerified = cli.Command{ + Name: "list-verified", + Usage: "List URLs that have been verified for this project.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "cursor", + QueryPath: "cursor", + }, + &requestflag.Flag[int64]{ + Name: "limit", + Default: 50, + QueryPath: "limit", + }, + &requestflag.Flag[string]{ + Name: "status", + Usage: "Filter by verification status.", + QueryPath: "status", + }, + &requestflag.Flag[int64]{ + Name: "max-items", + Usage: "The maximum number of items to return (use -1 for unlimited).", + }, + }, + Action: handleURLsListVerified, + HideHelpCommand: true, +} + +var urlsRetrieveDetails = cli.Command{ + Name: "retrieve-details", + Usage: "Get details of a specific verified URL.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "url-id", + Required: true, + }, + }, + Action: handleURLsRetrieveDetails, + HideHelpCommand: true, +} + +var urlsSubmitForVerification = cli.Command{ + Name: "submit-for-verification", + Usage: "Submit a URL for verification. URLs are automatically checked against Google Web\nRisk API. Safe URLs are auto-approved, malicious URLs are blocked. URL\nshorteners (bit.ly, t.co, etc.) are always blocked.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "url", + Usage: "The URL to submit for verification.", + Required: true, + BodyPath: "url", + }, + }, + Action: handleURLsSubmitForVerification, + HideHelpCommand: true, +} + +func handleURLsListVerified(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + params := zavudev.URLListVerifiedParams{} + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + if format == "raw" { + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.URLs.ListVerified(ctx, params, options...) + if err != nil { + return err + } + obj := gjson.ParseBytes(res) + return ShowJSON(os.Stdout, "urls list-verified", obj, format, transform) + } else { + iter := client.URLs.ListVerifiedAutoPaging(ctx, params, options...) + maxItems := int64(-1) + if cmd.IsSet("max-items") { + maxItems = cmd.Value("max-items").(int64) + } + return ShowJSONIterator(os.Stdout, "urls list-verified", iter, format, transform, maxItems) + } +} + +func handleURLsRetrieveDetails(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("url-id") && len(unusedArgs) > 0 { + cmd.Set("url-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.URLs.GetDetails(ctx, cmd.Value("url-id").(string), options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + return ShowJSON(os.Stdout, "urls retrieve-details", obj, format, transform) +} + +func handleURLsSubmitForVerification(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + params := zavudev.URLSubmitForVerificationParams{} + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.URLs.SubmitForVerification(ctx, params, options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + return ShowJSON(os.Stdout, "urls submit-for-verification", obj, format, transform) +} diff --git a/pkg/cmd/url_test.go b/pkg/cmd/url_test.go new file mode 100644 index 0000000..a40bafb --- /dev/null +++ b/pkg/cmd/url_test.go @@ -0,0 +1,58 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "testing" + + "github.com/zavudev/cli/internal/mocktest" +) + +func TestURLsListVerified(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "urls", "list-verified", + "--max-items", "10", + "--cursor", "cursor", + "--limit", "100", + "--status", "pending", + ) + }) +} + +func TestURLsRetrieveDetails(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "urls", "retrieve-details", + "--url-id", "urlId", + ) + }) +} + +func TestURLsSubmitForVerification(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "urls", "submit-for-verification", + "--url", "https://example.com/page", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("url: https://example.com/page") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "urls", "submit-for-verification", + ) + }) +} diff --git a/pkg/cmd/usage.go b/pkg/cmd/usage.go new file mode 100644 index 0000000..82144c8 --- /dev/null +++ b/pkg/cmd/usage.go @@ -0,0 +1,56 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "context" + "fmt" + "os" + + "github.com/tidwall/gjson" + "github.com/urfave/cli/v3" + "github.com/zavudev/cli/internal/apiquery" + "github.com/zavudev/sdk-go" + "github.com/zavudev/sdk-go/option" +) + +var usageRetrieve = cli.Command{ + Name: "retrieve", + Usage: "Get the current month's usage counters for A2P messages and emails, along with\nthe tier limits.", + Suggest: true, + Flags: []cli.Flag{}, + Action: handleUsageRetrieve, + HideHelpCommand: true, +} + +func handleUsageRetrieve(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Usage.Get(ctx, options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + transform := cmd.Root().String("transform") + return ShowJSON(os.Stdout, "usage retrieve", obj, format, transform) +} diff --git a/pkg/cmd/usage_test.go b/pkg/cmd/usage_test.go new file mode 100644 index 0000000..2a1fa7d --- /dev/null +++ b/pkg/cmd/usage_test.go @@ -0,0 +1,20 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "testing" + + "github.com/zavudev/cli/internal/mocktest" +) + +func TestUsageRetrieve(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "usage", "retrieve", + ) + }) +} From bacb946b9b18e89c5f33a09abe531c2c427a2e25 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 14 Apr 2026 18:13:09 +0000 Subject: [PATCH 04/56] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 9ed46fe..ea1e2c3 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 129 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu%2Fzavudev-8bebbc269d62102c587a3a2ed99f065e0336599697b55d21e30080aa9c160672.yml openapi_spec_hash: 1e701bc39fb0673ddef95c579da4a54b -config_hash: fd9bfe2e122ab75779fb52fed46d1d2e +config_hash: 280cf643b641e9d1b21852c7e2926d54 From b9b7c6049f69268590f059883babc559076e5945 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 15 Apr 2026 05:04:00 +0000 Subject: [PATCH 05/56] chore(cli): fall back to JSON when using default "explore" with non-TTY --- cmd/zavudev/main.go | 2 +- pkg/cmd/address.go | 11 ++-- pkg/cmd/balance.go | 3 +- pkg/cmd/broadcast.go | 32 ++++++---- pkg/cmd/broadcastcontact.go | 8 ++- pkg/cmd/cmdutil.go | 29 +++++++-- pkg/cmd/cmdutil_test.go | 70 ++++++++++++++++++++- pkg/cmd/contact.go | 20 +++--- pkg/cmd/contactchannel.go | 9 ++- pkg/cmd/export.go | 11 ++-- pkg/cmd/introspect.go | 3 +- pkg/cmd/invitation.go | 14 +++-- pkg/cmd/message.go | 14 +++-- pkg/cmd/number10dlcbrand.go | 23 ++++--- pkg/cmd/number10dlccampaign.go | 20 +++--- pkg/cmd/number10dlccampaignphonenumber.go | 6 +- pkg/cmd/phonenumber.go | 20 +++--- pkg/cmd/plan.go | 3 +- pkg/cmd/regulatorydocument.go | 14 +++-- pkg/cmd/sender.go | 26 +++++--- pkg/cmd/senderagent.go | 12 ++-- pkg/cmd/senderagentexecution.go | 5 +- pkg/cmd/senderagentflow.go | 17 +++-- pkg/cmd/senderagentknowledgebase.go | 14 +++-- pkg/cmd/senderagentknowledgebasedocument.go | 8 ++- pkg/cmd/senderagenttool.go | 17 +++-- pkg/cmd/senderwhatsappsync.go | 9 ++- pkg/cmd/subaccount.go | 20 +++--- pkg/cmd/subaccountapikey.go | 6 +- pkg/cmd/template.go | 14 +++-- pkg/cmd/url.go | 11 ++-- pkg/cmd/usage.go | 3 +- 32 files changed, 335 insertions(+), 139 deletions(-) diff --git a/cmd/zavudev/main.go b/cmd/zavudev/main.go index 0c097f4..746d1ec 100644 --- a/cmd/zavudev/main.go +++ b/cmd/zavudev/main.go @@ -43,7 +43,7 @@ func main() { fmt.Fprintf(os.Stderr, "%s %q: %d %s\n", apierr.Request.Method, apierr.Request.URL, apierr.Response.StatusCode, http.StatusText(apierr.Response.StatusCode)) format := app.String("format-error") json := gjson.Parse(apierr.RawJSON()) - show_err := cmd.ShowJSON(os.Stdout, "Error", json, format, app.String("transform-error")) + show_err := cmd.ShowJSON(os.Stdout, os.Stderr, "Error", json, format, app.IsSet("format-error"), app.String("transform-error")) if show_err != nil { // Just print the original error: fmt.Fprintf(os.Stderr, "%s\n", err.Error()) diff --git a/pkg/cmd/address.go b/pkg/cmd/address.go index d95a181..721e6fc 100644 --- a/pkg/cmd/address.go +++ b/pkg/cmd/address.go @@ -146,8 +146,9 @@ func handleAddressesCreate(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "addresses create", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "addresses create", obj, format, explicitFormat, transform) } func handleAddressesRetrieve(ctx context.Context, cmd *cli.Command) error { @@ -181,8 +182,9 @@ func handleAddressesRetrieve(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "addresses retrieve", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "addresses retrieve", obj, format, explicitFormat, transform) } func handleAddressesList(ctx context.Context, cmd *cli.Command) error { @@ -207,6 +209,7 @@ func handleAddressesList(ctx context.Context, cmd *cli.Command) error { } format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") if format == "raw" { var res []byte @@ -216,14 +219,14 @@ func handleAddressesList(ctx context.Context, cmd *cli.Command) error { return err } obj := gjson.ParseBytes(res) - return ShowJSON(os.Stdout, "addresses list", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "addresses list", obj, format, explicitFormat, transform) } else { iter := client.Addresses.ListAutoPaging(ctx, params, options...) maxItems := int64(-1) if cmd.IsSet("max-items") { maxItems = cmd.Value("max-items").(int64) } - return ShowJSONIterator(os.Stdout, "addresses list", iter, format, transform, maxItems) + return ShowJSONIterator(os.Stdout, os.Stderr, "addresses list", iter, format, explicitFormat, transform, maxItems) } } diff --git a/pkg/cmd/balance.go b/pkg/cmd/balance.go index a27b43c..9dce394 100644 --- a/pkg/cmd/balance.go +++ b/pkg/cmd/balance.go @@ -51,6 +51,7 @@ func handleBalanceRetrieve(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "balance retrieve", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "balance retrieve", obj, format, explicitFormat, transform) } diff --git a/pkg/cmd/broadcast.go b/pkg/cmd/broadcast.go index 6f64c9f..10a2cac 100644 --- a/pkg/cmd/broadcast.go +++ b/pkg/cmd/broadcast.go @@ -367,8 +367,9 @@ func handleBroadcastsCreate(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "broadcasts create", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "broadcasts create", obj, format, explicitFormat, transform) } func handleBroadcastsRetrieve(ctx context.Context, cmd *cli.Command) error { @@ -402,8 +403,9 @@ func handleBroadcastsRetrieve(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "broadcasts retrieve", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "broadcasts retrieve", obj, format, explicitFormat, transform) } func handleBroadcastsUpdate(ctx context.Context, cmd *cli.Command) error { @@ -444,8 +446,9 @@ func handleBroadcastsUpdate(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "broadcasts update", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "broadcasts update", obj, format, explicitFormat, transform) } func handleBroadcastsList(ctx context.Context, cmd *cli.Command) error { @@ -470,6 +473,7 @@ func handleBroadcastsList(ctx context.Context, cmd *cli.Command) error { } format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") if format == "raw" { var res []byte @@ -479,14 +483,14 @@ func handleBroadcastsList(ctx context.Context, cmd *cli.Command) error { return err } obj := gjson.ParseBytes(res) - return ShowJSON(os.Stdout, "broadcasts list", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "broadcasts list", obj, format, explicitFormat, transform) } else { iter := client.Broadcasts.ListAutoPaging(ctx, params, options...) maxItems := int64(-1) if cmd.IsSet("max-items") { maxItems = cmd.Value("max-items").(int64) } - return ShowJSONIterator(os.Stdout, "broadcasts list", iter, format, transform, maxItems) + return ShowJSONIterator(os.Stdout, os.Stderr, "broadcasts list", iter, format, explicitFormat, transform, maxItems) } } @@ -546,8 +550,9 @@ func handleBroadcastsCancel(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "broadcasts cancel", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "broadcasts cancel", obj, format, explicitFormat, transform) } func handleBroadcastsEscalateReview(ctx context.Context, cmd *cli.Command) error { @@ -581,8 +586,9 @@ func handleBroadcastsEscalateReview(ctx context.Context, cmd *cli.Command) error obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "broadcasts escalate-review", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "broadcasts escalate-review", obj, format, explicitFormat, transform) } func handleBroadcastsProgress(ctx context.Context, cmd *cli.Command) error { @@ -616,8 +622,9 @@ func handleBroadcastsProgress(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "broadcasts progress", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "broadcasts progress", obj, format, explicitFormat, transform) } func handleBroadcastsReschedule(ctx context.Context, cmd *cli.Command) error { @@ -658,8 +665,9 @@ func handleBroadcastsReschedule(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "broadcasts reschedule", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "broadcasts reschedule", obj, format, explicitFormat, transform) } func handleBroadcastsRetryReview(ctx context.Context, cmd *cli.Command) error { @@ -693,8 +701,9 @@ func handleBroadcastsRetryReview(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "broadcasts retry-review", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "broadcasts retry-review", obj, format, explicitFormat, transform) } func handleBroadcastsSend(ctx context.Context, cmd *cli.Command) error { @@ -735,6 +744,7 @@ func handleBroadcastsSend(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "broadcasts send", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "broadcasts send", obj, format, explicitFormat, transform) } diff --git a/pkg/cmd/broadcastcontact.go b/pkg/cmd/broadcastcontact.go index 07520a4..27517ae 100644 --- a/pkg/cmd/broadcastcontact.go +++ b/pkg/cmd/broadcastcontact.go @@ -123,6 +123,7 @@ func handleBroadcastsContactsList(ctx context.Context, cmd *cli.Command) error { } format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") if format == "raw" { var res []byte @@ -137,7 +138,7 @@ func handleBroadcastsContactsList(ctx context.Context, cmd *cli.Command) error { return err } obj := gjson.ParseBytes(res) - return ShowJSON(os.Stdout, "broadcasts:contacts list", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "broadcasts:contacts list", obj, format, explicitFormat, transform) } else { iter := client.Broadcasts.Contacts.ListAutoPaging( ctx, @@ -149,7 +150,7 @@ func handleBroadcastsContactsList(ctx context.Context, cmd *cli.Command) error { if cmd.IsSet("max-items") { maxItems = cmd.Value("max-items").(int64) } - return ShowJSONIterator(os.Stdout, "broadcasts:contacts list", iter, format, transform, maxItems) + return ShowJSONIterator(os.Stdout, os.Stderr, "broadcasts:contacts list", iter, format, explicitFormat, transform, maxItems) } } @@ -191,8 +192,9 @@ func handleBroadcastsContactsAdd(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "broadcasts:contacts add", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "broadcasts:contacts add", obj, format, explicitFormat, transform) } func handleBroadcastsContactsRemove(ctx context.Context, cmd *cli.Command) error { diff --git a/pkg/cmd/cmdutil.go b/pkg/cmd/cmdutil.go index 8b3da62..8fe5e9a 100644 --- a/pkg/cmd/cmdutil.go +++ b/pkg/cmd/cmdutil.go @@ -354,8 +354,13 @@ func formatJSON(expectedOutput *os.File, title string, res gjson.Result, format } } -// Display JSON to the user in various different formats -func ShowJSON(out *os.File, title string, res gjson.Result, format string, transform string) error { +const warningExploreNotSupported = "Warning: Output format 'explore' not supported for non-terminal output; falling back to 'json'\n" + +// Display JSON to the user in various different formats. The explicitFormat parameter indicates +// whether the format was explicitly set by the user (via --format), which controls whether we +// silently fall back to json when explore is requested on non-terminal output. Warnings are +// written to stderr. +func ShowJSON(out *os.File, stderr io.Writer, title string, res gjson.Result, format string, explicitFormat bool, transform string) error { if transform != "" { transformed := res.Get(transform) if transformed.Exists() { @@ -365,8 +370,14 @@ func ShowJSON(out *os.File, title string, res gjson.Result, format string, trans switch strings.ToLower(format) { case "auto": - return ShowJSON(out, title, res, "json", "") + return ShowJSON(out, stderr, title, res, "json", explicitFormat, "") case "explore": + if !isTerminal(out) { + if explicitFormat { + fmt.Fprint(stderr, warningExploreNotSupported) + } + return ShowJSON(out, stderr, title, res, "json", explicitFormat, transform) + } return jsonview.ExploreJSON(title, res) default: bytes, err := formatJSON(out, title, res, format, transform) @@ -391,9 +402,15 @@ type hasRawJSON interface { // For an iterator over different value types, display its values to the user in // different formats. // -1 is used to signal no limit of items to display -func ShowJSONIterator[T any](stdout *os.File, title string, iter jsonview.Iterator[T], format string, transform string, itemsToDisplay int64) error { +func ShowJSONIterator[T any](stdout *os.File, stderr io.Writer, title string, iter jsonview.Iterator[T], format string, explicitFormat bool, transform string, itemsToDisplay int64) error { if format == "explore" { - return jsonview.ExploreJSONStream(title, iter) + if isTerminal(stdout) { + return jsonview.ExploreJSONStream(title, iter) + } + if explicitFormat { + fmt.Fprint(stderr, warningExploreNotSupported) + } + format = "json" } terminalWidth, terminalHeight, err := term.GetSize(os.Stdout.Fd()) @@ -466,7 +483,7 @@ func ShowJSONIterator[T any](stdout *os.File, title string, iter jsonview.Iterat } obj = gjson.ParseBytes(jsonData) } - if err := ShowJSON(pager, title, obj, format, transform); err != nil { + if err := ShowJSON(pager, stderr, title, obj, format, explicitFormat, transform); err != nil { return err } itemsToDisplay -= 1 diff --git a/pkg/cmd/cmdutil_test.go b/pkg/cmd/cmdutil_test.go index 782496a..ef5baf7 100644 --- a/pkg/cmd/cmdutil_test.go +++ b/pkg/cmd/cmdutil_test.go @@ -231,6 +231,73 @@ func TestShowJSONIterator(t *testing.T) { }) } +func TestExploreFallback(t *testing.T) { + t.Parallel() + + t.Run("ShowJSONFallsBackToJsonOnNonTTY", func(t *testing.T) { + t.Parallel() + + // os.Pipe() produces a *os.File that isn't a terminal, so explore should fall back. + r, w, err := os.Pipe() + require.NoError(t, err) + defer r.Close() + + var stderr bytes.Buffer + res := gjson.Parse(`{"id":"abc"}`) + err = ShowJSON(w, &stderr, "test", res, "explore", false, "") + w.Close() + require.NoError(t, err) + + var buf bytes.Buffer + _, _ = buf.ReadFrom(r) + assert.Contains(t, buf.String(), `"id"`) + assert.Contains(t, buf.String(), `"abc"`) + }) + + t.Run("ShowJSONIteratorFallsBackToJsonOnNonTTY", func(t *testing.T) { + t.Parallel() + + iter := &sliceIterator[map[string]any]{items: []map[string]any{ + {"id": "abc"}, + }} + captured := captureShowJSONIterator(t, iter, "explore", "", -1) + assert.Contains(t, captured, `"id"`) + assert.Contains(t, captured, `"abc"`) + }) + + t.Run("ShowJSONWarnsWhenExplicitFormatOnNonTTY", func(t *testing.T) { + t.Parallel() + + r, w, err := os.Pipe() + require.NoError(t, err) + defer r.Close() + + var stderr bytes.Buffer + res := gjson.Parse(`{"id":"abc"}`) + err = ShowJSON(w, &stderr, "test", res, "explore", true, "") + w.Close() + require.NoError(t, err) + + assert.Equal(t, warningExploreNotSupported, stderr.String()) + }) + + t.Run("ShowJSONSilentWhenDefaultFormatOnNonTTY", func(t *testing.T) { + t.Parallel() + + r, w, err := os.Pipe() + require.NoError(t, err) + defer r.Close() + + var stderr bytes.Buffer + res := gjson.Parse(`{"id":"abc"}`) + err = ShowJSON(w, &stderr, "test", res, "explore", false, "") + w.Close() + require.NoError(t, err) + + assert.Empty(t, stderr.String(), "no warning expected when format was not explicit") + }) +} + // sliceIterator is a simple iterator over a slice for testing. type sliceIterator[T any] struct { index int @@ -260,7 +327,8 @@ func captureShowJSONIterator[T any](t *testing.T, iter jsonview.Iterator[T], for require.NoError(t, err) defer r.Close() - err = ShowJSONIterator(w, "test", iter, format, transform, itemsToDisplay) + var stderr bytes.Buffer + err = ShowJSONIterator(w, &stderr, "test", iter, format, false, transform, itemsToDisplay) w.Close() require.NoError(t, err) diff --git a/pkg/cmd/contact.go b/pkg/cmd/contact.go index efc7451..436d97c 100644 --- a/pkg/cmd/contact.go +++ b/pkg/cmd/contact.go @@ -211,8 +211,9 @@ func handleContactsCreate(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "contacts create", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "contacts create", obj, format, explicitFormat, transform) } func handleContactsRetrieve(ctx context.Context, cmd *cli.Command) error { @@ -246,8 +247,9 @@ func handleContactsRetrieve(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "contacts retrieve", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "contacts retrieve", obj, format, explicitFormat, transform) } func handleContactsUpdate(ctx context.Context, cmd *cli.Command) error { @@ -288,8 +290,9 @@ func handleContactsUpdate(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "contacts update", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "contacts update", obj, format, explicitFormat, transform) } func handleContactsList(ctx context.Context, cmd *cli.Command) error { @@ -314,6 +317,7 @@ func handleContactsList(ctx context.Context, cmd *cli.Command) error { } format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") if format == "raw" { var res []byte @@ -323,14 +327,14 @@ func handleContactsList(ctx context.Context, cmd *cli.Command) error { return err } obj := gjson.ParseBytes(res) - return ShowJSON(os.Stdout, "contacts list", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "contacts list", obj, format, explicitFormat, transform) } else { iter := client.Contacts.ListAutoPaging(ctx, params, options...) maxItems := int64(-1) if cmd.IsSet("max-items") { maxItems = cmd.Value("max-items").(int64) } - return ShowJSONIterator(os.Stdout, "contacts list", iter, format, transform, maxItems) + return ShowJSONIterator(os.Stdout, os.Stderr, "contacts list", iter, format, explicitFormat, transform, maxItems) } } @@ -397,8 +401,9 @@ func handleContactsMerge(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "contacts merge", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "contacts merge", obj, format, explicitFormat, transform) } func handleContactsRetrieveByPhone(ctx context.Context, cmd *cli.Command) error { @@ -432,6 +437,7 @@ func handleContactsRetrieveByPhone(ctx context.Context, cmd *cli.Command) error obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "contacts retrieve-by-phone", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "contacts retrieve-by-phone", obj, format, explicitFormat, transform) } diff --git a/pkg/cmd/contactchannel.go b/pkg/cmd/contactchannel.go index 9cd520a..2503c71 100644 --- a/pkg/cmd/contactchannel.go +++ b/pkg/cmd/contactchannel.go @@ -165,8 +165,9 @@ func handleContactsChannelsUpdate(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "contacts:channels update", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "contacts:channels update", obj, format, explicitFormat, transform) } func handleContactsChannelsAdd(ctx context.Context, cmd *cli.Command) error { @@ -207,8 +208,9 @@ func handleContactsChannelsAdd(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "contacts:channels add", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "contacts:channels add", obj, format, explicitFormat, transform) } func handleContactsChannelsRemove(ctx context.Context, cmd *cli.Command) error { @@ -285,6 +287,7 @@ func handleContactsChannelsSetPrimary(ctx context.Context, cmd *cli.Command) err obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "contacts:channels set-primary", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "contacts:channels set-primary", obj, format, explicitFormat, transform) } diff --git a/pkg/cmd/export.go b/pkg/cmd/export.go index 4ebec17..2004c29 100644 --- a/pkg/cmd/export.go +++ b/pkg/cmd/export.go @@ -113,8 +113,9 @@ func handleExportsCreate(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "exports create", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "exports create", obj, format, explicitFormat, transform) } func handleExportsRetrieve(ctx context.Context, cmd *cli.Command) error { @@ -148,8 +149,9 @@ func handleExportsRetrieve(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "exports retrieve", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "exports retrieve", obj, format, explicitFormat, transform) } func handleExportsList(ctx context.Context, cmd *cli.Command) error { @@ -174,6 +176,7 @@ func handleExportsList(ctx context.Context, cmd *cli.Command) error { } format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") if format == "raw" { var res []byte @@ -183,13 +186,13 @@ func handleExportsList(ctx context.Context, cmd *cli.Command) error { return err } obj := gjson.ParseBytes(res) - return ShowJSON(os.Stdout, "exports list", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "exports list", obj, format, explicitFormat, transform) } else { iter := client.Exports.ListAutoPaging(ctx, params, options...) maxItems := int64(-1) if cmd.IsSet("max-items") { maxItems = cmd.Value("max-items").(int64) } - return ShowJSONIterator(os.Stdout, "exports list", iter, format, transform, maxItems) + return ShowJSONIterator(os.Stdout, os.Stderr, "exports list", iter, format, explicitFormat, transform, maxItems) } } diff --git a/pkg/cmd/introspect.go b/pkg/cmd/introspect.go index e7d3cea..3509d27 100644 --- a/pkg/cmd/introspect.go +++ b/pkg/cmd/introspect.go @@ -60,6 +60,7 @@ func handleIntrospectValidatePhone(ctx context.Context, cmd *cli.Command) error obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "introspect validate-phone", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "introspect validate-phone", obj, format, explicitFormat, transform) } diff --git a/pkg/cmd/invitation.go b/pkg/cmd/invitation.go index 12cec7a..4d9532b 100644 --- a/pkg/cmd/invitation.go +++ b/pkg/cmd/invitation.go @@ -142,8 +142,9 @@ func handleInvitationsCreate(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "invitations create", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "invitations create", obj, format, explicitFormat, transform) } func handleInvitationsRetrieve(ctx context.Context, cmd *cli.Command) error { @@ -177,8 +178,9 @@ func handleInvitationsRetrieve(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "invitations retrieve", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "invitations retrieve", obj, format, explicitFormat, transform) } func handleInvitationsList(ctx context.Context, cmd *cli.Command) error { @@ -203,6 +205,7 @@ func handleInvitationsList(ctx context.Context, cmd *cli.Command) error { } format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") if format == "raw" { var res []byte @@ -212,14 +215,14 @@ func handleInvitationsList(ctx context.Context, cmd *cli.Command) error { return err } obj := gjson.ParseBytes(res) - return ShowJSON(os.Stdout, "invitations list", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "invitations list", obj, format, explicitFormat, transform) } else { iter := client.Invitations.ListAutoPaging(ctx, params, options...) maxItems := int64(-1) if cmd.IsSet("max-items") { maxItems = cmd.Value("max-items").(int64) } - return ShowJSONIterator(os.Stdout, "invitations list", iter, format, transform, maxItems) + return ShowJSONIterator(os.Stdout, os.Stderr, "invitations list", iter, format, explicitFormat, transform, maxItems) } } @@ -254,6 +257,7 @@ func handleInvitationsCancel(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "invitations cancel", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "invitations cancel", obj, format, explicitFormat, transform) } diff --git a/pkg/cmd/message.go b/pkg/cmd/message.go index 25fe35b..77a43ce 100644 --- a/pkg/cmd/message.go +++ b/pkg/cmd/message.go @@ -312,8 +312,9 @@ func handleMessagesRetrieve(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "messages retrieve", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "messages retrieve", obj, format, explicitFormat, transform) } func handleMessagesList(ctx context.Context, cmd *cli.Command) error { @@ -338,6 +339,7 @@ func handleMessagesList(ctx context.Context, cmd *cli.Command) error { } format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") if format == "raw" { var res []byte @@ -347,14 +349,14 @@ func handleMessagesList(ctx context.Context, cmd *cli.Command) error { return err } obj := gjson.ParseBytes(res) - return ShowJSON(os.Stdout, "messages list", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "messages list", obj, format, explicitFormat, transform) } else { iter := client.Messages.ListAutoPaging(ctx, params, options...) maxItems := int64(-1) if cmd.IsSet("max-items") { maxItems = cmd.Value("max-items").(int64) } - return ShowJSONIterator(os.Stdout, "messages list", iter, format, transform, maxItems) + return ShowJSONIterator(os.Stdout, os.Stderr, "messages list", iter, format, explicitFormat, transform, maxItems) } } @@ -396,8 +398,9 @@ func handleMessagesReact(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "messages react", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "messages react", obj, format, explicitFormat, transform) } func handleMessagesSend(ctx context.Context, cmd *cli.Command) error { @@ -430,6 +433,7 @@ func handleMessagesSend(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "messages send", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "messages send", obj, format, explicitFormat, transform) } diff --git a/pkg/cmd/number10dlcbrand.go b/pkg/cmd/number10dlcbrand.go index 9dc2c00..d3a27be 100644 --- a/pkg/cmd/number10dlcbrand.go +++ b/pkg/cmd/number10dlcbrand.go @@ -311,8 +311,9 @@ func handleNumber10dlcBrandsCreate(ctx context.Context, cmd *cli.Command) error obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "number-10dlc:brands create", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "number-10dlc:brands create", obj, format, explicitFormat, transform) } func handleNumber10dlcBrandsRetrieve(ctx context.Context, cmd *cli.Command) error { @@ -346,8 +347,9 @@ func handleNumber10dlcBrandsRetrieve(ctx context.Context, cmd *cli.Command) erro obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "number-10dlc:brands retrieve", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "number-10dlc:brands retrieve", obj, format, explicitFormat, transform) } func handleNumber10dlcBrandsUpdate(ctx context.Context, cmd *cli.Command) error { @@ -388,8 +390,9 @@ func handleNumber10dlcBrandsUpdate(ctx context.Context, cmd *cli.Command) error obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "number-10dlc:brands update", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "number-10dlc:brands update", obj, format, explicitFormat, transform) } func handleNumber10dlcBrandsList(ctx context.Context, cmd *cli.Command) error { @@ -414,6 +417,7 @@ func handleNumber10dlcBrandsList(ctx context.Context, cmd *cli.Command) error { } format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") if format == "raw" { var res []byte @@ -423,14 +427,14 @@ func handleNumber10dlcBrandsList(ctx context.Context, cmd *cli.Command) error { return err } obj := gjson.ParseBytes(res) - return ShowJSON(os.Stdout, "number-10dlc:brands list", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "number-10dlc:brands list", obj, format, explicitFormat, transform) } else { iter := client.Number10dlc.Brands.ListAutoPaging(ctx, params, options...) maxItems := int64(-1) if cmd.IsSet("max-items") { maxItems = cmd.Value("max-items").(int64) } - return ShowJSONIterator(os.Stdout, "number-10dlc:brands list", iter, format, transform, maxItems) + return ShowJSONIterator(os.Stdout, os.Stderr, "number-10dlc:brands list", iter, format, explicitFormat, transform, maxItems) } } @@ -487,8 +491,9 @@ func handleNumber10dlcBrandsListUseCases(ctx context.Context, cmd *cli.Command) obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "number-10dlc:brands list-use-cases", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "number-10dlc:brands list-use-cases", obj, format, explicitFormat, transform) } func handleNumber10dlcBrandsSubmit(ctx context.Context, cmd *cli.Command) error { @@ -522,8 +527,9 @@ func handleNumber10dlcBrandsSubmit(ctx context.Context, cmd *cli.Command) error obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "number-10dlc:brands submit", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "number-10dlc:brands submit", obj, format, explicitFormat, transform) } func handleNumber10dlcBrandsSyncStatus(ctx context.Context, cmd *cli.Command) error { @@ -557,6 +563,7 @@ func handleNumber10dlcBrandsSyncStatus(ctx context.Context, cmd *cli.Command) er obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "number-10dlc:brands sync-status", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "number-10dlc:brands sync-status", obj, format, explicitFormat, transform) } diff --git a/pkg/cmd/number10dlccampaign.go b/pkg/cmd/number10dlccampaign.go index 5796bd0..5a6055e 100644 --- a/pkg/cmd/number10dlccampaign.go +++ b/pkg/cmd/number10dlccampaign.go @@ -273,8 +273,9 @@ func handleNumber10dlcCampaignsCreate(ctx context.Context, cmd *cli.Command) err obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "number-10dlc:campaigns create", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "number-10dlc:campaigns create", obj, format, explicitFormat, transform) } func handleNumber10dlcCampaignsRetrieve(ctx context.Context, cmd *cli.Command) error { @@ -308,8 +309,9 @@ func handleNumber10dlcCampaignsRetrieve(ctx context.Context, cmd *cli.Command) e obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "number-10dlc:campaigns retrieve", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "number-10dlc:campaigns retrieve", obj, format, explicitFormat, transform) } func handleNumber10dlcCampaignsUpdate(ctx context.Context, cmd *cli.Command) error { @@ -350,8 +352,9 @@ func handleNumber10dlcCampaignsUpdate(ctx context.Context, cmd *cli.Command) err obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "number-10dlc:campaigns update", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "number-10dlc:campaigns update", obj, format, explicitFormat, transform) } func handleNumber10dlcCampaignsList(ctx context.Context, cmd *cli.Command) error { @@ -376,6 +379,7 @@ func handleNumber10dlcCampaignsList(ctx context.Context, cmd *cli.Command) error } format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") if format == "raw" { var res []byte @@ -385,14 +389,14 @@ func handleNumber10dlcCampaignsList(ctx context.Context, cmd *cli.Command) error return err } obj := gjson.ParseBytes(res) - return ShowJSON(os.Stdout, "number-10dlc:campaigns list", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "number-10dlc:campaigns list", obj, format, explicitFormat, transform) } else { iter := client.Number10dlc.Campaigns.ListAutoPaging(ctx, params, options...) maxItems := int64(-1) if cmd.IsSet("max-items") { maxItems = cmd.Value("max-items").(int64) } - return ShowJSONIterator(os.Stdout, "number-10dlc:campaigns list", iter, format, transform, maxItems) + return ShowJSONIterator(os.Stdout, os.Stderr, "number-10dlc:campaigns list", iter, format, explicitFormat, transform, maxItems) } } @@ -452,8 +456,9 @@ func handleNumber10dlcCampaignsSubmit(ctx context.Context, cmd *cli.Command) err obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "number-10dlc:campaigns submit", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "number-10dlc:campaigns submit", obj, format, explicitFormat, transform) } func handleNumber10dlcCampaignsSyncStatus(ctx context.Context, cmd *cli.Command) error { @@ -487,6 +492,7 @@ func handleNumber10dlcCampaignsSyncStatus(ctx context.Context, cmd *cli.Command) obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "number-10dlc:campaigns sync-status", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "number-10dlc:campaigns sync-status", obj, format, explicitFormat, transform) } diff --git a/pkg/cmd/number10dlccampaignphonenumber.go b/pkg/cmd/number10dlccampaignphonenumber.go index ae1874b..eafb5c0 100644 --- a/pkg/cmd/number10dlccampaignphonenumber.go +++ b/pkg/cmd/number10dlccampaignphonenumber.go @@ -98,8 +98,9 @@ func handleNumber10dlcCampaignsPhoneNumbersList(ctx context.Context, cmd *cli.Co obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "number-10dlc:campaigns:phone-numbers list", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "number-10dlc:campaigns:phone-numbers list", obj, format, explicitFormat, transform) } func handleNumber10dlcCampaignsPhoneNumbersAssign(ctx context.Context, cmd *cli.Command) error { @@ -140,8 +141,9 @@ func handleNumber10dlcCampaignsPhoneNumbersAssign(ctx context.Context, cmd *cli. obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "number-10dlc:campaigns:phone-numbers assign", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "number-10dlc:campaigns:phone-numbers assign", obj, format, explicitFormat, transform) } func handleNumber10dlcCampaignsPhoneNumbersUnassign(ctx context.Context, cmd *cli.Command) error { diff --git a/pkg/cmd/phonenumber.go b/pkg/cmd/phonenumber.go index 5f7fcdf..567900c 100644 --- a/pkg/cmd/phonenumber.go +++ b/pkg/cmd/phonenumber.go @@ -201,8 +201,9 @@ func handlePhoneNumbersRetrieve(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "phone-numbers retrieve", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "phone-numbers retrieve", obj, format, explicitFormat, transform) } func handlePhoneNumbersUpdate(ctx context.Context, cmd *cli.Command) error { @@ -243,8 +244,9 @@ func handlePhoneNumbersUpdate(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "phone-numbers update", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "phone-numbers update", obj, format, explicitFormat, transform) } func handlePhoneNumbersList(ctx context.Context, cmd *cli.Command) error { @@ -269,6 +271,7 @@ func handlePhoneNumbersList(ctx context.Context, cmd *cli.Command) error { } format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") if format == "raw" { var res []byte @@ -278,14 +281,14 @@ func handlePhoneNumbersList(ctx context.Context, cmd *cli.Command) error { return err } obj := gjson.ParseBytes(res) - return ShowJSON(os.Stdout, "phone-numbers list", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "phone-numbers list", obj, format, explicitFormat, transform) } else { iter := client.PhoneNumbers.ListAutoPaging(ctx, params, options...) maxItems := int64(-1) if cmd.IsSet("max-items") { maxItems = cmd.Value("max-items").(int64) } - return ShowJSONIterator(os.Stdout, "phone-numbers list", iter, format, transform, maxItems) + return ShowJSONIterator(os.Stdout, os.Stderr, "phone-numbers list", iter, format, explicitFormat, transform, maxItems) } } @@ -319,8 +322,9 @@ func handlePhoneNumbersPurchase(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "phone-numbers purchase", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "phone-numbers purchase", obj, format, explicitFormat, transform) } func handlePhoneNumbersRelease(ctx context.Context, cmd *cli.Command) error { @@ -378,8 +382,9 @@ func handlePhoneNumbersRequirements(ctx context.Context, cmd *cli.Command) error obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "phone-numbers requirements", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "phone-numbers requirements", obj, format, explicitFormat, transform) } func handlePhoneNumbersSearchAvailable(ctx context.Context, cmd *cli.Command) error { @@ -412,6 +417,7 @@ func handlePhoneNumbersSearchAvailable(ctx context.Context, cmd *cli.Command) er obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "phone-numbers search-available", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "phone-numbers search-available", obj, format, explicitFormat, transform) } diff --git a/pkg/cmd/plan.go b/pkg/cmd/plan.go index 5382a40..ca66381 100644 --- a/pkg/cmd/plan.go +++ b/pkg/cmd/plan.go @@ -51,6 +51,7 @@ func handlePlanRetrieve(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "plan retrieve", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "plan retrieve", obj, format, explicitFormat, transform) } diff --git a/pkg/cmd/regulatorydocument.go b/pkg/cmd/regulatorydocument.go index 12d2daf..2d88ac2 100644 --- a/pkg/cmd/regulatorydocument.go +++ b/pkg/cmd/regulatorydocument.go @@ -142,8 +142,9 @@ func handleRegulatoryDocumentsCreate(ctx context.Context, cmd *cli.Command) erro obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "regulatory-documents create", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "regulatory-documents create", obj, format, explicitFormat, transform) } func handleRegulatoryDocumentsRetrieve(ctx context.Context, cmd *cli.Command) error { @@ -177,8 +178,9 @@ func handleRegulatoryDocumentsRetrieve(ctx context.Context, cmd *cli.Command) er obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "regulatory-documents retrieve", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "regulatory-documents retrieve", obj, format, explicitFormat, transform) } func handleRegulatoryDocumentsList(ctx context.Context, cmd *cli.Command) error { @@ -203,6 +205,7 @@ func handleRegulatoryDocumentsList(ctx context.Context, cmd *cli.Command) error } format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") if format == "raw" { var res []byte @@ -212,14 +215,14 @@ func handleRegulatoryDocumentsList(ctx context.Context, cmd *cli.Command) error return err } obj := gjson.ParseBytes(res) - return ShowJSON(os.Stdout, "regulatory-documents list", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "regulatory-documents list", obj, format, explicitFormat, transform) } else { iter := client.RegulatoryDocuments.ListAutoPaging(ctx, params, options...) maxItems := int64(-1) if cmd.IsSet("max-items") { maxItems = cmd.Value("max-items").(int64) } - return ShowJSONIterator(os.Stdout, "regulatory-documents list", iter, format, transform, maxItems) + return ShowJSONIterator(os.Stdout, os.Stderr, "regulatory-documents list", iter, format, explicitFormat, transform, maxItems) } } @@ -276,6 +279,7 @@ func handleRegulatoryDocumentsUploadURL(ctx context.Context, cmd *cli.Command) e obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "regulatory-documents upload-url", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "regulatory-documents upload-url", obj, format, explicitFormat, transform) } diff --git a/pkg/cmd/sender.go b/pkg/cmd/sender.go index 1150c7d..8b7f406 100644 --- a/pkg/cmd/sender.go +++ b/pkg/cmd/sender.go @@ -271,8 +271,9 @@ func handleSendersCreate(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "senders create", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "senders create", obj, format, explicitFormat, transform) } func handleSendersRetrieve(ctx context.Context, cmd *cli.Command) error { @@ -306,8 +307,9 @@ func handleSendersRetrieve(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "senders retrieve", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "senders retrieve", obj, format, explicitFormat, transform) } func handleSendersUpdate(ctx context.Context, cmd *cli.Command) error { @@ -348,8 +350,9 @@ func handleSendersUpdate(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "senders update", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "senders update", obj, format, explicitFormat, transform) } func handleSendersList(ctx context.Context, cmd *cli.Command) error { @@ -374,6 +377,7 @@ func handleSendersList(ctx context.Context, cmd *cli.Command) error { } format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") if format == "raw" { var res []byte @@ -383,14 +387,14 @@ func handleSendersList(ctx context.Context, cmd *cli.Command) error { return err } obj := gjson.ParseBytes(res) - return ShowJSON(os.Stdout, "senders list", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "senders list", obj, format, explicitFormat, transform) } else { iter := client.Senders.ListAutoPaging(ctx, params, options...) maxItems := int64(-1) if cmd.IsSet("max-items") { maxItems = cmd.Value("max-items").(int64) } - return ShowJSONIterator(os.Stdout, "senders list", iter, format, transform, maxItems) + return ShowJSONIterator(os.Stdout, os.Stderr, "senders list", iter, format, explicitFormat, transform, maxItems) } } @@ -450,8 +454,9 @@ func handleSendersGetProfile(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "senders get-profile", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "senders get-profile", obj, format, explicitFormat, transform) } func handleSendersRegenerateWebhookSecret(ctx context.Context, cmd *cli.Command) error { @@ -485,8 +490,9 @@ func handleSendersRegenerateWebhookSecret(ctx context.Context, cmd *cli.Command) obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "senders regenerate-webhook-secret", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "senders regenerate-webhook-secret", obj, format, explicitFormat, transform) } func handleSendersUpdateProfile(ctx context.Context, cmd *cli.Command) error { @@ -527,8 +533,9 @@ func handleSendersUpdateProfile(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "senders update-profile", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "senders update-profile", obj, format, explicitFormat, transform) } func handleSendersUploadProfilePicture(ctx context.Context, cmd *cli.Command) error { @@ -569,6 +576,7 @@ func handleSendersUploadProfilePicture(ctx context.Context, cmd *cli.Command) er obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "senders upload-profile-picture", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "senders upload-profile-picture", obj, format, explicitFormat, transform) } diff --git a/pkg/cmd/senderagent.go b/pkg/cmd/senderagent.go index 9dc135c..47dc23c 100644 --- a/pkg/cmd/senderagent.go +++ b/pkg/cmd/senderagent.go @@ -226,8 +226,9 @@ func handleSendersAgentCreate(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "senders:agent create", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "senders:agent create", obj, format, explicitFormat, transform) } func handleSendersAgentRetrieve(ctx context.Context, cmd *cli.Command) error { @@ -261,8 +262,9 @@ func handleSendersAgentRetrieve(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "senders:agent retrieve", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "senders:agent retrieve", obj, format, explicitFormat, transform) } func handleSendersAgentUpdate(ctx context.Context, cmd *cli.Command) error { @@ -303,8 +305,9 @@ func handleSendersAgentUpdate(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "senders:agent update", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "senders:agent update", obj, format, explicitFormat, transform) } func handleSendersAgentDelete(ctx context.Context, cmd *cli.Command) error { @@ -363,6 +366,7 @@ func handleSendersAgentStats(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "senders:agent stats", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "senders:agent stats", obj, format, explicitFormat, transform) } diff --git a/pkg/cmd/senderagentexecution.go b/pkg/cmd/senderagentexecution.go index fb35434..34c496c 100644 --- a/pkg/cmd/senderagentexecution.go +++ b/pkg/cmd/senderagentexecution.go @@ -72,6 +72,7 @@ func handleSendersAgentExecutionsList(ctx context.Context, cmd *cli.Command) err } format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") if format == "raw" { var res []byte @@ -86,7 +87,7 @@ func handleSendersAgentExecutionsList(ctx context.Context, cmd *cli.Command) err return err } obj := gjson.ParseBytes(res) - return ShowJSON(os.Stdout, "senders:agent:executions list", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "senders:agent:executions list", obj, format, explicitFormat, transform) } else { iter := client.Senders.Agent.Executions.ListAutoPaging( ctx, @@ -98,6 +99,6 @@ func handleSendersAgentExecutionsList(ctx context.Context, cmd *cli.Command) err if cmd.IsSet("max-items") { maxItems = cmd.Value("max-items").(int64) } - return ShowJSONIterator(os.Stdout, "senders:agent:executions list", iter, format, transform, maxItems) + return ShowJSONIterator(os.Stdout, os.Stderr, "senders:agent:executions list", iter, format, explicitFormat, transform, maxItems) } } diff --git a/pkg/cmd/senderagentflow.go b/pkg/cmd/senderagentflow.go index 0560010..3d16dea 100644 --- a/pkg/cmd/senderagentflow.go +++ b/pkg/cmd/senderagentflow.go @@ -308,8 +308,9 @@ func handleSendersAgentFlowsCreate(ctx context.Context, cmd *cli.Command) error obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "senders:agent:flows create", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "senders:agent:flows create", obj, format, explicitFormat, transform) } func handleSendersAgentFlowsRetrieve(ctx context.Context, cmd *cli.Command) error { @@ -352,8 +353,9 @@ func handleSendersAgentFlowsRetrieve(ctx context.Context, cmd *cli.Command) erro obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "senders:agent:flows retrieve", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "senders:agent:flows retrieve", obj, format, explicitFormat, transform) } func handleSendersAgentFlowsUpdate(ctx context.Context, cmd *cli.Command) error { @@ -396,8 +398,9 @@ func handleSendersAgentFlowsUpdate(ctx context.Context, cmd *cli.Command) error obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "senders:agent:flows update", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "senders:agent:flows update", obj, format, explicitFormat, transform) } func handleSendersAgentFlowsList(ctx context.Context, cmd *cli.Command) error { @@ -425,6 +428,7 @@ func handleSendersAgentFlowsList(ctx context.Context, cmd *cli.Command) error { } format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") if format == "raw" { var res []byte @@ -439,7 +443,7 @@ func handleSendersAgentFlowsList(ctx context.Context, cmd *cli.Command) error { return err } obj := gjson.ParseBytes(res) - return ShowJSON(os.Stdout, "senders:agent:flows list", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "senders:agent:flows list", obj, format, explicitFormat, transform) } else { iter := client.Senders.Agent.Flows.ListAutoPaging( ctx, @@ -451,7 +455,7 @@ func handleSendersAgentFlowsList(ctx context.Context, cmd *cli.Command) error { if cmd.IsSet("max-items") { maxItems = cmd.Value("max-items").(int64) } - return ShowJSONIterator(os.Stdout, "senders:agent:flows list", iter, format, transform, maxItems) + return ShowJSONIterator(os.Stdout, os.Stderr, "senders:agent:flows list", iter, format, explicitFormat, transform, maxItems) } } @@ -529,6 +533,7 @@ func handleSendersAgentFlowsDuplicate(ctx context.Context, cmd *cli.Command) err obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "senders:agent:flows duplicate", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "senders:agent:flows duplicate", obj, format, explicitFormat, transform) } diff --git a/pkg/cmd/senderagentknowledgebase.go b/pkg/cmd/senderagentknowledgebase.go index 2a2902f..c77dd7e 100644 --- a/pkg/cmd/senderagentknowledgebase.go +++ b/pkg/cmd/senderagentknowledgebase.go @@ -165,8 +165,9 @@ func handleSendersAgentKnowledgeBasesCreate(ctx context.Context, cmd *cli.Comman obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "senders:agent:knowledge-bases create", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "senders:agent:knowledge-bases create", obj, format, explicitFormat, transform) } func handleSendersAgentKnowledgeBasesRetrieve(ctx context.Context, cmd *cli.Command) error { @@ -209,8 +210,9 @@ func handleSendersAgentKnowledgeBasesRetrieve(ctx context.Context, cmd *cli.Comm obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "senders:agent:knowledge-bases retrieve", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "senders:agent:knowledge-bases retrieve", obj, format, explicitFormat, transform) } func handleSendersAgentKnowledgeBasesUpdate(ctx context.Context, cmd *cli.Command) error { @@ -253,8 +255,9 @@ func handleSendersAgentKnowledgeBasesUpdate(ctx context.Context, cmd *cli.Comman obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "senders:agent:knowledge-bases update", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "senders:agent:knowledge-bases update", obj, format, explicitFormat, transform) } func handleSendersAgentKnowledgeBasesList(ctx context.Context, cmd *cli.Command) error { @@ -282,6 +285,7 @@ func handleSendersAgentKnowledgeBasesList(ctx context.Context, cmd *cli.Command) } format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") if format == "raw" { var res []byte @@ -296,7 +300,7 @@ func handleSendersAgentKnowledgeBasesList(ctx context.Context, cmd *cli.Command) return err } obj := gjson.ParseBytes(res) - return ShowJSON(os.Stdout, "senders:agent:knowledge-bases list", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "senders:agent:knowledge-bases list", obj, format, explicitFormat, transform) } else { iter := client.Senders.Agent.KnowledgeBases.ListAutoPaging( ctx, @@ -308,7 +312,7 @@ func handleSendersAgentKnowledgeBasesList(ctx context.Context, cmd *cli.Command) if cmd.IsSet("max-items") { maxItems = cmd.Value("max-items").(int64) } - return ShowJSONIterator(os.Stdout, "senders:agent:knowledge-bases list", iter, format, transform, maxItems) + return ShowJSONIterator(os.Stdout, os.Stderr, "senders:agent:knowledge-bases list", iter, format, explicitFormat, transform, maxItems) } } diff --git a/pkg/cmd/senderagentknowledgebasedocument.go b/pkg/cmd/senderagentknowledgebasedocument.go index 873079d..fd36e43 100644 --- a/pkg/cmd/senderagentknowledgebasedocument.go +++ b/pkg/cmd/senderagentknowledgebasedocument.go @@ -136,8 +136,9 @@ func handleSendersAgentKnowledgeBasesDocumentsCreate(ctx context.Context, cmd *c obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "senders:agent:knowledge-bases:documents create", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "senders:agent:knowledge-bases:documents create", obj, format, explicitFormat, transform) } func handleSendersAgentKnowledgeBasesDocumentsList(ctx context.Context, cmd *cli.Command) error { @@ -167,6 +168,7 @@ func handleSendersAgentKnowledgeBasesDocumentsList(ctx context.Context, cmd *cli } format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") if format == "raw" { var res []byte @@ -181,7 +183,7 @@ func handleSendersAgentKnowledgeBasesDocumentsList(ctx context.Context, cmd *cli return err } obj := gjson.ParseBytes(res) - return ShowJSON(os.Stdout, "senders:agent:knowledge-bases:documents list", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "senders:agent:knowledge-bases:documents list", obj, format, explicitFormat, transform) } else { iter := client.Senders.Agent.KnowledgeBases.Documents.ListAutoPaging( ctx, @@ -193,7 +195,7 @@ func handleSendersAgentKnowledgeBasesDocumentsList(ctx context.Context, cmd *cli if cmd.IsSet("max-items") { maxItems = cmd.Value("max-items").(int64) } - return ShowJSONIterator(os.Stdout, "senders:agent:knowledge-bases:documents list", iter, format, transform, maxItems) + return ShowJSONIterator(os.Stdout, os.Stderr, "senders:agent:knowledge-bases:documents list", iter, format, explicitFormat, transform, maxItems) } } diff --git a/pkg/cmd/senderagenttool.go b/pkg/cmd/senderagenttool.go index 60b970c..83d6d51 100644 --- a/pkg/cmd/senderagenttool.go +++ b/pkg/cmd/senderagenttool.go @@ -263,8 +263,9 @@ func handleSendersAgentToolsCreate(ctx context.Context, cmd *cli.Command) error obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "senders:agent:tools create", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "senders:agent:tools create", obj, format, explicitFormat, transform) } func handleSendersAgentToolsRetrieve(ctx context.Context, cmd *cli.Command) error { @@ -307,8 +308,9 @@ func handleSendersAgentToolsRetrieve(ctx context.Context, cmd *cli.Command) erro obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "senders:agent:tools retrieve", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "senders:agent:tools retrieve", obj, format, explicitFormat, transform) } func handleSendersAgentToolsUpdate(ctx context.Context, cmd *cli.Command) error { @@ -351,8 +353,9 @@ func handleSendersAgentToolsUpdate(ctx context.Context, cmd *cli.Command) error obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "senders:agent:tools update", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "senders:agent:tools update", obj, format, explicitFormat, transform) } func handleSendersAgentToolsList(ctx context.Context, cmd *cli.Command) error { @@ -380,6 +383,7 @@ func handleSendersAgentToolsList(ctx context.Context, cmd *cli.Command) error { } format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") if format == "raw" { var res []byte @@ -394,7 +398,7 @@ func handleSendersAgentToolsList(ctx context.Context, cmd *cli.Command) error { return err } obj := gjson.ParseBytes(res) - return ShowJSON(os.Stdout, "senders:agent:tools list", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "senders:agent:tools list", obj, format, explicitFormat, transform) } else { iter := client.Senders.Agent.Tools.ListAutoPaging( ctx, @@ -406,7 +410,7 @@ func handleSendersAgentToolsList(ctx context.Context, cmd *cli.Command) error { if cmd.IsSet("max-items") { maxItems = cmd.Value("max-items").(int64) } - return ShowJSONIterator(os.Stdout, "senders:agent:tools list", iter, format, transform, maxItems) + return ShowJSONIterator(os.Stdout, os.Stderr, "senders:agent:tools list", iter, format, explicitFormat, transform, maxItems) } } @@ -484,6 +488,7 @@ func handleSendersAgentToolsTest(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "senders:agent:tools test", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "senders:agent:tools test", obj, format, explicitFormat, transform) } diff --git a/pkg/cmd/senderwhatsappsync.go b/pkg/cmd/senderwhatsappsync.go index 3a7bb1c..41fbc80 100644 --- a/pkg/cmd/senderwhatsappsync.go +++ b/pkg/cmd/senderwhatsappsync.go @@ -88,8 +88,9 @@ func handleSendersWhatsappSyncRetrieve(ctx context.Context, cmd *cli.Command) er obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "senders:whatsapp-sync retrieve", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "senders:whatsapp-sync retrieve", obj, format, explicitFormat, transform) } func handleSendersWhatsappSyncStartContactsSync(ctx context.Context, cmd *cli.Command) error { @@ -123,8 +124,9 @@ func handleSendersWhatsappSyncStartContactsSync(ctx context.Context, cmd *cli.Co obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "senders:whatsapp-sync start-contacts-sync", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "senders:whatsapp-sync start-contacts-sync", obj, format, explicitFormat, transform) } func handleSendersWhatsappSyncStartHistorySync(ctx context.Context, cmd *cli.Command) error { @@ -158,6 +160,7 @@ func handleSendersWhatsappSyncStartHistorySync(ctx context.Context, cmd *cli.Com obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "senders:whatsapp-sync start-history-sync", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "senders:whatsapp-sync start-history-sync", obj, format, explicitFormat, transform) } diff --git a/pkg/cmd/subaccount.go b/pkg/cmd/subaccount.go index 622ce7a..2845ba4 100644 --- a/pkg/cmd/subaccount.go +++ b/pkg/cmd/subaccount.go @@ -175,8 +175,9 @@ func handleSubAccountsCreate(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "sub-accounts create", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "sub-accounts create", obj, format, explicitFormat, transform) } func handleSubAccountsRetrieve(ctx context.Context, cmd *cli.Command) error { @@ -210,8 +211,9 @@ func handleSubAccountsRetrieve(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "sub-accounts retrieve", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "sub-accounts retrieve", obj, format, explicitFormat, transform) } func handleSubAccountsUpdate(ctx context.Context, cmd *cli.Command) error { @@ -252,8 +254,9 @@ func handleSubAccountsUpdate(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "sub-accounts update", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "sub-accounts update", obj, format, explicitFormat, transform) } func handleSubAccountsList(ctx context.Context, cmd *cli.Command) error { @@ -278,6 +281,7 @@ func handleSubAccountsList(ctx context.Context, cmd *cli.Command) error { } format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") if format == "raw" { var res []byte @@ -287,14 +291,14 @@ func handleSubAccountsList(ctx context.Context, cmd *cli.Command) error { return err } obj := gjson.ParseBytes(res) - return ShowJSON(os.Stdout, "sub-accounts list", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "sub-accounts list", obj, format, explicitFormat, transform) } else { iter := client.SubAccounts.ListAutoPaging(ctx, params, options...) maxItems := int64(-1) if cmd.IsSet("max-items") { maxItems = cmd.Value("max-items").(int64) } - return ShowJSONIterator(os.Stdout, "sub-accounts list", iter, format, transform, maxItems) + return ShowJSONIterator(os.Stdout, os.Stderr, "sub-accounts list", iter, format, explicitFormat, transform, maxItems) } } @@ -329,8 +333,9 @@ func handleSubAccountsDeactivate(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "sub-accounts deactivate", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "sub-accounts deactivate", obj, format, explicitFormat, transform) } func handleSubAccountsGetBalance(ctx context.Context, cmd *cli.Command) error { @@ -364,6 +369,7 @@ func handleSubAccountsGetBalance(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "sub-accounts get-balance", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "sub-accounts get-balance", obj, format, explicitFormat, transform) } diff --git a/pkg/cmd/subaccountapikey.go b/pkg/cmd/subaccountapikey.go index 90af413..ec7c25d 100644 --- a/pkg/cmd/subaccountapikey.go +++ b/pkg/cmd/subaccountapikey.go @@ -114,8 +114,9 @@ func handleSubAccountsAPIKeysCreate(ctx context.Context, cmd *cli.Command) error obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "sub-accounts:api-keys create", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "sub-accounts:api-keys create", obj, format, explicitFormat, transform) } func handleSubAccountsAPIKeysList(ctx context.Context, cmd *cli.Command) error { @@ -149,8 +150,9 @@ func handleSubAccountsAPIKeysList(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "sub-accounts:api-keys list", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "sub-accounts:api-keys list", obj, format, explicitFormat, transform) } func handleSubAccountsAPIKeysRevoke(ctx context.Context, cmd *cli.Command) error { diff --git a/pkg/cmd/template.go b/pkg/cmd/template.go index d2a2274..da0b817 100644 --- a/pkg/cmd/template.go +++ b/pkg/cmd/template.go @@ -237,8 +237,9 @@ func handleTemplatesCreate(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "templates create", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "templates create", obj, format, explicitFormat, transform) } func handleTemplatesRetrieve(ctx context.Context, cmd *cli.Command) error { @@ -272,8 +273,9 @@ func handleTemplatesRetrieve(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "templates retrieve", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "templates retrieve", obj, format, explicitFormat, transform) } func handleTemplatesList(ctx context.Context, cmd *cli.Command) error { @@ -298,6 +300,7 @@ func handleTemplatesList(ctx context.Context, cmd *cli.Command) error { } format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") if format == "raw" { var res []byte @@ -307,14 +310,14 @@ func handleTemplatesList(ctx context.Context, cmd *cli.Command) error { return err } obj := gjson.ParseBytes(res) - return ShowJSON(os.Stdout, "templates list", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "templates list", obj, format, explicitFormat, transform) } else { iter := client.Templates.ListAutoPaging(ctx, params, options...) maxItems := int64(-1) if cmd.IsSet("max-items") { maxItems = cmd.Value("max-items").(int64) } - return ShowJSONIterator(os.Stdout, "templates list", iter, format, transform, maxItems) + return ShowJSONIterator(os.Stdout, os.Stderr, "templates list", iter, format, explicitFormat, transform, maxItems) } } @@ -381,6 +384,7 @@ func handleTemplatesSubmit(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "templates submit", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "templates submit", obj, format, explicitFormat, transform) } diff --git a/pkg/cmd/url.go b/pkg/cmd/url.go index 0bb8bef..aa1b7e2 100644 --- a/pkg/cmd/url.go +++ b/pkg/cmd/url.go @@ -95,6 +95,7 @@ func handleURLsListVerified(ctx context.Context, cmd *cli.Command) error { } format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") if format == "raw" { var res []byte @@ -104,14 +105,14 @@ func handleURLsListVerified(ctx context.Context, cmd *cli.Command) error { return err } obj := gjson.ParseBytes(res) - return ShowJSON(os.Stdout, "urls list-verified", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "urls list-verified", obj, format, explicitFormat, transform) } else { iter := client.URLs.ListVerifiedAutoPaging(ctx, params, options...) maxItems := int64(-1) if cmd.IsSet("max-items") { maxItems = cmd.Value("max-items").(int64) } - return ShowJSONIterator(os.Stdout, "urls list-verified", iter, format, transform, maxItems) + return ShowJSONIterator(os.Stdout, os.Stderr, "urls list-verified", iter, format, explicitFormat, transform, maxItems) } } @@ -146,8 +147,9 @@ func handleURLsRetrieveDetails(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "urls retrieve-details", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "urls retrieve-details", obj, format, explicitFormat, transform) } func handleURLsSubmitForVerification(ctx context.Context, cmd *cli.Command) error { @@ -180,6 +182,7 @@ func handleURLsSubmitForVerification(ctx context.Context, cmd *cli.Command) erro obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "urls submit-for-verification", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "urls submit-for-verification", obj, format, explicitFormat, transform) } diff --git a/pkg/cmd/usage.go b/pkg/cmd/usage.go index 82144c8..2ab6a96 100644 --- a/pkg/cmd/usage.go +++ b/pkg/cmd/usage.go @@ -51,6 +51,7 @@ func handleUsageRetrieve(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "usage retrieve", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "usage retrieve", obj, format, explicitFormat, transform) } From 3711e0654e91a0345effe9b901f8b8635fbe0c62 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 15 Apr 2026 05:05:04 +0000 Subject: [PATCH 06/56] feat(cli): alias parameters in data with `x-stainless-cli-data-alias` --- internal/requestflag/innerflag.go | 19 ++++++++++-- internal/requestflag/requestflag.go | 9 ++++++ pkg/cmd/flagoptions.go | 47 +++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 2 deletions(-) diff --git a/internal/requestflag/innerflag.go b/internal/requestflag/innerflag.go index 102624f..eeeb8bc 100644 --- a/internal/requestflag/innerflag.go +++ b/internal/requestflag/innerflag.go @@ -22,14 +22,29 @@ type InnerFlag[ Aliases []string // aliases that are allowed for this flag Validator func(T) error // custom function to validate this flag value - OuterFlag cli.Flag // The flag on which this inner flag will set values - InnerField string // The inner field which this flag will set + OuterFlag cli.Flag // The flag on which this inner flag will set values + InnerField string // The inner field which this flag will set + DataAliases []string // alternate names recognized in YAML values passed as the outer flag +} + +// GetDataAliases returns the aliases recognized when parsing inner field keys from piped or flag YAML. +func (f *InnerFlag[T]) GetDataAliases() []string { + return f.DataAliases +} + +// GetInnerField returns the API field name that this inner flag sets on its outer flag's value. +// For example, the flag --parent.foo targeting a parameter whose OpenAPI property name is "foo" +// would return "foo". This is distinct from the flag's CLI name and from any DataAliases entries. +func (f *InnerFlag[T]) GetInnerField() string { + return f.InnerField } type HasOuterFlag interface { cli.Flag SetOuterFlag(cli.Flag) GetOuterFlag() cli.Flag + GetInnerField() string + GetDataAliases() []string } func (f *InnerFlag[T]) SetOuterFlag(flag cli.Flag) { diff --git a/internal/requestflag/requestflag.go b/internal/requestflag/requestflag.go index bdef64f..bfaf064 100644 --- a/internal/requestflag/requestflag.go +++ b/internal/requestflag/requestflag.go @@ -48,6 +48,10 @@ type Flag[ // binary` in the OpenAPI spec. FileInput bool + // DataAliases is a list of alternate names for this parameter recognized when parsing piped YAML/JSON + // input. Values keyed by any alias are translated to the canonical API name before being sent. + DataAliases []string + // unexported fields for internal use count int // number of times the flag has been set hasBeenSet bool // whether the flag has been set from env or file @@ -65,6 +69,7 @@ type InRequest interface { GetBodyPath() string IsBodyRoot() bool IsFileInput() bool + GetDataAliases() []string } func (f Flag[T]) GetQueryPath() string { @@ -87,6 +92,10 @@ func (f Flag[T]) IsFileInput() bool { return f.FileInput } +func (f Flag[T]) GetDataAliases() []string { + return f.DataAliases +} + // The values that will be sent in different parts of a request. type RequestContents struct { Queries map[string]any diff --git a/pkg/cmd/flagoptions.go b/pkg/cmd/flagoptions.go index 3c0f276..604f403 100644 --- a/pkg/cmd/flagoptions.go +++ b/pkg/cmd/flagoptions.go @@ -309,6 +309,12 @@ func flagOptions( requestContents := requestflag.ExtractRequestContents(cmd) + // Translate inner-field aliases in YAML values that came from flags (e.g. + // `--parent '{"alias": val}'` resolving to the canonical inner field). + if bodyMap, ok := requestContents.Body.(map[string]any); ok { + applyDataAliases(cmd, bodyMap) + } + stdinConsumedByPipe := false if (bodyType == MultipartFormEncoded || bodyType == ApplicationJSON) && !ignoreStdin && isInputPiped() { pipeData, err := io.ReadAll(os.Stdin) @@ -323,6 +329,7 @@ func flagOptions( return nil, fmt.Errorf("Failed to parse piped data as YAML/JSON:\n%w", err) } if bodyMap, ok := bodyData.(map[string]any); ok { + applyDataAliases(cmd, bodyMap) if flagMap, ok := requestContents.Body.(map[string]any); ok { maps.Copy(bodyMap, flagMap) requestContents.Body = bodyMap @@ -485,6 +492,46 @@ func flagOptions( // as a file path without needing the "@" prefix. type FilePathValue string +// applyDataAliases rewrites keys in a body map based on flag `DataAliases` metadata. For top-level flags, +// `{alias: value}` becomes `{canonical: value}`. For inner flags (those registered under an outer flag +// via WithInnerFlags), the alias translation is also applied to the nested map under the outer flag's +// body path, so values like `--parent '{"alias": val}'` resolve to the canonical inner field name. +func applyDataAliases(cmd *cli.Command, bodyMap map[string]any) { + for _, flag := range cmd.Flags { + // Inner flags: rewrite aliases inside the nested map under the outer flag's body path. + if inner, ok := flag.(requestflag.HasOuterFlag); ok { + outer, outerOk := inner.GetOuterFlag().(requestflag.InRequest) + if !outerOk { + continue + } + if nested, ok := bodyMap[outer.GetBodyPath()].(map[string]any); ok && inner.GetInnerField() != "" { + rewriteAliases(nested, inner.GetInnerField(), inner.GetDataAliases()) + } + continue + } + // Top-level flags: rewrite aliases in the body map. + if inReq, ok := flag.(requestflag.InRequest); ok && inReq.GetBodyPath() != "" { + rewriteAliases(bodyMap, inReq.GetBodyPath(), inReq.GetDataAliases()) + } + } +} + +// rewriteAliases replaces each alias key in m with the canonical key, preserving the value. The +// "canonical" key is the name the API itself expects (the OpenAPI property/field name) — e.g. for +// a top-level flag, the parameter's BodyPath; for an inner flag, the inner field name. Aliases are +// the user-facing alternate names declared via x-stainless-cli-data-alias. +func rewriteAliases(m map[string]any, canonical string, aliases []string) { + for _, alias := range aliases { + if alias == "" || alias == canonical { + continue + } + if val, exists := m[alias]; exists { + m[canonical] = val + delete(m, alias) + } + } +} + // wrapFileInputValues replaces string values for FileInput flags (type: string, format: binary) with // FilePathValue sentinel values. embedFilesValue recognizes FilePathValue and reads the file contents // directly, so the user doesn't need to type the "@" prefix. This handles both values set via explicit From c97dd8703f6ca089d3c29307eab81e23bec7c1d4 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 16 Apr 2026 04:15:21 +0000 Subject: [PATCH 07/56] chore(cli): switch long lists of positional args over to param structs --- cmd/zavudev/main.go | 7 +- pkg/cmd/address.go | 29 +++++-- pkg/cmd/balance.go | 8 +- pkg/cmd/broadcast.go | 78 ++++++++++++++++--- pkg/cmd/broadcastcontact.go | 22 +++++- pkg/cmd/cmdutil.go | 84 ++++++++++++++------- pkg/cmd/cmdutil_test.go | 31 ++++++-- pkg/cmd/contact.go | 50 ++++++++++-- pkg/cmd/contactchannel.go | 22 +++++- pkg/cmd/export.go | 29 +++++-- pkg/cmd/introspect.go | 8 +- pkg/cmd/invitation.go | 36 +++++++-- pkg/cmd/message.go | 36 +++++++-- pkg/cmd/number10dlcbrand.go | 57 +++++++++++--- pkg/cmd/number10dlccampaign.go | 50 ++++++++++-- pkg/cmd/number10dlccampaignphonenumber.go | 15 +++- pkg/cmd/phonenumber.go | 50 ++++++++++-- pkg/cmd/plan.go | 8 +- pkg/cmd/regulatorydocument.go | 36 +++++++-- pkg/cmd/sender.go | 64 +++++++++++++--- pkg/cmd/senderagent.go | 29 +++++-- pkg/cmd/senderagentexecution.go | 15 +++- pkg/cmd/senderagentflow.go | 43 +++++++++-- pkg/cmd/senderagentknowledgebase.go | 36 +++++++-- pkg/cmd/senderagentknowledgebasedocument.go | 22 +++++- pkg/cmd/senderagenttool.go | 43 +++++++++-- pkg/cmd/senderwhatsappsync.go | 22 +++++- pkg/cmd/subaccount.go | 50 ++++++++++-- pkg/cmd/subaccountapikey.go | 15 +++- pkg/cmd/template.go | 36 +++++++-- pkg/cmd/url.go | 29 +++++-- pkg/cmd/usage.go | 8 +- 32 files changed, 873 insertions(+), 195 deletions(-) diff --git a/cmd/zavudev/main.go b/cmd/zavudev/main.go index 746d1ec..d7974c5 100644 --- a/cmd/zavudev/main.go +++ b/cmd/zavudev/main.go @@ -43,7 +43,12 @@ func main() { fmt.Fprintf(os.Stderr, "%s %q: %d %s\n", apierr.Request.Method, apierr.Request.URL, apierr.Response.StatusCode, http.StatusText(apierr.Response.StatusCode)) format := app.String("format-error") json := gjson.Parse(apierr.RawJSON()) - show_err := cmd.ShowJSON(os.Stdout, os.Stderr, "Error", json, format, app.IsSet("format-error"), app.String("transform-error")) + show_err := cmd.ShowJSON(json, cmd.ShowJSONOpts{ + ExplicitFormat: app.IsSet("format-error"), + Format: format, + Title: "Error", + Transform: app.String("transform-error"), + }) if show_err != nil { // Just print the original error: fmt.Fprintf(os.Stderr, "%s\n", err.Error()) diff --git a/pkg/cmd/address.go b/pkg/cmd/address.go index 721e6fc..dfa4242 100644 --- a/pkg/cmd/address.go +++ b/pkg/cmd/address.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" @@ -148,7 +147,12 @@ func handleAddressesCreate(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "addresses create", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "addresses create", + Transform: transform, + }) } func handleAddressesRetrieve(ctx context.Context, cmd *cli.Command) error { @@ -184,7 +188,12 @@ func handleAddressesRetrieve(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "addresses retrieve", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "addresses retrieve", + Transform: transform, + }) } func handleAddressesList(ctx context.Context, cmd *cli.Command) error { @@ -219,14 +228,24 @@ func handleAddressesList(ctx context.Context, cmd *cli.Command) error { return err } obj := gjson.ParseBytes(res) - return ShowJSON(os.Stdout, os.Stderr, "addresses list", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "addresses list", + Transform: transform, + }) } else { iter := client.Addresses.ListAutoPaging(ctx, params, options...) maxItems := int64(-1) if cmd.IsSet("max-items") { maxItems = cmd.Value("max-items").(int64) } - return ShowJSONIterator(os.Stdout, os.Stderr, "addresses list", iter, format, explicitFormat, transform, maxItems) + return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "addresses list", + Transform: transform, + }) } } diff --git a/pkg/cmd/balance.go b/pkg/cmd/balance.go index 9dce394..3c50c9d 100644 --- a/pkg/cmd/balance.go +++ b/pkg/cmd/balance.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" @@ -53,5 +52,10 @@ func handleBalanceRetrieve(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "balance retrieve", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "balance retrieve", + Transform: transform, + }) } diff --git a/pkg/cmd/broadcast.go b/pkg/cmd/broadcast.go index 10a2cac..d377f04 100644 --- a/pkg/cmd/broadcast.go +++ b/pkg/cmd/broadcast.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" @@ -369,7 +368,12 @@ func handleBroadcastsCreate(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "broadcasts create", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "broadcasts create", + Transform: transform, + }) } func handleBroadcastsRetrieve(ctx context.Context, cmd *cli.Command) error { @@ -405,7 +409,12 @@ func handleBroadcastsRetrieve(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "broadcasts retrieve", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "broadcasts retrieve", + Transform: transform, + }) } func handleBroadcastsUpdate(ctx context.Context, cmd *cli.Command) error { @@ -448,7 +457,12 @@ func handleBroadcastsUpdate(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "broadcasts update", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "broadcasts update", + Transform: transform, + }) } func handleBroadcastsList(ctx context.Context, cmd *cli.Command) error { @@ -483,14 +497,24 @@ func handleBroadcastsList(ctx context.Context, cmd *cli.Command) error { return err } obj := gjson.ParseBytes(res) - return ShowJSON(os.Stdout, os.Stderr, "broadcasts list", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "broadcasts list", + Transform: transform, + }) } else { iter := client.Broadcasts.ListAutoPaging(ctx, params, options...) maxItems := int64(-1) if cmd.IsSet("max-items") { maxItems = cmd.Value("max-items").(int64) } - return ShowJSONIterator(os.Stdout, os.Stderr, "broadcasts list", iter, format, explicitFormat, transform, maxItems) + return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "broadcasts list", + Transform: transform, + }) } } @@ -552,7 +576,12 @@ func handleBroadcastsCancel(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "broadcasts cancel", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "broadcasts cancel", + Transform: transform, + }) } func handleBroadcastsEscalateReview(ctx context.Context, cmd *cli.Command) error { @@ -588,7 +617,12 @@ func handleBroadcastsEscalateReview(ctx context.Context, cmd *cli.Command) error format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "broadcasts escalate-review", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "broadcasts escalate-review", + Transform: transform, + }) } func handleBroadcastsProgress(ctx context.Context, cmd *cli.Command) error { @@ -624,7 +658,12 @@ func handleBroadcastsProgress(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "broadcasts progress", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "broadcasts progress", + Transform: transform, + }) } func handleBroadcastsReschedule(ctx context.Context, cmd *cli.Command) error { @@ -667,7 +706,12 @@ func handleBroadcastsReschedule(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "broadcasts reschedule", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "broadcasts reschedule", + Transform: transform, + }) } func handleBroadcastsRetryReview(ctx context.Context, cmd *cli.Command) error { @@ -703,7 +747,12 @@ func handleBroadcastsRetryReview(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "broadcasts retry-review", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "broadcasts retry-review", + Transform: transform, + }) } func handleBroadcastsSend(ctx context.Context, cmd *cli.Command) error { @@ -746,5 +795,10 @@ func handleBroadcastsSend(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "broadcasts send", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "broadcasts send", + Transform: transform, + }) } diff --git a/pkg/cmd/broadcastcontact.go b/pkg/cmd/broadcastcontact.go index 27517ae..b2230fe 100644 --- a/pkg/cmd/broadcastcontact.go +++ b/pkg/cmd/broadcastcontact.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" @@ -138,7 +137,12 @@ func handleBroadcastsContactsList(ctx context.Context, cmd *cli.Command) error { return err } obj := gjson.ParseBytes(res) - return ShowJSON(os.Stdout, os.Stderr, "broadcasts:contacts list", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "broadcasts:contacts list", + Transform: transform, + }) } else { iter := client.Broadcasts.Contacts.ListAutoPaging( ctx, @@ -150,7 +154,12 @@ func handleBroadcastsContactsList(ctx context.Context, cmd *cli.Command) error { if cmd.IsSet("max-items") { maxItems = cmd.Value("max-items").(int64) } - return ShowJSONIterator(os.Stdout, os.Stderr, "broadcasts:contacts list", iter, format, explicitFormat, transform, maxItems) + return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "broadcasts:contacts list", + Transform: transform, + }) } } @@ -194,7 +203,12 @@ func handleBroadcastsContactsAdd(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "broadcasts:contacts add", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "broadcasts:contacts add", + Transform: transform, + }) } func handleBroadcastsContactsRemove(ctx context.Context, cmd *cli.Command) error { diff --git a/pkg/cmd/cmdutil.go b/pkg/cmd/cmdutil.go index 8fe5e9a..76e6cda 100644 --- a/pkg/cmd/cmdutil.go +++ b/pkg/cmd/cmdutil.go @@ -356,36 +356,58 @@ func formatJSON(expectedOutput *os.File, title string, res gjson.Result, format const warningExploreNotSupported = "Warning: Output format 'explore' not supported for non-terminal output; falling back to 'json'\n" -// Display JSON to the user in various different formats. The explicitFormat parameter indicates -// whether the format was explicitly set by the user (via --format), which controls whether we -// silently fall back to json when explore is requested on non-terminal output. Warnings are -// written to stderr. -func ShowJSON(out *os.File, stderr io.Writer, title string, res gjson.Result, format string, explicitFormat bool, transform string) error { - if transform != "" { - transformed := res.Get(transform) +// ShowJSONOpts configures how JSON output is displayed. +type ShowJSONOpts struct { + ExplicitFormat bool // true if the user explicitly passed --format + Format string // output format (auto, explore, json, jsonl, pretty, raw, yaml) + Stderr io.Writer // stderr for warnings; injectable for testing; defaults to os.Stderr + Stdout *os.File // stdout (or pager); injectable for testing; defaults to os.Stdout + Title string // display title + Transform string // GJSON path to extract before displaying +} + +func (o *ShowJSONOpts) setDefaults() { + if o.Stderr == nil { + o.Stderr = os.Stderr + } + if o.Stdout == nil { + o.Stdout = os.Stdout + } +} + +// ShowJSON displays a single JSON result to the user. +func ShowJSON(res gjson.Result, opts ShowJSONOpts) error { + opts.setDefaults() + + if opts.Transform != "" { + transformed := res.Get(opts.Transform) if transformed.Exists() { res = transformed } } - switch strings.ToLower(format) { + switch strings.ToLower(opts.Format) { case "auto": - return ShowJSON(out, stderr, title, res, "json", explicitFormat, "") + jsonOpts := opts + jsonOpts.Format = "json" + return ShowJSON(res, jsonOpts) case "explore": - if !isTerminal(out) { - if explicitFormat { - fmt.Fprint(stderr, warningExploreNotSupported) + if !isTerminal(opts.Stdout) { + if opts.ExplicitFormat { + fmt.Fprint(opts.Stderr, warningExploreNotSupported) } - return ShowJSON(out, stderr, title, res, "json", explicitFormat, transform) + jsonOpts := opts + jsonOpts.Format = "json" + return ShowJSON(res, jsonOpts) } - return jsonview.ExploreJSON(title, res) + return jsonview.ExploreJSON(opts.Title, res) default: - bytes, err := formatJSON(out, title, res, format, transform) + bytes, err := formatJSON(opts.Stdout, opts.Title, res, opts.Format, opts.Transform) if err != nil { return err } - _, err = out.Write(bytes) + _, err = opts.Stdout.Write(bytes) return err } } @@ -399,16 +421,17 @@ type hasRawJSON interface { RawJSON() string } -// For an iterator over different value types, display its values to the user in -// different formats. -// -1 is used to signal no limit of items to display -func ShowJSONIterator[T any](stdout *os.File, stderr io.Writer, title string, iter jsonview.Iterator[T], format string, explicitFormat bool, transform string, itemsToDisplay int64) error { +// ShowJSONIterator displays an iterator of values to the user. Use itemsToDisplay = -1 for no limit. +func ShowJSONIterator[T any](iter jsonview.Iterator[T], itemsToDisplay int64, opts ShowJSONOpts) error { + opts.setDefaults() + + format := opts.Format if format == "explore" { - if isTerminal(stdout) { - return jsonview.ExploreJSONStream(title, iter) + if isTerminal(opts.Stdout) { + return jsonview.ExploreJSONStream(opts.Title, iter) } - if explicitFormat { - fmt.Fprint(stderr, warningExploreNotSupported) + if opts.ExplicitFormat { + fmt.Fprint(opts.Stderr, warningExploreNotSupported) } format = "json" } @@ -436,7 +459,7 @@ func ShowJSONIterator[T any](stdout *os.File, stderr io.Writer, title string, it } obj = gjson.ParseBytes(jsonData) } - json, err := formatJSON(stdout, title, obj, format, transform) + json, err := formatJSON(opts.Stdout, opts.Title, obj, format, opts.Transform) if err != nil { return err } @@ -453,7 +476,7 @@ func ShowJSONIterator[T any](stdout *os.File, stderr io.Writer, title string, it } if !usePager { - _, err := stdout.Write(output) + _, err := opts.Stdout.Write(output) if err != nil { return err } @@ -461,13 +484,16 @@ func ShowJSONIterator[T any](stdout *os.File, stderr io.Writer, title string, it return iter.Err() } - return streamOutput(title, func(pager *os.File) error { - // Write the output we used during the initial terminal size computation + return streamOutput(opts.Title, func(pager *os.File) error { _, err := pager.Write(output) if err != nil { return err } + pagerOpts := opts + pagerOpts.Format = format + pagerOpts.Stdout = pager + for iter.Next() { if itemsToDisplay == 0 { break @@ -483,7 +509,7 @@ func ShowJSONIterator[T any](stdout *os.File, stderr io.Writer, title string, it } obj = gjson.ParseBytes(jsonData) } - if err := ShowJSON(pager, stderr, title, obj, format, explicitFormat, transform); err != nil { + if err := ShowJSON(obj, pagerOpts); err != nil { return err } itemsToDisplay -= 1 diff --git a/pkg/cmd/cmdutil_test.go b/pkg/cmd/cmdutil_test.go index ef5baf7..e403e39 100644 --- a/pkg/cmd/cmdutil_test.go +++ b/pkg/cmd/cmdutil_test.go @@ -244,7 +244,12 @@ func TestExploreFallback(t *testing.T) { var stderr bytes.Buffer res := gjson.Parse(`{"id":"abc"}`) - err = ShowJSON(w, &stderr, "test", res, "explore", false, "") + err = ShowJSON(res, ShowJSONOpts{ + Format: "explore", + Stderr: &stderr, + Stdout: w, + Title: "test", + }) w.Close() require.NoError(t, err) @@ -274,7 +279,13 @@ func TestExploreFallback(t *testing.T) { var stderr bytes.Buffer res := gjson.Parse(`{"id":"abc"}`) - err = ShowJSON(w, &stderr, "test", res, "explore", true, "") + err = ShowJSON(res, ShowJSONOpts{ + ExplicitFormat: true, + Format: "explore", + Stderr: &stderr, + Stdout: w, + Title: "test", + }) w.Close() require.NoError(t, err) @@ -290,7 +301,12 @@ func TestExploreFallback(t *testing.T) { var stderr bytes.Buffer res := gjson.Parse(`{"id":"abc"}`) - err = ShowJSON(w, &stderr, "test", res, "explore", false, "") + err = ShowJSON(res, ShowJSONOpts{ + Format: "explore", + Stderr: &stderr, + Stdout: w, + Title: "test", + }) w.Close() require.NoError(t, err) @@ -327,8 +343,13 @@ func captureShowJSONIterator[T any](t *testing.T, iter jsonview.Iterator[T], for require.NoError(t, err) defer r.Close() - var stderr bytes.Buffer - err = ShowJSONIterator(w, &stderr, "test", iter, format, false, transform, itemsToDisplay) + err = ShowJSONIterator(iter, itemsToDisplay, ShowJSONOpts{ + Format: format, + Stderr: io.Discard, + Stdout: w, + Title: "test", + Transform: transform, + }) w.Close() require.NoError(t, err) diff --git a/pkg/cmd/contact.go b/pkg/cmd/contact.go index 436d97c..3ba4fd2 100644 --- a/pkg/cmd/contact.go +++ b/pkg/cmd/contact.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" @@ -213,7 +212,12 @@ func handleContactsCreate(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "contacts create", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "contacts create", + Transform: transform, + }) } func handleContactsRetrieve(ctx context.Context, cmd *cli.Command) error { @@ -249,7 +253,12 @@ func handleContactsRetrieve(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "contacts retrieve", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "contacts retrieve", + Transform: transform, + }) } func handleContactsUpdate(ctx context.Context, cmd *cli.Command) error { @@ -292,7 +301,12 @@ func handleContactsUpdate(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "contacts update", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "contacts update", + Transform: transform, + }) } func handleContactsList(ctx context.Context, cmd *cli.Command) error { @@ -327,14 +341,24 @@ func handleContactsList(ctx context.Context, cmd *cli.Command) error { return err } obj := gjson.ParseBytes(res) - return ShowJSON(os.Stdout, os.Stderr, "contacts list", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "contacts list", + Transform: transform, + }) } else { iter := client.Contacts.ListAutoPaging(ctx, params, options...) maxItems := int64(-1) if cmd.IsSet("max-items") { maxItems = cmd.Value("max-items").(int64) } - return ShowJSONIterator(os.Stdout, os.Stderr, "contacts list", iter, format, explicitFormat, transform, maxItems) + return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "contacts list", + Transform: transform, + }) } } @@ -403,7 +427,12 @@ func handleContactsMerge(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "contacts merge", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "contacts merge", + Transform: transform, + }) } func handleContactsRetrieveByPhone(ctx context.Context, cmd *cli.Command) error { @@ -439,5 +468,10 @@ func handleContactsRetrieveByPhone(ctx context.Context, cmd *cli.Command) error format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "contacts retrieve-by-phone", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "contacts retrieve-by-phone", + Transform: transform, + }) } diff --git a/pkg/cmd/contactchannel.go b/pkg/cmd/contactchannel.go index 2503c71..8f8009b 100644 --- a/pkg/cmd/contactchannel.go +++ b/pkg/cmd/contactchannel.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" @@ -167,7 +166,12 @@ func handleContactsChannelsUpdate(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "contacts:channels update", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "contacts:channels update", + Transform: transform, + }) } func handleContactsChannelsAdd(ctx context.Context, cmd *cli.Command) error { @@ -210,7 +214,12 @@ func handleContactsChannelsAdd(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "contacts:channels add", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "contacts:channels add", + Transform: transform, + }) } func handleContactsChannelsRemove(ctx context.Context, cmd *cli.Command) error { @@ -289,5 +298,10 @@ func handleContactsChannelsSetPrimary(ctx context.Context, cmd *cli.Command) err format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "contacts:channels set-primary", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "contacts:channels set-primary", + Transform: transform, + }) } diff --git a/pkg/cmd/export.go b/pkg/cmd/export.go index 2004c29..4a5287c 100644 --- a/pkg/cmd/export.go +++ b/pkg/cmd/export.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" @@ -115,7 +114,12 @@ func handleExportsCreate(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "exports create", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "exports create", + Transform: transform, + }) } func handleExportsRetrieve(ctx context.Context, cmd *cli.Command) error { @@ -151,7 +155,12 @@ func handleExportsRetrieve(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "exports retrieve", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "exports retrieve", + Transform: transform, + }) } func handleExportsList(ctx context.Context, cmd *cli.Command) error { @@ -186,13 +195,23 @@ func handleExportsList(ctx context.Context, cmd *cli.Command) error { return err } obj := gjson.ParseBytes(res) - return ShowJSON(os.Stdout, os.Stderr, "exports list", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "exports list", + Transform: transform, + }) } else { iter := client.Exports.ListAutoPaging(ctx, params, options...) maxItems := int64(-1) if cmd.IsSet("max-items") { maxItems = cmd.Value("max-items").(int64) } - return ShowJSONIterator(os.Stdout, os.Stderr, "exports list", iter, format, explicitFormat, transform, maxItems) + return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "exports list", + Transform: transform, + }) } } diff --git a/pkg/cmd/introspect.go b/pkg/cmd/introspect.go index 3509d27..380aa72 100644 --- a/pkg/cmd/introspect.go +++ b/pkg/cmd/introspect.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" @@ -62,5 +61,10 @@ func handleIntrospectValidatePhone(ctx context.Context, cmd *cli.Command) error format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "introspect validate-phone", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "introspect validate-phone", + Transform: transform, + }) } diff --git a/pkg/cmd/invitation.go b/pkg/cmd/invitation.go index 4d9532b..4b01e0b 100644 --- a/pkg/cmd/invitation.go +++ b/pkg/cmd/invitation.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" @@ -144,7 +143,12 @@ func handleInvitationsCreate(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "invitations create", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "invitations create", + Transform: transform, + }) } func handleInvitationsRetrieve(ctx context.Context, cmd *cli.Command) error { @@ -180,7 +184,12 @@ func handleInvitationsRetrieve(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "invitations retrieve", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "invitations retrieve", + Transform: transform, + }) } func handleInvitationsList(ctx context.Context, cmd *cli.Command) error { @@ -215,14 +224,24 @@ func handleInvitationsList(ctx context.Context, cmd *cli.Command) error { return err } obj := gjson.ParseBytes(res) - return ShowJSON(os.Stdout, os.Stderr, "invitations list", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "invitations list", + Transform: transform, + }) } else { iter := client.Invitations.ListAutoPaging(ctx, params, options...) maxItems := int64(-1) if cmd.IsSet("max-items") { maxItems = cmd.Value("max-items").(int64) } - return ShowJSONIterator(os.Stdout, os.Stderr, "invitations list", iter, format, explicitFormat, transform, maxItems) + return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "invitations list", + Transform: transform, + }) } } @@ -259,5 +278,10 @@ func handleInvitationsCancel(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "invitations cancel", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "invitations cancel", + Transform: transform, + }) } diff --git a/pkg/cmd/message.go b/pkg/cmd/message.go index 77a43ce..7153845 100644 --- a/pkg/cmd/message.go +++ b/pkg/cmd/message.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" @@ -314,7 +313,12 @@ func handleMessagesRetrieve(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "messages retrieve", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "messages retrieve", + Transform: transform, + }) } func handleMessagesList(ctx context.Context, cmd *cli.Command) error { @@ -349,14 +353,24 @@ func handleMessagesList(ctx context.Context, cmd *cli.Command) error { return err } obj := gjson.ParseBytes(res) - return ShowJSON(os.Stdout, os.Stderr, "messages list", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "messages list", + Transform: transform, + }) } else { iter := client.Messages.ListAutoPaging(ctx, params, options...) maxItems := int64(-1) if cmd.IsSet("max-items") { maxItems = cmd.Value("max-items").(int64) } - return ShowJSONIterator(os.Stdout, os.Stderr, "messages list", iter, format, explicitFormat, transform, maxItems) + return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "messages list", + Transform: transform, + }) } } @@ -400,7 +414,12 @@ func handleMessagesReact(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "messages react", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "messages react", + Transform: transform, + }) } func handleMessagesSend(ctx context.Context, cmd *cli.Command) error { @@ -435,5 +454,10 @@ func handleMessagesSend(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "messages send", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "messages send", + Transform: transform, + }) } diff --git a/pkg/cmd/number10dlcbrand.go b/pkg/cmd/number10dlcbrand.go index d3a27be..2f574f3 100644 --- a/pkg/cmd/number10dlcbrand.go +++ b/pkg/cmd/number10dlcbrand.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" @@ -313,7 +312,12 @@ func handleNumber10dlcBrandsCreate(ctx context.Context, cmd *cli.Command) error format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "number-10dlc:brands create", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "number-10dlc:brands create", + Transform: transform, + }) } func handleNumber10dlcBrandsRetrieve(ctx context.Context, cmd *cli.Command) error { @@ -349,7 +353,12 @@ func handleNumber10dlcBrandsRetrieve(ctx context.Context, cmd *cli.Command) erro format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "number-10dlc:brands retrieve", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "number-10dlc:brands retrieve", + Transform: transform, + }) } func handleNumber10dlcBrandsUpdate(ctx context.Context, cmd *cli.Command) error { @@ -392,7 +401,12 @@ func handleNumber10dlcBrandsUpdate(ctx context.Context, cmd *cli.Command) error format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "number-10dlc:brands update", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "number-10dlc:brands update", + Transform: transform, + }) } func handleNumber10dlcBrandsList(ctx context.Context, cmd *cli.Command) error { @@ -427,14 +441,24 @@ func handleNumber10dlcBrandsList(ctx context.Context, cmd *cli.Command) error { return err } obj := gjson.ParseBytes(res) - return ShowJSON(os.Stdout, os.Stderr, "number-10dlc:brands list", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "number-10dlc:brands list", + Transform: transform, + }) } else { iter := client.Number10dlc.Brands.ListAutoPaging(ctx, params, options...) maxItems := int64(-1) if cmd.IsSet("max-items") { maxItems = cmd.Value("max-items").(int64) } - return ShowJSONIterator(os.Stdout, os.Stderr, "number-10dlc:brands list", iter, format, explicitFormat, transform, maxItems) + return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "number-10dlc:brands list", + Transform: transform, + }) } } @@ -493,7 +517,12 @@ func handleNumber10dlcBrandsListUseCases(ctx context.Context, cmd *cli.Command) format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "number-10dlc:brands list-use-cases", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "number-10dlc:brands list-use-cases", + Transform: transform, + }) } func handleNumber10dlcBrandsSubmit(ctx context.Context, cmd *cli.Command) error { @@ -529,7 +558,12 @@ func handleNumber10dlcBrandsSubmit(ctx context.Context, cmd *cli.Command) error format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "number-10dlc:brands submit", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "number-10dlc:brands submit", + Transform: transform, + }) } func handleNumber10dlcBrandsSyncStatus(ctx context.Context, cmd *cli.Command) error { @@ -565,5 +599,10 @@ func handleNumber10dlcBrandsSyncStatus(ctx context.Context, cmd *cli.Command) er format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "number-10dlc:brands sync-status", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "number-10dlc:brands sync-status", + Transform: transform, + }) } diff --git a/pkg/cmd/number10dlccampaign.go b/pkg/cmd/number10dlccampaign.go index 5a6055e..9d6634c 100644 --- a/pkg/cmd/number10dlccampaign.go +++ b/pkg/cmd/number10dlccampaign.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" @@ -275,7 +274,12 @@ func handleNumber10dlcCampaignsCreate(ctx context.Context, cmd *cli.Command) err format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "number-10dlc:campaigns create", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "number-10dlc:campaigns create", + Transform: transform, + }) } func handleNumber10dlcCampaignsRetrieve(ctx context.Context, cmd *cli.Command) error { @@ -311,7 +315,12 @@ func handleNumber10dlcCampaignsRetrieve(ctx context.Context, cmd *cli.Command) e format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "number-10dlc:campaigns retrieve", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "number-10dlc:campaigns retrieve", + Transform: transform, + }) } func handleNumber10dlcCampaignsUpdate(ctx context.Context, cmd *cli.Command) error { @@ -354,7 +363,12 @@ func handleNumber10dlcCampaignsUpdate(ctx context.Context, cmd *cli.Command) err format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "number-10dlc:campaigns update", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "number-10dlc:campaigns update", + Transform: transform, + }) } func handleNumber10dlcCampaignsList(ctx context.Context, cmd *cli.Command) error { @@ -389,14 +403,24 @@ func handleNumber10dlcCampaignsList(ctx context.Context, cmd *cli.Command) error return err } obj := gjson.ParseBytes(res) - return ShowJSON(os.Stdout, os.Stderr, "number-10dlc:campaigns list", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "number-10dlc:campaigns list", + Transform: transform, + }) } else { iter := client.Number10dlc.Campaigns.ListAutoPaging(ctx, params, options...) maxItems := int64(-1) if cmd.IsSet("max-items") { maxItems = cmd.Value("max-items").(int64) } - return ShowJSONIterator(os.Stdout, os.Stderr, "number-10dlc:campaigns list", iter, format, explicitFormat, transform, maxItems) + return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "number-10dlc:campaigns list", + Transform: transform, + }) } } @@ -458,7 +482,12 @@ func handleNumber10dlcCampaignsSubmit(ctx context.Context, cmd *cli.Command) err format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "number-10dlc:campaigns submit", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "number-10dlc:campaigns submit", + Transform: transform, + }) } func handleNumber10dlcCampaignsSyncStatus(ctx context.Context, cmd *cli.Command) error { @@ -494,5 +523,10 @@ func handleNumber10dlcCampaignsSyncStatus(ctx context.Context, cmd *cli.Command) format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "number-10dlc:campaigns sync-status", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "number-10dlc:campaigns sync-status", + Transform: transform, + }) } diff --git a/pkg/cmd/number10dlccampaignphonenumber.go b/pkg/cmd/number10dlccampaignphonenumber.go index eafb5c0..d7ad278 100644 --- a/pkg/cmd/number10dlccampaignphonenumber.go +++ b/pkg/cmd/number10dlccampaignphonenumber.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" @@ -100,7 +99,12 @@ func handleNumber10dlcCampaignsPhoneNumbersList(ctx context.Context, cmd *cli.Co format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "number-10dlc:campaigns:phone-numbers list", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "number-10dlc:campaigns:phone-numbers list", + Transform: transform, + }) } func handleNumber10dlcCampaignsPhoneNumbersAssign(ctx context.Context, cmd *cli.Command) error { @@ -143,7 +147,12 @@ func handleNumber10dlcCampaignsPhoneNumbersAssign(ctx context.Context, cmd *cli. format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "number-10dlc:campaigns:phone-numbers assign", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "number-10dlc:campaigns:phone-numbers assign", + Transform: transform, + }) } func handleNumber10dlcCampaignsPhoneNumbersUnassign(ctx context.Context, cmd *cli.Command) error { diff --git a/pkg/cmd/phonenumber.go b/pkg/cmd/phonenumber.go index 567900c..6303b10 100644 --- a/pkg/cmd/phonenumber.go +++ b/pkg/cmd/phonenumber.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" @@ -203,7 +202,12 @@ func handlePhoneNumbersRetrieve(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "phone-numbers retrieve", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "phone-numbers retrieve", + Transform: transform, + }) } func handlePhoneNumbersUpdate(ctx context.Context, cmd *cli.Command) error { @@ -246,7 +250,12 @@ func handlePhoneNumbersUpdate(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "phone-numbers update", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "phone-numbers update", + Transform: transform, + }) } func handlePhoneNumbersList(ctx context.Context, cmd *cli.Command) error { @@ -281,14 +290,24 @@ func handlePhoneNumbersList(ctx context.Context, cmd *cli.Command) error { return err } obj := gjson.ParseBytes(res) - return ShowJSON(os.Stdout, os.Stderr, "phone-numbers list", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "phone-numbers list", + Transform: transform, + }) } else { iter := client.PhoneNumbers.ListAutoPaging(ctx, params, options...) maxItems := int64(-1) if cmd.IsSet("max-items") { maxItems = cmd.Value("max-items").(int64) } - return ShowJSONIterator(os.Stdout, os.Stderr, "phone-numbers list", iter, format, explicitFormat, transform, maxItems) + return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "phone-numbers list", + Transform: transform, + }) } } @@ -324,7 +343,12 @@ func handlePhoneNumbersPurchase(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "phone-numbers purchase", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "phone-numbers purchase", + Transform: transform, + }) } func handlePhoneNumbersRelease(ctx context.Context, cmd *cli.Command) error { @@ -384,7 +408,12 @@ func handlePhoneNumbersRequirements(ctx context.Context, cmd *cli.Command) error format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "phone-numbers requirements", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "phone-numbers requirements", + Transform: transform, + }) } func handlePhoneNumbersSearchAvailable(ctx context.Context, cmd *cli.Command) error { @@ -419,5 +448,10 @@ func handlePhoneNumbersSearchAvailable(ctx context.Context, cmd *cli.Command) er format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "phone-numbers search-available", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "phone-numbers search-available", + Transform: transform, + }) } diff --git a/pkg/cmd/plan.go b/pkg/cmd/plan.go index ca66381..62dd288 100644 --- a/pkg/cmd/plan.go +++ b/pkg/cmd/plan.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" @@ -53,5 +52,10 @@ func handlePlanRetrieve(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "plan retrieve", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "plan retrieve", + Transform: transform, + }) } diff --git a/pkg/cmd/regulatorydocument.go b/pkg/cmd/regulatorydocument.go index 2d88ac2..90e5c64 100644 --- a/pkg/cmd/regulatorydocument.go +++ b/pkg/cmd/regulatorydocument.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" @@ -144,7 +143,12 @@ func handleRegulatoryDocumentsCreate(ctx context.Context, cmd *cli.Command) erro format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "regulatory-documents create", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "regulatory-documents create", + Transform: transform, + }) } func handleRegulatoryDocumentsRetrieve(ctx context.Context, cmd *cli.Command) error { @@ -180,7 +184,12 @@ func handleRegulatoryDocumentsRetrieve(ctx context.Context, cmd *cli.Command) er format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "regulatory-documents retrieve", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "regulatory-documents retrieve", + Transform: transform, + }) } func handleRegulatoryDocumentsList(ctx context.Context, cmd *cli.Command) error { @@ -215,14 +224,24 @@ func handleRegulatoryDocumentsList(ctx context.Context, cmd *cli.Command) error return err } obj := gjson.ParseBytes(res) - return ShowJSON(os.Stdout, os.Stderr, "regulatory-documents list", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "regulatory-documents list", + Transform: transform, + }) } else { iter := client.RegulatoryDocuments.ListAutoPaging(ctx, params, options...) maxItems := int64(-1) if cmd.IsSet("max-items") { maxItems = cmd.Value("max-items").(int64) } - return ShowJSONIterator(os.Stdout, os.Stderr, "regulatory-documents list", iter, format, explicitFormat, transform, maxItems) + return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "regulatory-documents list", + Transform: transform, + }) } } @@ -281,5 +300,10 @@ func handleRegulatoryDocumentsUploadURL(ctx context.Context, cmd *cli.Command) e format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "regulatory-documents upload-url", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "regulatory-documents upload-url", + Transform: transform, + }) } diff --git a/pkg/cmd/sender.go b/pkg/cmd/sender.go index 8b7f406..8e46aa9 100644 --- a/pkg/cmd/sender.go +++ b/pkg/cmd/sender.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" @@ -273,7 +272,12 @@ func handleSendersCreate(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "senders create", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "senders create", + Transform: transform, + }) } func handleSendersRetrieve(ctx context.Context, cmd *cli.Command) error { @@ -309,7 +313,12 @@ func handleSendersRetrieve(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "senders retrieve", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "senders retrieve", + Transform: transform, + }) } func handleSendersUpdate(ctx context.Context, cmd *cli.Command) error { @@ -352,7 +361,12 @@ func handleSendersUpdate(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "senders update", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "senders update", + Transform: transform, + }) } func handleSendersList(ctx context.Context, cmd *cli.Command) error { @@ -387,14 +401,24 @@ func handleSendersList(ctx context.Context, cmd *cli.Command) error { return err } obj := gjson.ParseBytes(res) - return ShowJSON(os.Stdout, os.Stderr, "senders list", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "senders list", + Transform: transform, + }) } else { iter := client.Senders.ListAutoPaging(ctx, params, options...) maxItems := int64(-1) if cmd.IsSet("max-items") { maxItems = cmd.Value("max-items").(int64) } - return ShowJSONIterator(os.Stdout, os.Stderr, "senders list", iter, format, explicitFormat, transform, maxItems) + return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "senders list", + Transform: transform, + }) } } @@ -456,7 +480,12 @@ func handleSendersGetProfile(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "senders get-profile", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "senders get-profile", + Transform: transform, + }) } func handleSendersRegenerateWebhookSecret(ctx context.Context, cmd *cli.Command) error { @@ -492,7 +521,12 @@ func handleSendersRegenerateWebhookSecret(ctx context.Context, cmd *cli.Command) format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "senders regenerate-webhook-secret", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "senders regenerate-webhook-secret", + Transform: transform, + }) } func handleSendersUpdateProfile(ctx context.Context, cmd *cli.Command) error { @@ -535,7 +569,12 @@ func handleSendersUpdateProfile(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "senders update-profile", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "senders update-profile", + Transform: transform, + }) } func handleSendersUploadProfilePicture(ctx context.Context, cmd *cli.Command) error { @@ -578,5 +617,10 @@ func handleSendersUploadProfilePicture(ctx context.Context, cmd *cli.Command) er format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "senders upload-profile-picture", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "senders upload-profile-picture", + Transform: transform, + }) } diff --git a/pkg/cmd/senderagent.go b/pkg/cmd/senderagent.go index 47dc23c..cdfc3d2 100644 --- a/pkg/cmd/senderagent.go +++ b/pkg/cmd/senderagent.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" @@ -228,7 +227,12 @@ func handleSendersAgentCreate(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "senders:agent create", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "senders:agent create", + Transform: transform, + }) } func handleSendersAgentRetrieve(ctx context.Context, cmd *cli.Command) error { @@ -264,7 +268,12 @@ func handleSendersAgentRetrieve(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "senders:agent retrieve", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "senders:agent retrieve", + Transform: transform, + }) } func handleSendersAgentUpdate(ctx context.Context, cmd *cli.Command) error { @@ -307,7 +316,12 @@ func handleSendersAgentUpdate(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "senders:agent update", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "senders:agent update", + Transform: transform, + }) } func handleSendersAgentDelete(ctx context.Context, cmd *cli.Command) error { @@ -368,5 +382,10 @@ func handleSendersAgentStats(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "senders:agent stats", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "senders:agent stats", + Transform: transform, + }) } diff --git a/pkg/cmd/senderagentexecution.go b/pkg/cmd/senderagentexecution.go index 34c496c..0b95ae2 100644 --- a/pkg/cmd/senderagentexecution.go +++ b/pkg/cmd/senderagentexecution.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" @@ -87,7 +86,12 @@ func handleSendersAgentExecutionsList(ctx context.Context, cmd *cli.Command) err return err } obj := gjson.ParseBytes(res) - return ShowJSON(os.Stdout, os.Stderr, "senders:agent:executions list", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "senders:agent:executions list", + Transform: transform, + }) } else { iter := client.Senders.Agent.Executions.ListAutoPaging( ctx, @@ -99,6 +103,11 @@ func handleSendersAgentExecutionsList(ctx context.Context, cmd *cli.Command) err if cmd.IsSet("max-items") { maxItems = cmd.Value("max-items").(int64) } - return ShowJSONIterator(os.Stdout, os.Stderr, "senders:agent:executions list", iter, format, explicitFormat, transform, maxItems) + return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "senders:agent:executions list", + Transform: transform, + }) } } diff --git a/pkg/cmd/senderagentflow.go b/pkg/cmd/senderagentflow.go index 3d16dea..da82448 100644 --- a/pkg/cmd/senderagentflow.go +++ b/pkg/cmd/senderagentflow.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" @@ -310,7 +309,12 @@ func handleSendersAgentFlowsCreate(ctx context.Context, cmd *cli.Command) error format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "senders:agent:flows create", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "senders:agent:flows create", + Transform: transform, + }) } func handleSendersAgentFlowsRetrieve(ctx context.Context, cmd *cli.Command) error { @@ -355,7 +359,12 @@ func handleSendersAgentFlowsRetrieve(ctx context.Context, cmd *cli.Command) erro format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "senders:agent:flows retrieve", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "senders:agent:flows retrieve", + Transform: transform, + }) } func handleSendersAgentFlowsUpdate(ctx context.Context, cmd *cli.Command) error { @@ -400,7 +409,12 @@ func handleSendersAgentFlowsUpdate(ctx context.Context, cmd *cli.Command) error format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "senders:agent:flows update", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "senders:agent:flows update", + Transform: transform, + }) } func handleSendersAgentFlowsList(ctx context.Context, cmd *cli.Command) error { @@ -443,7 +457,12 @@ func handleSendersAgentFlowsList(ctx context.Context, cmd *cli.Command) error { return err } obj := gjson.ParseBytes(res) - return ShowJSON(os.Stdout, os.Stderr, "senders:agent:flows list", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "senders:agent:flows list", + Transform: transform, + }) } else { iter := client.Senders.Agent.Flows.ListAutoPaging( ctx, @@ -455,7 +474,12 @@ func handleSendersAgentFlowsList(ctx context.Context, cmd *cli.Command) error { if cmd.IsSet("max-items") { maxItems = cmd.Value("max-items").(int64) } - return ShowJSONIterator(os.Stdout, os.Stderr, "senders:agent:flows list", iter, format, explicitFormat, transform, maxItems) + return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "senders:agent:flows list", + Transform: transform, + }) } } @@ -535,5 +559,10 @@ func handleSendersAgentFlowsDuplicate(ctx context.Context, cmd *cli.Command) err format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "senders:agent:flows duplicate", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "senders:agent:flows duplicate", + Transform: transform, + }) } diff --git a/pkg/cmd/senderagentknowledgebase.go b/pkg/cmd/senderagentknowledgebase.go index c77dd7e..8a0b5e2 100644 --- a/pkg/cmd/senderagentknowledgebase.go +++ b/pkg/cmd/senderagentknowledgebase.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" @@ -167,7 +166,12 @@ func handleSendersAgentKnowledgeBasesCreate(ctx context.Context, cmd *cli.Comman format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "senders:agent:knowledge-bases create", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "senders:agent:knowledge-bases create", + Transform: transform, + }) } func handleSendersAgentKnowledgeBasesRetrieve(ctx context.Context, cmd *cli.Command) error { @@ -212,7 +216,12 @@ func handleSendersAgentKnowledgeBasesRetrieve(ctx context.Context, cmd *cli.Comm format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "senders:agent:knowledge-bases retrieve", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "senders:agent:knowledge-bases retrieve", + Transform: transform, + }) } func handleSendersAgentKnowledgeBasesUpdate(ctx context.Context, cmd *cli.Command) error { @@ -257,7 +266,12 @@ func handleSendersAgentKnowledgeBasesUpdate(ctx context.Context, cmd *cli.Comman format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "senders:agent:knowledge-bases update", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "senders:agent:knowledge-bases update", + Transform: transform, + }) } func handleSendersAgentKnowledgeBasesList(ctx context.Context, cmd *cli.Command) error { @@ -300,7 +314,12 @@ func handleSendersAgentKnowledgeBasesList(ctx context.Context, cmd *cli.Command) return err } obj := gjson.ParseBytes(res) - return ShowJSON(os.Stdout, os.Stderr, "senders:agent:knowledge-bases list", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "senders:agent:knowledge-bases list", + Transform: transform, + }) } else { iter := client.Senders.Agent.KnowledgeBases.ListAutoPaging( ctx, @@ -312,7 +331,12 @@ func handleSendersAgentKnowledgeBasesList(ctx context.Context, cmd *cli.Command) if cmd.IsSet("max-items") { maxItems = cmd.Value("max-items").(int64) } - return ShowJSONIterator(os.Stdout, os.Stderr, "senders:agent:knowledge-bases list", iter, format, explicitFormat, transform, maxItems) + return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "senders:agent:knowledge-bases list", + Transform: transform, + }) } } diff --git a/pkg/cmd/senderagentknowledgebasedocument.go b/pkg/cmd/senderagentknowledgebasedocument.go index fd36e43..d864cbb 100644 --- a/pkg/cmd/senderagentknowledgebasedocument.go +++ b/pkg/cmd/senderagentknowledgebasedocument.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" @@ -138,7 +137,12 @@ func handleSendersAgentKnowledgeBasesDocumentsCreate(ctx context.Context, cmd *c format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "senders:agent:knowledge-bases:documents create", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "senders:agent:knowledge-bases:documents create", + Transform: transform, + }) } func handleSendersAgentKnowledgeBasesDocumentsList(ctx context.Context, cmd *cli.Command) error { @@ -183,7 +187,12 @@ func handleSendersAgentKnowledgeBasesDocumentsList(ctx context.Context, cmd *cli return err } obj := gjson.ParseBytes(res) - return ShowJSON(os.Stdout, os.Stderr, "senders:agent:knowledge-bases:documents list", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "senders:agent:knowledge-bases:documents list", + Transform: transform, + }) } else { iter := client.Senders.Agent.KnowledgeBases.Documents.ListAutoPaging( ctx, @@ -195,7 +204,12 @@ func handleSendersAgentKnowledgeBasesDocumentsList(ctx context.Context, cmd *cli if cmd.IsSet("max-items") { maxItems = cmd.Value("max-items").(int64) } - return ShowJSONIterator(os.Stdout, os.Stderr, "senders:agent:knowledge-bases:documents list", iter, format, explicitFormat, transform, maxItems) + return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "senders:agent:knowledge-bases:documents list", + Transform: transform, + }) } } diff --git a/pkg/cmd/senderagenttool.go b/pkg/cmd/senderagenttool.go index 83d6d51..40daf73 100644 --- a/pkg/cmd/senderagenttool.go +++ b/pkg/cmd/senderagenttool.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" @@ -265,7 +264,12 @@ func handleSendersAgentToolsCreate(ctx context.Context, cmd *cli.Command) error format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "senders:agent:tools create", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "senders:agent:tools create", + Transform: transform, + }) } func handleSendersAgentToolsRetrieve(ctx context.Context, cmd *cli.Command) error { @@ -310,7 +314,12 @@ func handleSendersAgentToolsRetrieve(ctx context.Context, cmd *cli.Command) erro format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "senders:agent:tools retrieve", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "senders:agent:tools retrieve", + Transform: transform, + }) } func handleSendersAgentToolsUpdate(ctx context.Context, cmd *cli.Command) error { @@ -355,7 +364,12 @@ func handleSendersAgentToolsUpdate(ctx context.Context, cmd *cli.Command) error format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "senders:agent:tools update", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "senders:agent:tools update", + Transform: transform, + }) } func handleSendersAgentToolsList(ctx context.Context, cmd *cli.Command) error { @@ -398,7 +412,12 @@ func handleSendersAgentToolsList(ctx context.Context, cmd *cli.Command) error { return err } obj := gjson.ParseBytes(res) - return ShowJSON(os.Stdout, os.Stderr, "senders:agent:tools list", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "senders:agent:tools list", + Transform: transform, + }) } else { iter := client.Senders.Agent.Tools.ListAutoPaging( ctx, @@ -410,7 +429,12 @@ func handleSendersAgentToolsList(ctx context.Context, cmd *cli.Command) error { if cmd.IsSet("max-items") { maxItems = cmd.Value("max-items").(int64) } - return ShowJSONIterator(os.Stdout, os.Stderr, "senders:agent:tools list", iter, format, explicitFormat, transform, maxItems) + return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "senders:agent:tools list", + Transform: transform, + }) } } @@ -490,5 +514,10 @@ func handleSendersAgentToolsTest(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "senders:agent:tools test", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "senders:agent:tools test", + Transform: transform, + }) } diff --git a/pkg/cmd/senderwhatsappsync.go b/pkg/cmd/senderwhatsappsync.go index 41fbc80..f900abf 100644 --- a/pkg/cmd/senderwhatsappsync.go +++ b/pkg/cmd/senderwhatsappsync.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" @@ -90,7 +89,12 @@ func handleSendersWhatsappSyncRetrieve(ctx context.Context, cmd *cli.Command) er format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "senders:whatsapp-sync retrieve", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "senders:whatsapp-sync retrieve", + Transform: transform, + }) } func handleSendersWhatsappSyncStartContactsSync(ctx context.Context, cmd *cli.Command) error { @@ -126,7 +130,12 @@ func handleSendersWhatsappSyncStartContactsSync(ctx context.Context, cmd *cli.Co format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "senders:whatsapp-sync start-contacts-sync", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "senders:whatsapp-sync start-contacts-sync", + Transform: transform, + }) } func handleSendersWhatsappSyncStartHistorySync(ctx context.Context, cmd *cli.Command) error { @@ -162,5 +171,10 @@ func handleSendersWhatsappSyncStartHistorySync(ctx context.Context, cmd *cli.Com format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "senders:whatsapp-sync start-history-sync", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "senders:whatsapp-sync start-history-sync", + Transform: transform, + }) } diff --git a/pkg/cmd/subaccount.go b/pkg/cmd/subaccount.go index 2845ba4..9085532 100644 --- a/pkg/cmd/subaccount.go +++ b/pkg/cmd/subaccount.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" @@ -177,7 +176,12 @@ func handleSubAccountsCreate(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "sub-accounts create", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "sub-accounts create", + Transform: transform, + }) } func handleSubAccountsRetrieve(ctx context.Context, cmd *cli.Command) error { @@ -213,7 +217,12 @@ func handleSubAccountsRetrieve(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "sub-accounts retrieve", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "sub-accounts retrieve", + Transform: transform, + }) } func handleSubAccountsUpdate(ctx context.Context, cmd *cli.Command) error { @@ -256,7 +265,12 @@ func handleSubAccountsUpdate(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "sub-accounts update", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "sub-accounts update", + Transform: transform, + }) } func handleSubAccountsList(ctx context.Context, cmd *cli.Command) error { @@ -291,14 +305,24 @@ func handleSubAccountsList(ctx context.Context, cmd *cli.Command) error { return err } obj := gjson.ParseBytes(res) - return ShowJSON(os.Stdout, os.Stderr, "sub-accounts list", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "sub-accounts list", + Transform: transform, + }) } else { iter := client.SubAccounts.ListAutoPaging(ctx, params, options...) maxItems := int64(-1) if cmd.IsSet("max-items") { maxItems = cmd.Value("max-items").(int64) } - return ShowJSONIterator(os.Stdout, os.Stderr, "sub-accounts list", iter, format, explicitFormat, transform, maxItems) + return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "sub-accounts list", + Transform: transform, + }) } } @@ -335,7 +359,12 @@ func handleSubAccountsDeactivate(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "sub-accounts deactivate", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "sub-accounts deactivate", + Transform: transform, + }) } func handleSubAccountsGetBalance(ctx context.Context, cmd *cli.Command) error { @@ -371,5 +400,10 @@ func handleSubAccountsGetBalance(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "sub-accounts get-balance", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "sub-accounts get-balance", + Transform: transform, + }) } diff --git a/pkg/cmd/subaccountapikey.go b/pkg/cmd/subaccountapikey.go index ec7c25d..85bbcf7 100644 --- a/pkg/cmd/subaccountapikey.go +++ b/pkg/cmd/subaccountapikey.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" @@ -116,7 +115,12 @@ func handleSubAccountsAPIKeysCreate(ctx context.Context, cmd *cli.Command) error format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "sub-accounts:api-keys create", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "sub-accounts:api-keys create", + Transform: transform, + }) } func handleSubAccountsAPIKeysList(ctx context.Context, cmd *cli.Command) error { @@ -152,7 +156,12 @@ func handleSubAccountsAPIKeysList(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "sub-accounts:api-keys list", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "sub-accounts:api-keys list", + Transform: transform, + }) } func handleSubAccountsAPIKeysRevoke(ctx context.Context, cmd *cli.Command) error { diff --git a/pkg/cmd/template.go b/pkg/cmd/template.go index da0b817..ddac0bf 100644 --- a/pkg/cmd/template.go +++ b/pkg/cmd/template.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" @@ -239,7 +238,12 @@ func handleTemplatesCreate(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "templates create", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "templates create", + Transform: transform, + }) } func handleTemplatesRetrieve(ctx context.Context, cmd *cli.Command) error { @@ -275,7 +279,12 @@ func handleTemplatesRetrieve(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "templates retrieve", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "templates retrieve", + Transform: transform, + }) } func handleTemplatesList(ctx context.Context, cmd *cli.Command) error { @@ -310,14 +319,24 @@ func handleTemplatesList(ctx context.Context, cmd *cli.Command) error { return err } obj := gjson.ParseBytes(res) - return ShowJSON(os.Stdout, os.Stderr, "templates list", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "templates list", + Transform: transform, + }) } else { iter := client.Templates.ListAutoPaging(ctx, params, options...) maxItems := int64(-1) if cmd.IsSet("max-items") { maxItems = cmd.Value("max-items").(int64) } - return ShowJSONIterator(os.Stdout, os.Stderr, "templates list", iter, format, explicitFormat, transform, maxItems) + return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "templates list", + Transform: transform, + }) } } @@ -386,5 +405,10 @@ func handleTemplatesSubmit(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "templates submit", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "templates submit", + Transform: transform, + }) } diff --git a/pkg/cmd/url.go b/pkg/cmd/url.go index aa1b7e2..57d15b4 100644 --- a/pkg/cmd/url.go +++ b/pkg/cmd/url.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" @@ -105,14 +104,24 @@ func handleURLsListVerified(ctx context.Context, cmd *cli.Command) error { return err } obj := gjson.ParseBytes(res) - return ShowJSON(os.Stdout, os.Stderr, "urls list-verified", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "urls list-verified", + Transform: transform, + }) } else { iter := client.URLs.ListVerifiedAutoPaging(ctx, params, options...) maxItems := int64(-1) if cmd.IsSet("max-items") { maxItems = cmd.Value("max-items").(int64) } - return ShowJSONIterator(os.Stdout, os.Stderr, "urls list-verified", iter, format, explicitFormat, transform, maxItems) + return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "urls list-verified", + Transform: transform, + }) } } @@ -149,7 +158,12 @@ func handleURLsRetrieveDetails(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "urls retrieve-details", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "urls retrieve-details", + Transform: transform, + }) } func handleURLsSubmitForVerification(ctx context.Context, cmd *cli.Command) error { @@ -184,5 +198,10 @@ func handleURLsSubmitForVerification(ctx context.Context, cmd *cli.Command) erro format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "urls submit-for-verification", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "urls submit-for-verification", + Transform: transform, + }) } diff --git a/pkg/cmd/usage.go b/pkg/cmd/usage.go index 2ab6a96..0fd1ebe 100644 --- a/pkg/cmd/usage.go +++ b/pkg/cmd/usage.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/urfave/cli/v3" @@ -53,5 +52,10 @@ func handleUsageRetrieve(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "usage retrieve", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "usage retrieve", + Transform: transform, + }) } From 6126b7ed48909667c2f3b2979332602b7b5b154c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 16 Apr 2026 19:23:36 +0000 Subject: [PATCH 08/56] feat(api): api update --- .stats.yml | 4 ++-- pkg/cmd/message.go | 30 ++++++++++++++++++++++++++++++ pkg/cmd/message_test.go | 14 +++++++++++++- 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/.stats.yml b/.stats.yml index ea1e2c3..ef91d55 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 129 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu%2Fzavudev-8bebbc269d62102c587a3a2ed99f065e0336599697b55d21e30080aa9c160672.yml -openapi_spec_hash: 1e701bc39fb0673ddef95c579da4a54b +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu%2Fzavudev-c8bb1a9fc6c1d9e1aab7143d1da312ca1ec8affb008b288b3feb82374c071e49.yml +openapi_spec_hash: 8c0566a30358b9d8ec8c185e429e6cb1 config_hash: 280cf643b641e9d1b21852c7e2926d54 diff --git a/pkg/cmd/message.go b/pkg/cmd/message.go index 7153845..8030f99 100644 --- a/pkg/cmd/message.go +++ b/pkg/cmd/message.go @@ -207,6 +207,31 @@ var messagesSend = requestflag.WithInnerFlags(cli.Command{ Usage: "Contact cards for contact messages.", InnerField: "contacts", }, + &requestflag.InnerFlag[string]{ + Name: "content.cta-display-text", + Usage: "Button label for cta_url messages.", + InnerField: "ctaDisplayText", + }, + &requestflag.InnerFlag[string]{ + Name: "content.cta-header-media-url", + Usage: "Public URL of the header media when ctaHeaderType is 'image', 'video', or 'document'.", + InnerField: "ctaHeaderMediaUrl", + }, + &requestflag.InnerFlag[string]{ + Name: "content.cta-header-text", + Usage: "Header text when ctaHeaderType is 'text'.", + InnerField: "ctaHeaderText", + }, + &requestflag.InnerFlag[string]{ + Name: "content.cta-header-type", + Usage: "Optional header type for cta_url messages.", + InnerField: "ctaHeaderType", + }, + &requestflag.InnerFlag[string]{ + Name: "content.cta-url", + Usage: "Destination URL opened in the device's default browser when the button is tapped. Used with messageType=cta_url.", + InnerField: "ctaUrl", + }, &requestflag.InnerFlag[string]{ Name: "content.emoji", Usage: "Emoji for reaction messages.", @@ -217,6 +242,11 @@ var messagesSend = requestflag.WithInnerFlags(cli.Command{ Usage: "Filename for documents.", InnerField: "filename", }, + &requestflag.InnerFlag[string]{ + Name: "content.footer-text", + Usage: "Optional footer text for cta_url messages.", + InnerField: "footerText", + }, &requestflag.InnerFlag[float64]{ Name: "content.latitude", Usage: "Latitude for location messages.", diff --git a/pkg/cmd/message_test.go b/pkg/cmd/message_test.go index cb21547..6903962 100644 --- a/pkg/cmd/message_test.go +++ b/pkg/cmd/message_test.go @@ -74,7 +74,7 @@ func TestMessagesSend(t *testing.T) { "--to", "+56912345678", "--attachment", "{filename: invoice.pdf, content: content, content_id: logo, content_type: application/pdf, path: https://example.com}", "--channel", "auto", - "--content", "{buttons: [{id: id, title: title}], contacts: [{name: name, phones: [string]}], emoji: emoji, filename: invoice.pdf, latitude: 0, listButton: listButton, locationAddress: locationAddress, locationName: locationName, longitude: 0, mediaId: mediaId, mediaUrl: https://example.com/image.jpg, mimeType: image/jpeg, reactToMessageId: reactToMessageId, sections: [{rows: [{id: id, title: title, description: description}], title: title}], templateId: templateId, templateVariables: {'1': John, '2': ORD-12345}}", + "--content", "{buttons: [{id: id, title: title}], contacts: [{name: name, phones: [string]}], ctaDisplayText: See Dates, ctaHeaderMediaUrl: https://example.com, ctaHeaderText: ctaHeaderText, ctaHeaderType: text, ctaUrl: https://example.com/schedule, emoji: emoji, filename: invoice.pdf, footerText: Dates subject to change., latitude: 0, listButton: listButton, locationAddress: locationAddress, locationName: locationName, longitude: 0, mediaId: mediaId, mediaUrl: https://example.com/image.jpg, mimeType: image/jpeg, reactToMessageId: reactToMessageId, sections: [{rows: [{id: id, title: title, description: description}], title: title}], templateId: templateId, templateVariables: {'1': John, '2': ORD-12345}}", "--fallback-enabled=true", "--html-body", "htmlBody", "--idempotency-key", "msg_01HZY4ZP7VQY2J3BRW7Z6G0QGE", @@ -106,8 +106,14 @@ func TestMessagesSend(t *testing.T) { "--channel", "auto", "--content.buttons", "[{id: id, title: title}]", "--content.contacts", "[{name: name, phones: [string]}]", + "--content.cta-display-text", "See Dates", + "--content.cta-header-media-url", "https://example.com", + "--content.cta-header-text", "ctaHeaderText", + "--content.cta-header-type", "text", + "--content.cta-url", "https://example.com/schedule", "--content.emoji", "emoji", "--content.filename", "invoice.pdf", + "--content.footer-text", "Dates subject to change.", "--content.latitude", "0", "--content.list-button", "listButton", "--content.location-address", "locationAddress", @@ -152,8 +158,14 @@ func TestMessagesSend(t *testing.T) { " - name: name\n" + " phones:\n" + " - string\n" + + " ctaDisplayText: See Dates\n" + + " ctaHeaderMediaUrl: https://example.com\n" + + " ctaHeaderText: ctaHeaderText\n" + + " ctaHeaderType: text\n" + + " ctaUrl: https://example.com/schedule\n" + " emoji: emoji\n" + " filename: invoice.pdf\n" + + " footerText: Dates subject to change.\n" + " latitude: 0\n" + " listButton: listButton\n" + " locationAddress: locationAddress\n" + From 00f5deb659ce9dca56f617f547d31ef461bb0f7f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 16 Apr 2026 19:51:54 +0000 Subject: [PATCH 09/56] feat(api): api update --- .stats.yml | 4 ++-- pkg/cmd/message.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.stats.yml b/.stats.yml index ef91d55..6e3c3cd 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 129 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu%2Fzavudev-c8bb1a9fc6c1d9e1aab7143d1da312ca1ec8affb008b288b3feb82374c071e49.yml -openapi_spec_hash: 8c0566a30358b9d8ec8c185e429e6cb1 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu%2Fzavudev-77943605e8ccf7c6c70bfe2fcd14e41b45f018747af93fbd3b54c92187fc6d18.yml +openapi_spec_hash: 700792ca051b1bc51d100b7267d9f90b config_hash: 280cf643b641e9d1b21852c7e2926d54 diff --git a/pkg/cmd/message.go b/pkg/cmd/message.go index 8030f99..febc646 100644 --- a/pkg/cmd/message.go +++ b/pkg/cmd/message.go @@ -214,7 +214,7 @@ var messagesSend = requestflag.WithInnerFlags(cli.Command{ }, &requestflag.InnerFlag[string]{ Name: "content.cta-header-media-url", - Usage: "Public URL of the header media when ctaHeaderType is 'image', 'video', or 'document'.", + Usage: "Public HTTPS URL of the header media when ctaHeaderType is 'image', 'video', or 'document'. WhatsApp fetches this URL — it must be publicly reachable and return the declared content type.", InnerField: "ctaHeaderMediaUrl", }, &requestflag.InnerFlag[string]{ @@ -229,7 +229,7 @@ var messagesSend = requestflag.WithInnerFlags(cli.Command{ }, &requestflag.InnerFlag[string]{ Name: "content.cta-url", - Usage: "Destination URL opened in the device's default browser when the button is tapped. Used with messageType=cta_url.", + Usage: "Destination URL opened in the device's default browser when the button is tapped. Used with messageType=cta_url. WhatsApp requires HTTPS in production.", InnerField: "ctaUrl", }, &requestflag.InnerFlag[string]{ From 0c7441d9ef8c2da629a88ba8fc750319ca4e6225 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 07:52:30 +0000 Subject: [PATCH 10/56] chore(ci): support manually triggering release workflow --- .github/workflows/publish-release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 49983f5..3e8ffd2 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -10,6 +10,7 @@ on: push: tags: - "v*" + workflow_dispatch: {} jobs: goreleaser: runs-on: ubuntu-latest From 82faa3cc225b7e931602af05d49a1482d42d0fcd Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 07:54:22 +0000 Subject: [PATCH 11/56] feat(cli): send filename and content type when reading input from files --- pkg/cmd/flagoptions.go | 65 +++++++++++++++++++++++++++++++++++-- pkg/cmd/flagoptions_test.go | 59 ++++++++++++++++++++++++++++++--- 2 files changed, 117 insertions(+), 7 deletions(-) diff --git a/pkg/cmd/flagoptions.go b/pkg/cmd/flagoptions.go index 604f403..d19a1ad 100644 --- a/pkg/cmd/flagoptions.go +++ b/pkg/cmd/flagoptions.go @@ -7,9 +7,11 @@ import ( "fmt" "io" "maps" + "mime" "mime/multipart" "net/http" "os" + "path/filepath" "reflect" "strings" "unicode/utf8" @@ -36,7 +38,14 @@ const ( type FileEmbedStyle int const ( + // EmbedText reads referenced files fully into memory and substitutes the file's contents back into the + // value as a string. Binary files are base64-encoded. Used for JSON request bodies and for headers and + // query parameters, where the file contents need to be serialized inline. EmbedText FileEmbedStyle = iota + + // EmbedIOReader replaces file references with an io.Reader that streams the file's contents. Used for + // `multipart/form-data` and `application/octet-stream` request bodies, where files are uploaded as binary + // parts rather than embedded into a text value. EmbedIOReader ) @@ -142,6 +151,20 @@ func embedFilesValue(v reflect.Value, embedStyle FileEmbedStyle, stdin *onceStdi if s == "" { return v, nil } + if embedStyle == EmbedIOReader { + if isStdinPath(s) { + r, err := stdin.read() + if err != nil { + return v, err + } + return reflect.ValueOf(io.NopCloser(r)), nil + } + upload, err := openFileUpload(s) + if err != nil { + return v, err + } + return reflect.ValueOf(upload), nil + } if isStdinPath(s) { content, err := stdin.readAll() if err != nil { @@ -250,7 +273,7 @@ func embedFilesValue(v reflect.Value, embedStyle FileEmbedStyle, stdin *onceStdi return reflect.ValueOf(io.NopCloser(r)), nil } - file, err := os.Open(filename) + upload, err := openFileUpload(filename) if err != nil { if !expectsFile { // For strings that start with "@" and don't look like a filename, return the string @@ -258,7 +281,7 @@ func embedFilesValue(v reflect.Value, embedStyle FileEmbedStyle, stdin *onceStdi } return v, err } - return reflect.ValueOf(file), nil + return reflect.ValueOf(upload), nil } } return v, nil @@ -492,6 +515,44 @@ func flagOptions( // as a file path without needing the "@" prefix. type FilePathValue string +// fileUpload wraps an io.Reader with filename and content-type metadata for +// use as a multipart form part. The apiform encoder detects the Filename and +// ContentType methods and uses them to populate the Content-Disposition +// filename and the Content-Type header on the part. +type fileUpload struct { + io.Reader // apiform checks for reader and reads its contents during encode + filename string + contentType string +} + +func (f fileUpload) Filename() string { return f.filename } +func (f fileUpload) ContentType() string { return f.contentType } +func (f fileUpload) Close() error { + if c, ok := f.Reader.(io.Closer); ok { + return c.Close() + } + return nil +} + +// openFileUpload opens the file at path and returns a fileUpload whose filename +// is the path's basename and whose content type is derived from the file +// extension (falling back to application/octet-stream when unknown). +func openFileUpload(path string) (fileUpload, error) { + file, err := os.Open(path) + if err != nil { + return fileUpload{}, err + } + contentType := mime.TypeByExtension(filepath.Ext(path)) + if contentType == "" { + contentType = "application/octet-stream" + } + return fileUpload{ + Reader: file, + filename: filepath.Base(path), + contentType: contentType, + }, nil +} + // applyDataAliases rewrites keys in a body map based on flag `DataAliases` metadata. For top-level flags, // `{alias: value}` becomes `{canonical: value}`. For inner flags (those registered under an outer flag // via WithInnerFlags), the alias translation is also applied to the nested map under the outer flag's diff --git a/pkg/cmd/flagoptions_test.go b/pkg/cmd/flagoptions_test.go index 039b9ff..00734ca 100644 --- a/pkg/cmd/flagoptions_test.go +++ b/pkg/cmd/flagoptions_test.go @@ -8,7 +8,6 @@ import ( "strings" "testing" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -31,7 +30,7 @@ func TestIsUTF8TextFile(t *testing.T) { } for _, tt := range tests { - assert.Equal(t, tt.expected, isUTF8TextFile(tt.content)) + require.Equal(t, tt.expected, isUTF8TextFile(tt.content)) } } @@ -226,10 +225,10 @@ func TestEmbedFiles(t *testing.T) { got, err := embedFiles(tt.input, EmbedText, nil) if tt.wantErr { - assert.Error(t, err) + require.Error(t, err) } else { require.NoError(t, err) - assert.Equal(t, tt.want, got) + require.Equal(t, tt.want, got) } }) @@ -238,7 +237,7 @@ func TestEmbedFiles(t *testing.T) { _, err := embedFiles(tt.input, EmbedIOReader, nil) if tt.wantErr { - assert.Error(t, err) + require.Error(t, err) } else { require.NoError(t, err) } @@ -333,6 +332,56 @@ func TestEmbedFilesStdin(t *testing.T) { }) } +// TestEmbedFilesUploadMetadata verifies that EmbedIOReader mode wraps file readers with filename and +// content-type metadata so the multipart encoder populates `Content-Disposition` and `Content-Type` headers. +func TestEmbedFilesUploadMetadata(t *testing.T) { + t.Parallel() + + tmpDir := t.TempDir() + writeTestFile(t, tmpDir, "hello.txt", "hi") + writeTestFile(t, tmpDir, "page.html", "") + writeTestFile(t, tmpDir, "blob.bin", "\x00\x01") + + cases := []struct { + basename string + wantContentType string + }{ + {"hello.txt", "text/plain; charset=utf-8"}, + {"page.html", "text/html; charset=utf-8"}, + {"blob.bin", "application/octet-stream"}, + } + + for _, tc := range cases { + t.Run("AtPrefix_"+tc.basename, func(t *testing.T) { + t.Parallel() + + path := filepath.Join(tmpDir, tc.basename) + withEmbedded, err := embedFiles(map[string]any{"file": "@" + path}, EmbedIOReader, nil) + require.NoError(t, err) + + upload, ok := withEmbedded.(map[string]any)["file"].(fileUpload) + require.True(t, ok, "expected fileUpload, got %T", withEmbedded.(map[string]any)["file"]) + require.Equal(t, tc.basename, upload.Filename()) + require.Equal(t, upload.ContentType(), tc.wantContentType) + require.NoError(t, upload.Close()) + }) + + t.Run("FilePathValue_"+tc.basename, func(t *testing.T) { + t.Parallel() + + path := filepath.Join(tmpDir, tc.basename) + withEmbedded, err := embedFiles(map[string]any{"file": FilePathValue(path)}, EmbedIOReader, nil) + require.NoError(t, err) + + upload, ok := withEmbedded.(map[string]any)["file"].(fileUpload) + require.True(t, ok, "expected fileUpload, got %T", withEmbedded.(map[string]any)["file"]) + require.Equal(t, tc.basename, upload.Filename()) + require.Equal(t, upload.ContentType(), tc.wantContentType) + require.NoError(t, upload.Close()) + }) + } +} + func writeTestFile(t *testing.T, dir, filename, content string) { t.Helper() From d76418669cafbae10aedd4c7e7d42f3fa356cf20 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 07:56:25 +0000 Subject: [PATCH 12/56] feat(cli): add `--raw-output`/`-r` option to print raw (non-JSON) strings --- pkg/cmd/address.go | 4 +++ pkg/cmd/balance.go | 1 + pkg/cmd/broadcast.go | 11 ++++++ pkg/cmd/broadcastcontact.go | 3 ++ pkg/cmd/cmd.go | 5 +++ pkg/cmd/cmdutil.go | 14 +++++--- pkg/cmd/cmdutil_test.go | 37 ++++++++++++++++++--- pkg/cmd/contact.go | 7 ++++ pkg/cmd/contactchannel.go | 3 ++ pkg/cmd/export.go | 4 +++ pkg/cmd/introspect.go | 1 + pkg/cmd/invitation.go | 5 +++ pkg/cmd/message.go | 5 +++ pkg/cmd/number10dlcbrand.go | 8 +++++ pkg/cmd/number10dlccampaign.go | 7 ++++ pkg/cmd/number10dlccampaignphonenumber.go | 2 ++ pkg/cmd/phonenumber.go | 7 ++++ pkg/cmd/plan.go | 1 + pkg/cmd/regulatorydocument.go | 5 +++ pkg/cmd/sender.go | 9 +++++ pkg/cmd/senderagent.go | 4 +++ pkg/cmd/senderagentexecution.go | 2 ++ pkg/cmd/senderagentflow.go | 6 ++++ pkg/cmd/senderagentknowledgebase.go | 5 +++ pkg/cmd/senderagentknowledgebasedocument.go | 3 ++ pkg/cmd/senderagenttool.go | 6 ++++ pkg/cmd/senderwhatsappsync.go | 3 ++ pkg/cmd/subaccount.go | 7 ++++ pkg/cmd/subaccountapikey.go | 2 ++ pkg/cmd/template.go | 5 +++ pkg/cmd/url.go | 4 +++ pkg/cmd/usage.go | 1 + 32 files changed, 179 insertions(+), 8 deletions(-) diff --git a/pkg/cmd/address.go b/pkg/cmd/address.go index dfa4242..7a36f3a 100644 --- a/pkg/cmd/address.go +++ b/pkg/cmd/address.go @@ -150,6 +150,7 @@ func handleAddressesCreate(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "addresses create", Transform: transform, }) @@ -191,6 +192,7 @@ func handleAddressesRetrieve(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "addresses retrieve", Transform: transform, }) @@ -231,6 +233,7 @@ func handleAddressesList(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "addresses list", Transform: transform, }) @@ -243,6 +246,7 @@ func handleAddressesList(ctx context.Context, cmd *cli.Command) error { return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "addresses list", Transform: transform, }) diff --git a/pkg/cmd/balance.go b/pkg/cmd/balance.go index 3c50c9d..97a6480 100644 --- a/pkg/cmd/balance.go +++ b/pkg/cmd/balance.go @@ -55,6 +55,7 @@ func handleBalanceRetrieve(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "balance retrieve", Transform: transform, }) diff --git a/pkg/cmd/broadcast.go b/pkg/cmd/broadcast.go index d377f04..8888960 100644 --- a/pkg/cmd/broadcast.go +++ b/pkg/cmd/broadcast.go @@ -371,6 +371,7 @@ func handleBroadcastsCreate(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "broadcasts create", Transform: transform, }) @@ -412,6 +413,7 @@ func handleBroadcastsRetrieve(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "broadcasts retrieve", Transform: transform, }) @@ -460,6 +462,7 @@ func handleBroadcastsUpdate(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "broadcasts update", Transform: transform, }) @@ -500,6 +503,7 @@ func handleBroadcastsList(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "broadcasts list", Transform: transform, }) @@ -512,6 +516,7 @@ func handleBroadcastsList(ctx context.Context, cmd *cli.Command) error { return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "broadcasts list", Transform: transform, }) @@ -579,6 +584,7 @@ func handleBroadcastsCancel(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "broadcasts cancel", Transform: transform, }) @@ -620,6 +626,7 @@ func handleBroadcastsEscalateReview(ctx context.Context, cmd *cli.Command) error return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "broadcasts escalate-review", Transform: transform, }) @@ -661,6 +668,7 @@ func handleBroadcastsProgress(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "broadcasts progress", Transform: transform, }) @@ -709,6 +717,7 @@ func handleBroadcastsReschedule(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "broadcasts reschedule", Transform: transform, }) @@ -750,6 +759,7 @@ func handleBroadcastsRetryReview(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "broadcasts retry-review", Transform: transform, }) @@ -798,6 +808,7 @@ func handleBroadcastsSend(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "broadcasts send", Transform: transform, }) diff --git a/pkg/cmd/broadcastcontact.go b/pkg/cmd/broadcastcontact.go index b2230fe..a1e010f 100644 --- a/pkg/cmd/broadcastcontact.go +++ b/pkg/cmd/broadcastcontact.go @@ -140,6 +140,7 @@ func handleBroadcastsContactsList(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "broadcasts:contacts list", Transform: transform, }) @@ -157,6 +158,7 @@ func handleBroadcastsContactsList(ctx context.Context, cmd *cli.Command) error { return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "broadcasts:contacts list", Transform: transform, }) @@ -206,6 +208,7 @@ func handleBroadcastsContactsAdd(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "broadcasts:contacts add", Transform: transform, }) diff --git a/pkg/cmd/cmd.go b/pkg/cmd/cmd.go index 93facec..922a4f8 100644 --- a/pkg/cmd/cmd.go +++ b/pkg/cmd/cmd.go @@ -73,6 +73,11 @@ func init() { Name: "transform-error", Usage: "The GJSON transformation for errors.", }, + &cli.BoolFlag{ + Name: "raw-output", + Aliases: []string{"r"}, + Usage: "If the result is a string, print it without JSON quotes. This can be useful for making output transforms talk to non-JSON-based systems.", + }, &requestflag.Flag[string]{ Name: "api-key", Sources: cli.EnvVars("ZAVUDEV_API_KEY"), diff --git a/pkg/cmd/cmdutil.go b/pkg/cmd/cmdutil.go index 76e6cda..7d8497a 100644 --- a/pkg/cmd/cmdutil.go +++ b/pkg/cmd/cmdutil.go @@ -311,16 +311,21 @@ func shouldUseColors(w io.Writer) bool { return isTerminal(w) } -func formatJSON(expectedOutput *os.File, title string, res gjson.Result, format string, transform string) ([]byte, error) { +func formatJSON(expectedOutput *os.File, title string, res gjson.Result, format string, transform string, rawOutput bool) ([]byte, error) { if transform != "" { transformed := res.Get(transform) if transformed.Exists() { res = transformed } } + // Modeled after `jq -r` (`--raw-output`): if the result is a string, print it without JSON quotes so that + // it's easier to pipe into other programs. + if rawOutput && res.Type == gjson.String { + return []byte(res.Str + "\n"), nil + } switch strings.ToLower(format) { case "auto": - return formatJSON(expectedOutput, title, res, "json", "") + return formatJSON(expectedOutput, title, res, "json", "", rawOutput) case "pretty": return []byte(jsonview.RenderJSON(title, res) + "\n"), nil case "json": @@ -360,6 +365,7 @@ const warningExploreNotSupported = "Warning: Output format 'explore' not support type ShowJSONOpts struct { ExplicitFormat bool // true if the user explicitly passed --format Format string // output format (auto, explore, json, jsonl, pretty, raw, yaml) + RawOutput bool // like jq -r: print strings without JSON quotes Stderr io.Writer // stderr for warnings; injectable for testing; defaults to os.Stderr Stdout *os.File // stdout (or pager); injectable for testing; defaults to os.Stdout Title string // display title @@ -402,7 +408,7 @@ func ShowJSON(res gjson.Result, opts ShowJSONOpts) error { } return jsonview.ExploreJSON(opts.Title, res) default: - bytes, err := formatJSON(opts.Stdout, opts.Title, res, opts.Format, opts.Transform) + bytes, err := formatJSON(opts.Stdout, opts.Title, res, opts.Format, opts.Transform, opts.RawOutput) if err != nil { return err } @@ -459,7 +465,7 @@ func ShowJSONIterator[T any](iter jsonview.Iterator[T], itemsToDisplay int64, op } obj = gjson.ParseBytes(jsonData) } - json, err := formatJSON(opts.Stdout, opts.Title, obj, format, opts.Transform) + json, err := formatJSON(opts.Stdout, opts.Title, obj, format, opts.Transform, opts.RawOutput) if err != nil { return err } diff --git a/pkg/cmd/cmdutil_test.go b/pkg/cmd/cmdutil_test.go index e403e39..a232f6b 100644 --- a/pkg/cmd/cmdutil_test.go +++ b/pkg/cmd/cmdutil_test.go @@ -159,7 +159,7 @@ func TestFormatJSON(t *testing.T) { t.Parallel() res := gjson.Parse(`{"id":"abc123","name":"test"}`) - formatted, err := formatJSON(os.Stdout, "test", res, "raw", "id") + formatted, err := formatJSON(os.Stdout, "test", res, "raw", "id", false) require.NoError(t, err) require.Equal(t, `"abc123"`+"\n", string(formatted)) }) @@ -168,7 +168,7 @@ func TestFormatJSON(t *testing.T) { t.Parallel() res := gjson.Parse(`{"id":"abc123","name":"test"}`) - formatted, err := formatJSON(os.Stdout, "test", res, "raw", "") + formatted, err := formatJSON(os.Stdout, "test", res, "raw", "", false) require.NoError(t, err) require.Equal(t, `{"id":"abc123","name":"test"}`+"\n", string(formatted)) }) @@ -177,7 +177,7 @@ func TestFormatJSON(t *testing.T) { t.Parallel() res := gjson.Parse(`{"data":{"items":[1,2,3]}}`) - formatted, err := formatJSON(os.Stdout, "test", res, "raw", "data.items") + formatted, err := formatJSON(os.Stdout, "test", res, "raw", "data.items", false) require.NoError(t, err) require.Equal(t, "[1,2,3]\n", string(formatted)) }) @@ -186,11 +186,40 @@ func TestFormatJSON(t *testing.T) { t.Parallel() res := gjson.Parse(`{"id":"abc123"}`) - formatted, err := formatJSON(os.Stdout, "test", res, "raw", "missing") + formatted, err := formatJSON(os.Stdout, "test", res, "raw", "missing", false) require.NoError(t, err) // Transform path doesn't exist, so original result is returned require.Equal(t, `{"id":"abc123"}`+"\n", string(formatted)) }) + + t.Run("RawOutputString", func(t *testing.T) { + t.Parallel() + + res := gjson.Parse(`{"id":"abc123","name":"test"}`) + formatted, err := formatJSON(os.Stdout, "test", res, "json", "id", true) + require.NoError(t, err) + require.Equal(t, "abc123\n", string(formatted)) + }) + + t.Run("RawOutputNonString", func(t *testing.T) { + t.Parallel() + + // --raw-output has no effect on non-string values + res := gjson.Parse(`{"count":42}`) + formatted, err := formatJSON(os.Stdout, "test", res, "raw", "count", true) + require.NoError(t, err) + require.Equal(t, "42\n", string(formatted)) + }) + + t.Run("RawOutputObject", func(t *testing.T) { + t.Parallel() + + // --raw-output has no effect on objects + res := gjson.Parse(`{"nested":{"a":1}}`) + formatted, err := formatJSON(os.Stdout, "test", res, "raw", "nested", true) + require.NoError(t, err) + require.Equal(t, `{"a":1}`+"\n", string(formatted)) + }) } func TestShowJSONIterator(t *testing.T) { diff --git a/pkg/cmd/contact.go b/pkg/cmd/contact.go index 3ba4fd2..d3dece0 100644 --- a/pkg/cmd/contact.go +++ b/pkg/cmd/contact.go @@ -215,6 +215,7 @@ func handleContactsCreate(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "contacts create", Transform: transform, }) @@ -256,6 +257,7 @@ func handleContactsRetrieve(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "contacts retrieve", Transform: transform, }) @@ -304,6 +306,7 @@ func handleContactsUpdate(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "contacts update", Transform: transform, }) @@ -344,6 +347,7 @@ func handleContactsList(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "contacts list", Transform: transform, }) @@ -356,6 +360,7 @@ func handleContactsList(ctx context.Context, cmd *cli.Command) error { return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "contacts list", Transform: transform, }) @@ -430,6 +435,7 @@ func handleContactsMerge(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "contacts merge", Transform: transform, }) @@ -471,6 +477,7 @@ func handleContactsRetrieveByPhone(ctx context.Context, cmd *cli.Command) error return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "contacts retrieve-by-phone", Transform: transform, }) diff --git a/pkg/cmd/contactchannel.go b/pkg/cmd/contactchannel.go index 8f8009b..89ff700 100644 --- a/pkg/cmd/contactchannel.go +++ b/pkg/cmd/contactchannel.go @@ -169,6 +169,7 @@ func handleContactsChannelsUpdate(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "contacts:channels update", Transform: transform, }) @@ -217,6 +218,7 @@ func handleContactsChannelsAdd(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "contacts:channels add", Transform: transform, }) @@ -301,6 +303,7 @@ func handleContactsChannelsSetPrimary(ctx context.Context, cmd *cli.Command) err return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "contacts:channels set-primary", Transform: transform, }) diff --git a/pkg/cmd/export.go b/pkg/cmd/export.go index 4a5287c..d5eb49e 100644 --- a/pkg/cmd/export.go +++ b/pkg/cmd/export.go @@ -117,6 +117,7 @@ func handleExportsCreate(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "exports create", Transform: transform, }) @@ -158,6 +159,7 @@ func handleExportsRetrieve(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "exports retrieve", Transform: transform, }) @@ -198,6 +200,7 @@ func handleExportsList(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "exports list", Transform: transform, }) @@ -210,6 +213,7 @@ func handleExportsList(ctx context.Context, cmd *cli.Command) error { return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "exports list", Transform: transform, }) diff --git a/pkg/cmd/introspect.go b/pkg/cmd/introspect.go index 380aa72..eddbbe6 100644 --- a/pkg/cmd/introspect.go +++ b/pkg/cmd/introspect.go @@ -64,6 +64,7 @@ func handleIntrospectValidatePhone(ctx context.Context, cmd *cli.Command) error return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "introspect validate-phone", Transform: transform, }) diff --git a/pkg/cmd/invitation.go b/pkg/cmd/invitation.go index 4b01e0b..5f20a47 100644 --- a/pkg/cmd/invitation.go +++ b/pkg/cmd/invitation.go @@ -146,6 +146,7 @@ func handleInvitationsCreate(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "invitations create", Transform: transform, }) @@ -187,6 +188,7 @@ func handleInvitationsRetrieve(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "invitations retrieve", Transform: transform, }) @@ -227,6 +229,7 @@ func handleInvitationsList(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "invitations list", Transform: transform, }) @@ -239,6 +242,7 @@ func handleInvitationsList(ctx context.Context, cmd *cli.Command) error { return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "invitations list", Transform: transform, }) @@ -281,6 +285,7 @@ func handleInvitationsCancel(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "invitations cancel", Transform: transform, }) diff --git a/pkg/cmd/message.go b/pkg/cmd/message.go index febc646..618ed9f 100644 --- a/pkg/cmd/message.go +++ b/pkg/cmd/message.go @@ -346,6 +346,7 @@ func handleMessagesRetrieve(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "messages retrieve", Transform: transform, }) @@ -386,6 +387,7 @@ func handleMessagesList(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "messages list", Transform: transform, }) @@ -398,6 +400,7 @@ func handleMessagesList(ctx context.Context, cmd *cli.Command) error { return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "messages list", Transform: transform, }) @@ -447,6 +450,7 @@ func handleMessagesReact(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "messages react", Transform: transform, }) @@ -487,6 +491,7 @@ func handleMessagesSend(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "messages send", Transform: transform, }) diff --git a/pkg/cmd/number10dlcbrand.go b/pkg/cmd/number10dlcbrand.go index 2f574f3..4ebd5f9 100644 --- a/pkg/cmd/number10dlcbrand.go +++ b/pkg/cmd/number10dlcbrand.go @@ -315,6 +315,7 @@ func handleNumber10dlcBrandsCreate(ctx context.Context, cmd *cli.Command) error return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "number-10dlc:brands create", Transform: transform, }) @@ -356,6 +357,7 @@ func handleNumber10dlcBrandsRetrieve(ctx context.Context, cmd *cli.Command) erro return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "number-10dlc:brands retrieve", Transform: transform, }) @@ -404,6 +406,7 @@ func handleNumber10dlcBrandsUpdate(ctx context.Context, cmd *cli.Command) error return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "number-10dlc:brands update", Transform: transform, }) @@ -444,6 +447,7 @@ func handleNumber10dlcBrandsList(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "number-10dlc:brands list", Transform: transform, }) @@ -456,6 +460,7 @@ func handleNumber10dlcBrandsList(ctx context.Context, cmd *cli.Command) error { return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "number-10dlc:brands list", Transform: transform, }) @@ -520,6 +525,7 @@ func handleNumber10dlcBrandsListUseCases(ctx context.Context, cmd *cli.Command) return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "number-10dlc:brands list-use-cases", Transform: transform, }) @@ -561,6 +567,7 @@ func handleNumber10dlcBrandsSubmit(ctx context.Context, cmd *cli.Command) error return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "number-10dlc:brands submit", Transform: transform, }) @@ -602,6 +609,7 @@ func handleNumber10dlcBrandsSyncStatus(ctx context.Context, cmd *cli.Command) er return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "number-10dlc:brands sync-status", Transform: transform, }) diff --git a/pkg/cmd/number10dlccampaign.go b/pkg/cmd/number10dlccampaign.go index 9d6634c..f29eb88 100644 --- a/pkg/cmd/number10dlccampaign.go +++ b/pkg/cmd/number10dlccampaign.go @@ -277,6 +277,7 @@ func handleNumber10dlcCampaignsCreate(ctx context.Context, cmd *cli.Command) err return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "number-10dlc:campaigns create", Transform: transform, }) @@ -318,6 +319,7 @@ func handleNumber10dlcCampaignsRetrieve(ctx context.Context, cmd *cli.Command) e return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "number-10dlc:campaigns retrieve", Transform: transform, }) @@ -366,6 +368,7 @@ func handleNumber10dlcCampaignsUpdate(ctx context.Context, cmd *cli.Command) err return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "number-10dlc:campaigns update", Transform: transform, }) @@ -406,6 +409,7 @@ func handleNumber10dlcCampaignsList(ctx context.Context, cmd *cli.Command) error return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "number-10dlc:campaigns list", Transform: transform, }) @@ -418,6 +422,7 @@ func handleNumber10dlcCampaignsList(ctx context.Context, cmd *cli.Command) error return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "number-10dlc:campaigns list", Transform: transform, }) @@ -485,6 +490,7 @@ func handleNumber10dlcCampaignsSubmit(ctx context.Context, cmd *cli.Command) err return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "number-10dlc:campaigns submit", Transform: transform, }) @@ -526,6 +532,7 @@ func handleNumber10dlcCampaignsSyncStatus(ctx context.Context, cmd *cli.Command) return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "number-10dlc:campaigns sync-status", Transform: transform, }) diff --git a/pkg/cmd/number10dlccampaignphonenumber.go b/pkg/cmd/number10dlccampaignphonenumber.go index d7ad278..c287481 100644 --- a/pkg/cmd/number10dlccampaignphonenumber.go +++ b/pkg/cmd/number10dlccampaignphonenumber.go @@ -102,6 +102,7 @@ func handleNumber10dlcCampaignsPhoneNumbersList(ctx context.Context, cmd *cli.Co return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "number-10dlc:campaigns:phone-numbers list", Transform: transform, }) @@ -150,6 +151,7 @@ func handleNumber10dlcCampaignsPhoneNumbersAssign(ctx context.Context, cmd *cli. return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "number-10dlc:campaigns:phone-numbers assign", Transform: transform, }) diff --git a/pkg/cmd/phonenumber.go b/pkg/cmd/phonenumber.go index 6303b10..58b0c9e 100644 --- a/pkg/cmd/phonenumber.go +++ b/pkg/cmd/phonenumber.go @@ -205,6 +205,7 @@ func handlePhoneNumbersRetrieve(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "phone-numbers retrieve", Transform: transform, }) @@ -253,6 +254,7 @@ func handlePhoneNumbersUpdate(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "phone-numbers update", Transform: transform, }) @@ -293,6 +295,7 @@ func handlePhoneNumbersList(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "phone-numbers list", Transform: transform, }) @@ -305,6 +308,7 @@ func handlePhoneNumbersList(ctx context.Context, cmd *cli.Command) error { return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "phone-numbers list", Transform: transform, }) @@ -346,6 +350,7 @@ func handlePhoneNumbersPurchase(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "phone-numbers purchase", Transform: transform, }) @@ -411,6 +416,7 @@ func handlePhoneNumbersRequirements(ctx context.Context, cmd *cli.Command) error return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "phone-numbers requirements", Transform: transform, }) @@ -451,6 +457,7 @@ func handlePhoneNumbersSearchAvailable(ctx context.Context, cmd *cli.Command) er return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "phone-numbers search-available", Transform: transform, }) diff --git a/pkg/cmd/plan.go b/pkg/cmd/plan.go index 62dd288..d76c5d7 100644 --- a/pkg/cmd/plan.go +++ b/pkg/cmd/plan.go @@ -55,6 +55,7 @@ func handlePlanRetrieve(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "plan retrieve", Transform: transform, }) diff --git a/pkg/cmd/regulatorydocument.go b/pkg/cmd/regulatorydocument.go index 90e5c64..e0310bb 100644 --- a/pkg/cmd/regulatorydocument.go +++ b/pkg/cmd/regulatorydocument.go @@ -146,6 +146,7 @@ func handleRegulatoryDocumentsCreate(ctx context.Context, cmd *cli.Command) erro return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "regulatory-documents create", Transform: transform, }) @@ -187,6 +188,7 @@ func handleRegulatoryDocumentsRetrieve(ctx context.Context, cmd *cli.Command) er return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "regulatory-documents retrieve", Transform: transform, }) @@ -227,6 +229,7 @@ func handleRegulatoryDocumentsList(ctx context.Context, cmd *cli.Command) error return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "regulatory-documents list", Transform: transform, }) @@ -239,6 +242,7 @@ func handleRegulatoryDocumentsList(ctx context.Context, cmd *cli.Command) error return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "regulatory-documents list", Transform: transform, }) @@ -303,6 +307,7 @@ func handleRegulatoryDocumentsUploadURL(ctx context.Context, cmd *cli.Command) e return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "regulatory-documents upload-url", Transform: transform, }) diff --git a/pkg/cmd/sender.go b/pkg/cmd/sender.go index 8e46aa9..935d471 100644 --- a/pkg/cmd/sender.go +++ b/pkg/cmd/sender.go @@ -275,6 +275,7 @@ func handleSendersCreate(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "senders create", Transform: transform, }) @@ -316,6 +317,7 @@ func handleSendersRetrieve(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "senders retrieve", Transform: transform, }) @@ -364,6 +366,7 @@ func handleSendersUpdate(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "senders update", Transform: transform, }) @@ -404,6 +407,7 @@ func handleSendersList(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "senders list", Transform: transform, }) @@ -416,6 +420,7 @@ func handleSendersList(ctx context.Context, cmd *cli.Command) error { return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "senders list", Transform: transform, }) @@ -483,6 +488,7 @@ func handleSendersGetProfile(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "senders get-profile", Transform: transform, }) @@ -524,6 +530,7 @@ func handleSendersRegenerateWebhookSecret(ctx context.Context, cmd *cli.Command) return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "senders regenerate-webhook-secret", Transform: transform, }) @@ -572,6 +579,7 @@ func handleSendersUpdateProfile(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "senders update-profile", Transform: transform, }) @@ -620,6 +628,7 @@ func handleSendersUploadProfilePicture(ctx context.Context, cmd *cli.Command) er return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "senders upload-profile-picture", Transform: transform, }) diff --git a/pkg/cmd/senderagent.go b/pkg/cmd/senderagent.go index cdfc3d2..72b5d9d 100644 --- a/pkg/cmd/senderagent.go +++ b/pkg/cmd/senderagent.go @@ -230,6 +230,7 @@ func handleSendersAgentCreate(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "senders:agent create", Transform: transform, }) @@ -271,6 +272,7 @@ func handleSendersAgentRetrieve(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "senders:agent retrieve", Transform: transform, }) @@ -319,6 +321,7 @@ func handleSendersAgentUpdate(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "senders:agent update", Transform: transform, }) @@ -385,6 +388,7 @@ func handleSendersAgentStats(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "senders:agent stats", Transform: transform, }) diff --git a/pkg/cmd/senderagentexecution.go b/pkg/cmd/senderagentexecution.go index 0b95ae2..0a4d347 100644 --- a/pkg/cmd/senderagentexecution.go +++ b/pkg/cmd/senderagentexecution.go @@ -89,6 +89,7 @@ func handleSendersAgentExecutionsList(ctx context.Context, cmd *cli.Command) err return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "senders:agent:executions list", Transform: transform, }) @@ -106,6 +107,7 @@ func handleSendersAgentExecutionsList(ctx context.Context, cmd *cli.Command) err return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "senders:agent:executions list", Transform: transform, }) diff --git a/pkg/cmd/senderagentflow.go b/pkg/cmd/senderagentflow.go index da82448..a200fea 100644 --- a/pkg/cmd/senderagentflow.go +++ b/pkg/cmd/senderagentflow.go @@ -312,6 +312,7 @@ func handleSendersAgentFlowsCreate(ctx context.Context, cmd *cli.Command) error return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "senders:agent:flows create", Transform: transform, }) @@ -362,6 +363,7 @@ func handleSendersAgentFlowsRetrieve(ctx context.Context, cmd *cli.Command) erro return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "senders:agent:flows retrieve", Transform: transform, }) @@ -412,6 +414,7 @@ func handleSendersAgentFlowsUpdate(ctx context.Context, cmd *cli.Command) error return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "senders:agent:flows update", Transform: transform, }) @@ -460,6 +463,7 @@ func handleSendersAgentFlowsList(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "senders:agent:flows list", Transform: transform, }) @@ -477,6 +481,7 @@ func handleSendersAgentFlowsList(ctx context.Context, cmd *cli.Command) error { return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "senders:agent:flows list", Transform: transform, }) @@ -562,6 +567,7 @@ func handleSendersAgentFlowsDuplicate(ctx context.Context, cmd *cli.Command) err return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "senders:agent:flows duplicate", Transform: transform, }) diff --git a/pkg/cmd/senderagentknowledgebase.go b/pkg/cmd/senderagentknowledgebase.go index 8a0b5e2..82cb6f7 100644 --- a/pkg/cmd/senderagentknowledgebase.go +++ b/pkg/cmd/senderagentknowledgebase.go @@ -169,6 +169,7 @@ func handleSendersAgentKnowledgeBasesCreate(ctx context.Context, cmd *cli.Comman return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "senders:agent:knowledge-bases create", Transform: transform, }) @@ -219,6 +220,7 @@ func handleSendersAgentKnowledgeBasesRetrieve(ctx context.Context, cmd *cli.Comm return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "senders:agent:knowledge-bases retrieve", Transform: transform, }) @@ -269,6 +271,7 @@ func handleSendersAgentKnowledgeBasesUpdate(ctx context.Context, cmd *cli.Comman return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "senders:agent:knowledge-bases update", Transform: transform, }) @@ -317,6 +320,7 @@ func handleSendersAgentKnowledgeBasesList(ctx context.Context, cmd *cli.Command) return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "senders:agent:knowledge-bases list", Transform: transform, }) @@ -334,6 +338,7 @@ func handleSendersAgentKnowledgeBasesList(ctx context.Context, cmd *cli.Command) return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "senders:agent:knowledge-bases list", Transform: transform, }) diff --git a/pkg/cmd/senderagentknowledgebasedocument.go b/pkg/cmd/senderagentknowledgebasedocument.go index d864cbb..5275a5a 100644 --- a/pkg/cmd/senderagentknowledgebasedocument.go +++ b/pkg/cmd/senderagentknowledgebasedocument.go @@ -140,6 +140,7 @@ func handleSendersAgentKnowledgeBasesDocumentsCreate(ctx context.Context, cmd *c return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "senders:agent:knowledge-bases:documents create", Transform: transform, }) @@ -190,6 +191,7 @@ func handleSendersAgentKnowledgeBasesDocumentsList(ctx context.Context, cmd *cli return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "senders:agent:knowledge-bases:documents list", Transform: transform, }) @@ -207,6 +209,7 @@ func handleSendersAgentKnowledgeBasesDocumentsList(ctx context.Context, cmd *cli return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "senders:agent:knowledge-bases:documents list", Transform: transform, }) diff --git a/pkg/cmd/senderagenttool.go b/pkg/cmd/senderagenttool.go index 40daf73..15bcd03 100644 --- a/pkg/cmd/senderagenttool.go +++ b/pkg/cmd/senderagenttool.go @@ -267,6 +267,7 @@ func handleSendersAgentToolsCreate(ctx context.Context, cmd *cli.Command) error return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "senders:agent:tools create", Transform: transform, }) @@ -317,6 +318,7 @@ func handleSendersAgentToolsRetrieve(ctx context.Context, cmd *cli.Command) erro return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "senders:agent:tools retrieve", Transform: transform, }) @@ -367,6 +369,7 @@ func handleSendersAgentToolsUpdate(ctx context.Context, cmd *cli.Command) error return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "senders:agent:tools update", Transform: transform, }) @@ -415,6 +418,7 @@ func handleSendersAgentToolsList(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "senders:agent:tools list", Transform: transform, }) @@ -432,6 +436,7 @@ func handleSendersAgentToolsList(ctx context.Context, cmd *cli.Command) error { return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "senders:agent:tools list", Transform: transform, }) @@ -517,6 +522,7 @@ func handleSendersAgentToolsTest(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "senders:agent:tools test", Transform: transform, }) diff --git a/pkg/cmd/senderwhatsappsync.go b/pkg/cmd/senderwhatsappsync.go index f900abf..a389ac0 100644 --- a/pkg/cmd/senderwhatsappsync.go +++ b/pkg/cmd/senderwhatsappsync.go @@ -92,6 +92,7 @@ func handleSendersWhatsappSyncRetrieve(ctx context.Context, cmd *cli.Command) er return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "senders:whatsapp-sync retrieve", Transform: transform, }) @@ -133,6 +134,7 @@ func handleSendersWhatsappSyncStartContactsSync(ctx context.Context, cmd *cli.Co return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "senders:whatsapp-sync start-contacts-sync", Transform: transform, }) @@ -174,6 +176,7 @@ func handleSendersWhatsappSyncStartHistorySync(ctx context.Context, cmd *cli.Com return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "senders:whatsapp-sync start-history-sync", Transform: transform, }) diff --git a/pkg/cmd/subaccount.go b/pkg/cmd/subaccount.go index 9085532..07b03bb 100644 --- a/pkg/cmd/subaccount.go +++ b/pkg/cmd/subaccount.go @@ -179,6 +179,7 @@ func handleSubAccountsCreate(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "sub-accounts create", Transform: transform, }) @@ -220,6 +221,7 @@ func handleSubAccountsRetrieve(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "sub-accounts retrieve", Transform: transform, }) @@ -268,6 +270,7 @@ func handleSubAccountsUpdate(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "sub-accounts update", Transform: transform, }) @@ -308,6 +311,7 @@ func handleSubAccountsList(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "sub-accounts list", Transform: transform, }) @@ -320,6 +324,7 @@ func handleSubAccountsList(ctx context.Context, cmd *cli.Command) error { return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "sub-accounts list", Transform: transform, }) @@ -362,6 +367,7 @@ func handleSubAccountsDeactivate(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "sub-accounts deactivate", Transform: transform, }) @@ -403,6 +409,7 @@ func handleSubAccountsGetBalance(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "sub-accounts get-balance", Transform: transform, }) diff --git a/pkg/cmd/subaccountapikey.go b/pkg/cmd/subaccountapikey.go index 85bbcf7..9b1e727 100644 --- a/pkg/cmd/subaccountapikey.go +++ b/pkg/cmd/subaccountapikey.go @@ -118,6 +118,7 @@ func handleSubAccountsAPIKeysCreate(ctx context.Context, cmd *cli.Command) error return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "sub-accounts:api-keys create", Transform: transform, }) @@ -159,6 +160,7 @@ func handleSubAccountsAPIKeysList(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "sub-accounts:api-keys list", Transform: transform, }) diff --git a/pkg/cmd/template.go b/pkg/cmd/template.go index ddac0bf..1942d7e 100644 --- a/pkg/cmd/template.go +++ b/pkg/cmd/template.go @@ -241,6 +241,7 @@ func handleTemplatesCreate(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "templates create", Transform: transform, }) @@ -282,6 +283,7 @@ func handleTemplatesRetrieve(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "templates retrieve", Transform: transform, }) @@ -322,6 +324,7 @@ func handleTemplatesList(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "templates list", Transform: transform, }) @@ -334,6 +337,7 @@ func handleTemplatesList(ctx context.Context, cmd *cli.Command) error { return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "templates list", Transform: transform, }) @@ -408,6 +412,7 @@ func handleTemplatesSubmit(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "templates submit", Transform: transform, }) diff --git a/pkg/cmd/url.go b/pkg/cmd/url.go index 57d15b4..48c4232 100644 --- a/pkg/cmd/url.go +++ b/pkg/cmd/url.go @@ -107,6 +107,7 @@ func handleURLsListVerified(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "urls list-verified", Transform: transform, }) @@ -119,6 +120,7 @@ func handleURLsListVerified(ctx context.Context, cmd *cli.Command) error { return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "urls list-verified", Transform: transform, }) @@ -161,6 +163,7 @@ func handleURLsRetrieveDetails(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "urls retrieve-details", Transform: transform, }) @@ -201,6 +204,7 @@ func handleURLsSubmitForVerification(ctx context.Context, cmd *cli.Command) erro return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "urls submit-for-verification", Transform: transform, }) diff --git a/pkg/cmd/usage.go b/pkg/cmd/usage.go index 0fd1ebe..d6b584b 100644 --- a/pkg/cmd/usage.go +++ b/pkg/cmd/usage.go @@ -55,6 +55,7 @@ func handleUsageRetrieve(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "usage retrieve", Transform: transform, }) From c0c62210622f72686086063f315649eb8bc35ec4 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 18 Apr 2026 08:11:47 +0000 Subject: [PATCH 13/56] chore(cli): use `ShowJSONOpts` as argument to `formatJSON` instead of many positionals --- pkg/cmd/cmdutil.go | 54 ++++++++++++++++++++--------------------- pkg/cmd/cmdutil_test.go | 14 +++++------ 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/pkg/cmd/cmdutil.go b/pkg/cmd/cmdutil.go index 7d8497a..251d3a1 100644 --- a/pkg/cmd/cmdutil.go +++ b/pkg/cmd/cmdutil.go @@ -311,26 +311,29 @@ func shouldUseColors(w io.Writer) bool { return isTerminal(w) } -func formatJSON(expectedOutput *os.File, title string, res gjson.Result, format string, transform string, rawOutput bool) ([]byte, error) { - if transform != "" { - transformed := res.Get(transform) +func formatJSON(res gjson.Result, opts ShowJSONOpts) ([]byte, error) { + if opts.Transform != "" { + transformed := res.Get(opts.Transform) if transformed.Exists() { res = transformed } } // Modeled after `jq -r` (`--raw-output`): if the result is a string, print it without JSON quotes so that // it's easier to pipe into other programs. - if rawOutput && res.Type == gjson.String { + if opts.RawOutput && res.Type == gjson.String { return []byte(res.Str + "\n"), nil } - switch strings.ToLower(format) { + switch strings.ToLower(opts.Format) { case "auto": - return formatJSON(expectedOutput, title, res, "json", "", rawOutput) + autoOpts := opts + autoOpts.Format = "json" + autoOpts.Transform = "" + return formatJSON(res, autoOpts) case "pretty": - return []byte(jsonview.RenderJSON(title, res) + "\n"), nil + return []byte(jsonview.RenderJSON(opts.Title, res) + "\n"), nil case "json": prettyJSON := pretty.Pretty([]byte(res.Raw)) - if shouldUseColors(expectedOutput) { + if shouldUseColors(opts.Stdout) { return pretty.Color(prettyJSON, pretty.TerminalStyle), nil } else { return prettyJSON, nil @@ -338,7 +341,7 @@ func formatJSON(expectedOutput *os.File, title string, res gjson.Result, format case "jsonl": // @ugly is gjson syntax for "no whitespace", so it fits on one line oneLineJSON := res.Get("@ugly").Raw - if shouldUseColors(expectedOutput) { + if shouldUseColors(opts.Stdout) { bytes := append(pretty.Color([]byte(oneLineJSON), pretty.TerminalStyle), '\n') return bytes, nil } else { @@ -352,10 +355,10 @@ func formatJSON(expectedOutput *os.File, title string, res gjson.Result, format if err := json2yaml.Convert(&yaml, input); err != nil { return nil, err } - _, err := expectedOutput.Write([]byte(yaml.String())) + _, err := opts.Stdout.Write([]byte(yaml.String())) return nil, err default: - return nil, fmt.Errorf("Invalid format: %s, valid formats are: %s", format, strings.Join(OutputFormats, ", ")) + return nil, fmt.Errorf("Invalid format: %s, valid formats are: %s", opts.Format, strings.Join(OutputFormats, ", ")) } } @@ -385,18 +388,11 @@ func (o *ShowJSONOpts) setDefaults() { func ShowJSON(res gjson.Result, opts ShowJSONOpts) error { opts.setDefaults() - if opts.Transform != "" { - transformed := res.Get(opts.Transform) - if transformed.Exists() { - res = transformed - } - } - switch strings.ToLower(opts.Format) { case "auto": - jsonOpts := opts - jsonOpts.Format = "json" - return ShowJSON(res, jsonOpts) + autoOpts := opts + autoOpts.Format = "json" + return ShowJSON(res, autoOpts) case "explore": if !isTerminal(opts.Stdout) { if opts.ExplicitFormat { @@ -406,9 +402,15 @@ func ShowJSON(res gjson.Result, opts ShowJSONOpts) error { jsonOpts.Format = "json" return ShowJSON(res, jsonOpts) } + if opts.Transform != "" { + transformed := res.Get(opts.Transform) + if transformed.Exists() { + res = transformed + } + } return jsonview.ExploreJSON(opts.Title, res) default: - bytes, err := formatJSON(opts.Stdout, opts.Title, res, opts.Format, opts.Transform, opts.RawOutput) + bytes, err := formatJSON(res, opts) if err != nil { return err } @@ -431,15 +433,14 @@ type hasRawJSON interface { func ShowJSONIterator[T any](iter jsonview.Iterator[T], itemsToDisplay int64, opts ShowJSONOpts) error { opts.setDefaults() - format := opts.Format - if format == "explore" { + if opts.Format == "explore" { if isTerminal(opts.Stdout) { return jsonview.ExploreJSONStream(opts.Title, iter) } if opts.ExplicitFormat { fmt.Fprint(opts.Stderr, warningExploreNotSupported) } - format = "json" + opts.Format = "json" } terminalWidth, terminalHeight, err := term.GetSize(os.Stdout.Fd()) @@ -465,7 +466,7 @@ func ShowJSONIterator[T any](iter jsonview.Iterator[T], itemsToDisplay int64, op } obj = gjson.ParseBytes(jsonData) } - json, err := formatJSON(opts.Stdout, opts.Title, obj, format, opts.Transform, opts.RawOutput) + json, err := formatJSON(obj, opts) if err != nil { return err } @@ -497,7 +498,6 @@ func ShowJSONIterator[T any](iter jsonview.Iterator[T], itemsToDisplay int64, op } pagerOpts := opts - pagerOpts.Format = format pagerOpts.Stdout = pager for iter.Next() { diff --git a/pkg/cmd/cmdutil_test.go b/pkg/cmd/cmdutil_test.go index a232f6b..518985b 100644 --- a/pkg/cmd/cmdutil_test.go +++ b/pkg/cmd/cmdutil_test.go @@ -159,7 +159,7 @@ func TestFormatJSON(t *testing.T) { t.Parallel() res := gjson.Parse(`{"id":"abc123","name":"test"}`) - formatted, err := formatJSON(os.Stdout, "test", res, "raw", "id", false) + formatted, err := formatJSON(res, ShowJSONOpts{Format: "raw", Stdout: os.Stdout, Transform: "id"}) require.NoError(t, err) require.Equal(t, `"abc123"`+"\n", string(formatted)) }) @@ -168,7 +168,7 @@ func TestFormatJSON(t *testing.T) { t.Parallel() res := gjson.Parse(`{"id":"abc123","name":"test"}`) - formatted, err := formatJSON(os.Stdout, "test", res, "raw", "", false) + formatted, err := formatJSON(res, ShowJSONOpts{Format: "raw", Stdout: os.Stdout}) require.NoError(t, err) require.Equal(t, `{"id":"abc123","name":"test"}`+"\n", string(formatted)) }) @@ -177,7 +177,7 @@ func TestFormatJSON(t *testing.T) { t.Parallel() res := gjson.Parse(`{"data":{"items":[1,2,3]}}`) - formatted, err := formatJSON(os.Stdout, "test", res, "raw", "data.items", false) + formatted, err := formatJSON(res, ShowJSONOpts{Format: "raw", Stdout: os.Stdout, Transform: "data.items"}) require.NoError(t, err) require.Equal(t, "[1,2,3]\n", string(formatted)) }) @@ -186,7 +186,7 @@ func TestFormatJSON(t *testing.T) { t.Parallel() res := gjson.Parse(`{"id":"abc123"}`) - formatted, err := formatJSON(os.Stdout, "test", res, "raw", "missing", false) + formatted, err := formatJSON(res, ShowJSONOpts{Format: "raw", Stdout: os.Stdout, Transform: "missing"}) require.NoError(t, err) // Transform path doesn't exist, so original result is returned require.Equal(t, `{"id":"abc123"}`+"\n", string(formatted)) @@ -196,7 +196,7 @@ func TestFormatJSON(t *testing.T) { t.Parallel() res := gjson.Parse(`{"id":"abc123","name":"test"}`) - formatted, err := formatJSON(os.Stdout, "test", res, "json", "id", true) + formatted, err := formatJSON(res, ShowJSONOpts{Format: "json", Stdout: os.Stdout, Transform: "id", RawOutput: true}) require.NoError(t, err) require.Equal(t, "abc123\n", string(formatted)) }) @@ -206,7 +206,7 @@ func TestFormatJSON(t *testing.T) { // --raw-output has no effect on non-string values res := gjson.Parse(`{"count":42}`) - formatted, err := formatJSON(os.Stdout, "test", res, "raw", "count", true) + formatted, err := formatJSON(res, ShowJSONOpts{Format: "raw", Stdout: os.Stdout, Transform: "count", RawOutput: true}) require.NoError(t, err) require.Equal(t, "42\n", string(formatted)) }) @@ -216,7 +216,7 @@ func TestFormatJSON(t *testing.T) { // --raw-output has no effect on objects res := gjson.Parse(`{"nested":{"a":1}}`) - formatted, err := formatJSON(os.Stdout, "test", res, "raw", "nested", true) + formatted, err := formatJSON(res, ShowJSONOpts{Format: "raw", Stdout: os.Stdout, Transform: "nested", RawOutput: true}) require.NoError(t, err) require.Equal(t, `{"a":1}`+"\n", string(formatted)) }) From 873650f94169cc8ee552d5dab9e426b6b1007fa8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 23 Apr 2026 04:05:37 +0000 Subject: [PATCH 14/56] chore(internal): more robust bootstrap script --- scripts/bootstrap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/bootstrap b/scripts/bootstrap index 9ebb7d3..bbc786d 100755 --- a/scripts/bootstrap +++ b/scripts/bootstrap @@ -4,7 +4,7 @@ set -e cd "$(dirname "$0")/.." -if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "$SKIP_BREW" != "1" ] && [ -t 0 ]; then +if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "${SKIP_BREW:-}" != "1" ] && [ -t 0 ]; then brew bundle check >/dev/null 2>&1 || { echo -n "==> Install Homebrew dependencies? (y/N): " read -r response From ace00816df72353a38c8d9427988c61b21631528 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 27 Apr 2026 19:27:37 +0000 Subject: [PATCH 15/56] feat(api): api update --- .stats.yml | 2 +- pkg/cmd/broadcast.go | 14 ++++++++++++-- pkg/cmd/broadcast_test.go | 10 ++++++++-- pkg/cmd/broadcastcontact.go | 7 ++++++- pkg/cmd/broadcastcontact_test.go | 10 ++++++++-- pkg/cmd/message.go | 7 ++++++- pkg/cmd/message_test.go | 5 ++++- 7 files changed, 45 insertions(+), 10 deletions(-) diff --git a/.stats.yml b/.stats.yml index 6e3c3cd..e5ef4fe 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 129 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu%2Fzavudev-77943605e8ccf7c6c70bfe2fcd14e41b45f018747af93fbd3b54c92187fc6d18.yml -openapi_spec_hash: 700792ca051b1bc51d100b7267d9f90b +openapi_spec_hash: d120508439dfb8de2fb579ebc9543893 config_hash: 280cf643b641e9d1b21852c7e2926d54 diff --git a/pkg/cmd/broadcast.go b/pkg/cmd/broadcast.go index 8888960..4b714b7 100644 --- a/pkg/cmd/broadcast.go +++ b/pkg/cmd/broadcast.go @@ -100,6 +100,11 @@ var broadcastsCreate = requestflag.WithInnerFlags(cli.Command{ Usage: "MIME type of the media.", InnerField: "mimeType", }, + &requestflag.InnerFlag[map[string]any]{ + Name: "content.template-button-variables", + Usage: "Default button variables for dynamic URL/OTP buttons. Keys are the button index (0, 1, 2). Per-contact values override these.", + InnerField: "templateButtonVariables", + }, &requestflag.InnerFlag[string]{ Name: "content.template-id", Usage: "Template ID for template messages.", @@ -107,7 +112,7 @@ var broadcastsCreate = requestflag.WithInnerFlags(cli.Command{ }, &requestflag.InnerFlag[map[string]any]{ Name: "content.template-variables", - Usage: "Default template variables (can be overridden per contact).", + Usage: "Default body variables (can be overridden per contact). Keys are positions (1, 2, ...).", InnerField: "templateVariables", }, }, @@ -186,6 +191,11 @@ var broadcastsUpdate = requestflag.WithInnerFlags(cli.Command{ Usage: "MIME type of the media.", InnerField: "mimeType", }, + &requestflag.InnerFlag[map[string]any]{ + Name: "content.template-button-variables", + Usage: "Default button variables for dynamic URL/OTP buttons. Keys are the button index (0, 1, 2). Per-contact values override these.", + InnerField: "templateButtonVariables", + }, &requestflag.InnerFlag[string]{ Name: "content.template-id", Usage: "Template ID for template messages.", @@ -193,7 +203,7 @@ var broadcastsUpdate = requestflag.WithInnerFlags(cli.Command{ }, &requestflag.InnerFlag[map[string]any]{ Name: "content.template-variables", - Usage: "Default template variables (can be overridden per contact).", + Usage: "Default body variables (can be overridden per contact). Keys are positions (1, 2, ...).", InnerField: "templateVariables", }, }, diff --git a/pkg/cmd/broadcast_test.go b/pkg/cmd/broadcast_test.go index 819ea40..b6e7e22 100644 --- a/pkg/cmd/broadcast_test.go +++ b/pkg/cmd/broadcast_test.go @@ -18,7 +18,7 @@ func TestBroadcastsCreate(t *testing.T) { "broadcasts", "create", "--channel", "sms", "--name", "Black Friday Sale", - "--content", "{filename: filename, mediaId: mediaId, mediaUrl: mediaUrl, mimeType: mimeType, templateId: templateId, templateVariables: {foo: string}}", + "--content", "{filename: filename, mediaId: mediaId, mediaUrl: mediaUrl, mimeType: mimeType, templateButtonVariables: {foo: string}, templateId: templateId, templateVariables: {foo: string}}", "--email-html-body", "emailHtmlBody", "--email-subject", "emailSubject", "--idempotency-key", "idempotencyKey", @@ -45,6 +45,7 @@ func TestBroadcastsCreate(t *testing.T) { "--content.media-id", "mediaId", "--content.media-url", "mediaUrl", "--content.mime-type", "mimeType", + "--content.template-button-variables", "{foo: string}", "--content.template-id", "templateId", "--content.template-variables", "{foo: string}", "--email-html-body", "emailHtmlBody", @@ -68,6 +69,8 @@ func TestBroadcastsCreate(t *testing.T) { " mediaId: mediaId\n" + " mediaUrl: mediaUrl\n" + " mimeType: mimeType\n" + + " templateButtonVariables:\n" + + " foo: string\n" + " templateId: templateId\n" + " templateVariables:\n" + " foo: string\n" + @@ -108,7 +111,7 @@ func TestBroadcastsUpdate(t *testing.T) { "--api-key", "string", "broadcasts", "update", "--broadcast-id", "broadcastId", - "--content", "{filename: filename, mediaId: mediaId, mediaUrl: mediaUrl, mimeType: mimeType, templateId: templateId, templateVariables: {foo: string}}", + "--content", "{filename: filename, mediaId: mediaId, mediaUrl: mediaUrl, mimeType: mimeType, templateButtonVariables: {foo: string}, templateId: templateId, templateVariables: {foo: string}}", "--email-html-body", "emailHtmlBody", "--email-subject", "emailSubject", "--metadata", "{foo: string}", @@ -131,6 +134,7 @@ func TestBroadcastsUpdate(t *testing.T) { "--content.media-id", "mediaId", "--content.media-url", "mediaUrl", "--content.mime-type", "mimeType", + "--content.template-button-variables", "{foo: string}", "--content.template-id", "templateId", "--content.template-variables", "{foo: string}", "--email-html-body", "emailHtmlBody", @@ -149,6 +153,8 @@ func TestBroadcastsUpdate(t *testing.T) { " mediaId: mediaId\n" + " mediaUrl: mediaUrl\n" + " mimeType: mimeType\n" + + " templateButtonVariables:\n" + + " foo: string\n" + " templateId: templateId\n" + " templateVariables:\n" + " foo: string\n" + diff --git a/pkg/cmd/broadcastcontact.go b/pkg/cmd/broadcastcontact.go index a1e010f..855ee9d 100644 --- a/pkg/cmd/broadcastcontact.go +++ b/pkg/cmd/broadcastcontact.go @@ -71,9 +71,14 @@ var broadcastsContactsAdd = requestflag.WithInnerFlags(cli.Command{ Usage: "Phone number (E.164) or email address.", InnerField: "recipient", }, + &requestflag.InnerFlag[map[string]any]{ + Name: "contact.template-button-variables", + Usage: "Per-contact button variables for dynamic URL/OTP buttons. Keys are the button index (0, 1, 2).", + InnerField: "templateButtonVariables", + }, &requestflag.InnerFlag[map[string]any]{ Name: "contact.template-variables", - Usage: "Per-contact template variables to personalize the message.", + Usage: "Per-contact body variables. Keys are positions (1, 2, ...) matching the order placeholders appear in the template body.", InnerField: "templateVariables", }, }, diff --git a/pkg/cmd/broadcastcontact_test.go b/pkg/cmd/broadcastcontact_test.go index 488f14a..da47b43 100644 --- a/pkg/cmd/broadcastcontact_test.go +++ b/pkg/cmd/broadcastcontact_test.go @@ -33,8 +33,8 @@ func TestBroadcastsContactsAdd(t *testing.T) { "--api-key", "string", "broadcasts:contacts", "add", "--broadcast-id", "broadcastId", - "--contact", "{recipient: '+14155551234', templateVariables: {name: John, order_id: ORD-001}}", - "--contact", "{recipient: '+14155555678', templateVariables: {name: Jane, order_id: ORD-002}}", + "--contact", "{recipient: '+14155551234', templateButtonVariables: {'0': abc-report-token}, templateVariables: {name: John, order_id: ORD-001}}", + "--contact", "{recipient: '+14155555678', templateButtonVariables: {'0': abc-report-token}, templateVariables: {name: Jane, order_id: ORD-002}}", ) }) @@ -49,8 +49,10 @@ func TestBroadcastsContactsAdd(t *testing.T) { "broadcasts:contacts", "add", "--broadcast-id", "broadcastId", "--contact.recipient", "+14155551234", + "--contact.template-button-variables", "{'0': abc-report-token}", "--contact.template-variables", "{name: John, order_id: ORD-001}", "--contact.recipient", "+14155555678", + "--contact.template-button-variables", "{'0': abc-report-token}", "--contact.template-variables", "{name: Jane, order_id: ORD-002}", ) }) @@ -60,10 +62,14 @@ func TestBroadcastsContactsAdd(t *testing.T) { pipeData := []byte("" + "contacts:\n" + " - recipient: '+14155551234'\n" + + " templateButtonVariables:\n" + + " '0': abc-report-token\n" + " templateVariables:\n" + " name: John\n" + " order_id: ORD-001\n" + " - recipient: '+14155555678'\n" + + " templateButtonVariables:\n" + + " '0': abc-report-token\n" + " templateVariables:\n" + " name: Jane\n" + " order_id: ORD-002\n") diff --git a/pkg/cmd/message.go b/pkg/cmd/message.go index 618ed9f..3c7f0b9 100644 --- a/pkg/cmd/message.go +++ b/pkg/cmd/message.go @@ -297,6 +297,11 @@ var messagesSend = requestflag.WithInnerFlags(cli.Command{ Usage: "Sections for list messages.", InnerField: "sections", }, + &requestflag.InnerFlag[map[string]any]{ + Name: "content.template-button-variables", + Usage: "Variables for dynamic button placeholders (URL buttons and OTP buttons). Keys are the button index (0, 1, 2) in the template's `buttons` array. Values substitute the single placeholder allowed inside that button's URL.\n\n**WhatsApp constraints:**\n- Each URL button supports at most one placeholder, numeric (`{{1}}`) or named (`{{order_id}}`).\n- A template may have at most three buttons.\n- Static URL buttons (no placeholder) are not included here.", + InnerField: "templateButtonVariables", + }, &requestflag.InnerFlag[string]{ Name: "content.template-id", Usage: "Template ID for template messages.", @@ -304,7 +309,7 @@ var messagesSend = requestflag.WithInnerFlags(cli.Command{ }, &requestflag.InnerFlag[map[string]any]{ Name: "content.template-variables", - Usage: "Variables for template rendering. Keys are variable positions (1, 2, 3...).", + Usage: "Variables for body placeholders. Keys are positions (1, 2, 3, ...) matching the order placeholders appear in the template body.", InnerField: "templateVariables", }, }, diff --git a/pkg/cmd/message_test.go b/pkg/cmd/message_test.go index 6903962..35377bc 100644 --- a/pkg/cmd/message_test.go +++ b/pkg/cmd/message_test.go @@ -74,7 +74,7 @@ func TestMessagesSend(t *testing.T) { "--to", "+56912345678", "--attachment", "{filename: invoice.pdf, content: content, content_id: logo, content_type: application/pdf, path: https://example.com}", "--channel", "auto", - "--content", "{buttons: [{id: id, title: title}], contacts: [{name: name, phones: [string]}], ctaDisplayText: See Dates, ctaHeaderMediaUrl: https://example.com, ctaHeaderText: ctaHeaderText, ctaHeaderType: text, ctaUrl: https://example.com/schedule, emoji: emoji, filename: invoice.pdf, footerText: Dates subject to change., latitude: 0, listButton: listButton, locationAddress: locationAddress, locationName: locationName, longitude: 0, mediaId: mediaId, mediaUrl: https://example.com/image.jpg, mimeType: image/jpeg, reactToMessageId: reactToMessageId, sections: [{rows: [{id: id, title: title, description: description}], title: title}], templateId: templateId, templateVariables: {'1': John, '2': ORD-12345}}", + "--content", "{buttons: [{id: id, title: title}], contacts: [{name: name, phones: [string]}], ctaDisplayText: See Dates, ctaHeaderMediaUrl: https://example.com, ctaHeaderText: ctaHeaderText, ctaHeaderType: text, ctaUrl: https://example.com/schedule, emoji: emoji, filename: invoice.pdf, footerText: Dates subject to change., latitude: 0, listButton: listButton, locationAddress: locationAddress, locationName: locationName, longitude: 0, mediaId: mediaId, mediaUrl: https://example.com/image.jpg, mimeType: image/jpeg, reactToMessageId: reactToMessageId, sections: [{rows: [{id: id, title: title, description: description}], title: title}], templateButtonVariables: {'0': abc-report-token}, templateId: templateId, templateVariables: {'1': John, '2': ORD-12345}}", "--fallback-enabled=true", "--html-body", "htmlBody", "--idempotency-key", "msg_01HZY4ZP7VQY2J3BRW7Z6G0QGE", @@ -124,6 +124,7 @@ func TestMessagesSend(t *testing.T) { "--content.mime-type", "image/jpeg", "--content.react-to-message-id", "reactToMessageId", "--content.sections", "[{rows: [{id: id, title: title, description: description}], title: title}]", + "--content.template-button-variables", "{'0': abc-report-token}", "--content.template-id", "templateId", "--content.template-variables", "{'1': John, '2': ORD-12345}", "--fallback-enabled=true", @@ -181,6 +182,8 @@ func TestMessagesSend(t *testing.T) { " title: title\n" + " description: description\n" + " title: title\n" + + " templateButtonVariables:\n" + + " '0': abc-report-token\n" + " templateId: templateId\n" + " templateVariables:\n" + " '1': John\n" + From 771f45e52871e2e8499b5477b9f19c04e4167a1d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 27 Apr 2026 19:44:44 +0000 Subject: [PATCH 16/56] feat(api): api update --- .stats.yml | 2 +- pkg/cmd/message.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index e5ef4fe..5396deb 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 129 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu%2Fzavudev-77943605e8ccf7c6c70bfe2fcd14e41b45f018747af93fbd3b54c92187fc6d18.yml -openapi_spec_hash: d120508439dfb8de2fb579ebc9543893 +openapi_spec_hash: e7ce2938f14b0c7cf52a256f61c19dab config_hash: 280cf643b641e9d1b21852c7e2926d54 diff --git a/pkg/cmd/message.go b/pkg/cmd/message.go index 3c7f0b9..e4f9261 100644 --- a/pkg/cmd/message.go +++ b/pkg/cmd/message.go @@ -299,7 +299,7 @@ var messagesSend = requestflag.WithInnerFlags(cli.Command{ }, &requestflag.InnerFlag[map[string]any]{ Name: "content.template-button-variables", - Usage: "Variables for dynamic button placeholders (URL buttons and OTP buttons). Keys are the button index (0, 1, 2) in the template's `buttons` array. Values substitute the single placeholder allowed inside that button's URL.\n\n**WhatsApp constraints:**\n- Each URL button supports at most one placeholder, numeric (`{{1}}`) or named (`{{order_id}}`).\n- A template may have at most three buttons.\n- Static URL buttons (no placeholder) are not included here.", + Usage: "Variables for dynamic button placeholders (URL buttons and OTP buttons). Keys are the button index (0, 1, 2) in the template's `buttons` array — not the placeholder name. Values substitute the `{{1}}` placeholder inside that button's URL.\n\n**WhatsApp constraints:**\n- URL buttons only accept `{{1}}` — positional, numeric, no whitespace, no name. Named placeholders like `{{token}}` are stored as literal URL text by Meta and cannot be substituted.\n- At most one placeholder per URL button.\n- A template may have at most three buttons.\n- Static URL buttons (no placeholder) and `quick_reply` buttons are not included here.", InnerField: "templateButtonVariables", }, &requestflag.InnerFlag[string]{ From 0a0f73f9f8a329e1370c2787c27904752e6ef801 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 27 Apr 2026 20:51:18 +0000 Subject: [PATCH 17/56] feat(api): api update --- .stats.yml | 2 +- pkg/cmd/template.go | 6 ++++++ pkg/cmd/template_test.go | 4 +++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 5396deb..72ad6ca 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 129 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu%2Fzavudev-77943605e8ccf7c6c70bfe2fcd14e41b45f018747af93fbd3b54c92187fc6d18.yml -openapi_spec_hash: e7ce2938f14b0c7cf52a256f61c19dab +openapi_spec_hash: 3224f62d65122936495737a363a0d8bf config_hash: 280cf643b641e9d1b21852c7e2926d54 diff --git a/pkg/cmd/template.go b/pkg/cmd/template.go index 1942d7e..9dff13e 100644 --- a/pkg/cmd/template.go +++ b/pkg/cmd/template.go @@ -104,6 +104,11 @@ var templatesCreate = requestflag.WithInnerFlags(cli.Command{ Usage: `Allowed values: "quick_reply", "url", "phone", "otp".`, InnerField: "type", }, + &requestflag.InnerFlag[string]{ + Name: "button.example", + Usage: "Sample value Meta uses to review templates with a dynamic URL button. Substituted into `{{1}}` of the URL when the template is submitted to Meta. Only meaningful when `url` contains `{{1}}`; ignored for static URLs.", + InnerField: "example", + }, &requestflag.InnerFlag[string]{ Name: "button.otp-type", Usage: "Required when type is 'otp'. COPY_CODE shows copy button, ONE_TAP enables Android autofill.", @@ -125,6 +130,7 @@ var templatesCreate = requestflag.WithInnerFlags(cli.Command{ }, &requestflag.InnerFlag[string]{ Name: "button.url", + Usage: "Button destination. Use `{{1}}` exactly once for a dynamic URL (e.g. `https://example.com/orders/{{1}}`); WhatsApp only accepts the strict `{{1}}` form. Static URLs must not contain any `{{...}}` placeholder.", InnerField: "url", }, }, diff --git a/pkg/cmd/template_test.go b/pkg/cmd/template_test.go index 78bbf24..fd83751 100644 --- a/pkg/cmd/template_test.go +++ b/pkg/cmd/template_test.go @@ -20,7 +20,7 @@ func TestTemplatesCreate(t *testing.T) { "--language", "en", "--name", "order_confirmation", "--add-security-recommendation=true", - "--button", "{text: text, type: quick_reply, otpType: COPY_CODE, packageName: packageName, phoneNumber: phoneNumber, signatureHash: signatureHash, url: https://example.com}", + "--button", "{text: text, type: quick_reply, example: ORD-12345, otpType: COPY_CODE, packageName: packageName, phoneNumber: phoneNumber, signatureHash: signatureHash, url: https://example.com}", "--code-expiration-minutes", "1", "--footer", "footer", "--header-content", "headerContent", @@ -49,6 +49,7 @@ func TestTemplatesCreate(t *testing.T) { "--add-security-recommendation=true", "--button.text", "text", "--button.type", "quick_reply", + "--button.example", "ORD-12345", "--button.otp-type", "COPY_CODE", "--button.package-name", "packageName", "--button.phone-number", "phoneNumber", @@ -77,6 +78,7 @@ func TestTemplatesCreate(t *testing.T) { "buttons:\n" + " - text: text\n" + " type: quick_reply\n" + + " example: ORD-12345\n" + " otpType: COPY_CODE\n" + " packageName: packageName\n" + " phoneNumber: phoneNumber\n" + From d9200b09fa86ca1c86f2ae140a2b2f22554840a9 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 28 Apr 2026 00:59:59 +0000 Subject: [PATCH 18/56] feat(api): api update --- .stats.yml | 2 +- pkg/cmd/subaccount.go | 12 ++++++------ pkg/cmd/subaccountapikey.go | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.stats.yml b/.stats.yml index 72ad6ca..44e6a22 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 129 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu%2Fzavudev-77943605e8ccf7c6c70bfe2fcd14e41b45f018747af93fbd3b54c92187fc6d18.yml -openapi_spec_hash: 3224f62d65122936495737a363a0d8bf +openapi_spec_hash: bd55c12bdf43e14e11d79c58c16995ca config_hash: 280cf643b641e9d1b21852c7e2926d54 diff --git a/pkg/cmd/subaccount.go b/pkg/cmd/subaccount.go index 07b03bb..da927bb 100644 --- a/pkg/cmd/subaccount.go +++ b/pkg/cmd/subaccount.go @@ -16,7 +16,7 @@ import ( var subAccountsCreate = cli.Command{ Name: "create", - Usage: "Create a new sub-account (project) with its own API key. All charges are billed\nto the parent team's balance. Use creditLimit to set a spending cap. The\nsub-account's API key is returned only in the creation response.", + Usage: "Create a new sub-account (project) with its own API key. All charges are billed\nto the parent team's balance. Use creditLimit to set a spending cap. The\nsub-account's API key is returned only in the creation response. Requires a\nparent project API key; sub-account API keys receive HTTP 403.", Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ @@ -46,7 +46,7 @@ var subAccountsCreate = cli.Command{ var subAccountsRetrieve = cli.Command{ Name: "retrieve", - Usage: "Get sub-account", + Usage: "Get sub-account. Requires a parent project API key; sub-account API keys receive\nHTTP 403.", Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ @@ -60,7 +60,7 @@ var subAccountsRetrieve = cli.Command{ var subAccountsUpdate = cli.Command{ Name: "update", - Usage: "Update sub-account", + Usage: "Update sub-account. Requires a parent project API key; sub-account API keys\nreceive HTTP 403.", Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ @@ -95,7 +95,7 @@ var subAccountsUpdate = cli.Command{ var subAccountsList = cli.Command{ Name: "list", - Usage: "List sub-accounts for this team.", + Usage: "List sub-accounts for this team. Requires a parent project API key; sub-account\nAPI keys receive HTTP 403.", Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ @@ -118,7 +118,7 @@ var subAccountsList = cli.Command{ var subAccountsDeactivate = cli.Command{ Name: "deactivate", - Usage: "Deactivate a sub-account. Remaining balance is returned to the parent team and\nall API keys are revoked.", + Usage: "Deactivate a sub-account. Remaining balance is returned to the parent team and\nall API keys are revoked. Requires a parent project API key; sub-account API\nkeys receive HTTP 403.", Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ @@ -132,7 +132,7 @@ var subAccountsDeactivate = cli.Command{ var subAccountsGetBalance = cli.Command{ Name: "get-balance", - Usage: "Get spending information for a sub-account. Returns the parent team's balance,\nthe sub-account's total spending, and its credit limit (spending cap).", + Usage: "Get spending information for a sub-account. Returns the parent team's balance,\nthe sub-account's total spending, and its credit limit (spending cap). Requires\na parent project API key; sub-account API keys receive HTTP 403.", Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ diff --git a/pkg/cmd/subaccountapikey.go b/pkg/cmd/subaccountapikey.go index 9b1e727..109a88d 100644 --- a/pkg/cmd/subaccountapikey.go +++ b/pkg/cmd/subaccountapikey.go @@ -16,7 +16,7 @@ import ( var subAccountsAPIKeysCreate = cli.Command{ Name: "create", - Usage: "Create sub-account API key", + Usage: "Create sub-account API key. Requires a parent project API key; sub-account API\nkeys receive HTTP 403.", Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ @@ -45,7 +45,7 @@ var subAccountsAPIKeysCreate = cli.Command{ var subAccountsAPIKeysList = cli.Command{ Name: "list", - Usage: "List sub-account API keys", + Usage: "List sub-account API keys. Requires a parent project API key; sub-account API\nkeys receive HTTP 403.", Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ @@ -59,7 +59,7 @@ var subAccountsAPIKeysList = cli.Command{ var subAccountsAPIKeysRevoke = cli.Command{ Name: "revoke", - Usage: "Revoke sub-account API key", + Usage: "Revoke sub-account API key. Requires a parent project API key; sub-account API\nkeys receive HTTP 403.", Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ From 2e72e33ee98319d6ebdba55f435051fe4c1914f9 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 28 Apr 2026 01:47:16 +0000 Subject: [PATCH 19/56] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 44e6a22..74f9874 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 129 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu%2Fzavudev-77943605e8ccf7c6c70bfe2fcd14e41b45f018747af93fbd3b54c92187fc6d18.yml -openapi_spec_hash: bd55c12bdf43e14e11d79c58c16995ca +openapi_spec_hash: d831e4cd9855111c1f9a1ee04a508564 config_hash: 280cf643b641e9d1b21852c7e2926d54 From 21a5d1f389e272b3f2320a88388680ea65bfc8b3 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 29 Apr 2026 03:45:33 +0000 Subject: [PATCH 20/56] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 74f9874..62e401f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 129 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu%2Fzavudev-77943605e8ccf7c6c70bfe2fcd14e41b45f018747af93fbd3b54c92187fc6d18.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu%2Fzavudev-5c9ea51bd891ecc175b2e7e85fea22d7b8624e9463dc3410e08aef2b21ed536b.yml openapi_spec_hash: d831e4cd9855111c1f9a1ee04a508564 config_hash: 280cf643b641e9d1b21852c7e2926d54 From a29fdc57fec69b50adf10f5ca9d76da5267c60cc Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 1 May 2026 04:59:20 +0000 Subject: [PATCH 21/56] fix(cli): correctly load zsh autocompletion --- .../autocomplete/shellscripts/zsh_autocomplete.zsh | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) 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 From 222ad14f85ed4790ee5da1021595425ead41e187 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 1 May 2026 05:02:57 +0000 Subject: [PATCH 22/56] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 62e401f..6122602 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 129 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu%2Fzavudev-5c9ea51bd891ecc175b2e7e85fea22d7b8624e9463dc3410e08aef2b21ed536b.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-5c9ea51bd891ecc175b2e7e85fea22d7b8624e9463dc3410e08aef2b21ed536b.yml openapi_spec_hash: d831e4cd9855111c1f9a1ee04a508564 config_hash: 280cf643b641e9d1b21852c7e2926d54 From f71454c4db5b528adceda8850a41857a80da27d7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 1 May 2026 05:04:26 +0000 Subject: [PATCH 23/56] fix: flags for nullable body scalar fields are strictly typed --- internal/requestflag/innerflag.go | 16 +- internal/requestflag/requestflag.go | 128 ++++++++++++- internal/requestflag/requestflag_test.go | 234 +++++++++++++++++++++++ pkg/cmd/contact.go | 2 +- pkg/cmd/contactchannel.go | 2 +- pkg/cmd/phonenumber.go | 4 +- pkg/cmd/sender.go | 2 +- pkg/cmd/senderagent.go | 4 +- pkg/cmd/senderagentflow.go | 4 +- pkg/cmd/senderagentknowledgebase.go | 2 +- pkg/cmd/senderagenttool.go | 2 +- pkg/cmd/subaccount.go | 2 +- 12 files changed, 386 insertions(+), 16 deletions(-) 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..54c2509 100644 --- a/internal/requestflag/requestflag.go +++ b/internal/requestflag/requestflag.go @@ -15,10 +15,15 @@ import ( // 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 @@ -341,6 +346,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 +406,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 +431,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 +442,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 +493,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 +571,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 +671,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 +791,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 +813,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..06ffb72 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,64 @@ 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)) + }) +} diff --git a/pkg/cmd/contact.go b/pkg/cmd/contact.go index d3dece0..ad60c7d 100644 --- a/pkg/cmd/contact.go +++ b/pkg/cmd/contact.go @@ -91,7 +91,7 @@ var contactsUpdate = cli.Command{ Name: "contact-id", Required: true, }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "default-channel", Usage: "Preferred channel for this contact. Set to null to clear.", BodyPath: "defaultChannel", diff --git a/pkg/cmd/contactchannel.go b/pkg/cmd/contactchannel.go index 89ff700..0782c37 100644 --- a/pkg/cmd/contactchannel.go +++ b/pkg/cmd/contactchannel.go @@ -27,7 +27,7 @@ var contactsChannelsUpdate = cli.Command{ Name: "channel-id", Required: true, }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "label", Usage: "Optional label for the channel. Set to null to clear.", BodyPath: "label", diff --git a/pkg/cmd/phonenumber.go b/pkg/cmd/phonenumber.go index 58b0c9e..034ec9b 100644 --- a/pkg/cmd/phonenumber.go +++ b/pkg/cmd/phonenumber.go @@ -37,12 +37,12 @@ var phoneNumbersUpdate = cli.Command{ Name: "phone-number-id", Required: true, }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "name", Usage: "Custom name for the phone number. Set to null to clear.", BodyPath: "name", }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "sender-id", Usage: "Sender ID to assign the phone number to. Set to null to unassign.", BodyPath: "senderId", diff --git a/pkg/cmd/sender.go b/pkg/cmd/sender.go index 935d471..b59ef17 100644 --- a/pkg/cmd/sender.go +++ b/pkg/cmd/sender.go @@ -95,7 +95,7 @@ var sendersUpdate = cli.Command{ Usage: "Events to subscribe to.", BodyPath: "webhookEvents", }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "webhook-url", Usage: "HTTPS URL for webhook events. Set to null to remove webhook.", BodyPath: "webhookUrl", diff --git a/pkg/cmd/senderagent.go b/pkg/cmd/senderagent.go index 72b5d9d..b77836d 100644 --- a/pkg/cmd/senderagent.go +++ b/pkg/cmd/senderagent.go @@ -121,7 +121,7 @@ var sendersAgentUpdate = cli.Command{ Name: "include-contact-metadata", BodyPath: "includeContactMetadata", }, - &requestflag.Flag[any]{ + &requestflag.Flag[*int64]{ Name: "max-tokens", BodyPath: "maxTokens", }, @@ -142,7 +142,7 @@ var sendersAgentUpdate = cli.Command{ Name: "system-prompt", BodyPath: "systemPrompt", }, - &requestflag.Flag[any]{ + &requestflag.Flag[*float64]{ Name: "temperature", BodyPath: "temperature", }, diff --git a/pkg/cmd/senderagentflow.go b/pkg/cmd/senderagentflow.go index a200fea..4ab21ec 100644 --- a/pkg/cmd/senderagentflow.go +++ b/pkg/cmd/senderagentflow.go @@ -72,7 +72,7 @@ var sendersAgentFlowsCreate = requestflag.WithInnerFlags(cli.Command{ Usage: "Type of flow step.", InnerField: "type", }, - &requestflag.InnerFlag[any]{ + &requestflag.InnerFlag[*string]{ Name: "step.next-step-id", Usage: "ID of the next step to execute.", InnerField: "nextStepId", @@ -172,7 +172,7 @@ var sendersAgentFlowsUpdate = requestflag.WithInnerFlags(cli.Command{ Usage: "Type of flow step.", InnerField: "type", }, - &requestflag.InnerFlag[any]{ + &requestflag.InnerFlag[*string]{ Name: "step.next-step-id", Usage: "ID of the next step to execute.", InnerField: "nextStepId", diff --git a/pkg/cmd/senderagentknowledgebase.go b/pkg/cmd/senderagentknowledgebase.go index 82cb6f7..38e7394 100644 --- a/pkg/cmd/senderagentknowledgebase.go +++ b/pkg/cmd/senderagentknowledgebase.go @@ -68,7 +68,7 @@ var sendersAgentKnowledgeBasesUpdate = cli.Command{ Name: "kb-id", Required: true, }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "description", BodyPath: "description", }, diff --git a/pkg/cmd/senderagenttool.go b/pkg/cmd/senderagenttool.go index 15bcd03..ed57ffc 100644 --- a/pkg/cmd/senderagenttool.go +++ b/pkg/cmd/senderagenttool.go @@ -122,7 +122,7 @@ var sendersAgentToolsUpdate = requestflag.WithInnerFlags(cli.Command{ Name: "parameters", BodyPath: "parameters", }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "webhook-secret", BodyPath: "webhookSecret", }, diff --git a/pkg/cmd/subaccount.go b/pkg/cmd/subaccount.go index da927bb..7c7a89c 100644 --- a/pkg/cmd/subaccount.go +++ b/pkg/cmd/subaccount.go @@ -67,7 +67,7 @@ var subAccountsUpdate = cli.Command{ Name: "id", Required: true, }, - &requestflag.Flag[any]{ + &requestflag.Flag[*int64]{ Name: "credit-limit", BodyPath: "creditLimit", }, From b8f994dc81af29fb1526cbe06cfc7704e886b573 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 1 May 2026 05:07:13 +0000 Subject: [PATCH 24/56] feat: support passing path and query params over stdin --- internal/requestflag/requestflag.go | 115 ++++++- internal/requestflag/requestflag_test.go | 345 ++++++++++++++++++++ pkg/cmd/address.go | 18 +- pkg/cmd/broadcast.go | 65 ++-- pkg/cmd/broadcastcontact.go | 36 +- pkg/cmd/contact.go | 41 ++- pkg/cmd/contactchannel.go | 63 ++-- pkg/cmd/export.go | 13 +- pkg/cmd/flagoptions.go | 48 ++- pkg/cmd/introspect.go | 4 +- pkg/cmd/invitation.go | 18 +- pkg/cmd/message.go | 22 +- pkg/cmd/number10dlcbrand.go | 37 ++- pkg/cmd/number10dlccampaign.go | 37 ++- pkg/cmd/number10dlccampaignphonenumber.go | 32 +- pkg/cmd/phonenumber.go | 35 +- pkg/cmd/regulatorydocument.go | 18 +- pkg/cmd/sender.go | 55 ++-- pkg/cmd/senderagent.go | 33 +- pkg/cmd/senderagentexecution.go | 9 +- pkg/cmd/senderagentflow.go | 90 ++--- pkg/cmd/senderagentknowledgebase.go | 72 ++-- pkg/cmd/senderagentknowledgebasedocument.go | 61 ++-- pkg/cmd/senderagenttool.go | 90 ++--- pkg/cmd/senderwhatsappsync.go | 15 +- pkg/cmd/subaccount.go | 32 +- pkg/cmd/subaccountapikey.go | 32 +- pkg/cmd/template.go | 27 +- pkg/cmd/url.go | 13 +- 29 files changed, 1037 insertions(+), 439 deletions(-) diff --git a/internal/requestflag/requestflag.go b/internal/requestflag/requestflag.go index 54c2509..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,6 +13,26 @@ 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.). @@ -41,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, @@ -72,6 +94,7 @@ type InRequest interface { GetQueryPath() string GetHeaderPath() string GetBodyPath() string + GetPathParam() string IsBodyRoot() bool IsFileInput() bool GetDataAliases() []string @@ -89,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 } @@ -108,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{ @@ -291,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 diff --git a/internal/requestflag/requestflag_test.go b/internal/requestflag/requestflag_test.go index 06ffb72..779bd57 100644 --- a/internal/requestflag/requestflag_test.go +++ b/internal/requestflag/requestflag_test.go @@ -880,3 +880,348 @@ func TestInnerFlagDispatchOnUntypedFlag(t *testing.T) { 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/address.go b/pkg/cmd/address.go index 7a36f3a..b21556a 100644 --- a/pkg/cmd/address.go +++ b/pkg/cmd/address.go @@ -70,8 +70,9 @@ var addressesRetrieve = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "address-id", - Required: true, + Name: "address-id", + Required: true, + PathParam: "addressId", }, }, Action: handleAddressesRetrieve, @@ -107,8 +108,9 @@ var addressesDelete = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "address-id", - Required: true, + Name: "address-id", + Required: true, + PathParam: "addressId", }, }, Action: handleAddressesDelete, @@ -123,8 +125,6 @@ func handleAddressesCreate(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.AddressNewParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -136,6 +136,8 @@ func handleAddressesCreate(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.AddressNewParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Addresses.New(ctx, params, options...) @@ -206,8 +208,6 @@ func handleAddressesList(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.AddressListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -219,6 +219,8 @@ func handleAddressesList(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.AddressListParams{} + format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") diff --git a/pkg/cmd/broadcast.go b/pkg/cmd/broadcast.go index 4b714b7..7050d8c 100644 --- a/pkg/cmd/broadcast.go +++ b/pkg/cmd/broadcast.go @@ -124,8 +124,9 @@ var broadcastsRetrieve = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "broadcast-id", - Required: true, + Name: "broadcast-id", + Required: true, + PathParam: "broadcastId", }, }, Action: handleBroadcastsRetrieve, @@ -138,8 +139,9 @@ var broadcastsUpdate = requestflag.WithInnerFlags(cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "broadcast-id", - Required: true, + Name: "broadcast-id", + Required: true, + PathParam: "broadcastId", }, &requestflag.Flag[map[string]any]{ Name: "content", @@ -243,8 +245,9 @@ var broadcastsDelete = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "broadcast-id", - Required: true, + Name: "broadcast-id", + Required: true, + PathParam: "broadcastId", }, }, Action: handleBroadcastsDelete, @@ -257,8 +260,9 @@ var broadcastsCancel = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "broadcast-id", - Required: true, + Name: "broadcast-id", + Required: true, + PathParam: "broadcastId", }, }, Action: handleBroadcastsCancel, @@ -271,8 +275,9 @@ var broadcastsEscalateReview = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "broadcast-id", - Required: true, + Name: "broadcast-id", + Required: true, + PathParam: "broadcastId", }, }, Action: handleBroadcastsEscalateReview, @@ -285,8 +290,9 @@ var broadcastsProgress = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "broadcast-id", - Required: true, + Name: "broadcast-id", + Required: true, + PathParam: "broadcastId", }, }, Action: handleBroadcastsProgress, @@ -299,8 +305,9 @@ var broadcastsReschedule = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "broadcast-id", - Required: true, + Name: "broadcast-id", + Required: true, + PathParam: "broadcastId", }, &requestflag.Flag[any]{ Name: "scheduled-at", @@ -319,8 +326,9 @@ var broadcastsRetryReview = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "broadcast-id", - Required: true, + Name: "broadcast-id", + Required: true, + PathParam: "broadcastId", }, }, Action: handleBroadcastsRetryReview, @@ -333,8 +341,9 @@ var broadcastsSend = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "broadcast-id", - Required: true, + Name: "broadcast-id", + Required: true, + PathParam: "broadcastId", }, &requestflag.Flag[any]{ Name: "scheduled-at", @@ -354,8 +363,6 @@ func handleBroadcastsCreate(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.BroadcastNewParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -367,6 +374,8 @@ func handleBroadcastsCreate(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.BroadcastNewParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Broadcasts.New(ctx, params, options...) @@ -440,8 +449,6 @@ func handleBroadcastsUpdate(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.BroadcastUpdateParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -453,6 +460,8 @@ func handleBroadcastsUpdate(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.BroadcastUpdateParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Broadcasts.Update( @@ -486,8 +495,6 @@ func handleBroadcastsList(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.BroadcastListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -499,6 +506,8 @@ func handleBroadcastsList(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.BroadcastListParams{} + format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") @@ -695,8 +704,6 @@ func handleBroadcastsReschedule(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.BroadcastRescheduleParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -708,6 +715,8 @@ func handleBroadcastsReschedule(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.BroadcastRescheduleParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Broadcasts.Reschedule( @@ -786,8 +795,6 @@ func handleBroadcastsSend(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.BroadcastSendParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -799,6 +806,8 @@ func handleBroadcastsSend(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.BroadcastSendParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Broadcasts.Send( diff --git a/pkg/cmd/broadcastcontact.go b/pkg/cmd/broadcastcontact.go index 855ee9d..206339b 100644 --- a/pkg/cmd/broadcastcontact.go +++ b/pkg/cmd/broadcastcontact.go @@ -20,8 +20,9 @@ var broadcastsContactsList = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "broadcast-id", - Required: true, + Name: "broadcast-id", + Required: true, + PathParam: "broadcastId", }, &requestflag.Flag[string]{ Name: "cursor", @@ -52,8 +53,9 @@ var broadcastsContactsAdd = requestflag.WithInnerFlags(cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "broadcast-id", - Required: true, + Name: "broadcast-id", + Required: true, + PathParam: "broadcastId", }, &requestflag.Flag[[]map[string]any]{ Name: "contact", @@ -90,12 +92,14 @@ var broadcastsContactsRemove = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "broadcast-id", - Required: true, + Name: "broadcast-id", + Required: true, + PathParam: "broadcastId", }, &requestflag.Flag[string]{ - Name: "contact-id", - Required: true, + Name: "contact-id", + Required: true, + PathParam: "contactId", }, }, Action: handleBroadcastsContactsRemove, @@ -113,8 +117,6 @@ func handleBroadcastsContactsList(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.BroadcastContactListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -126,6 +128,8 @@ func handleBroadcastsContactsList(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.BroadcastContactListParams{} + format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") @@ -181,8 +185,6 @@ func handleBroadcastsContactsAdd(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.BroadcastContactAddParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -194,6 +196,8 @@ func handleBroadcastsContactsAdd(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.BroadcastContactAddParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Broadcasts.Contacts.Add( @@ -230,10 +234,6 @@ func handleBroadcastsContactsRemove(ctx context.Context, cmd *cli.Command) error return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.BroadcastContactRemoveParams{ - BroadcastID: cmd.Value("broadcast-id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -245,6 +245,10 @@ func handleBroadcastsContactsRemove(ctx context.Context, cmd *cli.Command) error return err } + params := zavudev.BroadcastContactRemoveParams{ + BroadcastID: cmd.Value("broadcast-id").(string), + } + return client.Broadcasts.Contacts.Remove( ctx, cmd.Value("contact-id").(string), diff --git a/pkg/cmd/contact.go b/pkg/cmd/contact.go index ad60c7d..ad76820 100644 --- a/pkg/cmd/contact.go +++ b/pkg/cmd/contact.go @@ -74,8 +74,9 @@ var contactsRetrieve = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "contact-id", - Required: true, + Name: "contact-id", + Required: true, + PathParam: "contactId", }, }, Action: handleContactsRetrieve, @@ -88,8 +89,9 @@ var contactsUpdate = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "contact-id", - Required: true, + Name: "contact-id", + Required: true, + PathParam: "contactId", }, &requestflag.Flag[*string]{ Name: "default-channel", @@ -138,8 +140,9 @@ var contactsDismissMergeSuggestion = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "contact-id", - Required: true, + Name: "contact-id", + Required: true, + PathParam: "contactId", }, }, Action: handleContactsDismissMergeSuggestion, @@ -152,8 +155,9 @@ var contactsMerge = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "contact-id", - Required: true, + Name: "contact-id", + Required: true, + PathParam: "contactId", }, &requestflag.Flag[string]{ Name: "source-contact-id", @@ -172,8 +176,9 @@ var contactsRetrieveByPhone = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "phone-number", - Required: true, + Name: "phone-number", + Required: true, + PathParam: "phoneNumber", }, }, Action: handleContactsRetrieveByPhone, @@ -188,8 +193,6 @@ func handleContactsCreate(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.ContactNewParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -201,6 +204,8 @@ func handleContactsCreate(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.ContactNewParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Contacts.New(ctx, params, options...) @@ -274,8 +279,6 @@ func handleContactsUpdate(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.ContactUpdateParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -287,6 +290,8 @@ func handleContactsUpdate(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.ContactUpdateParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Contacts.Update( @@ -320,8 +325,6 @@ func handleContactsList(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.ContactListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -333,6 +336,8 @@ func handleContactsList(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.ContactListParams{} + format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") @@ -403,8 +408,6 @@ func handleContactsMerge(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.ContactMergeParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -416,6 +419,8 @@ func handleContactsMerge(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.ContactMergeParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Contacts.Merge( diff --git a/pkg/cmd/contactchannel.go b/pkg/cmd/contactchannel.go index 0782c37..73ee1c8 100644 --- a/pkg/cmd/contactchannel.go +++ b/pkg/cmd/contactchannel.go @@ -20,12 +20,14 @@ var contactsChannelsUpdate = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "contact-id", - Required: true, + Name: "contact-id", + Required: true, + PathParam: "contactId", }, &requestflag.Flag[string]{ - Name: "channel-id", - Required: true, + Name: "channel-id", + Required: true, + PathParam: "channelId", }, &requestflag.Flag[*string]{ Name: "label", @@ -52,8 +54,9 @@ var contactsChannelsAdd = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "contact-id", - Required: true, + Name: "contact-id", + Required: true, + PathParam: "contactId", }, &requestflag.Flag[string]{ Name: "channel", @@ -94,12 +97,14 @@ var contactsChannelsRemove = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "contact-id", - Required: true, + Name: "contact-id", + Required: true, + PathParam: "contactId", }, &requestflag.Flag[string]{ - Name: "channel-id", - Required: true, + Name: "channel-id", + Required: true, + PathParam: "channelId", }, }, Action: handleContactsChannelsRemove, @@ -112,12 +117,14 @@ var contactsChannelsSetPrimary = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "contact-id", - Required: true, + Name: "contact-id", + Required: true, + PathParam: "contactId", }, &requestflag.Flag[string]{ - Name: "channel-id", - Required: true, + Name: "channel-id", + Required: true, + PathParam: "channelId", }, }, Action: handleContactsChannelsSetPrimary, @@ -135,10 +142,6 @@ func handleContactsChannelsUpdate(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.ContactChannelUpdateParams{ - ContactID: cmd.Value("contact-id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -150,6 +153,10 @@ func handleContactsChannelsUpdate(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.ContactChannelUpdateParams{ + ContactID: cmd.Value("contact-id").(string), + } + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Contacts.Channels.Update( @@ -186,8 +193,6 @@ func handleContactsChannelsAdd(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.ContactChannelAddParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -199,6 +204,8 @@ func handleContactsChannelsAdd(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.ContactChannelAddParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Contacts.Channels.Add( @@ -235,10 +242,6 @@ func handleContactsChannelsRemove(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.ContactChannelRemoveParams{ - ContactID: cmd.Value("contact-id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -250,6 +253,10 @@ func handleContactsChannelsRemove(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.ContactChannelRemoveParams{ + ContactID: cmd.Value("contact-id").(string), + } + return client.Contacts.Channels.Remove( ctx, cmd.Value("channel-id").(string), @@ -269,10 +276,6 @@ func handleContactsChannelsSetPrimary(ctx context.Context, cmd *cli.Command) err return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.ContactChannelSetPrimaryParams{ - ContactID: cmd.Value("contact-id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -284,6 +287,10 @@ func handleContactsChannelsSetPrimary(ctx context.Context, cmd *cli.Command) err return err } + params := zavudev.ContactChannelSetPrimaryParams{ + ContactID: cmd.Value("contact-id").(string), + } + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Contacts.Channels.SetPrimary( diff --git a/pkg/cmd/export.go b/pkg/cmd/export.go index d5eb49e..3c979d5 100644 --- a/pkg/cmd/export.go +++ b/pkg/cmd/export.go @@ -46,8 +46,9 @@ var exportsRetrieve = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "export-id", - Required: true, + Name: "export-id", + Required: true, + PathParam: "exportId", }, }, Action: handleExportsRetrieve, @@ -90,8 +91,6 @@ func handleExportsCreate(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.ExportNewParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -103,6 +102,8 @@ func handleExportsCreate(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.ExportNewParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Exports.New(ctx, params, options...) @@ -173,8 +174,6 @@ func handleExportsList(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.ExportListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -186,6 +185,8 @@ func handleExportsList(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.ExportListParams{} + format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") diff --git a/pkg/cmd/flagoptions.go b/pkg/cmd/flagoptions.go index d19a1ad..8ed654c 100644 --- a/pkg/cmd/flagoptions.go +++ b/pkg/cmd/flagoptions.go @@ -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/introspect.go b/pkg/cmd/introspect.go index eddbbe6..7b3a980 100644 --- a/pkg/cmd/introspect.go +++ b/pkg/cmd/introspect.go @@ -37,8 +37,6 @@ func handleIntrospectValidatePhone(ctx context.Context, cmd *cli.Command) error return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.IntrospectValidatePhoneParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -50,6 +48,8 @@ func handleIntrospectValidatePhone(ctx context.Context, cmd *cli.Command) error return err } + params := zavudev.IntrospectValidatePhoneParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Introspect.ValidatePhone(ctx, params, options...) diff --git a/pkg/cmd/invitation.go b/pkg/cmd/invitation.go index 5f20a47..1ac248a 100644 --- a/pkg/cmd/invitation.go +++ b/pkg/cmd/invitation.go @@ -61,8 +61,9 @@ var invitationsRetrieve = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "invitation-id", - Required: true, + Name: "invitation-id", + Required: true, + PathParam: "invitationId", }, }, Action: handleInvitationsRetrieve, @@ -103,8 +104,9 @@ var invitationsCancel = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "invitation-id", - Required: true, + Name: "invitation-id", + Required: true, + PathParam: "invitationId", }, }, Action: handleInvitationsCancel, @@ -119,8 +121,6 @@ func handleInvitationsCreate(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.InvitationNewParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -132,6 +132,8 @@ func handleInvitationsCreate(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.InvitationNewParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Invitations.New(ctx, params, options...) @@ -202,8 +204,6 @@ func handleInvitationsList(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.InvitationListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -215,6 +215,8 @@ func handleInvitationsList(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.InvitationListParams{} + format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") diff --git a/pkg/cmd/message.go b/pkg/cmd/message.go index e4f9261..9afdd11 100644 --- a/pkg/cmd/message.go +++ b/pkg/cmd/message.go @@ -20,8 +20,9 @@ var messagesRetrieve = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "message-id", - Required: true, + Name: "message-id", + Required: true, + PathParam: "messageId", }, }, Action: handleMessagesRetrieve, @@ -71,8 +72,9 @@ var messagesReact = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "message-id", - Required: true, + Name: "message-id", + Required: true, + PathParam: "messageId", }, &requestflag.Flag[string]{ Name: "emoji", @@ -365,8 +367,6 @@ func handleMessagesList(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.MessageListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -378,6 +378,8 @@ func handleMessagesList(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.MessageListParams{} + format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") @@ -423,8 +425,6 @@ func handleMessagesReact(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.MessageReactParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -436,6 +436,8 @@ func handleMessagesReact(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.MessageReactParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Messages.React( @@ -469,8 +471,6 @@ func handleMessagesSend(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.MessageSendParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -482,6 +482,8 @@ func handleMessagesSend(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.MessageSendParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Messages.Send(ctx, params, options...) diff --git a/pkg/cmd/number10dlcbrand.go b/pkg/cmd/number10dlcbrand.go index 4ebd5f9..7a91c20 100644 --- a/pkg/cmd/number10dlcbrand.go +++ b/pkg/cmd/number10dlcbrand.go @@ -115,8 +115,9 @@ var number10dlcBrandsRetrieve = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "brand-id", - Required: true, + Name: "brand-id", + Required: true, + PathParam: "brandId", }, }, Action: handleNumber10dlcBrandsRetrieve, @@ -129,8 +130,9 @@ var number10dlcBrandsUpdate = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "brand-id", - Required: true, + Name: "brand-id", + Required: true, + PathParam: "brandId", }, &requestflag.Flag[string]{ Name: "city", @@ -235,8 +237,9 @@ var number10dlcBrandsDelete = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "brand-id", - Required: true, + Name: "brand-id", + Required: true, + PathParam: "brandId", }, }, Action: handleNumber10dlcBrandsDelete, @@ -258,8 +261,9 @@ var number10dlcBrandsSubmit = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "brand-id", - Required: true, + Name: "brand-id", + Required: true, + PathParam: "brandId", }, }, Action: handleNumber10dlcBrandsSubmit, @@ -272,8 +276,9 @@ var number10dlcBrandsSyncStatus = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "brand-id", - Required: true, + Name: "brand-id", + Required: true, + PathParam: "brandId", }, }, Action: handleNumber10dlcBrandsSyncStatus, @@ -288,8 +293,6 @@ func handleNumber10dlcBrandsCreate(ctx context.Context, cmd *cli.Command) error return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.Number10dlcBrandNewParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -301,6 +304,8 @@ func handleNumber10dlcBrandsCreate(ctx context.Context, cmd *cli.Command) error return err } + params := zavudev.Number10dlcBrandNewParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Number10dlc.Brands.New(ctx, params, options...) @@ -374,8 +379,6 @@ func handleNumber10dlcBrandsUpdate(ctx context.Context, cmd *cli.Command) error return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.Number10dlcBrandUpdateParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -387,6 +390,8 @@ func handleNumber10dlcBrandsUpdate(ctx context.Context, cmd *cli.Command) error return err } + params := zavudev.Number10dlcBrandUpdateParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Number10dlc.Brands.Update( @@ -420,8 +425,6 @@ func handleNumber10dlcBrandsList(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.Number10dlcBrandListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -433,6 +436,8 @@ func handleNumber10dlcBrandsList(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.Number10dlcBrandListParams{} + format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") diff --git a/pkg/cmd/number10dlccampaign.go b/pkg/cmd/number10dlccampaign.go index f29eb88..dbed674 100644 --- a/pkg/cmd/number10dlccampaign.go +++ b/pkg/cmd/number10dlccampaign.go @@ -122,8 +122,9 @@ var number10dlcCampaignsRetrieve = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "campaign-id", - Required: true, + Name: "campaign-id", + Required: true, + PathParam: "campaignId", }, }, Action: handleNumber10dlcCampaignsRetrieve, @@ -136,8 +137,9 @@ var number10dlcCampaignsUpdate = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "campaign-id", - Required: true, + Name: "campaign-id", + Required: true, + PathParam: "campaignId", }, &requestflag.Flag[string]{ Name: "description", @@ -206,8 +208,9 @@ var number10dlcCampaignsDelete = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "campaign-id", - Required: true, + Name: "campaign-id", + Required: true, + PathParam: "campaignId", }, }, Action: handleNumber10dlcCampaignsDelete, @@ -220,8 +223,9 @@ var number10dlcCampaignsSubmit = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "campaign-id", - Required: true, + Name: "campaign-id", + Required: true, + PathParam: "campaignId", }, }, Action: handleNumber10dlcCampaignsSubmit, @@ -234,8 +238,9 @@ var number10dlcCampaignsSyncStatus = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "campaign-id", - Required: true, + Name: "campaign-id", + Required: true, + PathParam: "campaignId", }, }, Action: handleNumber10dlcCampaignsSyncStatus, @@ -250,8 +255,6 @@ func handleNumber10dlcCampaignsCreate(ctx context.Context, cmd *cli.Command) err return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.Number10dlcCampaignNewParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -263,6 +266,8 @@ func handleNumber10dlcCampaignsCreate(ctx context.Context, cmd *cli.Command) err return err } + params := zavudev.Number10dlcCampaignNewParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Number10dlc.Campaigns.New(ctx, params, options...) @@ -336,8 +341,6 @@ func handleNumber10dlcCampaignsUpdate(ctx context.Context, cmd *cli.Command) err return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.Number10dlcCampaignUpdateParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -349,6 +352,8 @@ func handleNumber10dlcCampaignsUpdate(ctx context.Context, cmd *cli.Command) err return err } + params := zavudev.Number10dlcCampaignUpdateParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Number10dlc.Campaigns.Update( @@ -382,8 +387,6 @@ func handleNumber10dlcCampaignsList(ctx context.Context, cmd *cli.Command) error return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.Number10dlcCampaignListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -395,6 +398,8 @@ func handleNumber10dlcCampaignsList(ctx context.Context, cmd *cli.Command) error return err } + params := zavudev.Number10dlcCampaignListParams{} + format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") diff --git a/pkg/cmd/number10dlccampaignphonenumber.go b/pkg/cmd/number10dlccampaignphonenumber.go index c287481..b70daeb 100644 --- a/pkg/cmd/number10dlccampaignphonenumber.go +++ b/pkg/cmd/number10dlccampaignphonenumber.go @@ -20,8 +20,9 @@ var number10dlcCampaignsPhoneNumbersList = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "campaign-id", - Required: true, + Name: "campaign-id", + Required: true, + PathParam: "campaignId", }, }, Action: handleNumber10dlcCampaignsPhoneNumbersList, @@ -34,8 +35,9 @@ var number10dlcCampaignsPhoneNumbersAssign = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "campaign-id", - Required: true, + Name: "campaign-id", + Required: true, + PathParam: "campaignId", }, &requestflag.Flag[string]{ Name: "phone-number-id", @@ -54,12 +56,14 @@ var number10dlcCampaignsPhoneNumbersUnassign = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "campaign-id", - Required: true, + Name: "campaign-id", + Required: true, + PathParam: "campaignId", }, &requestflag.Flag[string]{ - Name: "assignment-id", - Required: true, + Name: "assignment-id", + Required: true, + PathParam: "assignmentId", }, }, Action: handleNumber10dlcCampaignsPhoneNumbersUnassign, @@ -119,8 +123,6 @@ func handleNumber10dlcCampaignsPhoneNumbersAssign(ctx context.Context, cmd *cli. return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.Number10dlcCampaignPhoneNumberAssignParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -132,6 +134,8 @@ func handleNumber10dlcCampaignsPhoneNumbersAssign(ctx context.Context, cmd *cli. return err } + params := zavudev.Number10dlcCampaignPhoneNumberAssignParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Number10dlc.Campaigns.PhoneNumbers.Assign( @@ -168,10 +172,6 @@ func handleNumber10dlcCampaignsPhoneNumbersUnassign(ctx context.Context, cmd *cl return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.Number10dlcCampaignPhoneNumberUnassignParams{ - CampaignID: cmd.Value("campaign-id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -183,6 +183,10 @@ func handleNumber10dlcCampaignsPhoneNumbersUnassign(ctx context.Context, cmd *cl return err } + params := zavudev.Number10dlcCampaignPhoneNumberUnassignParams{ + CampaignID: cmd.Value("campaign-id").(string), + } + return client.Number10dlc.Campaigns.PhoneNumbers.Unassign( ctx, cmd.Value("assignment-id").(string), diff --git a/pkg/cmd/phonenumber.go b/pkg/cmd/phonenumber.go index 034ec9b..432e71d 100644 --- a/pkg/cmd/phonenumber.go +++ b/pkg/cmd/phonenumber.go @@ -20,8 +20,9 @@ var phoneNumbersRetrieve = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "phone-number-id", - Required: true, + Name: "phone-number-id", + Required: true, + PathParam: "phoneNumberId", }, }, Action: handlePhoneNumbersRetrieve, @@ -34,8 +35,9 @@ var phoneNumbersUpdate = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "phone-number-id", - Required: true, + Name: "phone-number-id", + Required: true, + PathParam: "phoneNumberId", }, &requestflag.Flag[*string]{ Name: "name", @@ -108,8 +110,9 @@ var phoneNumbersRelease = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "phone-number-id", - Required: true, + Name: "phone-number-id", + Required: true, + PathParam: "phoneNumberId", }, }, Action: handlePhoneNumbersRelease, @@ -222,8 +225,6 @@ func handlePhoneNumbersUpdate(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.PhoneNumberUpdateParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -235,6 +236,8 @@ func handlePhoneNumbersUpdate(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.PhoneNumberUpdateParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.PhoneNumbers.Update( @@ -268,8 +271,6 @@ func handlePhoneNumbersList(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.PhoneNumberListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -281,6 +282,8 @@ func handlePhoneNumbersList(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.PhoneNumberListParams{} + format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") @@ -323,8 +326,6 @@ func handlePhoneNumbersPurchase(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.PhoneNumberPurchaseParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -336,6 +337,8 @@ func handlePhoneNumbersPurchase(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.PhoneNumberPurchaseParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.PhoneNumbers.Purchase(ctx, params, options...) @@ -389,8 +392,6 @@ func handlePhoneNumbersRequirements(ctx context.Context, cmd *cli.Command) error return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.PhoneNumberRequirementsParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -402,6 +403,8 @@ func handlePhoneNumbersRequirements(ctx context.Context, cmd *cli.Command) error return err } + params := zavudev.PhoneNumberRequirementsParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.PhoneNumbers.Requirements(ctx, params, options...) @@ -430,8 +433,6 @@ func handlePhoneNumbersSearchAvailable(ctx context.Context, cmd *cli.Command) er return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.PhoneNumberSearchAvailableParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -443,6 +444,8 @@ func handlePhoneNumbersSearchAvailable(ctx context.Context, cmd *cli.Command) er return err } + params := zavudev.PhoneNumberSearchAvailableParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.PhoneNumbers.SearchAvailable(ctx, params, options...) diff --git a/pkg/cmd/regulatorydocument.go b/pkg/cmd/regulatorydocument.go index e0310bb..3b31a0f 100644 --- a/pkg/cmd/regulatorydocument.go +++ b/pkg/cmd/regulatorydocument.go @@ -57,8 +57,9 @@ var regulatoryDocumentsRetrieve = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "document-id", - Required: true, + Name: "document-id", + Required: true, + PathParam: "documentId", }, }, Action: handleRegulatoryDocumentsRetrieve, @@ -94,8 +95,9 @@ var regulatoryDocumentsDelete = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "document-id", - Required: true, + Name: "document-id", + Required: true, + PathParam: "documentId", }, }, Action: handleRegulatoryDocumentsDelete, @@ -119,8 +121,6 @@ func handleRegulatoryDocumentsCreate(ctx context.Context, cmd *cli.Command) erro return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.RegulatoryDocumentNewParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -132,6 +132,8 @@ func handleRegulatoryDocumentsCreate(ctx context.Context, cmd *cli.Command) erro return err } + params := zavudev.RegulatoryDocumentNewParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.RegulatoryDocuments.New(ctx, params, options...) @@ -202,8 +204,6 @@ func handleRegulatoryDocumentsList(ctx context.Context, cmd *cli.Command) error return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.RegulatoryDocumentListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -215,6 +215,8 @@ func handleRegulatoryDocumentsList(ctx context.Context, cmd *cli.Command) error return err } + params := zavudev.RegulatoryDocumentListParams{} + format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") diff --git a/pkg/cmd/sender.go b/pkg/cmd/sender.go index b59ef17..9c7edf9 100644 --- a/pkg/cmd/sender.go +++ b/pkg/cmd/sender.go @@ -55,8 +55,9 @@ var sendersRetrieve = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "sender-id", - Required: true, + Name: "sender-id", + Required: true, + PathParam: "senderId", }, }, Action: handleSendersRetrieve, @@ -69,8 +70,9 @@ var sendersUpdate = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "sender-id", - Required: true, + Name: "sender-id", + Required: true, + PathParam: "senderId", }, &requestflag.Flag[bool]{ Name: "email-receiving-enabled", @@ -134,8 +136,9 @@ var sendersDelete = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "sender-id", - Required: true, + Name: "sender-id", + Required: true, + PathParam: "senderId", }, }, Action: handleSendersDelete, @@ -148,8 +151,9 @@ var sendersGetProfile = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "sender-id", - Required: true, + Name: "sender-id", + Required: true, + PathParam: "senderId", }, }, Action: handleSendersGetProfile, @@ -162,8 +166,9 @@ var sendersRegenerateWebhookSecret = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "sender-id", - Required: true, + Name: "sender-id", + Required: true, + PathParam: "senderId", }, }, Action: handleSendersRegenerateWebhookSecret, @@ -176,8 +181,9 @@ var sendersUpdateProfile = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "sender-id", - Required: true, + Name: "sender-id", + Required: true, + PathParam: "senderId", }, &requestflag.Flag[string]{ Name: "about", @@ -220,8 +226,9 @@ var sendersUploadProfilePicture = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "sender-id", - Required: true, + Name: "sender-id", + Required: true, + PathParam: "senderId", }, &requestflag.Flag[string]{ Name: "image-url", @@ -248,8 +255,6 @@ func handleSendersCreate(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.SenderNewParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -261,6 +266,8 @@ func handleSendersCreate(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.SenderNewParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Senders.New(ctx, params, options...) @@ -334,8 +341,6 @@ func handleSendersUpdate(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.SenderUpdateParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -347,6 +352,8 @@ func handleSendersUpdate(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.SenderUpdateParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Senders.Update( @@ -380,8 +387,6 @@ func handleSendersList(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.SenderListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -393,6 +398,8 @@ func handleSendersList(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.SenderListParams{} + format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") @@ -547,8 +554,6 @@ func handleSendersUpdateProfile(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.SenderUpdateProfileParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -560,6 +565,8 @@ func handleSendersUpdateProfile(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.SenderUpdateProfileParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Senders.UpdateProfile( @@ -596,8 +603,6 @@ func handleSendersUploadProfilePicture(ctx context.Context, cmd *cli.Command) er return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.SenderUploadProfilePictureParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -609,6 +614,8 @@ func handleSendersUploadProfilePicture(ctx context.Context, cmd *cli.Command) er return err } + params := zavudev.SenderUploadProfilePictureParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Senders.UploadProfilePicture( diff --git a/pkg/cmd/senderagent.go b/pkg/cmd/senderagent.go index b77836d..3538432 100644 --- a/pkg/cmd/senderagent.go +++ b/pkg/cmd/senderagent.go @@ -20,8 +20,9 @@ var sendersAgentCreate = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "sender-id", - Required: true, + Name: "sender-id", + Required: true, + PathParam: "senderId", }, &requestflag.Flag[string]{ Name: "model", @@ -88,8 +89,9 @@ var sendersAgentRetrieve = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "sender-id", - Required: true, + Name: "sender-id", + Required: true, + PathParam: "senderId", }, }, Action: handleSendersAgentRetrieve, @@ -102,8 +104,9 @@ var sendersAgentUpdate = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "sender-id", - Required: true, + Name: "sender-id", + Required: true, + PathParam: "senderId", }, &requestflag.Flag[string]{ Name: "api-key", @@ -165,8 +168,9 @@ var sendersAgentDelete = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "sender-id", - Required: true, + Name: "sender-id", + Required: true, + PathParam: "senderId", }, }, Action: handleSendersAgentDelete, @@ -179,8 +183,9 @@ var sendersAgentStats = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "sender-id", - Required: true, + Name: "sender-id", + Required: true, + PathParam: "senderId", }, }, Action: handleSendersAgentStats, @@ -198,8 +203,6 @@ func handleSendersAgentCreate(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.SenderAgentNewParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -211,6 +214,8 @@ func handleSendersAgentCreate(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.SenderAgentNewParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Senders.Agent.New( @@ -289,8 +294,6 @@ func handleSendersAgentUpdate(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.SenderAgentUpdateParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -302,6 +305,8 @@ func handleSendersAgentUpdate(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.SenderAgentUpdateParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Senders.Agent.Update( diff --git a/pkg/cmd/senderagentexecution.go b/pkg/cmd/senderagentexecution.go index 0a4d347..f41cf56 100644 --- a/pkg/cmd/senderagentexecution.go +++ b/pkg/cmd/senderagentexecution.go @@ -20,8 +20,9 @@ var sendersAgentExecutionsList = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "sender-id", - Required: true, + Name: "sender-id", + Required: true, + PathParam: "senderId", }, &requestflag.Flag[string]{ Name: "cursor", @@ -57,8 +58,6 @@ func handleSendersAgentExecutionsList(ctx context.Context, cmd *cli.Command) err return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.SenderAgentExecutionListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -70,6 +69,8 @@ func handleSendersAgentExecutionsList(ctx context.Context, cmd *cli.Command) err return err } + params := zavudev.SenderAgentExecutionListParams{} + format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") diff --git a/pkg/cmd/senderagentflow.go b/pkg/cmd/senderagentflow.go index 4ab21ec..682c11a 100644 --- a/pkg/cmd/senderagentflow.go +++ b/pkg/cmd/senderagentflow.go @@ -20,8 +20,9 @@ var sendersAgentFlowsCreate = requestflag.WithInnerFlags(cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "sender-id", - Required: true, + Name: "sender-id", + Required: true, + PathParam: "senderId", }, &requestflag.Flag[string]{ Name: "name", @@ -103,12 +104,14 @@ var sendersAgentFlowsRetrieve = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "sender-id", - Required: true, + Name: "sender-id", + Required: true, + PathParam: "senderId", }, &requestflag.Flag[string]{ - Name: "flow-id", - Required: true, + Name: "flow-id", + Required: true, + PathParam: "flowId", }, }, Action: handleSendersAgentFlowsRetrieve, @@ -121,12 +124,14 @@ var sendersAgentFlowsUpdate = requestflag.WithInnerFlags(cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "sender-id", - Required: true, + Name: "sender-id", + Required: true, + PathParam: "senderId", }, &requestflag.Flag[string]{ - Name: "flow-id", - Required: true, + Name: "flow-id", + Required: true, + PathParam: "flowId", }, &requestflag.Flag[string]{ Name: "description", @@ -203,8 +208,9 @@ var sendersAgentFlowsList = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "sender-id", - Required: true, + Name: "sender-id", + Required: true, + PathParam: "senderId", }, &requestflag.Flag[string]{ Name: "cursor", @@ -234,12 +240,14 @@ var sendersAgentFlowsDelete = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "sender-id", - Required: true, + Name: "sender-id", + Required: true, + PathParam: "senderId", }, &requestflag.Flag[string]{ - Name: "flow-id", - Required: true, + Name: "flow-id", + Required: true, + PathParam: "flowId", }, }, Action: handleSendersAgentFlowsDelete, @@ -252,12 +260,14 @@ var sendersAgentFlowsDuplicate = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "sender-id", - Required: true, + Name: "sender-id", + Required: true, + PathParam: "senderId", }, &requestflag.Flag[string]{ - Name: "flow-id", - Required: true, + Name: "flow-id", + Required: true, + PathParam: "flowId", }, &requestflag.Flag[string]{ Name: "new-name", @@ -280,8 +290,6 @@ func handleSendersAgentFlowsCreate(ctx context.Context, cmd *cli.Command) error return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.SenderAgentFlowNewParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -293,6 +301,8 @@ func handleSendersAgentFlowsCreate(ctx context.Context, cmd *cli.Command) error return err } + params := zavudev.SenderAgentFlowNewParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Senders.Agent.Flows.New( @@ -329,10 +339,6 @@ func handleSendersAgentFlowsRetrieve(ctx context.Context, cmd *cli.Command) erro return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.SenderAgentFlowGetParams{ - SenderID: cmd.Value("sender-id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -344,6 +350,10 @@ func handleSendersAgentFlowsRetrieve(ctx context.Context, cmd *cli.Command) erro return err } + params := zavudev.SenderAgentFlowGetParams{ + SenderID: cmd.Value("sender-id").(string), + } + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Senders.Agent.Flows.Get( @@ -380,10 +390,6 @@ func handleSendersAgentFlowsUpdate(ctx context.Context, cmd *cli.Command) error return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.SenderAgentFlowUpdateParams{ - SenderID: cmd.Value("sender-id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -395,6 +401,10 @@ func handleSendersAgentFlowsUpdate(ctx context.Context, cmd *cli.Command) error return err } + params := zavudev.SenderAgentFlowUpdateParams{ + SenderID: cmd.Value("sender-id").(string), + } + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Senders.Agent.Flows.Update( @@ -431,8 +441,6 @@ func handleSendersAgentFlowsList(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.SenderAgentFlowListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -444,6 +452,8 @@ func handleSendersAgentFlowsList(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.SenderAgentFlowListParams{} + format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") @@ -499,10 +509,6 @@ func handleSendersAgentFlowsDelete(ctx context.Context, cmd *cli.Command) error return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.SenderAgentFlowDeleteParams{ - SenderID: cmd.Value("sender-id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -514,6 +520,10 @@ func handleSendersAgentFlowsDelete(ctx context.Context, cmd *cli.Command) error return err } + params := zavudev.SenderAgentFlowDeleteParams{ + SenderID: cmd.Value("sender-id").(string), + } + return client.Senders.Agent.Flows.Delete( ctx, cmd.Value("flow-id").(string), @@ -533,10 +543,6 @@ func handleSendersAgentFlowsDuplicate(ctx context.Context, cmd *cli.Command) err return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.SenderAgentFlowDuplicateParams{ - SenderID: cmd.Value("sender-id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -548,6 +554,10 @@ func handleSendersAgentFlowsDuplicate(ctx context.Context, cmd *cli.Command) err return err } + params := zavudev.SenderAgentFlowDuplicateParams{ + SenderID: cmd.Value("sender-id").(string), + } + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Senders.Agent.Flows.Duplicate( diff --git a/pkg/cmd/senderagentknowledgebase.go b/pkg/cmd/senderagentknowledgebase.go index 38e7394..fa8f1d3 100644 --- a/pkg/cmd/senderagentknowledgebase.go +++ b/pkg/cmd/senderagentknowledgebase.go @@ -20,8 +20,9 @@ var sendersAgentKnowledgeBasesCreate = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "sender-id", - Required: true, + Name: "sender-id", + Required: true, + PathParam: "senderId", }, &requestflag.Flag[string]{ Name: "name", @@ -43,12 +44,14 @@ var sendersAgentKnowledgeBasesRetrieve = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "sender-id", - Required: true, + Name: "sender-id", + Required: true, + PathParam: "senderId", }, &requestflag.Flag[string]{ - Name: "kb-id", - Required: true, + Name: "kb-id", + Required: true, + PathParam: "kbId", }, }, Action: handleSendersAgentKnowledgeBasesRetrieve, @@ -61,12 +64,14 @@ var sendersAgentKnowledgeBasesUpdate = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "sender-id", - Required: true, + Name: "sender-id", + Required: true, + PathParam: "senderId", }, &requestflag.Flag[string]{ - Name: "kb-id", - Required: true, + Name: "kb-id", + Required: true, + PathParam: "kbId", }, &requestflag.Flag[*string]{ Name: "description", @@ -87,8 +92,9 @@ var sendersAgentKnowledgeBasesList = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "sender-id", - Required: true, + Name: "sender-id", + Required: true, + PathParam: "senderId", }, &requestflag.Flag[string]{ Name: "cursor", @@ -114,12 +120,14 @@ var sendersAgentKnowledgeBasesDelete = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "sender-id", - Required: true, + Name: "sender-id", + Required: true, + PathParam: "senderId", }, &requestflag.Flag[string]{ - Name: "kb-id", - Required: true, + Name: "kb-id", + Required: true, + PathParam: "kbId", }, }, Action: handleSendersAgentKnowledgeBasesDelete, @@ -137,8 +145,6 @@ func handleSendersAgentKnowledgeBasesCreate(ctx context.Context, cmd *cli.Comman return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.SenderAgentKnowledgeBaseNewParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -150,6 +156,8 @@ func handleSendersAgentKnowledgeBasesCreate(ctx context.Context, cmd *cli.Comman return err } + params := zavudev.SenderAgentKnowledgeBaseNewParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Senders.Agent.KnowledgeBases.New( @@ -186,10 +194,6 @@ func handleSendersAgentKnowledgeBasesRetrieve(ctx context.Context, cmd *cli.Comm return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.SenderAgentKnowledgeBaseGetParams{ - SenderID: cmd.Value("sender-id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -201,6 +205,10 @@ func handleSendersAgentKnowledgeBasesRetrieve(ctx context.Context, cmd *cli.Comm return err } + params := zavudev.SenderAgentKnowledgeBaseGetParams{ + SenderID: cmd.Value("sender-id").(string), + } + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Senders.Agent.KnowledgeBases.Get( @@ -237,10 +245,6 @@ func handleSendersAgentKnowledgeBasesUpdate(ctx context.Context, cmd *cli.Comman return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.SenderAgentKnowledgeBaseUpdateParams{ - SenderID: cmd.Value("sender-id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -252,6 +256,10 @@ func handleSendersAgentKnowledgeBasesUpdate(ctx context.Context, cmd *cli.Comman return err } + params := zavudev.SenderAgentKnowledgeBaseUpdateParams{ + SenderID: cmd.Value("sender-id").(string), + } + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Senders.Agent.KnowledgeBases.Update( @@ -288,8 +296,6 @@ func handleSendersAgentKnowledgeBasesList(ctx context.Context, cmd *cli.Command) return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.SenderAgentKnowledgeBaseListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -301,6 +307,8 @@ func handleSendersAgentKnowledgeBasesList(ctx context.Context, cmd *cli.Command) return err } + params := zavudev.SenderAgentKnowledgeBaseListParams{} + format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") @@ -356,10 +364,6 @@ func handleSendersAgentKnowledgeBasesDelete(ctx context.Context, cmd *cli.Comman return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.SenderAgentKnowledgeBaseDeleteParams{ - SenderID: cmd.Value("sender-id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -371,6 +375,10 @@ func handleSendersAgentKnowledgeBasesDelete(ctx context.Context, cmd *cli.Comman return err } + params := zavudev.SenderAgentKnowledgeBaseDeleteParams{ + SenderID: cmd.Value("sender-id").(string), + } + return client.Senders.Agent.KnowledgeBases.Delete( ctx, cmd.Value("kb-id").(string), diff --git a/pkg/cmd/senderagentknowledgebasedocument.go b/pkg/cmd/senderagentknowledgebasedocument.go index 5275a5a..abe6325 100644 --- a/pkg/cmd/senderagentknowledgebasedocument.go +++ b/pkg/cmd/senderagentknowledgebasedocument.go @@ -20,12 +20,14 @@ var sendersAgentKnowledgeBasesDocumentsCreate = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "sender-id", - Required: true, + Name: "sender-id", + Required: true, + PathParam: "senderId", }, &requestflag.Flag[string]{ - Name: "kb-id", - Required: true, + Name: "kb-id", + Required: true, + PathParam: "kbId", }, &requestflag.Flag[string]{ Name: "content", @@ -48,12 +50,14 @@ var sendersAgentKnowledgeBasesDocumentsList = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "sender-id", - Required: true, + Name: "sender-id", + Required: true, + PathParam: "senderId", }, &requestflag.Flag[string]{ - Name: "kb-id", - Required: true, + Name: "kb-id", + Required: true, + PathParam: "kbId", }, &requestflag.Flag[string]{ Name: "cursor", @@ -79,16 +83,19 @@ var sendersAgentKnowledgeBasesDocumentsDelete = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "sender-id", - Required: true, + Name: "sender-id", + Required: true, + PathParam: "senderId", }, &requestflag.Flag[string]{ - Name: "kb-id", - Required: true, + Name: "kb-id", + Required: true, + PathParam: "kbId", }, &requestflag.Flag[string]{ - Name: "doc-id", - Required: true, + Name: "doc-id", + Required: true, + PathParam: "docId", }, }, Action: handleSendersAgentKnowledgeBasesDocumentsDelete, @@ -106,10 +113,6 @@ func handleSendersAgentKnowledgeBasesDocumentsCreate(ctx context.Context, cmd *c return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.SenderAgentKnowledgeBaseDocumentNewParams{ - SenderID: cmd.Value("sender-id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -121,6 +124,10 @@ func handleSendersAgentKnowledgeBasesDocumentsCreate(ctx context.Context, cmd *c return err } + params := zavudev.SenderAgentKnowledgeBaseDocumentNewParams{ + SenderID: cmd.Value("sender-id").(string), + } + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Senders.Agent.KnowledgeBases.Documents.New( @@ -157,10 +164,6 @@ func handleSendersAgentKnowledgeBasesDocumentsList(ctx context.Context, cmd *cli return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.SenderAgentKnowledgeBaseDocumentListParams{ - SenderID: cmd.Value("sender-id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -172,6 +175,10 @@ func handleSendersAgentKnowledgeBasesDocumentsList(ctx context.Context, cmd *cli return err } + params := zavudev.SenderAgentKnowledgeBaseDocumentListParams{ + SenderID: cmd.Value("sender-id").(string), + } + format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") @@ -227,11 +234,6 @@ func handleSendersAgentKnowledgeBasesDocumentsDelete(ctx context.Context, cmd *c return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.SenderAgentKnowledgeBaseDocumentDeleteParams{ - SenderID: cmd.Value("sender-id").(string), - KBID: cmd.Value("kb-id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -243,6 +245,11 @@ func handleSendersAgentKnowledgeBasesDocumentsDelete(ctx context.Context, cmd *c return err } + params := zavudev.SenderAgentKnowledgeBaseDocumentDeleteParams{ + SenderID: cmd.Value("sender-id").(string), + KBID: cmd.Value("kb-id").(string), + } + return client.Senders.Agent.KnowledgeBases.Documents.Delete( ctx, cmd.Value("doc-id").(string), diff --git a/pkg/cmd/senderagenttool.go b/pkg/cmd/senderagenttool.go index ed57ffc..813a6ee 100644 --- a/pkg/cmd/senderagenttool.go +++ b/pkg/cmd/senderagenttool.go @@ -20,8 +20,9 @@ var sendersAgentToolsCreate = requestflag.WithInnerFlags(cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "sender-id", - Required: true, + Name: "sender-id", + Required: true, + PathParam: "senderId", }, &requestflag.Flag[string]{ Name: "description", @@ -81,12 +82,14 @@ var sendersAgentToolsRetrieve = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "sender-id", - Required: true, + Name: "sender-id", + Required: true, + PathParam: "senderId", }, &requestflag.Flag[string]{ - Name: "tool-id", - Required: true, + Name: "tool-id", + Required: true, + PathParam: "toolId", }, }, Action: handleSendersAgentToolsRetrieve, @@ -99,12 +102,14 @@ var sendersAgentToolsUpdate = requestflag.WithInnerFlags(cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "sender-id", - Required: true, + Name: "sender-id", + Required: true, + PathParam: "senderId", }, &requestflag.Flag[string]{ - Name: "tool-id", - Required: true, + Name: "tool-id", + Required: true, + PathParam: "toolId", }, &requestflag.Flag[string]{ Name: "description", @@ -157,8 +162,9 @@ var sendersAgentToolsList = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "sender-id", - Required: true, + Name: "sender-id", + Required: true, + PathParam: "senderId", }, &requestflag.Flag[string]{ Name: "cursor", @@ -188,12 +194,14 @@ var sendersAgentToolsDelete = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "sender-id", - Required: true, + Name: "sender-id", + Required: true, + PathParam: "senderId", }, &requestflag.Flag[string]{ - Name: "tool-id", - Required: true, + Name: "tool-id", + Required: true, + PathParam: "toolId", }, }, Action: handleSendersAgentToolsDelete, @@ -206,12 +214,14 @@ var sendersAgentToolsTest = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "sender-id", - Required: true, + Name: "sender-id", + Required: true, + PathParam: "senderId", }, &requestflag.Flag[string]{ - Name: "tool-id", - Required: true, + Name: "tool-id", + Required: true, + PathParam: "toolId", }, &requestflag.Flag[map[string]any]{ Name: "test-params", @@ -235,8 +245,6 @@ func handleSendersAgentToolsCreate(ctx context.Context, cmd *cli.Command) error return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.SenderAgentToolNewParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -248,6 +256,8 @@ func handleSendersAgentToolsCreate(ctx context.Context, cmd *cli.Command) error return err } + params := zavudev.SenderAgentToolNewParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Senders.Agent.Tools.New( @@ -284,10 +294,6 @@ func handleSendersAgentToolsRetrieve(ctx context.Context, cmd *cli.Command) erro return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.SenderAgentToolGetParams{ - SenderID: cmd.Value("sender-id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -299,6 +305,10 @@ func handleSendersAgentToolsRetrieve(ctx context.Context, cmd *cli.Command) erro return err } + params := zavudev.SenderAgentToolGetParams{ + SenderID: cmd.Value("sender-id").(string), + } + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Senders.Agent.Tools.Get( @@ -335,10 +345,6 @@ func handleSendersAgentToolsUpdate(ctx context.Context, cmd *cli.Command) error return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.SenderAgentToolUpdateParams{ - SenderID: cmd.Value("sender-id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -350,6 +356,10 @@ func handleSendersAgentToolsUpdate(ctx context.Context, cmd *cli.Command) error return err } + params := zavudev.SenderAgentToolUpdateParams{ + SenderID: cmd.Value("sender-id").(string), + } + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Senders.Agent.Tools.Update( @@ -386,8 +396,6 @@ func handleSendersAgentToolsList(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.SenderAgentToolListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -399,6 +407,8 @@ func handleSendersAgentToolsList(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.SenderAgentToolListParams{} + format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") @@ -454,10 +464,6 @@ func handleSendersAgentToolsDelete(ctx context.Context, cmd *cli.Command) error return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.SenderAgentToolDeleteParams{ - SenderID: cmd.Value("sender-id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -469,6 +475,10 @@ func handleSendersAgentToolsDelete(ctx context.Context, cmd *cli.Command) error return err } + params := zavudev.SenderAgentToolDeleteParams{ + SenderID: cmd.Value("sender-id").(string), + } + return client.Senders.Agent.Tools.Delete( ctx, cmd.Value("tool-id").(string), @@ -488,10 +498,6 @@ func handleSendersAgentToolsTest(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.SenderAgentToolTestParams{ - SenderID: cmd.Value("sender-id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -503,6 +509,10 @@ func handleSendersAgentToolsTest(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.SenderAgentToolTestParams{ + SenderID: cmd.Value("sender-id").(string), + } + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Senders.Agent.Tools.Test( diff --git a/pkg/cmd/senderwhatsappsync.go b/pkg/cmd/senderwhatsappsync.go index a389ac0..287ccdf 100644 --- a/pkg/cmd/senderwhatsappsync.go +++ b/pkg/cmd/senderwhatsappsync.go @@ -20,8 +20,9 @@ var sendersWhatsappSyncRetrieve = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "sender-id", - Required: true, + Name: "sender-id", + Required: true, + PathParam: "senderId", }, }, Action: handleSendersWhatsappSyncRetrieve, @@ -34,8 +35,9 @@ var sendersWhatsappSyncStartContactsSync = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "sender-id", - Required: true, + Name: "sender-id", + Required: true, + PathParam: "senderId", }, }, Action: handleSendersWhatsappSyncStartContactsSync, @@ -48,8 +50,9 @@ var sendersWhatsappSyncStartHistorySync = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "sender-id", - Required: true, + Name: "sender-id", + Required: true, + PathParam: "senderId", }, }, Action: handleSendersWhatsappSyncStartHistorySync, diff --git a/pkg/cmd/subaccount.go b/pkg/cmd/subaccount.go index 7c7a89c..fe52978 100644 --- a/pkg/cmd/subaccount.go +++ b/pkg/cmd/subaccount.go @@ -50,8 +50,9 @@ var subAccountsRetrieve = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "id", - Required: true, + Name: "id", + Required: true, + PathParam: "id", }, }, Action: handleSubAccountsRetrieve, @@ -64,8 +65,9 @@ var subAccountsUpdate = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "id", - Required: true, + Name: "id", + Required: true, + PathParam: "id", }, &requestflag.Flag[*int64]{ Name: "credit-limit", @@ -122,8 +124,9 @@ var subAccountsDeactivate = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "id", - Required: true, + Name: "id", + Required: true, + PathParam: "id", }, }, Action: handleSubAccountsDeactivate, @@ -136,8 +139,9 @@ var subAccountsGetBalance = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "id", - Required: true, + Name: "id", + Required: true, + PathParam: "id", }, }, Action: handleSubAccountsGetBalance, @@ -152,8 +156,6 @@ func handleSubAccountsCreate(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.SubAccountNewParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -165,6 +167,8 @@ func handleSubAccountsCreate(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.SubAccountNewParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.SubAccounts.New(ctx, params, options...) @@ -238,8 +242,6 @@ func handleSubAccountsUpdate(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.SubAccountUpdateParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -251,6 +253,8 @@ func handleSubAccountsUpdate(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.SubAccountUpdateParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.SubAccounts.Update( @@ -284,8 +288,6 @@ func handleSubAccountsList(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.SubAccountListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -297,6 +299,8 @@ func handleSubAccountsList(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.SubAccountListParams{} + format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") diff --git a/pkg/cmd/subaccountapikey.go b/pkg/cmd/subaccountapikey.go index 109a88d..abe4d7b 100644 --- a/pkg/cmd/subaccountapikey.go +++ b/pkg/cmd/subaccountapikey.go @@ -20,8 +20,9 @@ var subAccountsAPIKeysCreate = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "id", - Required: true, + Name: "id", + Required: true, + PathParam: "id", }, &requestflag.Flag[string]{ Name: "name", @@ -49,8 +50,9 @@ var subAccountsAPIKeysList = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "id", - Required: true, + Name: "id", + Required: true, + PathParam: "id", }, }, Action: handleSubAccountsAPIKeysList, @@ -63,12 +65,14 @@ var subAccountsAPIKeysRevoke = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "id", - Required: true, + Name: "id", + Required: true, + PathParam: "id", }, &requestflag.Flag[string]{ - Name: "key-id", - Required: true, + Name: "key-id", + Required: true, + PathParam: "keyId", }, }, Action: handleSubAccountsAPIKeysRevoke, @@ -86,8 +90,6 @@ func handleSubAccountsAPIKeysCreate(ctx context.Context, cmd *cli.Command) error return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.SubAccountAPIKeyNewParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -99,6 +101,8 @@ func handleSubAccountsAPIKeysCreate(ctx context.Context, cmd *cli.Command) error return err } + params := zavudev.SubAccountAPIKeyNewParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.SubAccounts.APIKeys.New( @@ -177,10 +181,6 @@ func handleSubAccountsAPIKeysRevoke(ctx context.Context, cmd *cli.Command) error return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.SubAccountAPIKeyRevokeParams{ - ID: cmd.Value("id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -192,6 +192,10 @@ func handleSubAccountsAPIKeysRevoke(ctx context.Context, cmd *cli.Command) error return err } + params := zavudev.SubAccountAPIKeyRevokeParams{ + ID: cmd.Value("id").(string), + } + return client.SubAccounts.APIKeys.Revoke( ctx, cmd.Value("key-id").(string), diff --git a/pkg/cmd/template.go b/pkg/cmd/template.go index 9dff13e..de4749a 100644 --- a/pkg/cmd/template.go +++ b/pkg/cmd/template.go @@ -142,8 +142,9 @@ var templatesRetrieve = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "template-id", - Required: true, + Name: "template-id", + Required: true, + PathParam: "templateId", }, }, Action: handleTemplatesRetrieve, @@ -179,8 +180,9 @@ var templatesDelete = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "template-id", - Required: true, + Name: "template-id", + Required: true, + PathParam: "templateId", }, }, Action: handleTemplatesDelete, @@ -193,8 +195,9 @@ var templatesSubmit = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "template-id", - Required: true, + Name: "template-id", + Required: true, + PathParam: "templateId", }, &requestflag.Flag[string]{ Name: "sender-id", @@ -220,8 +223,6 @@ func handleTemplatesCreate(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.TemplateNewParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -233,6 +234,8 @@ func handleTemplatesCreate(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.TemplateNewParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Templates.New(ctx, params, options...) @@ -303,8 +306,6 @@ func handleTemplatesList(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.TemplateListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -316,6 +317,8 @@ func handleTemplatesList(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.TemplateListParams{} + format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") @@ -386,8 +389,6 @@ func handleTemplatesSubmit(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.TemplateSubmitParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -399,6 +400,8 @@ func handleTemplatesSubmit(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.TemplateSubmitParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Templates.Submit( diff --git a/pkg/cmd/url.go b/pkg/cmd/url.go index 48c4232..e0b53db 100644 --- a/pkg/cmd/url.go +++ b/pkg/cmd/url.go @@ -48,8 +48,9 @@ var urlsRetrieveDetails = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "url-id", - Required: true, + Name: "url-id", + Required: true, + PathParam: "urlId", }, }, Action: handleURLsRetrieveDetails, @@ -80,8 +81,6 @@ func handleURLsListVerified(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.URLListVerifiedParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -93,6 +92,8 @@ func handleURLsListVerified(ctx context.Context, cmd *cli.Command) error { return err } + params := zavudev.URLListVerifiedParams{} + format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") @@ -177,8 +178,6 @@ func handleURLsSubmitForVerification(ctx context.Context, cmd *cli.Command) erro return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := zavudev.URLSubmitForVerificationParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -190,6 +189,8 @@ func handleURLsSubmitForVerification(ctx context.Context, cmd *cli.Command) erro return err } + params := zavudev.URLSubmitForVerificationParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.URLs.SubmitForVerification(ctx, params, options...) From e321f1672549e22ddc7b8b2aebfcf87a00670747 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 1 May 2026 05:07:45 +0000 Subject: [PATCH 25/56] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 6122602..2f5b857 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 129 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-5c9ea51bd891ecc175b2e7e85fea22d7b8624e9463dc3410e08aef2b21ed536b.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-c54c98614d8fdb8639da7e60c32765e624bb984ffea2be2276b0e0865f3cc5fc.yml openapi_spec_hash: d831e4cd9855111c1f9a1ee04a508564 config_hash: 280cf643b641e9d1b21852c7e2926d54 From 4a93079e546fcc9437520a3d9d72a1c8fc49a5e1 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 8 May 2026 05:12:35 +0000 Subject: [PATCH 26/56] chore: redact api-key headers in debug logs --- internal/debugmiddleware/debug_middleware.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/internal/debugmiddleware/debug_middleware.go b/internal/debugmiddleware/debug_middleware.go index f07b93b..647f1de 100644 --- a/internal/debugmiddleware/debug_middleware.go +++ b/internal/debugmiddleware/debug_middleware.go @@ -21,7 +21,12 @@ const redactedPlaceholder = "" // Headers known to contain sensitive information like an API key. Note that this exclude `Authorization`, // which is handled specially in `redactRequest` below. -var sensitiveHeaders = []string{} +var sensitiveHeaders = []string{ + "api-key", + "x-api-key", + "cookie", + "set-cookie", +} // RequestLogger is a middleware that logs HTTP requests and responses. type RequestLogger struct { From 21c4e03a87b377ea67bb38283cb29fb368df461a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 13 May 2026 03:06:01 +0000 Subject: [PATCH 27/56] ci: pin GitHub Actions to commit SHAs Pin all GitHub Actions referenced in generated workflows (both first-party `actions/*` and third-party) to immutable commit SHAs. Updating pinned actions is now a deliberate codegen-side bump rather than implicit on every workflow run. --- .github/actions/setup-go/action.yml | 4 ++-- .github/workflows/ci.yml | 10 +++++----- .github/workflows/publish-release.yml | 6 +++--- .github/workflows/release-doctor.yml | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/actions/setup-go/action.yml b/.github/actions/setup-go/action.yml index d847169..56e8dbe 100644 --- a/.github/actions/setup-go/action.yml +++ b/.github/actions/setup-go/action.yml @@ -7,7 +7,7 @@ inputs: runs: using: composite steps: - - uses: stainless-api/retrieve-github-access-token@v1 + - uses: stainless-api/retrieve-github-access-token@1f03f929b746c5b03dcdafa2bebbb18ca5672e1a # v1.0.0 if: github.repository == 'stainless-sdks/zavudev-cli' id: get_token with: @@ -20,7 +20,7 @@ runs: run: git config --global url."https://x-access-token:${{ steps.get_token.outputs.github_access_token }}@github.com/stainless-sdks/zavudev-go".insteadOf "https://github.com/stainless-sdks/zavudev-go" - name: Setup go - uses: actions/setup-go@v5 + uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0 with: go-version-file: ./go.mod diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e7c6b36..fe7e6cf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: ./.github/actions/setup-go with: @@ -51,7 +51,7 @@ jobs: runs-on: ${{ github.repository == 'stainless-sdks/zavudev-cli' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: ./.github/actions/setup-go with: @@ -66,7 +66,7 @@ jobs: run: ./scripts/bootstrap - name: Run goreleaser - uses: goreleaser/goreleaser-action@v6.1.0 + uses: goreleaser/goreleaser-action@9ed2f89a662bf1735a48bc8557fd212fa902bebf # v6.1.0 with: version: latest args: release --snapshot --clean --skip=publish @@ -78,7 +78,7 @@ jobs: github.repository == 'stainless-sdks/zavudev-cli' && !startsWith(github.ref, 'refs/heads/stl/') id: github-oidc - uses: actions/github-script@v8 + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: script: core.setOutput('github_token', await core.getIDToken()); @@ -98,7 +98,7 @@ jobs: runs-on: ${{ github.repository == 'stainless-sdks/zavudev-cli' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: ./.github/actions/setup-go with: diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 3e8ffd2..40c07b2 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -16,15 +16,15 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - name: Set up Go - uses: actions/setup-go@v5 + uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0 with: go-version-file: "go.mod" - name: Run GoReleaser - uses: goreleaser/goreleaser-action@v6.1.0 + uses: goreleaser/goreleaser-action@9ed2f89a662bf1735a48bc8557fd212fa902bebf # v6.1.0 with: version: latest args: release --clean diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml index 09a93d7..0ea3b8f 100644 --- a/.github/workflows/release-doctor.yml +++ b/.github/workflows/release-doctor.yml @@ -12,7 +12,7 @@ jobs: if: github.repository == 'zavudev/cli' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next') steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Check release environment run: | From fa939a050faf945c493951eebd4b82766cb28e3a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 15 May 2026 02:19:11 +0000 Subject: [PATCH 28/56] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 2f5b857..9c70ad4 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 129 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-c54c98614d8fdb8639da7e60c32765e624bb984ffea2be2276b0e0865f3cc5fc.yml -openapi_spec_hash: d831e4cd9855111c1f9a1ee04a508564 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-0785a0edecab161c257a40d7c5ef4a05c566af2d5a6b675092501feb33354f80.yml +openapi_spec_hash: 6a405a56107cbaacef7aa25fdc50f83c config_hash: 280cf643b641e9d1b21852c7e2926d54 From 04933e7fc77425c1d8f67e7f19ad54ba42cefaec Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 15 May 2026 14:20:40 +0000 Subject: [PATCH 29/56] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 9c70ad4..bded552 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 129 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-0785a0edecab161c257a40d7c5ef4a05c566af2d5a6b675092501feb33354f80.yml -openapi_spec_hash: 6a405a56107cbaacef7aa25fdc50f83c +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-0ea72a3a2a86934b9738ef955e752cb15c2d5ce39869655f163333b1ad4cc87d.yml +openapi_spec_hash: 4965aeea8f269bcc0f4308a4ce5f9c58 config_hash: 280cf643b641e9d1b21852c7e2926d54 From f7037818453dadaac1a379e1fce5d78179c93ef1 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 10 Aug 2026 23:46:12 +0000 Subject: [PATCH 30/56] chore: configure new SDK language --- .github/workflows/ci.yml | 6 +- .stats.yml | 8 +- pkg/cmd/broadcast.go | 16 +- pkg/cmd/broadcast_test.go | 10 +- pkg/cmd/broadcastcontact.go | 7 +- pkg/cmd/broadcastcontact_test.go | 10 +- pkg/cmd/cmd.go | 61 +- pkg/cmd/contact.go | 40 ++ pkg/cmd/contact_test.go | 12 + pkg/cmd/export.go | 222 -------- pkg/cmd/function.go | 525 ++++++++++++++++++ pkg/cmd/function_test.go | 165 ++++++ pkg/cmd/functionsecret.go | 202 +++++++ ...{export_test.go => functionsecret_test.go} | 54 +- pkg/cmd/invitation.go | 14 +- pkg/cmd/invitation_test.go | 2 + pkg/cmd/{usage.go => me.go} | 12 +- pkg/cmd/{plan_test.go => me_test.go} | 4 +- pkg/cmd/message.go | 108 +++- pkg/cmd/message_test.go | 30 +- pkg/cmd/number10dlcbrand.go | 2 +- pkg/cmd/number10dlccampaign.go | 2 +- pkg/cmd/phonenumber.go | 11 +- pkg/cmd/phonenumber_test.go | 1 + pkg/cmd/plan.go | 62 --- pkg/cmd/sender.go | 74 ++- pkg/cmd/sender_test.go | 28 + pkg/cmd/senderagent.go | 194 ++++++- pkg/cmd/senderagent_test.go | 128 ++++- pkg/cmd/senderagentexecution.go | 71 +++ pkg/cmd/senderagentexecution_test.go | 13 + pkg/cmd/senderagenttool.go | 4 +- pkg/cmd/template.go | 11 +- pkg/cmd/template_test.go | 8 +- pkg/cmd/usage_test.go | 20 - 35 files changed, 1720 insertions(+), 417 deletions(-) delete mode 100644 pkg/cmd/export.go create mode 100644 pkg/cmd/function.go create mode 100644 pkg/cmd/function_test.go create mode 100644 pkg/cmd/functionsecret.go rename pkg/cmd/{export_test.go => functionsecret_test.go} (55%) rename pkg/cmd/{usage.go => me.go} (76%) rename pkg/cmd/{plan_test.go => me_test.go} (84%) delete mode 100644 pkg/cmd/plan.go delete mode 100644 pkg/cmd/usage_test.go diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fe7e6cf..060b69f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: lint: timeout-minutes: 10 name: lint - runs-on: ${{ github.repository == 'stainless-sdks/zavudev-cli' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + runs-on: ${{ startsWith(github.repository, 'stainless-sdks/') && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') steps: @@ -48,7 +48,7 @@ jobs: permissions: contents: read id-token: write - runs-on: ${{ github.repository == 'stainless-sdks/zavudev-cli' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + runs-on: ${{ startsWith(github.repository, 'stainless-sdks/') && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -95,7 +95,7 @@ jobs: test: timeout-minutes: 10 name: test - runs-on: ${{ github.repository == 'stainless-sdks/zavudev-cli' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + runs-on: ${{ startsWith(github.repository, 'stainless-sdks/') && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.stats.yml b/.stats.yml index bded552..4c021c1 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 129 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-0ea72a3a2a86934b9738ef955e752cb15c2d5ce39869655f163333b1ad4cc87d.yml -openapi_spec_hash: 4965aeea8f269bcc0f4308a4ce5f9c58 -config_hash: 280cf643b641e9d1b21852c7e2926d54 +configured_endpoints: 138 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-722f1deadb9046d6c3dd01a7ea4022c276f4675bf24b56cd6cab6fe7f7b82d26.yml +openapi_spec_hash: 6b119a3213e324f3826453deca57f10b +config_hash: 2cb79865930afed7a1592e2c19b61397 diff --git a/pkg/cmd/broadcast.go b/pkg/cmd/broadcast.go index 7050d8c..4cd3c82 100644 --- a/pkg/cmd/broadcast.go +++ b/pkg/cmd/broadcast.go @@ -105,6 +105,11 @@ var broadcastsCreate = requestflag.WithInnerFlags(cli.Command{ Usage: "Default button variables for dynamic URL/OTP buttons. Keys are the button index (0, 1, 2). Per-contact values override these.", InnerField: "templateButtonVariables", }, + &requestflag.InnerFlag[map[string]any]{ + Name: "content.template-header-variables", + Usage: "Default value for a text-header variable, keyed by `1` (can be overridden per contact). If omitted, Zavu resolves the header from `templateVariables` by the header placeholder's name.", + InnerField: "templateHeaderVariables", + }, &requestflag.InnerFlag[string]{ Name: "content.template-id", Usage: "Template ID for template messages.", @@ -112,7 +117,7 @@ var broadcastsCreate = requestflag.WithInnerFlags(cli.Command{ }, &requestflag.InnerFlag[map[string]any]{ Name: "content.template-variables", - Usage: "Default body variables (can be overridden per contact). Keys are positions (1, 2, ...).", + Usage: "Default body variables (can be overridden per contact). Key them to match the template body: by position (`1`, `2`, ...) for positional templates, or by name (e.g. `customer_name`) for named templates. Zavu detects the template's format and sends the correct payload to Meta. Do not mix positional and named keys.", InnerField: "templateVariables", }, }, @@ -198,6 +203,11 @@ var broadcastsUpdate = requestflag.WithInnerFlags(cli.Command{ Usage: "Default button variables for dynamic URL/OTP buttons. Keys are the button index (0, 1, 2). Per-contact values override these.", InnerField: "templateButtonVariables", }, + &requestflag.InnerFlag[map[string]any]{ + Name: "content.template-header-variables", + Usage: "Default value for a text-header variable, keyed by `1` (can be overridden per contact). If omitted, Zavu resolves the header from `templateVariables` by the header placeholder's name.", + InnerField: "templateHeaderVariables", + }, &requestflag.InnerFlag[string]{ Name: "content.template-id", Usage: "Template ID for template messages.", @@ -205,7 +215,7 @@ var broadcastsUpdate = requestflag.WithInnerFlags(cli.Command{ }, &requestflag.InnerFlag[map[string]any]{ Name: "content.template-variables", - Usage: "Default body variables (can be overridden per contact). Keys are positions (1, 2, ...).", + Usage: "Default body variables (can be overridden per contact). Key them to match the template body: by position (`1`, `2`, ...) for positional templates, or by name (e.g. `customer_name`) for named templates. Zavu detects the template's format and sends the correct payload to Meta. Do not mix positional and named keys.", InnerField: "templateVariables", }, }, @@ -337,7 +347,7 @@ var broadcastsRetryReview = cli.Command{ var broadcastsSend = cli.Command{ Name: "send", - Usage: "Start sending the broadcast immediately or schedule for later. Broadcasts go\nthrough automated AI content review before sending. If the review passes, the\nbroadcast proceeds. If rejected, use PATCH to edit content, then call POST\n/retry-review. Reserves the estimated cost from your balance.", + Usage: "Start sending the broadcast immediately or schedule for later.", Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ diff --git a/pkg/cmd/broadcast_test.go b/pkg/cmd/broadcast_test.go index b6e7e22..bc05c62 100644 --- a/pkg/cmd/broadcast_test.go +++ b/pkg/cmd/broadcast_test.go @@ -18,7 +18,7 @@ func TestBroadcastsCreate(t *testing.T) { "broadcasts", "create", "--channel", "sms", "--name", "Black Friday Sale", - "--content", "{filename: filename, mediaId: mediaId, mediaUrl: mediaUrl, mimeType: mimeType, templateButtonVariables: {foo: string}, templateId: templateId, templateVariables: {foo: string}}", + "--content", "{filename: filename, mediaId: mediaId, mediaUrl: mediaUrl, mimeType: mimeType, templateButtonVariables: {foo: string}, templateHeaderVariables: {foo: string}, templateId: templateId, templateVariables: {foo: string}}", "--email-html-body", "emailHtmlBody", "--email-subject", "emailSubject", "--idempotency-key", "idempotencyKey", @@ -46,6 +46,7 @@ func TestBroadcastsCreate(t *testing.T) { "--content.media-url", "mediaUrl", "--content.mime-type", "mimeType", "--content.template-button-variables", "{foo: string}", + "--content.template-header-variables", "{foo: string}", "--content.template-id", "templateId", "--content.template-variables", "{foo: string}", "--email-html-body", "emailHtmlBody", @@ -71,6 +72,8 @@ func TestBroadcastsCreate(t *testing.T) { " mimeType: mimeType\n" + " templateButtonVariables:\n" + " foo: string\n" + + " templateHeaderVariables:\n" + + " foo: string\n" + " templateId: templateId\n" + " templateVariables:\n" + " foo: string\n" + @@ -111,7 +114,7 @@ func TestBroadcastsUpdate(t *testing.T) { "--api-key", "string", "broadcasts", "update", "--broadcast-id", "broadcastId", - "--content", "{filename: filename, mediaId: mediaId, mediaUrl: mediaUrl, mimeType: mimeType, templateButtonVariables: {foo: string}, templateId: templateId, templateVariables: {foo: string}}", + "--content", "{filename: filename, mediaId: mediaId, mediaUrl: mediaUrl, mimeType: mimeType, templateButtonVariables: {foo: string}, templateHeaderVariables: {foo: string}, templateId: templateId, templateVariables: {foo: string}}", "--email-html-body", "emailHtmlBody", "--email-subject", "emailSubject", "--metadata", "{foo: string}", @@ -135,6 +138,7 @@ func TestBroadcastsUpdate(t *testing.T) { "--content.media-url", "mediaUrl", "--content.mime-type", "mimeType", "--content.template-button-variables", "{foo: string}", + "--content.template-header-variables", "{foo: string}", "--content.template-id", "templateId", "--content.template-variables", "{foo: string}", "--email-html-body", "emailHtmlBody", @@ -155,6 +159,8 @@ func TestBroadcastsUpdate(t *testing.T) { " mimeType: mimeType\n" + " templateButtonVariables:\n" + " foo: string\n" + + " templateHeaderVariables:\n" + + " foo: string\n" + " templateId: templateId\n" + " templateVariables:\n" + " foo: string\n" + diff --git a/pkg/cmd/broadcastcontact.go b/pkg/cmd/broadcastcontact.go index 206339b..04ae086 100644 --- a/pkg/cmd/broadcastcontact.go +++ b/pkg/cmd/broadcastcontact.go @@ -78,9 +78,14 @@ var broadcastsContactsAdd = requestflag.WithInnerFlags(cli.Command{ Usage: "Per-contact button variables for dynamic URL/OTP buttons. Keys are the button index (0, 1, 2).", InnerField: "templateButtonVariables", }, + &requestflag.InnerFlag[map[string]any]{ + Name: "contact.template-header-variables", + Usage: "Per-contact value for a text-header variable, keyed by `1`. If omitted, Zavu resolves the header from `templateVariables` by the header placeholder's name.", + InnerField: "templateHeaderVariables", + }, &requestflag.InnerFlag[map[string]any]{ Name: "contact.template-variables", - Usage: "Per-contact body variables. Keys are positions (1, 2, ...) matching the order placeholders appear in the template body.", + Usage: "Per-contact body variables. Key them to match the template body: by position (`1`, `2`, ...) for positional templates, or by name (e.g. `customer_name`) for named templates. Zavu detects the template's format and sends the correct payload to Meta. Do not mix positional and named keys.", InnerField: "templateVariables", }, }, diff --git a/pkg/cmd/broadcastcontact_test.go b/pkg/cmd/broadcastcontact_test.go index da47b43..90846ad 100644 --- a/pkg/cmd/broadcastcontact_test.go +++ b/pkg/cmd/broadcastcontact_test.go @@ -33,8 +33,8 @@ func TestBroadcastsContactsAdd(t *testing.T) { "--api-key", "string", "broadcasts:contacts", "add", "--broadcast-id", "broadcastId", - "--contact", "{recipient: '+14155551234', templateButtonVariables: {'0': abc-report-token}, templateVariables: {name: John, order_id: ORD-001}}", - "--contact", "{recipient: '+14155555678', templateButtonVariables: {'0': abc-report-token}, templateVariables: {name: Jane, order_id: ORD-002}}", + "--contact", "{recipient: '+14155551234', templateButtonVariables: {'0': abc-report-token}, templateHeaderVariables: {'1': Jorge y Laura}, templateVariables: {name: John, order_id: ORD-001}}", + "--contact", "{recipient: '+14155555678', templateButtonVariables: {'0': abc-report-token}, templateHeaderVariables: {'1': Jorge y Laura}, templateVariables: {name: Jane, order_id: ORD-002}}", ) }) @@ -50,9 +50,11 @@ func TestBroadcastsContactsAdd(t *testing.T) { "--broadcast-id", "broadcastId", "--contact.recipient", "+14155551234", "--contact.template-button-variables", "{'0': abc-report-token}", + "--contact.template-header-variables", "{'1': Jorge y Laura}", "--contact.template-variables", "{name: John, order_id: ORD-001}", "--contact.recipient", "+14155555678", "--contact.template-button-variables", "{'0': abc-report-token}", + "--contact.template-header-variables", "{'1': Jorge y Laura}", "--contact.template-variables", "{name: Jane, order_id: ORD-002}", ) }) @@ -64,12 +66,16 @@ func TestBroadcastsContactsAdd(t *testing.T) { " - recipient: '+14155551234'\n" + " templateButtonVariables:\n" + " '0': abc-report-token\n" + + " templateHeaderVariables:\n" + + " '1': Jorge y Laura\n" + " templateVariables:\n" + " name: John\n" + " order_id: ORD-001\n" + " - recipient: '+14155555678'\n" + " templateButtonVariables:\n" + " '0': abc-report-token\n" + + " templateHeaderVariables:\n" + + " '1': Jorge y Laura\n" + " templateVariables:\n" + " name: Jane\n" + " order_id: ORD-002\n") diff --git a/pkg/cmd/cmd.go b/pkg/cmd/cmd.go index 922a4f8..d2f0a41 100644 --- a/pkg/cmd/cmd.go +++ b/pkg/cmd/cmd.go @@ -93,6 +93,7 @@ func init() { &messagesList, &messagesReact, &messagesSend, + &messagesShowTyping, }, }, { @@ -140,6 +141,7 @@ func init() { Category: "API RESOURCE", Suggest: true, Commands: []*cli.Command{ + &sendersAgentExecutionsRetrieve, &sendersAgentExecutionsList, }, }, @@ -210,6 +212,7 @@ func init() { &contactsRetrieve, &contactsUpdate, &contactsList, + &contactsDelete, &contactsDismissMergeSuggestion, &contactsMerge, &contactsRetrieveByPhone, @@ -310,16 +313,6 @@ func init() { &invitationsCancel, }, }, - { - Name: "exports", - Category: "API RESOURCE", - Suggest: true, - Commands: []*cli.Command{ - &exportsCreate, - &exportsRetrieve, - &exportsList, - }, - }, { Name: "urls", Category: "API RESOURCE", @@ -338,22 +331,6 @@ func init() { &balanceRetrieve, }, }, - { - Name: "plan", - Category: "API RESOURCE", - Suggest: true, - Commands: []*cli.Command{ - &planRetrieve, - }, - }, - { - Name: "usage", - Category: "API RESOURCE", - Suggest: true, - Commands: []*cli.Command{ - &usageRetrieve, - }, - }, { Name: "sub-accounts", Category: "API RESOURCE", @@ -416,6 +393,38 @@ func init() { &number10dlcCampaignsPhoneNumbersUnassign, }, }, + { + Name: "me", + Category: "API RESOURCE", + Suggest: true, + Commands: []*cli.Command{ + &meRetrieve, + }, + }, + { + Name: "functions", + Category: "API RESOURCE", + Suggest: true, + Commands: []*cli.Command{ + &functionsCreate, + &functionsRetrieve, + &functionsUpdate, + &functionsDelete, + &functionsDeploy, + &functionsGetDeployment, + &functionsTailLogs, + }, + }, + { + Name: "functions:secrets", + Category: "API RESOURCE", + Suggest: true, + Commands: []*cli.Command{ + &functionsSecretsList, + &functionsSecretsSet, + &functionsSecretsUnset, + }, + }, { Name: "@manpages", Usage: "Generate documentation for 'man'", diff --git a/pkg/cmd/contact.go b/pkg/cmd/contact.go index ad76820..afccb55 100644 --- a/pkg/cmd/contact.go +++ b/pkg/cmd/contact.go @@ -134,6 +134,21 @@ var contactsList = cli.Command{ HideHelpCommand: true, } +var contactsDelete = cli.Command{ + Name: "delete", + Usage: "Permanently delete a contact and its communication channels. Implements\nright-to-erasure obligations under GDPR Art. 17, Ley 19.628 (Chile) Art. 12,\nCCPA § 1798.105, and LGPD Art. 18.VI. The contact, its channels, and any\nassociated agent flow sessions and conversation threads are removed. Past\nmessage records and broadcast delivery logs are retained for billing/audit but\nno longer reference the deleted contact.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "contact-id", + Required: true, + PathParam: "contactId", + }, + }, + Action: handleContactsDelete, + HideHelpCommand: true, +} + var contactsDismissMergeSuggestion = cli.Command{ Name: "dismiss-merge-suggestion", Usage: "Dismiss the merge suggestion for a contact.", @@ -372,6 +387,31 @@ func handleContactsList(ctx context.Context, cmd *cli.Command) error { } } +func handleContactsDelete(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("contact-id") && len(unusedArgs) > 0 { + cmd.Set("contact-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + return client.Contacts.Delete(ctx, cmd.Value("contact-id").(string), options...) +} + func handleContactsDismissMergeSuggestion(ctx context.Context, cmd *cli.Command) error { client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) unusedArgs := cmd.Args().Slice() diff --git a/pkg/cmd/contact_test.go b/pkg/cmd/contact_test.go index 24fc89e..da62200 100644 --- a/pkg/cmd/contact_test.go +++ b/pkg/cmd/contact_test.go @@ -116,6 +116,18 @@ func TestContactsList(t *testing.T) { }) } +func TestContactsDelete(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "contacts", "delete", + "--contact-id", "contactId", + ) + }) +} + func TestContactsDismissMergeSuggestion(t *testing.T) { t.Skip("Mock server tests are disabled") t.Run("regular flags", func(t *testing.T) { diff --git a/pkg/cmd/export.go b/pkg/cmd/export.go deleted file mode 100644 index 3c979d5..0000000 --- a/pkg/cmd/export.go +++ /dev/null @@ -1,222 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -package cmd - -import ( - "context" - "fmt" - - "github.com/tidwall/gjson" - "github.com/urfave/cli/v3" - "github.com/zavudev/cli/internal/apiquery" - "github.com/zavudev/cli/internal/requestflag" - "github.com/zavudev/sdk-go" - "github.com/zavudev/sdk-go/option" -) - -var exportsCreate = cli.Command{ - Name: "create", - Usage: "Create a new data export job. The export will be processed asynchronously and\nthe download URL will be available when status is 'completed'. Export links\nexpire after 24 hours.", - Suggest: true, - Flags: []cli.Flag{ - &requestflag.Flag[[]string]{ - Name: "data-type", - Usage: "List of data types to include in the export.", - Required: true, - BodyPath: "dataTypes", - }, - &requestflag.Flag[any]{ - Name: "date-from", - Usage: "Start date for data to export (inclusive).", - BodyPath: "dateFrom", - }, - &requestflag.Flag[any]{ - Name: "date-to", - Usage: "End date for data to export (inclusive).", - BodyPath: "dateTo", - }, - }, - Action: handleExportsCreate, - HideHelpCommand: true, -} - -var exportsRetrieve = cli.Command{ - Name: "retrieve", - Usage: "Get details of a specific data export, including download URL when completed.", - Suggest: true, - Flags: []cli.Flag{ - &requestflag.Flag[string]{ - Name: "export-id", - Required: true, - PathParam: "exportId", - }, - }, - Action: handleExportsRetrieve, - HideHelpCommand: true, -} - -var exportsList = cli.Command{ - Name: "list", - Usage: "List data exports for this project.", - Suggest: true, - Flags: []cli.Flag{ - &requestflag.Flag[string]{ - Name: "cursor", - QueryPath: "cursor", - }, - &requestflag.Flag[int64]{ - Name: "limit", - Default: 50, - QueryPath: "limit", - }, - &requestflag.Flag[string]{ - Name: "status", - Usage: "Status of a data export job.", - QueryPath: "status", - }, - &requestflag.Flag[int64]{ - Name: "max-items", - Usage: "The maximum number of items to return (use -1 for unlimited).", - }, - }, - Action: handleExportsList, - HideHelpCommand: true, -} - -func handleExportsCreate(ctx context.Context, cmd *cli.Command) error { - client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) - unusedArgs := cmd.Args().Slice() - - if len(unusedArgs) > 0 { - return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) - } - - options, err := flagOptions( - cmd, - apiquery.NestedQueryFormatBrackets, - apiquery.ArrayQueryFormatComma, - ApplicationJSON, - false, - ) - if err != nil { - return err - } - - params := zavudev.ExportNewParams{} - - var res []byte - options = append(options, option.WithResponseBodyInto(&res)) - _, err = client.Exports.New(ctx, params, options...) - if err != nil { - return err - } - - obj := gjson.ParseBytes(res) - format := cmd.Root().String("format") - explicitFormat := cmd.Root().IsSet("format") - transform := cmd.Root().String("transform") - return ShowJSON(obj, ShowJSONOpts{ - ExplicitFormat: explicitFormat, - Format: format, - RawOutput: cmd.Root().Bool("raw-output"), - Title: "exports create", - Transform: transform, - }) -} - -func handleExportsRetrieve(ctx context.Context, cmd *cli.Command) error { - client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) - unusedArgs := cmd.Args().Slice() - if !cmd.IsSet("export-id") && len(unusedArgs) > 0 { - cmd.Set("export-id", unusedArgs[0]) - unusedArgs = unusedArgs[1:] - } - if len(unusedArgs) > 0 { - return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) - } - - options, err := flagOptions( - cmd, - apiquery.NestedQueryFormatBrackets, - apiquery.ArrayQueryFormatComma, - EmptyBody, - false, - ) - if err != nil { - return err - } - - var res []byte - options = append(options, option.WithResponseBodyInto(&res)) - _, err = client.Exports.Get(ctx, cmd.Value("export-id").(string), options...) - if err != nil { - return err - } - - obj := gjson.ParseBytes(res) - format := cmd.Root().String("format") - explicitFormat := cmd.Root().IsSet("format") - transform := cmd.Root().String("transform") - return ShowJSON(obj, ShowJSONOpts{ - ExplicitFormat: explicitFormat, - Format: format, - RawOutput: cmd.Root().Bool("raw-output"), - Title: "exports retrieve", - Transform: transform, - }) -} - -func handleExportsList(ctx context.Context, cmd *cli.Command) error { - client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) - unusedArgs := cmd.Args().Slice() - - if len(unusedArgs) > 0 { - return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) - } - - options, err := flagOptions( - cmd, - apiquery.NestedQueryFormatBrackets, - apiquery.ArrayQueryFormatComma, - EmptyBody, - false, - ) - if err != nil { - return err - } - - params := zavudev.ExportListParams{} - - format := cmd.Root().String("format") - explicitFormat := cmd.Root().IsSet("format") - transform := cmd.Root().String("transform") - if format == "raw" { - var res []byte - options = append(options, option.WithResponseBodyInto(&res)) - _, err = client.Exports.List(ctx, params, options...) - if err != nil { - return err - } - obj := gjson.ParseBytes(res) - return ShowJSON(obj, ShowJSONOpts{ - ExplicitFormat: explicitFormat, - Format: format, - RawOutput: cmd.Root().Bool("raw-output"), - Title: "exports list", - Transform: transform, - }) - } else { - iter := client.Exports.ListAutoPaging(ctx, params, options...) - maxItems := int64(-1) - if cmd.IsSet("max-items") { - maxItems = cmd.Value("max-items").(int64) - } - return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ - ExplicitFormat: explicitFormat, - Format: format, - RawOutput: cmd.Root().Bool("raw-output"), - Title: "exports list", - Transform: transform, - }) - } -} diff --git a/pkg/cmd/function.go b/pkg/cmd/function.go new file mode 100644 index 0000000..37d40db --- /dev/null +++ b/pkg/cmd/function.go @@ -0,0 +1,525 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "context" + "fmt" + + "github.com/tidwall/gjson" + "github.com/urfave/cli/v3" + "github.com/zavudev/cli/internal/apiquery" + "github.com/zavudev/cli/internal/requestflag" + "github.com/zavudev/sdk-go" + "github.com/zavudev/sdk-go/option" +) + +var functionsCreate = cli.Command{ + Name: "create", + Usage: "Create a new Zavu Function. The function starts in `draft` status. A dedicated\nAPI key is auto-provisioned and injected as the `ZAVU_API_KEY` secret so the\nfunction can call back into the Zavu API without manual setup.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "name", + Required: true, + BodyPath: "name", + }, + &requestflag.Flag[string]{ + Name: "slug", + Usage: "URL-safe identifier (lowercase, digits, hyphens). Must be unique per project.", + Required: true, + BodyPath: "slug", + }, + &requestflag.Flag[map[string]any]{ + Name: "dependencies", + Usage: "npm dependencies. Keys are package names, values are semver ranges.", + BodyPath: "dependencies", + }, + &requestflag.Flag[string]{ + Name: "description", + BodyPath: "description", + }, + &requestflag.Flag[bool]{ + Name: "http-enabled", + Usage: "Whether to expose a public HTTPS URL for this function.", + Default: false, + BodyPath: "httpEnabled", + }, + &requestflag.Flag[int64]{ + Name: "memory-mb", + Usage: "Allowed values: 128, 256, 512, 1024.", + Default: 256, + BodyPath: "memoryMb", + }, + &requestflag.Flag[string]{ + Name: "runtime", + Usage: "Runtime the function is deployed on.", + BodyPath: "runtime", + }, + &requestflag.Flag[string]{ + Name: "source-code", + Usage: "TypeScript source code for the function entry point (max ~900KB).", + BodyPath: "sourceCode", + }, + &requestflag.Flag[int64]{ + Name: "timeout-sec", + Usage: "Per-invocation timeout in seconds. Event and cron invocations are asynchronous, so a long timeout only bounds cost; a tool called during a live conversation holds up the reply, and a function exposed over HTTP is additionally bounded by the platform's HTTP response limit.", + Default: 30, + BodyPath: "timeoutSec", + }, + }, + Action: handleFunctionsCreate, + HideHelpCommand: true, +} + +var functionsRetrieve = cli.Command{ + Name: "retrieve", + Usage: "Get function", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "function-id", + Required: true, + PathParam: "functionId", + }, + }, + Action: handleFunctionsRetrieve, + HideHelpCommand: true, +} + +var functionsUpdate = cli.Command{ + Name: "update", + Usage: "Update an existing function. `sourceCode` / `dependencies` edit the draft\nwithout triggering a build — they go live on the next\n`POST /v1/functions/{functionId}/deploy`. `httpEnabled` is applied to the\ndeployed function immediately, so turning the public endpoint on or off does not\nrequire a redeploy.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "function-id", + Required: true, + PathParam: "functionId", + }, + &requestflag.Flag[map[string]any]{ + Name: "dependencies", + Usage: "New dependency map (replaces existing dependencies).", + BodyPath: "dependencies", + }, + &requestflag.Flag[bool]{ + Name: "http-enabled", + Usage: "Expose the function on its public HTTPS URL, or take it down. Applies to the already-deployed function without redeploying; the URL is returned as `publicUrl`.", + BodyPath: "httpEnabled", + }, + &requestflag.Flag[string]{ + Name: "source-code", + Usage: "New source code for the draft (replaces it).", + BodyPath: "sourceCode", + }, + }, + Action: handleFunctionsUpdate, + HideHelpCommand: true, +} + +var functionsDelete = cli.Command{ + Name: "delete", + Usage: "Permanently delete a function and cascade: triggers, secrets, deployment\nhistory, managed agents+tools, and revoke the auto-provisioned API key. The AWS\nLambda + log group are torn down asynchronously.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "function-id", + Required: true, + PathParam: "functionId", + }, + }, + Action: handleFunctionsDelete, + HideHelpCommand: true, +} + +var functionsDeploy = cli.Command{ + Name: "deploy", + Usage: "Publish the function. If `sourceCode` or `dependencies` are provided in the\nbody, they replace the current draft before deployment. Returns immediately with\na deployment ID — poll `GET /v1/functions/deployments/{deploymentId}` until\nstatus is `active` or `failed`.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "function-id", + Required: true, + PathParam: "functionId", + }, + &requestflag.Flag[map[string]any]{ + Name: "dependencies", + Usage: "New dependency map (replaces existing dependencies).", + BodyPath: "dependencies", + }, + &requestflag.Flag[string]{ + Name: "source-code", + Usage: "New source code to publish (replaces the draft).", + BodyPath: "sourceCode", + }, + }, + Action: handleFunctionsDeploy, + HideHelpCommand: true, +} + +var functionsGetDeployment = cli.Command{ + Name: "get-deployment", + Usage: "Fetch a deployment to poll its status during a deploy.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "deployment-id", + Required: true, + PathParam: "deploymentId", + }, + }, + Action: handleFunctionsGetDeployment, + HideHelpCommand: true, +} + +var functionsTailLogs = cli.Command{ + Name: "tail-logs", + Usage: "Fetch invocation logs for a function. Logs are paginated via `nextToken`. Pass\n`startTime` / `endTime` (Unix epoch milliseconds) to bound the window, or\n`filterPattern` to filter messages.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "function-id", + Required: true, + PathParam: "functionId", + }, + &requestflag.Flag[int64]{ + Name: "end-time", + Usage: "End of the log window in Unix epoch milliseconds.", + QueryPath: "endTime", + }, + &requestflag.Flag[string]{ + Name: "filter-pattern", + QueryPath: "filterPattern", + }, + &requestflag.Flag[int64]{ + Name: "limit", + Default: 100, + QueryPath: "limit", + }, + &requestflag.Flag[string]{ + Name: "next-token", + QueryPath: "nextToken", + }, + &requestflag.Flag[int64]{ + Name: "start-time", + Usage: "Start of the log window in Unix epoch milliseconds.", + QueryPath: "startTime", + }, + }, + Action: handleFunctionsTailLogs, + HideHelpCommand: true, +} + +func handleFunctionsCreate(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + params := zavudev.FunctionNewParams{} + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Functions.New(ctx, params, options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "functions create", + Transform: transform, + }) +} + +func handleFunctionsRetrieve(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("function-id") && len(unusedArgs) > 0 { + cmd.Set("function-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Functions.Get(ctx, cmd.Value("function-id").(string), options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "functions retrieve", + Transform: transform, + }) +} + +func handleFunctionsUpdate(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("function-id") && len(unusedArgs) > 0 { + cmd.Set("function-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + params := zavudev.FunctionUpdateParams{} + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Functions.Update( + ctx, + cmd.Value("function-id").(string), + params, + options..., + ) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "functions update", + Transform: transform, + }) +} + +func handleFunctionsDelete(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("function-id") && len(unusedArgs) > 0 { + cmd.Set("function-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Functions.Delete(ctx, cmd.Value("function-id").(string), options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "functions delete", + Transform: transform, + }) +} + +func handleFunctionsDeploy(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("function-id") && len(unusedArgs) > 0 { + cmd.Set("function-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + params := zavudev.FunctionDeployParams{} + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Functions.Deploy( + ctx, + cmd.Value("function-id").(string), + params, + options..., + ) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "functions deploy", + Transform: transform, + }) +} + +func handleFunctionsGetDeployment(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("deployment-id") && len(unusedArgs) > 0 { + cmd.Set("deployment-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Functions.GetDeployment(ctx, cmd.Value("deployment-id").(string), options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "functions get-deployment", + Transform: transform, + }) +} + +func handleFunctionsTailLogs(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("function-id") && len(unusedArgs) > 0 { + cmd.Set("function-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + params := zavudev.FunctionTailLogsParams{} + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Functions.TailLogs( + ctx, + cmd.Value("function-id").(string), + params, + options..., + ) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "functions tail-logs", + Transform: transform, + }) +} diff --git a/pkg/cmd/function_test.go b/pkg/cmd/function_test.go new file mode 100644 index 0000000..3931b36 --- /dev/null +++ b/pkg/cmd/function_test.go @@ -0,0 +1,165 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "testing" + + "github.com/zavudev/cli/internal/mocktest" +) + +func TestFunctionsCreate(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "functions", "create", + "--name", "Order Bot", + "--slug", "order-bot", + "--dependencies", "{openai: ^4.20.0}", + "--description", "Replies to order status questions on WhatsApp.", + "--http-enabled=true", + "--memory-mb", "128", + "--runtime", "nodejs24", + "--source-code", "import { defineFunction } from '@zavudev/functions';\n\nexport default defineFunction(async (event, ctx) => {\n ctx.log('received', event.type);\n});\n", + "--timeout-sec", "1", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("" + + "name: Order Bot\n" + + "slug: order-bot\n" + + "dependencies:\n" + + " openai: ^4.20.0\n" + + "description: Replies to order status questions on WhatsApp.\n" + + "httpEnabled: true\n" + + "memoryMb: 128\n" + + "runtime: nodejs24\n" + + "sourceCode: |\n" + + " import { defineFunction } from '@zavudev/functions';\n" + + "\n" + + " export default defineFunction(async (event, ctx) => {\n" + + " ctx.log('received', event.type);\n" + + " });\n" + + "timeoutSec: 1\n") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "functions", "create", + ) + }) +} + +func TestFunctionsRetrieve(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "functions", "retrieve", + "--function-id", "functionId", + ) + }) +} + +func TestFunctionsUpdate(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "functions", "update", + "--function-id", "functionId", + "--dependencies", "{foo: string}", + "--http-enabled=true", + "--source-code", "sourceCode", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("" + + "dependencies:\n" + + " foo: string\n" + + "httpEnabled: true\n" + + "sourceCode: sourceCode\n") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "functions", "update", + "--function-id", "functionId", + ) + }) +} + +func TestFunctionsDelete(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "functions", "delete", + "--function-id", "functionId", + ) + }) +} + +func TestFunctionsDeploy(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "functions", "deploy", + "--function-id", "functionId", + "--dependencies", "{foo: string}", + "--source-code", "sourceCode", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("" + + "dependencies:\n" + + " foo: string\n" + + "sourceCode: sourceCode\n") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "functions", "deploy", + "--function-id", "functionId", + ) + }) +} + +func TestFunctionsGetDeployment(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "functions", "get-deployment", + "--deployment-id", "deploymentId", + ) + }) +} + +func TestFunctionsTailLogs(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "functions", "tail-logs", + "--function-id", "functionId", + "--end-time", "0", + "--filter-pattern", "filterPattern", + "--limit", "1", + "--next-token", "nextToken", + "--start-time", "0", + ) + }) +} diff --git a/pkg/cmd/functionsecret.go b/pkg/cmd/functionsecret.go new file mode 100644 index 0000000..d5af2a3 --- /dev/null +++ b/pkg/cmd/functionsecret.go @@ -0,0 +1,202 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "context" + "fmt" + + "github.com/tidwall/gjson" + "github.com/urfave/cli/v3" + "github.com/zavudev/cli/internal/apiquery" + "github.com/zavudev/cli/internal/requestflag" + "github.com/zavudev/sdk-go" + "github.com/zavudev/sdk-go/option" +) + +var functionsSecretsList = cli.Command{ + Name: "list", + Usage: "Lists every secret key set on the function. Plaintext is NEVER returned — only\nthe last 4 characters of each value, for visual confirmation.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "function-id", + Required: true, + PathParam: "functionId", + }, + }, + Action: handleFunctionsSecretsList, + HideHelpCommand: true, +} + +var functionsSecretsSet = cli.Command{ + Name: "set", + Usage: "Create or update a secret on a function. Marks the function out-of-sync; the\nnext `POST /deploy` re-publishes the Lambda with the new env. Keys must match\n`[A-Z_][A-Z0-9_]*` (uppercase env-var style) and cannot start with reserved\nprefixes (AWS*, LAMBDA*, etc).", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "function-id", + Required: true, + PathParam: "functionId", + }, + &requestflag.Flag[string]{ + Name: "key", + Required: true, + PathParam: "key", + }, + &requestflag.Flag[string]{ + Name: "value", + Required: true, + BodyPath: "value", + }, + }, + Action: handleFunctionsSecretsSet, + HideHelpCommand: true, +} + +var functionsSecretsUnset = cli.Command{ + Name: "unset", + Usage: "Remove a secret from a function. Doesn't take effect on the running Lambda until\nthe next deploy.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "function-id", + Required: true, + PathParam: "functionId", + }, + &requestflag.Flag[string]{ + Name: "key", + Required: true, + PathParam: "key", + }, + }, + Action: handleFunctionsSecretsUnset, + HideHelpCommand: true, +} + +func handleFunctionsSecretsList(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("function-id") && len(unusedArgs) > 0 { + cmd.Set("function-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Functions.Secrets.List(ctx, cmd.Value("function-id").(string), options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "functions:secrets list", + Transform: transform, + }) +} + +func handleFunctionsSecretsSet(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("key") && len(unusedArgs) > 0 { + cmd.Set("key", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + params := zavudev.FunctionSecretSetParams{ + FunctionID: cmd.Value("function-id").(string), + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Functions.Secrets.Set( + ctx, + cmd.Value("key").(string), + params, + options..., + ) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "functions:secrets set", + Transform: transform, + }) +} + +func handleFunctionsSecretsUnset(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("key") && len(unusedArgs) > 0 { + cmd.Set("key", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + params := zavudev.FunctionSecretUnsetParams{ + FunctionID: cmd.Value("function-id").(string), + } + + return client.Functions.Secrets.Unset( + ctx, + cmd.Value("key").(string), + params, + options..., + ) +} diff --git a/pkg/cmd/export_test.go b/pkg/cmd/functionsecret_test.go similarity index 55% rename from pkg/cmd/export_test.go rename to pkg/cmd/functionsecret_test.go index 71041f6..9040f62 100644 --- a/pkg/cmd/export_test.go +++ b/pkg/cmd/functionsecret_test.go @@ -8,59 +8,53 @@ import ( "github.com/zavudev/cli/internal/mocktest" ) -func TestExportsCreate(t *testing.T) { +func TestFunctionsSecretsList(t *testing.T) { t.Skip("Mock server tests are disabled") t.Run("regular flags", func(t *testing.T) { mocktest.TestRunMockTestWithFlags( t, "--api-key", "string", - "exports", "create", - "--data-type", "messages", - "--data-type", "conversations", - "--date-from", "'2024-01-01T00:00:00Z'", - "--date-to", "'2024-12-31T23:59:59Z'", - ) - }) - - t.Run("piping data", func(t *testing.T) { - // Test piping YAML data over stdin - pipeData := []byte("" + - "dataTypes:\n" + - " - messages\n" + - " - conversations\n" + - "dateFrom: '2024-01-01T00:00:00Z'\n" + - "dateTo: '2024-12-31T23:59:59Z'\n") - mocktest.TestRunMockTestWithPipeAndFlags( - t, pipeData, - "--api-key", "string", - "exports", "create", + "functions:secrets", "list", + "--function-id", "functionId", ) }) } -func TestExportsRetrieve(t *testing.T) { +func TestFunctionsSecretsSet(t *testing.T) { t.Skip("Mock server tests are disabled") t.Run("regular flags", func(t *testing.T) { mocktest.TestRunMockTestWithFlags( t, "--api-key", "string", - "exports", "retrieve", - "--export-id", "exportId", + "functions:secrets", "set", + "--function-id", "functionId", + "--key", "key", + "--value", "value", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("value: value") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "functions:secrets", "set", + "--function-id", "functionId", + "--key", "key", ) }) } -func TestExportsList(t *testing.T) { +func TestFunctionsSecretsUnset(t *testing.T) { t.Skip("Mock server tests are disabled") t.Run("regular flags", func(t *testing.T) { mocktest.TestRunMockTestWithFlags( t, "--api-key", "string", - "exports", "list", - "--max-items", "10", - "--cursor", "cursor", - "--limit", "100", - "--status", "pending", + "functions:secrets", "unset", + "--function-id", "functionId", + "--key", "key", ) }) } diff --git a/pkg/cmd/invitation.go b/pkg/cmd/invitation.go index 1ac248a..c983459 100644 --- a/pkg/cmd/invitation.go +++ b/pkg/cmd/invitation.go @@ -16,12 +16,12 @@ import ( var invitationsCreate = cli.Command{ Name: "create", - Usage: "Create a partner invitation link for a client to connect their WhatsApp Business\naccount. The client will complete Meta's embedded signup flow and the resulting\nsender will be created in your project.", + Usage: "Create a partner invitation link for a client to connect a Meta channel. The\nclient opens the returned `url` and authorizes with Meta; the resulting sender\nis created in your project when they finish, and the invitation transitions to\n`completed`.", Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[[]string]{ Name: "allowed-phone-country", - Usage: "ISO country codes for allowed phone numbers.", + Usage: "ISO country codes for allowed phone numbers. Only valid when `connectionType` is `whatsapp_waba` — sending it with `messenger` returns 400.", BodyPath: "allowedPhoneCountries", }, &requestflag.Flag[string]{ @@ -39,6 +39,12 @@ var invitationsCreate = cli.Command{ Usage: "Phone number of the client in E.164 format.", BodyPath: "clientPhone", }, + &requestflag.Flag[string]{ + Name: "connection-type", + Usage: "Which Meta channel the client connects, and how.\n- `whatsapp_waba` (default): Meta's embedded signup links an official WhatsApp Business Account. Accepts `phoneNumberId` and `allowedPhoneCountries`.\n- `messenger`: the client authorizes with Facebook and picks a Facebook Page they administer. The Page's Messenger inbox — including Marketplace chats — is routed to Zavu. They must be an admin of at least one Page. A Page can only be connected to one Zavu project at a time: if the client picks a Page that another project already connected, the newer connection wins and the older one is disconnected.\n\nOne invitation connects one channel. To onboard a client on several channels, create one invitation per channel; each completes into its own sender.", + Default: "whatsapp_waba", + BodyPath: "connectionType", + }, &requestflag.Flag[int64]{ Name: "expires-in-days", Usage: "Number of days until the invitation expires.", @@ -47,7 +53,7 @@ var invitationsCreate = cli.Command{ }, &requestflag.Flag[string]{ Name: "phone-number-id", - Usage: "ID of a Zavu phone number to pre-assign for WhatsApp registration. If provided, the client will use this number instead of their own.", + Usage: "ID of a Zavu phone number to pre-assign for WhatsApp registration. If provided, the client will use this number instead of their own. Only valid when `connectionType` is `whatsapp_waba` — sending it with `messenger` returns 400, since a Facebook Page has no phone number.", BodyPath: "phoneNumberId", }, }, @@ -86,7 +92,7 @@ var invitationsList = cli.Command{ }, &requestflag.Flag[string]{ Name: "status", - Usage: "Current status of the partner invitation.", + Usage: "Current status of the partner invitation.\n\n`failed` means the client started the connection and it did not finish (they cancelled Meta's dialog, denied a permission, or abandoned the tab). A failed invitation is still usable: the same link can be retried, and it moves back to `in_progress` when the client tries again.", QueryPath: "status", }, &requestflag.Flag[int64]{ diff --git a/pkg/cmd/invitation_test.go b/pkg/cmd/invitation_test.go index 2a09733..99140bb 100644 --- a/pkg/cmd/invitation_test.go +++ b/pkg/cmd/invitation_test.go @@ -20,6 +20,7 @@ func TestInvitationsCreate(t *testing.T) { "--client-email", "contact@acme.com", "--client-name", "Acme Corp", "--client-phone", "+14155551234", + "--connection-type", "whatsapp_waba", "--expires-in-days", "1", "--phone-number-id", "pn_abc123", ) @@ -34,6 +35,7 @@ func TestInvitationsCreate(t *testing.T) { "clientEmail: contact@acme.com\n" + "clientName: Acme Corp\n" + "clientPhone: '+14155551234'\n" + + "connectionType: whatsapp_waba\n" + "expiresInDays: 1\n" + "phoneNumberId: pn_abc123\n") mocktest.TestRunMockTestWithPipeAndFlags( diff --git a/pkg/cmd/usage.go b/pkg/cmd/me.go similarity index 76% rename from pkg/cmd/usage.go rename to pkg/cmd/me.go index d6b584b..02d3892 100644 --- a/pkg/cmd/usage.go +++ b/pkg/cmd/me.go @@ -13,16 +13,16 @@ import ( "github.com/zavudev/sdk-go/option" ) -var usageRetrieve = cli.Command{ +var meRetrieve = cli.Command{ Name: "retrieve", - Usage: "Get the current month's usage counters for A2P messages and emails, along with\nthe tier limits.", + Usage: "Returns the project, team, and API key metadata bound to the current Bearer\ntoken. Used by CLIs and SDKs to confirm which project they will operate on.", Suggest: true, Flags: []cli.Flag{}, - Action: handleUsageRetrieve, + Action: handleMeRetrieve, HideHelpCommand: true, } -func handleUsageRetrieve(ctx context.Context, cmd *cli.Command) error { +func handleMeRetrieve(ctx context.Context, cmd *cli.Command) error { client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) unusedArgs := cmd.Args().Slice() @@ -43,7 +43,7 @@ func handleUsageRetrieve(ctx context.Context, cmd *cli.Command) error { var res []byte options = append(options, option.WithResponseBodyInto(&res)) - _, err = client.Usage.Get(ctx, options...) + _, err = client.Me.Get(ctx, options...) if err != nil { return err } @@ -56,7 +56,7 @@ func handleUsageRetrieve(ctx context.Context, cmd *cli.Command) error { ExplicitFormat: explicitFormat, Format: format, RawOutput: cmd.Root().Bool("raw-output"), - Title: "usage retrieve", + Title: "me retrieve", Transform: transform, }) } diff --git a/pkg/cmd/plan_test.go b/pkg/cmd/me_test.go similarity index 84% rename from pkg/cmd/plan_test.go rename to pkg/cmd/me_test.go index 57fb0ef..51b532a 100644 --- a/pkg/cmd/plan_test.go +++ b/pkg/cmd/me_test.go @@ -8,13 +8,13 @@ import ( "github.com/zavudev/cli/internal/mocktest" ) -func TestPlanRetrieve(t *testing.T) { +func TestMeRetrieve(t *testing.T) { t.Skip("Mock server tests are disabled") t.Run("regular flags", func(t *testing.T) { mocktest.TestRunMockTestWithFlags( t, "--api-key", "string", - "plan", "retrieve", + "me", "retrieve", ) }) } diff --git a/pkg/cmd/message.go b/pkg/cmd/message.go index 9afdd11..558bfa3 100644 --- a/pkg/cmd/message.go +++ b/pkg/cmd/message.go @@ -36,7 +36,7 @@ var messagesList = cli.Command{ Flags: []cli.Flag{ &requestflag.Flag[string]{ Name: "channel", - Usage: "Delivery channel. Use 'auto' for intelligent routing.", + Usage: "Filter by delivery channel.", QueryPath: "channel", }, &requestflag.Flag[string]{ @@ -50,7 +50,7 @@ var messagesList = cli.Command{ }, &requestflag.Flag[string]{ Name: "status", - Usage: `Allowed values: "queued", "sending", "sent", "delivered", "read", "failed", "received", "pending_url_verification".`, + Usage: "Filter by status. Not all stored statuses are filterable.", QueryPath: "status", }, &requestflag.Flag[string]{ @@ -98,7 +98,7 @@ var messagesSend = requestflag.WithInnerFlags(cli.Command{ Flags: []cli.Flag{ &requestflag.Flag[string]{ Name: "to", - Usage: "Recipient phone number in E.164 format, email address, or numeric chat ID (for Telegram/Instagram).", + Usage: "Recipient phone number in E.164 format, email address, WhatsApp business-scoped user ID (BSUID, e.g. `US.13491208655302741918`), or numeric chat ID (for Telegram/Instagram/Messenger). A BSUID is routed to WhatsApp and sent via the `recipient` field; use it to message a contact who adopted a username and whose phone number is hidden.", Required: true, BodyPath: "to", }, @@ -135,7 +135,7 @@ var messagesSend = requestflag.WithInnerFlags(cli.Command{ }, &requestflag.Flag[string]{ Name: "message-type", - Usage: "Type of message. Non-text types are supported by WhatsApp and Telegram (varies by type).", + Usage: "Type of message. Non-text types are supported by WhatsApp and Telegram (varies by type).\n\n`location_request` asks the recipient to share their location and is WhatsApp-only. It takes no `content` object — the prompt goes in `text` (max 1024 characters) and the button label is fixed by WhatsApp. The recipient's answer arrives as an inbound `location` message whose `content.replyToMessageId` is the ID of the request.\n\n`request_contact_info` asks the recipient to share their phone number and is WhatsApp-only. Like `location_request` it takes no `content` object — the prompt goes in `text` (max 1024 characters) and WhatsApp renders a fixed **Share Contact Info** button. The answer arrives as an inbound `contact` message. Use it to recover the phone number of a contact who adopted a WhatsApp username and is only known by their business-scoped user ID (BSUID); when they share it, Zavu automatically links the phone number to that contact.", BodyPath: "messageType", }, &requestflag.Flag[map[string]any]{ @@ -294,6 +294,31 @@ var messagesSend = requestflag.WithInnerFlags(cli.Command{ Usage: "Message ID to react to.", InnerField: "reactToMessageId", }, + &requestflag.InnerFlag[string]{ + Name: "content.reply-to-from", + Usage: "Sender of the quoted message (phone number in E.164 format).", + InnerField: "replyToFrom", + }, + &requestflag.InnerFlag[string]{ + Name: "content.reply-to-message-id", + Usage: "Zavu message ID of the quoted message this message replies to. Present on inbound messages that quote an earlier message. Omitted when the quoted message is not found in Zavu (e.g. an old or unknown message) — use replyToProviderMessageId in that case.", + InnerField: "replyToMessageId", + }, + &requestflag.InnerFlag[string]{ + Name: "content.reply-to-message-type", + Usage: "Type of the quoted message (text, image, video, etc.).", + InnerField: "replyToMessageType", + }, + &requestflag.InnerFlag[string]{ + Name: "content.reply-to-provider-message-id", + Usage: "Provider message ID (WhatsApp WAMID) of the quoted message. Present whenever an inbound message is a reply, even if the quoted message is not stored in Zavu.", + InnerField: "replyToProviderMessageId", + }, + &requestflag.InnerFlag[string]{ + Name: "content.reply-to-text", + Usage: "Truncated snippet of the quoted message's text, for display. Empty when the quoted message has no text (e.g. media).", + InnerField: "replyToText", + }, &requestflag.InnerFlag[[]map[string]any]{ Name: "content.sections", Usage: "Sections for list messages.", @@ -304,6 +329,11 @@ var messagesSend = requestflag.WithInnerFlags(cli.Command{ Usage: "Variables for dynamic button placeholders (URL buttons and OTP buttons). Keys are the button index (0, 1, 2) in the template's `buttons` array — not the placeholder name. Values substitute the `{{1}}` placeholder inside that button's URL.\n\n**WhatsApp constraints:**\n- URL buttons only accept `{{1}}` — positional, numeric, no whitespace, no name. Named placeholders like `{{token}}` are stored as literal URL text by Meta and cannot be substituted.\n- At most one placeholder per URL button.\n- A template may have at most three buttons.\n- Static URL buttons (no placeholder) and `quick_reply` buttons are not included here.", InnerField: "templateButtonVariables", }, + &requestflag.InnerFlag[map[string]any]{ + Name: "content.template-header-variables", + Usage: "Value for a text-header variable, keyed by `1` (WhatsApp text headers allow at most one variable). Optional override. If omitted, Zavu resolves the header from `templateVariables` using the header placeholder's name (e.g. `novios`). Static text headers need no value.", + InnerField: "templateHeaderVariables", + }, &requestflag.InnerFlag[string]{ Name: "content.template-id", Usage: "Template ID for template messages.", @@ -311,12 +341,31 @@ var messagesSend = requestflag.WithInnerFlags(cli.Command{ }, &requestflag.InnerFlag[map[string]any]{ Name: "content.template-variables", - Usage: "Variables for body placeholders. Keys are positions (1, 2, 3, ...) matching the order placeholders appear in the template body.", + Usage: "Variables for body placeholders. Key them to match the template body: by position (`1`, `2`, ...) for positional templates, or by name (e.g. `customer_name`) for named templates. Zavu detects the template's format and sends the correct payload to Meta. Named keys also resolve a named text-header variable. Do not mix positional and named keys in the same request.", InnerField: "templateVariables", }, }, }) +var messagesShowTyping = cli.Command{ + Name: "show-typing", + Usage: "Mark an inbound WhatsApp message as read and display a typing indicator to the\nuser while you prepare a response. The indicator is automatically dismissed when\nyou send a reply, or after 25 seconds — whichever comes first. Only valid for\ninbound WhatsApp messages. Use this when a reply will take more than a couple of\nseconds (LLM agent, tool call, lookup) to improve the recipient's experience.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "message-id", + Required: true, + PathParam: "messageId", + }, + &requestflag.Flag[string]{ + Name: "zavu-sender", + HeaderPath: "Zavu-Sender", + }, + }, + Action: handleMessagesShowTyping, + HideHelpCommand: true, +} + func handleMessagesRetrieve(ctx context.Context, cmd *cli.Command) error { client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) unusedArgs := cmd.Args().Slice() @@ -503,3 +552,52 @@ func handleMessagesSend(ctx context.Context, cmd *cli.Command) error { Transform: transform, }) } + +func handleMessagesShowTyping(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("message-id") && len(unusedArgs) > 0 { + cmd.Set("message-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + params := zavudev.MessageShowTypingParams{} + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Messages.ShowTyping( + ctx, + cmd.Value("message-id").(string), + params, + options..., + ) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "messages show-typing", + Transform: transform, + }) +} diff --git a/pkg/cmd/message_test.go b/pkg/cmd/message_test.go index 35377bc..fdb2e27 100644 --- a/pkg/cmd/message_test.go +++ b/pkg/cmd/message_test.go @@ -29,7 +29,7 @@ func TestMessagesList(t *testing.T) { "--api-key", "string", "messages", "list", "--max-items", "10", - "--channel", "auto", + "--channel", "sms", "--cursor", "cursor", "--limit", "100", "--status", "queued", @@ -74,7 +74,7 @@ func TestMessagesSend(t *testing.T) { "--to", "+56912345678", "--attachment", "{filename: invoice.pdf, content: content, content_id: logo, content_type: application/pdf, path: https://example.com}", "--channel", "auto", - "--content", "{buttons: [{id: id, title: title}], contacts: [{name: name, phones: [string]}], ctaDisplayText: See Dates, ctaHeaderMediaUrl: https://example.com, ctaHeaderText: ctaHeaderText, ctaHeaderType: text, ctaUrl: https://example.com/schedule, emoji: emoji, filename: invoice.pdf, footerText: Dates subject to change., latitude: 0, listButton: listButton, locationAddress: locationAddress, locationName: locationName, longitude: 0, mediaId: mediaId, mediaUrl: https://example.com/image.jpg, mimeType: image/jpeg, reactToMessageId: reactToMessageId, sections: [{rows: [{id: id, title: title, description: description}], title: title}], templateButtonVariables: {'0': abc-report-token}, templateId: templateId, templateVariables: {'1': John, '2': ORD-12345}}", + "--content", "{buttons: [{id: id, title: title}], contacts: [{name: name, phones: [string]}], ctaDisplayText: See Dates, ctaHeaderMediaUrl: https://example.com, ctaHeaderText: ctaHeaderText, ctaHeaderType: text, ctaUrl: https://example.com/schedule, emoji: emoji, filename: invoice.pdf, footerText: Dates subject to change., latitude: 0, listButton: listButton, locationAddress: locationAddress, locationName: locationName, longitude: 0, mediaId: mediaId, mediaUrl: https://example.com/image.jpg, mimeType: image/jpeg, reactToMessageId: reactToMessageId, replyToFrom: replyToFrom, replyToMessageId: replyToMessageId, replyToMessageType: replyToMessageType, replyToProviderMessageId: replyToProviderMessageId, replyToText: replyToText, sections: [{rows: [{id: id, title: title, description: description}], title: title}], templateButtonVariables: {'0': abc-report-token}, templateHeaderVariables: {'1': Jorge y Laura}, templateId: templateId, templateVariables: {'1': John, '2': ORD-12345}}", "--fallback-enabled=true", "--html-body", "htmlBody", "--idempotency-key", "msg_01HZY4ZP7VQY2J3BRW7Z6G0QGE", @@ -123,8 +123,14 @@ func TestMessagesSend(t *testing.T) { "--content.media-url", "https://example.com/image.jpg", "--content.mime-type", "image/jpeg", "--content.react-to-message-id", "reactToMessageId", + "--content.reply-to-from", "replyToFrom", + "--content.reply-to-message-id", "replyToMessageId", + "--content.reply-to-message-type", "replyToMessageType", + "--content.reply-to-provider-message-id", "replyToProviderMessageId", + "--content.reply-to-text", "replyToText", "--content.sections", "[{rows: [{id: id, title: title, description: description}], title: title}]", "--content.template-button-variables", "{'0': abc-report-token}", + "--content.template-header-variables", "{'1': Jorge y Laura}", "--content.template-id", "templateId", "--content.template-variables", "{'1': John, '2': ORD-12345}", "--fallback-enabled=true", @@ -176,6 +182,11 @@ func TestMessagesSend(t *testing.T) { " mediaUrl: https://example.com/image.jpg\n" + " mimeType: image/jpeg\n" + " reactToMessageId: reactToMessageId\n" + + " replyToFrom: replyToFrom\n" + + " replyToMessageId: replyToMessageId\n" + + " replyToMessageType: replyToMessageType\n" + + " replyToProviderMessageId: replyToProviderMessageId\n" + + " replyToText: replyToText\n" + " sections:\n" + " - rows:\n" + " - id: id\n" + @@ -184,6 +195,8 @@ func TestMessagesSend(t *testing.T) { " title: title\n" + " templateButtonVariables:\n" + " '0': abc-report-token\n" + + " templateHeaderVariables:\n" + + " '1': Jorge y Laura\n" + " templateId: templateId\n" + " templateVariables:\n" + " '1': John\n" + @@ -206,3 +219,16 @@ func TestMessagesSend(t *testing.T) { ) }) } + +func TestMessagesShowTyping(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "messages", "show-typing", + "--message-id", "messageId", + "--zavu-sender", "sender_12345", + ) + }) +} diff --git a/pkg/cmd/number10dlcbrand.go b/pkg/cmd/number10dlcbrand.go index 7a91c20..6511979 100644 --- a/pkg/cmd/number10dlcbrand.go +++ b/pkg/cmd/number10dlcbrand.go @@ -257,7 +257,7 @@ var number10dlcBrandsListUseCases = cli.Command{ var number10dlcBrandsSubmit = cli.Command{ Name: "submit", - Usage: "Submit a draft brand to The Campaign Registry (TCR) for vetting. The brand must\nbe in draft status. A $35 registration fee is charged from your balance.", + Usage: "Submit a draft brand to The Campaign Registry (TCR) for vetting. The brand must\nbe in draft status. TCR's one-time $4 brand registration fee is charged from\nyour balance at submission (passed through at cost) and refunded if the carrier\nrejects the registration. A team that already paid this fee through the\ncompliance flow is not charged again. Campaign registration is billed separately\nwhen a campaign is submitted.", Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ diff --git a/pkg/cmd/number10dlccampaign.go b/pkg/cmd/number10dlccampaign.go index dbed674..496ca75 100644 --- a/pkg/cmd/number10dlccampaign.go +++ b/pkg/cmd/number10dlccampaign.go @@ -219,7 +219,7 @@ var number10dlcCampaignsDelete = cli.Command{ var number10dlcCampaignsSubmit = cli.Command{ Name: "submit", - Usage: "Submit a draft campaign for carrier review. The campaign must be in draft status\nand its brand must be verified.", + Usage: "Submit a draft campaign for carrier review. The campaign must be in draft status\nand its brand must be verified. TCR's one-time registration fee is charged from\nyour balance at submission ($15 for standard use cases, $2 for LOW_VOLUME),\npassed through at cost and refunded if the carrier rejects it. Once approved,\nthe campaign's monthly TCR fee ($10 standard, $2 LOW_VOLUME) is charged from\nyour balance while the campaign is active — see registrationCostCents and\nmonthlyFeeCents on the campaign object.", Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ diff --git a/pkg/cmd/phonenumber.go b/pkg/cmd/phonenumber.go index 432e71d..d70fa19 100644 --- a/pkg/cmd/phonenumber.go +++ b/pkg/cmd/phonenumber.go @@ -85,7 +85,7 @@ var phoneNumbersList = cli.Command{ var phoneNumbersPurchase = cli.Command{ Name: "purchase", - Usage: "Purchase an available phone number. The first US phone number is free for each\nteam.", + Usage: "Purchase an available phone number. Requires a paid plan: the Free plan cannot\npurchase phone numbers and receives `402` with code `paid_plan_required`. Paid\nplans include one US number at no charge. The included number is one per account\nand is granted once: claiming it spends the benefit for good, so releasing that\nnumber does not make another one free, and numbers the account already bought do\nnot consume it.", Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ @@ -132,7 +132,7 @@ var phoneNumbersRequirements = cli.Command{ }, &requestflag.Flag[string]{ Name: "type", - Usage: `Allowed values: "local", "mobile", "tollFree".`, + Usage: `Allowed values: "local", "national", "tollFree".`, QueryPath: "type", }, }, @@ -151,6 +151,11 @@ var phoneNumbersSearchAvailable = cli.Command{ Required: true, QueryPath: "countryCode", }, + &requestflag.Flag[string]{ + Name: "capabilities", + Usage: "Comma-separated capabilities the number must have: `sms`, `voice`, `mms`. Numbers missing any of them are dropped.", + QueryPath: "capabilities", + }, &requestflag.Flag[string]{ Name: "contains", Usage: "Search for numbers containing this string.", @@ -164,7 +169,7 @@ var phoneNumbersSearchAvailable = cli.Command{ }, &requestflag.Flag[string]{ Name: "type", - Usage: `Allowed values: "local", "mobile", "tollFree".`, + Usage: `Allowed values: "local", "national", "tollFree".`, QueryPath: "type", }, }, diff --git a/pkg/cmd/phonenumber_test.go b/pkg/cmd/phonenumber_test.go index cf0ed8e..9fdcb84 100644 --- a/pkg/cmd/phonenumber_test.go +++ b/pkg/cmd/phonenumber_test.go @@ -120,6 +120,7 @@ func TestPhoneNumbersSearchAvailable(t *testing.T) { "--api-key", "string", "phone-numbers", "search-available", "--country-code", "xx", + "--capabilities", "voice,sms", "--contains", "contains", "--limit", "50", "--type", "local", diff --git a/pkg/cmd/plan.go b/pkg/cmd/plan.go deleted file mode 100644 index d76c5d7..0000000 --- a/pkg/cmd/plan.go +++ /dev/null @@ -1,62 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -package cmd - -import ( - "context" - "fmt" - - "github.com/tidwall/gjson" - "github.com/urfave/cli/v3" - "github.com/zavudev/cli/internal/apiquery" - "github.com/zavudev/sdk-go" - "github.com/zavudev/sdk-go/option" -) - -var planRetrieve = cli.Command{ - Name: "retrieve", - Usage: "Get the current subscription plan for the API key's team, including tier,\nbilling interval, and period dates.", - Suggest: true, - Flags: []cli.Flag{}, - Action: handlePlanRetrieve, - HideHelpCommand: true, -} - -func handlePlanRetrieve(ctx context.Context, cmd *cli.Command) error { - client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) - unusedArgs := cmd.Args().Slice() - - if len(unusedArgs) > 0 { - return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) - } - - options, err := flagOptions( - cmd, - apiquery.NestedQueryFormatBrackets, - apiquery.ArrayQueryFormatComma, - EmptyBody, - false, - ) - if err != nil { - return err - } - - var res []byte - options = append(options, option.WithResponseBodyInto(&res)) - _, err = client.Plan.Get(ctx, options...) - if err != nil { - return err - } - - obj := gjson.ParseBytes(res) - format := cmd.Root().String("format") - explicitFormat := cmd.Root().IsSet("format") - transform := cmd.Root().String("transform") - return ShowJSON(obj, ShowJSONOpts{ - ExplicitFormat: explicitFormat, - Format: format, - RawOutput: cmd.Root().Bool("raw-output"), - Title: "plan retrieve", - Transform: transform, - }) -} diff --git a/pkg/cmd/sender.go b/pkg/cmd/sender.go index 9c7edf9..d42f630 100644 --- a/pkg/cmd/sender.go +++ b/pkg/cmd/sender.go @@ -24,9 +24,41 @@ var sendersCreate = cli.Command{ Required: true, BodyPath: "name", }, + &requestflag.Flag[string]{ + Name: "email-address", + Usage: "From-address for the email channel (e.g. noreply@yourdomain.com). The address's domain must be a verified email domain in your project. Setting this attaches the email channel to the sender.", + BodyPath: "emailAddress", + }, + &requestflag.Flag[string]{ + Name: "email-domain-id", + Usage: "ID of the verified email domain to attach. Optional — resolved from `emailAddress`'s domain when omitted.", + BodyPath: "emailDomainId", + }, + &requestflag.Flag[string]{ + Name: "email-from-name", + Usage: "Display name shown in the recipient's inbox for the email channel.", + BodyPath: "emailFromName", + }, + &requestflag.Flag[bool]{ + Name: "email-receiving-enabled", + Usage: "Enable inbound email receiving on this sender. Requires a verified MX record on the domain; ignored otherwise.", + BodyPath: "emailReceivingEnabled", + }, + &requestflag.Flag[bool]{ + Name: "enable-sms-oneway", + Usage: "Enable the one-way SMS channel (`sms_oneway`). Needs nothing else — no phone number, no credential — so it is the fastest way to get a sender that can send. Recipients cannot reply. Confirm with `sms_oneway` in the `channels` array on the response.", + Default: false, + BodyPath: "enableSmsOneway", + }, + &requestflag.Flag[bool]{ + Name: "enable-voice", + Usage: "Let this sender place and answer phone calls. Requires `phoneNumber`; enabling it without one returns 400. Check the `channels` array on the response to confirm `voice` is on.", + Default: false, + BodyPath: "enableVoice", + }, &requestflag.Flag[string]{ Name: "phone-number", - Required: true, + Usage: "Phone number in E.164 format, and it must be a number your project already owns (see `GET /v1/phone-numbers`). The number is routed to the sender as part of this call, which is what turns the SMS channel on. Passing a number the project does not own, or one already attached to another sender, returns 400 rather than creating a sender that cannot send. Omit for an email-only sender.", BodyPath: "phoneNumber", }, &requestflag.Flag[bool]{ @@ -39,6 +71,11 @@ var sendersCreate = cli.Command{ Usage: "Events to subscribe to.", BodyPath: "webhookEvents", }, + &requestflag.Flag[string]{ + Name: "webhook-signature-version", + Usage: "Which `X-Zavu-Signature` scheme this receiver is sent.\n\n- `v1`: `v1=HMAC_SHA256(secret, body)`. The scheme used before this was configurable. Existing webhooks stay on it until you move them.\n- `v2`: `v2=HMAC_SHA256(secret, \"{t}.{body}\")`. The current scheme, and the default for new senders. It signs the timestamp together with the body.\n- `v1+v2`: both signatures, sharing one `t`. The migration setting: a receiver reading either one works, so you can deploy and confirm your new verifier before switching over.\n\nMoving from `v1` straight to `v2` returns `400`. Set `v1+v2` first. See https://docs.zavu.dev/guides/receiving-messages/signature-migration", + BodyPath: "webhookSignatureVersion", + }, &requestflag.Flag[string]{ Name: "webhook-url", Usage: "HTTPS URL for webhook events.", @@ -74,11 +111,41 @@ var sendersUpdate = cli.Command{ Required: true, PathParam: "senderId", }, + &requestflag.Flag[string]{ + Name: "email-address", + Usage: "Attach or change the sender's email from-address (e.g. noreply@yourdomain.com). The domain must be a verified email domain in your project.", + BodyPath: "emailAddress", + }, + &requestflag.Flag[bool]{ + Name: "email-catch-all-enabled", + Usage: "Enable or disable domain catch-all. When enabled (with emailReceivingEnabled true), this sender receives email for any address at its domain. Ignored (treated as false) if receiving is not enabled.", + BodyPath: "emailCatchAllEnabled", + }, + &requestflag.Flag[string]{ + Name: "email-domain-id", + Usage: "ID of the verified email domain to attach. Optional — resolved from `emailAddress`'s domain when omitted.", + BodyPath: "emailDomainId", + }, + &requestflag.Flag[string]{ + Name: "email-from-name", + Usage: "Display name shown in the recipient's inbox for the email channel.", + BodyPath: "emailFromName", + }, &requestflag.Flag[bool]{ Name: "email-receiving-enabled", Usage: "Enable or disable inbound email receiving for this sender.", BodyPath: "emailReceivingEnabled", }, + &requestflag.Flag[bool]{ + Name: "enable-sms-oneway", + Usage: "Turn the one-way SMS channel on or off. Enabling needs nothing else and takes effect immediately; disabling removes the channel from the sender. Confirm with the `channels` array on the response.", + BodyPath: "enableSmsOneway", + }, + &requestflag.Flag[bool]{ + Name: "enable-voice", + Usage: "Turn the voice channel on or off. The sender must already have a phone number provisioned for calls; enabling it otherwise returns 400 instead of storing a flag that changes nothing. Confirm with the `channels` array on the response.", + BodyPath: "enableVoice", + }, &requestflag.Flag[string]{ Name: "name", BodyPath: "name", @@ -97,6 +164,11 @@ var sendersUpdate = cli.Command{ Usage: "Events to subscribe to.", BodyPath: "webhookEvents", }, + &requestflag.Flag[string]{ + Name: "webhook-signature-version", + Usage: "Which `X-Zavu-Signature` scheme this receiver is sent.\n\n- `v1`: `v1=HMAC_SHA256(secret, body)`. The scheme used before this was configurable. Existing webhooks stay on it until you move them.\n- `v2`: `v2=HMAC_SHA256(secret, \"{t}.{body}\")`. The current scheme, and the default for new senders. It signs the timestamp together with the body.\n- `v1+v2`: both signatures, sharing one `t`. The migration setting: a receiver reading either one works, so you can deploy and confirm your new verifier before switching over.\n\nMoving from `v1` straight to `v2` returns `400`. Set `v1+v2` first. See https://docs.zavu.dev/guides/receiving-messages/signature-migration", + BodyPath: "webhookSignatureVersion", + }, &requestflag.Flag[*string]{ Name: "webhook-url", Usage: "HTTPS URL for webhook events. Set to null to remove webhook.", diff --git a/pkg/cmd/sender_test.go b/pkg/cmd/sender_test.go index b3b796f..68a7f18 100644 --- a/pkg/cmd/sender_test.go +++ b/pkg/cmd/sender_test.go @@ -16,9 +16,16 @@ func TestSendersCreate(t *testing.T) { "--api-key", "string", "senders", "create", "--name", "name", + "--email-address", "noreply@yourdomain.com", + "--email-domain-id", "emailDomainId", + "--email-from-name", "emailFromName", + "--email-receiving-enabled=true", + "--enable-sms-oneway=true", + "--enable-voice=true", "--phone-number", "phoneNumber", "--set-as-default=true", "--webhook-event", "message.queued", + "--webhook-signature-version", "v2", "--webhook-url", "https://example.com", ) }) @@ -27,10 +34,17 @@ func TestSendersCreate(t *testing.T) { // Test piping YAML data over stdin pipeData := []byte("" + "name: name\n" + + "emailAddress: noreply@yourdomain.com\n" + + "emailDomainId: emailDomainId\n" + + "emailFromName: emailFromName\n" + + "emailReceivingEnabled: true\n" + + "enableSmsOneway: true\n" + + "enableVoice: true\n" + "phoneNumber: phoneNumber\n" + "setAsDefault: true\n" + "webhookEvents:\n" + " - message.queued\n" + + "webhookSignatureVersion: v2\n" + "webhookUrl: https://example.com\n") mocktest.TestRunMockTestWithPipeAndFlags( t, pipeData, @@ -60,11 +74,18 @@ func TestSendersUpdate(t *testing.T) { "--api-key", "string", "senders", "update", "--sender-id", "senderId", + "--email-address", "noreply@yourdomain.com", + "--email-catch-all-enabled=true", + "--email-domain-id", "emailDomainId", + "--email-from-name", "emailFromName", "--email-receiving-enabled=true", + "--enable-sms-oneway=true", + "--enable-voice=true", "--name", "name", "--set-as-default=true", "--webhook-active=true", "--webhook-event", "message.queued", + "--webhook-signature-version", "v2", "--webhook-url", "https://example.com", ) }) @@ -72,12 +93,19 @@ func TestSendersUpdate(t *testing.T) { t.Run("piping data", func(t *testing.T) { // Test piping YAML data over stdin pipeData := []byte("" + + "emailAddress: noreply@yourdomain.com\n" + + "emailCatchAllEnabled: true\n" + + "emailDomainId: emailDomainId\n" + + "emailFromName: emailFromName\n" + "emailReceivingEnabled: true\n" + + "enableSmsOneway: true\n" + + "enableVoice: true\n" + "name: name\n" + "setAsDefault: true\n" + "webhookActive: true\n" + "webhookEvents:\n" + " - message.queued\n" + + "webhookSignatureVersion: v2\n" + "webhookUrl: https://example.com\n") mocktest.TestRunMockTestWithPipeAndFlags( t, pipeData, diff --git a/pkg/cmd/senderagent.go b/pkg/cmd/senderagent.go index 3538432..d69f5c3 100644 --- a/pkg/cmd/senderagent.go +++ b/pkg/cmd/senderagent.go @@ -14,7 +14,7 @@ import ( "github.com/zavudev/sdk-go/option" ) -var sendersAgentCreate = cli.Command{ +var sendersAgentCreate = requestflag.WithInnerFlags(cli.Command{ Name: "create", Usage: "Create an AI agent for a sender. Each sender can have at most one agent.", Suggest: true, @@ -78,10 +78,103 @@ var sendersAgentCreate = cli.Command{ Default: []string{"text"}, BodyPath: "triggerOnMessageTypes", }, + &requestflag.Flag[map[string]any]{ + Name: "voice", + Usage: "Voice Agent configuration. Enable this to let the agent answer and place phone calls with Zavu's managed voice pipeline. Requires the Voice Agents feature to be enabled for your team.", + BodyPath: "voice", + }, }, Action: handleSendersAgentCreate, HideHelpCommand: true, -} +}, map[string][]requestflag.HasOuterFlag{ + "voice": { + &requestflag.InnerFlag[bool]{ + Name: "voice.enabled", + Usage: "Whether the agent handles voice calls. When false, the sender's number is not answered by the voice agent and outbound calls are rejected.", + InnerField: "enabled", + }, + &requestflag.InnerFlag[string]{ + Name: "voice.greeting", + Usage: "Opening line the agent speaks when the call connects. If omitted, the agent waits for the caller to speak first.", + InnerField: "greeting", + }, + &requestflag.InnerFlag[map[string]any]{ + Name: "voice.greetings", + Usage: "Greeting per language, keyed by language code. Used when the caller's language differs from the one `greeting` is written in.", + InnerField: "greetings", + }, + &requestflag.InnerFlag[bool]{ + Name: "voice.interruptible", + Usage: "Whether the caller can interrupt the agent while it is speaking (barge-in). When true, the agent stops talking as soon as the caller starts.", + InnerField: "interruptible", + }, + &requestflag.InnerFlag[string]{ + Name: "voice.language", + Usage: "BCP-47 language code used for both speech recognition and speech synthesis (e.g. `en`, `es`, `pt-BR`). Auto-detected from the recipient when omitted.", + InnerField: "language", + }, + &requestflag.InnerFlag[int64]{ + Name: "voice.max-call-duration-minutes", + Usage: "Hard limit on call length in minutes. The call ends automatically when reached.", + InnerField: "maxCallDurationMinutes", + }, + &requestflag.InnerFlag[int64]{ + Name: "voice.max-idle-seconds", + Usage: "How long the agent waits during silence before ending the call.", + InnerField: "maxIdleSeconds", + }, + &requestflag.InnerFlag[string]{ + Name: "voice.model", + Usage: "Model that runs the conversation, co-located in the voice network for lowest latency. Independent of the model used for text messaging. Derived from the agent's text model when omitted.", + InnerField: "model", + }, + &requestflag.InnerFlag[bool]{ + Name: "voice.record-calls", + Usage: "Whether the call audio is recorded.", + InnerField: "recordCalls", + }, + &requestflag.InnerFlag[string]{ + Name: "voice.stt-model", + Usage: "Speech-recognition model. Uses the default when omitted.", + InnerField: "sttModel", + }, + &requestflag.InnerFlag[string]{ + Name: "voice.stt-provider", + Usage: "Speech-recognition provider. Uses the default when omitted.", + InnerField: "sttProvider", + }, + &requestflag.InnerFlag[string]{ + Name: "voice.transfer-phone-number", + Usage: "E.164 phone number the agent can transfer the call to. When set, the agent is given a transfer tool it can use to hand the call to a human.", + InnerField: "transferPhoneNumber", + }, + &requestflag.InnerFlag[string]{ + Name: "voice.tts-provider", + Usage: "Speech-synthesis provider. Uses the default when omitted.", + InnerField: "ttsProvider", + }, + &requestflag.InnerFlag[string]{ + Name: "voice.tts-voice-id", + Usage: "Identifier of the synthesized voice that speaks. Choose from the voices available in the dashboard. Uses a neutral default when omitted.", + InnerField: "ttsVoiceId", + }, + &requestflag.InnerFlag[string]{ + Name: "voice.voicemail-action", + Usage: "What the agent does when an answering machine or voicemail is detected on an outbound call.", + InnerField: "voicemailAction", + }, + &requestflag.InnerFlag[string]{ + Name: "voice.voicemail-message", + Usage: "Message spoken when `voicemailAction` is `leave_message`. Falls back to `greeting` when omitted.", + InnerField: "voicemailMessage", + }, + &requestflag.InnerFlag[float64]{ + Name: "voice.voice-speed", + Usage: "Speech rate. 1.0 is natural. Only honoured by voices that support rate control; ignored by the others.", + InnerField: "voiceSpeed", + }, + }, +}) var sendersAgentRetrieve = cli.Command{ Name: "retrieve", @@ -98,7 +191,7 @@ var sendersAgentRetrieve = cli.Command{ HideHelpCommand: true, } -var sendersAgentUpdate = cli.Command{ +var sendersAgentUpdate = requestflag.WithInnerFlags(cli.Command{ Name: "update", Usage: "Update an AI agent's configuration.", Suggest: true, @@ -157,10 +250,103 @@ var sendersAgentUpdate = cli.Command{ Name: "trigger-on-message-type", BodyPath: "triggerOnMessageTypes", }, + &requestflag.Flag[map[string]any]{ + Name: "voice", + Usage: "Voice Agent configuration. Patch this object to enable voice, change the greeting, or adjust call limits. Requires the Voice Agents feature to be enabled for your team.", + BodyPath: "voice", + }, }, Action: handleSendersAgentUpdate, HideHelpCommand: true, -} +}, map[string][]requestflag.HasOuterFlag{ + "voice": { + &requestflag.InnerFlag[bool]{ + Name: "voice.enabled", + Usage: "Whether the agent handles voice calls. When false, the sender's number is not answered by the voice agent and outbound calls are rejected.", + InnerField: "enabled", + }, + &requestflag.InnerFlag[string]{ + Name: "voice.greeting", + Usage: "Opening line the agent speaks when the call connects. If omitted, the agent waits for the caller to speak first.", + InnerField: "greeting", + }, + &requestflag.InnerFlag[map[string]any]{ + Name: "voice.greetings", + Usage: "Greeting per language, keyed by language code. Used when the caller's language differs from the one `greeting` is written in.", + InnerField: "greetings", + }, + &requestflag.InnerFlag[bool]{ + Name: "voice.interruptible", + Usage: "Whether the caller can interrupt the agent while it is speaking (barge-in). When true, the agent stops talking as soon as the caller starts.", + InnerField: "interruptible", + }, + &requestflag.InnerFlag[string]{ + Name: "voice.language", + Usage: "BCP-47 language code used for both speech recognition and speech synthesis (e.g. `en`, `es`, `pt-BR`). Auto-detected from the recipient when omitted.", + InnerField: "language", + }, + &requestflag.InnerFlag[int64]{ + Name: "voice.max-call-duration-minutes", + Usage: "Hard limit on call length in minutes. The call ends automatically when reached.", + InnerField: "maxCallDurationMinutes", + }, + &requestflag.InnerFlag[int64]{ + Name: "voice.max-idle-seconds", + Usage: "How long the agent waits during silence before ending the call.", + InnerField: "maxIdleSeconds", + }, + &requestflag.InnerFlag[string]{ + Name: "voice.model", + Usage: "Model that runs the conversation, co-located in the voice network for lowest latency. Independent of the model used for text messaging. Derived from the agent's text model when omitted.", + InnerField: "model", + }, + &requestflag.InnerFlag[bool]{ + Name: "voice.record-calls", + Usage: "Whether the call audio is recorded.", + InnerField: "recordCalls", + }, + &requestflag.InnerFlag[string]{ + Name: "voice.stt-model", + Usage: "Speech-recognition model. Uses the default when omitted.", + InnerField: "sttModel", + }, + &requestflag.InnerFlag[string]{ + Name: "voice.stt-provider", + Usage: "Speech-recognition provider. Uses the default when omitted.", + InnerField: "sttProvider", + }, + &requestflag.InnerFlag[string]{ + Name: "voice.transfer-phone-number", + Usage: "E.164 phone number the agent can transfer the call to. When set, the agent is given a transfer tool it can use to hand the call to a human.", + InnerField: "transferPhoneNumber", + }, + &requestflag.InnerFlag[string]{ + Name: "voice.tts-provider", + Usage: "Speech-synthesis provider. Uses the default when omitted.", + InnerField: "ttsProvider", + }, + &requestflag.InnerFlag[string]{ + Name: "voice.tts-voice-id", + Usage: "Identifier of the synthesized voice that speaks. Choose from the voices available in the dashboard. Uses a neutral default when omitted.", + InnerField: "ttsVoiceId", + }, + &requestflag.InnerFlag[string]{ + Name: "voice.voicemail-action", + Usage: "What the agent does when an answering machine or voicemail is detected on an outbound call.", + InnerField: "voicemailAction", + }, + &requestflag.InnerFlag[string]{ + Name: "voice.voicemail-message", + Usage: "Message spoken when `voicemailAction` is `leave_message`. Falls back to `greeting` when omitted.", + InnerField: "voicemailMessage", + }, + &requestflag.InnerFlag[float64]{ + Name: "voice.voice-speed", + Usage: "Speech rate. 1.0 is natural. Only honoured by voices that support rate control; ignored by the others.", + InnerField: "voiceSpeed", + }, + }, +}) var sendersAgentDelete = cli.Command{ Name: "delete", diff --git a/pkg/cmd/senderagent_test.go b/pkg/cmd/senderagent_test.go index 8425f0e..5e77159 100644 --- a/pkg/cmd/senderagent_test.go +++ b/pkg/cmd/senderagent_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/zavudev/cli/internal/mocktest" + "github.com/zavudev/cli/internal/requestflag" ) func TestSendersAgentCreate(t *testing.T) { @@ -27,6 +28,48 @@ func TestSendersAgentCreate(t *testing.T) { "--temperature", "0", "--trigger-on-channel", "string", "--trigger-on-message-type", "string", + "--voice", "{enabled: true, greeting: 'Hi, thanks for calling Acme. How can I help you today?', greetings: {es: 'Hola, soy Atlas. Preguntame lo que quieras.'}, interruptible: true, language: en, maxCallDurationMinutes: 1, maxIdleSeconds: 5, model: openai/gpt-4o, recordCalls: true, sttModel: sttModel, sttProvider: sttProvider, transferPhoneNumber: '+14155551234', ttsProvider: ttsProvider, ttsVoiceId: aria, voicemailAction: hangup, voicemailMessage: voicemailMessage, voiceSpeed: 0.5}", + ) + }) + + t.Run("inner flags", func(t *testing.T) { + // Check that inner flags have been set up correctly + requestflag.CheckInnerFlags(sendersAgentCreate) + + // Alternative argument passing style using inner flags + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "senders:agent", "create", + "--sender-id", "senderId", + "--model", "gpt-4o-mini", + "--name", "Customer Support", + "--provider", "openai", + "--system-prompt", "You are a helpful customer support agent. Be friendly and concise.", + "--api-key", "sk-...", + "--context-window-messages", "1", + "--include-contact-metadata=true", + "--max-tokens", "1", + "--temperature", "0", + "--trigger-on-channel", "string", + "--trigger-on-message-type", "string", + "--voice.enabled=true", + "--voice.greeting", "Hi, thanks for calling Acme. How can I help you today?", + "--voice.greetings", "{es: 'Hola, soy Atlas. Preguntame lo que quieras.'}", + "--voice.interruptible=true", + "--voice.language", "en", + "--voice.max-call-duration-minutes", "1", + "--voice.max-idle-seconds", "5", + "--voice.model", "openai/gpt-4o", + "--voice.record-calls=true", + "--voice.stt-model", "sttModel", + "--voice.stt-provider", "sttProvider", + "--voice.transfer-phone-number", "+14155551234", + "--voice.tts-provider", "ttsProvider", + "--voice.tts-voice-id", "aria", + "--voice.voicemail-action", "hangup", + "--voice.voicemail-message", "voicemailMessage", + "--voice.voice-speed", "0.5", ) }) @@ -45,7 +88,26 @@ func TestSendersAgentCreate(t *testing.T) { "triggerOnChannels:\n" + " - string\n" + "triggerOnMessageTypes:\n" + - " - string\n") + " - string\n" + + "voice:\n" + + " enabled: true\n" + + " greeting: Hi, thanks for calling Acme. How can I help you today?\n" + + " greetings:\n" + + " es: Hola, soy Atlas. Preguntame lo que quieras.\n" + + " interruptible: true\n" + + " language: en\n" + + " maxCallDurationMinutes: 1\n" + + " maxIdleSeconds: 5\n" + + " model: openai/gpt-4o\n" + + " recordCalls: true\n" + + " sttModel: sttModel\n" + + " sttProvider: sttProvider\n" + + " transferPhoneNumber: '+14155551234'\n" + + " ttsProvider: ttsProvider\n" + + " ttsVoiceId: aria\n" + + " voicemailAction: hangup\n" + + " voicemailMessage: voicemailMessage\n" + + " voiceSpeed: 0.5\n") mocktest.TestRunMockTestWithPipeAndFlags( t, pipeData, "--api-key", "string", @@ -87,6 +149,49 @@ func TestSendersAgentUpdate(t *testing.T) { "--temperature", "0", "--trigger-on-channel", "string", "--trigger-on-message-type", "string", + "--voice", "{enabled: true, greeting: 'Hi, thanks for calling Acme. How can I help you today?', greetings: {es: 'Hola, soy Atlas. Preguntame lo que quieras.'}, interruptible: true, language: en, maxCallDurationMinutes: 1, maxIdleSeconds: 5, model: openai/gpt-4o, recordCalls: true, sttModel: sttModel, sttProvider: sttProvider, transferPhoneNumber: '+14155551234', ttsProvider: ttsProvider, ttsVoiceId: aria, voicemailAction: hangup, voicemailMessage: voicemailMessage, voiceSpeed: 0.5}", + ) + }) + + t.Run("inner flags", func(t *testing.T) { + // Check that inner flags have been set up correctly + requestflag.CheckInnerFlags(sendersAgentUpdate) + + // Alternative argument passing style using inner flags + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "senders:agent", "update", + "--sender-id", "senderId", + "--api-key", "apiKey", + "--context-window-messages", "1", + "--enabled=true", + "--include-contact-metadata=true", + "--max-tokens", "1", + "--model", "model", + "--name", "name", + "--provider", "openai", + "--system-prompt", "systemPrompt", + "--temperature", "0", + "--trigger-on-channel", "string", + "--trigger-on-message-type", "string", + "--voice.enabled=true", + "--voice.greeting", "Hi, thanks for calling Acme. How can I help you today?", + "--voice.greetings", "{es: 'Hola, soy Atlas. Preguntame lo que quieras.'}", + "--voice.interruptible=true", + "--voice.language", "en", + "--voice.max-call-duration-minutes", "1", + "--voice.max-idle-seconds", "5", + "--voice.model", "openai/gpt-4o", + "--voice.record-calls=true", + "--voice.stt-model", "sttModel", + "--voice.stt-provider", "sttProvider", + "--voice.transfer-phone-number", "+14155551234", + "--voice.tts-provider", "ttsProvider", + "--voice.tts-voice-id", "aria", + "--voice.voicemail-action", "hangup", + "--voice.voicemail-message", "voicemailMessage", + "--voice.voice-speed", "0.5", ) }) @@ -106,7 +211,26 @@ func TestSendersAgentUpdate(t *testing.T) { "triggerOnChannels:\n" + " - string\n" + "triggerOnMessageTypes:\n" + - " - string\n") + " - string\n" + + "voice:\n" + + " enabled: true\n" + + " greeting: Hi, thanks for calling Acme. How can I help you today?\n" + + " greetings:\n" + + " es: Hola, soy Atlas. Preguntame lo que quieras.\n" + + " interruptible: true\n" + + " language: en\n" + + " maxCallDurationMinutes: 1\n" + + " maxIdleSeconds: 5\n" + + " model: openai/gpt-4o\n" + + " recordCalls: true\n" + + " sttModel: sttModel\n" + + " sttProvider: sttProvider\n" + + " transferPhoneNumber: '+14155551234'\n" + + " ttsProvider: ttsProvider\n" + + " ttsVoiceId: aria\n" + + " voicemailAction: hangup\n" + + " voicemailMessage: voicemailMessage\n" + + " voiceSpeed: 0.5\n") mocktest.TestRunMockTestWithPipeAndFlags( t, pipeData, "--api-key", "string", diff --git a/pkg/cmd/senderagentexecution.go b/pkg/cmd/senderagentexecution.go index f41cf56..695e892 100644 --- a/pkg/cmd/senderagentexecution.go +++ b/pkg/cmd/senderagentexecution.go @@ -14,6 +14,26 @@ import ( "github.com/zavudev/sdk-go/option" ) +var sendersAgentExecutionsRetrieve = cli.Command{ + Name: "retrieve", + Usage: "Fetch full details for one execution — including `errorMessage`, `errorCode`,\nand `responseText`. Use this to debug failures surfaced by the list endpoint.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "sender-id", + Required: true, + PathParam: "senderId", + }, + &requestflag.Flag[string]{ + Name: "execution-id", + Required: true, + PathParam: "executionId", + }, + }, + Action: handleSendersAgentExecutionsRetrieve, + HideHelpCommand: true, +} + var sendersAgentExecutionsList = cli.Command{ Name: "list", Usage: "List recent agent executions with pagination.", @@ -47,6 +67,57 @@ var sendersAgentExecutionsList = cli.Command{ HideHelpCommand: true, } +func handleSendersAgentExecutionsRetrieve(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("execution-id") && len(unusedArgs) > 0 { + cmd.Set("execution-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + params := zavudev.SenderAgentExecutionGetParams{ + SenderID: cmd.Value("sender-id").(string), + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Senders.Agent.Executions.Get( + ctx, + cmd.Value("execution-id").(string), + params, + options..., + ) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "senders:agent:executions retrieve", + Transform: transform, + }) +} + func handleSendersAgentExecutionsList(ctx context.Context, cmd *cli.Command) error { client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) unusedArgs := cmd.Args().Slice() diff --git a/pkg/cmd/senderagentexecution_test.go b/pkg/cmd/senderagentexecution_test.go index a4c6811..15d7276 100644 --- a/pkg/cmd/senderagentexecution_test.go +++ b/pkg/cmd/senderagentexecution_test.go @@ -8,6 +8,19 @@ import ( "github.com/zavudev/cli/internal/mocktest" ) +func TestSendersAgentExecutionsRetrieve(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "senders:agent:executions", "retrieve", + "--sender-id", "senderId", + "--execution-id", "executionId", + ) + }) +} + func TestSendersAgentExecutionsList(t *testing.T) { t.Skip("Mock server tests are disabled") t.Run("regular flags", func(t *testing.T) { diff --git a/pkg/cmd/senderagenttool.go b/pkg/cmd/senderagenttool.go index 813a6ee..f67bfad 100644 --- a/pkg/cmd/senderagenttool.go +++ b/pkg/cmd/senderagenttool.go @@ -52,7 +52,7 @@ var sendersAgentToolsCreate = requestflag.WithInnerFlags(cli.Command{ }, &requestflag.Flag[string]{ Name: "webhook-secret", - Usage: "Optional secret for webhook signature verification.", + Usage: "Signing secret for the webhook. Optional: Zavu generates one when omitted and returns it on this response only. Supply your own if you already have a secret you want reused.", BodyPath: "webhookSecret", }, }, @@ -210,7 +210,7 @@ var sendersAgentToolsDelete = cli.Command{ var sendersAgentToolsTest = cli.Command{ Name: "test", - Usage: "Test a tool by triggering its webhook with test parameters.", + Usage: "Run a tool with the parameters you supply and return what it answered.", Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ diff --git a/pkg/cmd/template.go b/pkg/cmd/template.go index de4749a..ca11d01 100644 --- a/pkg/cmd/template.go +++ b/pkg/cmd/template.go @@ -95,13 +95,9 @@ var templatesCreate = requestflag.WithInnerFlags(cli.Command{ HideHelpCommand: true, }, map[string][]requestflag.HasOuterFlag{ "button": { - &requestflag.InnerFlag[string]{ - Name: "button.text", - InnerField: "text", - }, &requestflag.InnerFlag[string]{ Name: "button.type", - Usage: `Allowed values: "quick_reply", "url", "phone", "otp".`, + Usage: "`request_contact_info` renders a fixed **Share Contact Info** button that asks the recipient to share their phone number — useful when a contact adopted a WhatsApp username and you only know their BSUID. It takes no other fields.", InnerField: "type", }, &requestflag.InnerFlag[string]{ @@ -128,6 +124,11 @@ var templatesCreate = requestflag.WithInnerFlags(cli.Command{ Usage: "Android app signature hash. Required for ONE_TAP buttons.", InnerField: "signatureHash", }, + &requestflag.InnerFlag[string]{ + Name: "button.text", + Usage: "Button label. Required for every type except `request_contact_info`, whose label is fixed by WhatsApp.", + InnerField: "text", + }, &requestflag.InnerFlag[string]{ Name: "button.url", Usage: "Button destination. Use `{{1}}` exactly once for a dynamic URL (e.g. `https://example.com/orders/{{1}}`); WhatsApp only accepts the strict `{{1}}` form. Static URLs must not contain any `{{...}}` placeholder.", diff --git a/pkg/cmd/template_test.go b/pkg/cmd/template_test.go index fd83751..217c4f4 100644 --- a/pkg/cmd/template_test.go +++ b/pkg/cmd/template_test.go @@ -20,7 +20,7 @@ func TestTemplatesCreate(t *testing.T) { "--language", "en", "--name", "order_confirmation", "--add-security-recommendation=true", - "--button", "{text: text, type: quick_reply, example: ORD-12345, otpType: COPY_CODE, packageName: packageName, phoneNumber: phoneNumber, signatureHash: signatureHash, url: https://example.com}", + "--button", "{type: quick_reply, example: ORD-12345, otpType: COPY_CODE, packageName: packageName, phoneNumber: phoneNumber, signatureHash: signatureHash, text: text, url: https://example.com}", "--code-expiration-minutes", "1", "--footer", "footer", "--header-content", "headerContent", @@ -47,13 +47,13 @@ func TestTemplatesCreate(t *testing.T) { "--language", "en", "--name", "order_confirmation", "--add-security-recommendation=true", - "--button.text", "text", "--button.type", "quick_reply", "--button.example", "ORD-12345", "--button.otp-type", "COPY_CODE", "--button.package-name", "packageName", "--button.phone-number", "phoneNumber", "--button.signature-hash", "signatureHash", + "--button.text", "text", "--button.url", "https://example.com", "--code-expiration-minutes", "1", "--footer", "footer", @@ -76,13 +76,13 @@ func TestTemplatesCreate(t *testing.T) { "name: order_confirmation\n" + "addSecurityRecommendation: true\n" + "buttons:\n" + - " - text: text\n" + - " type: quick_reply\n" + + " - type: quick_reply\n" + " example: ORD-12345\n" + " otpType: COPY_CODE\n" + " packageName: packageName\n" + " phoneNumber: phoneNumber\n" + " signatureHash: signatureHash\n" + + " text: text\n" + " url: https://example.com\n" + "codeExpirationMinutes: 1\n" + "footer: footer\n" + diff --git a/pkg/cmd/usage_test.go b/pkg/cmd/usage_test.go deleted file mode 100644 index 2a1fa7d..0000000 --- a/pkg/cmd/usage_test.go +++ /dev/null @@ -1,20 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -package cmd - -import ( - "testing" - - "github.com/zavudev/cli/internal/mocktest" -) - -func TestUsageRetrieve(t *testing.T) { - t.Skip("Mock server tests are disabled") - t.Run("regular flags", func(t *testing.T) { - mocktest.TestRunMockTestWithFlags( - t, - "--api-key", "string", - "usage", "retrieve", - ) - }) -} From a6fae71604d4464c79c988e1c9ba3e01ff465447 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 10 Aug 2026 23:46:31 +0000 Subject: [PATCH 31/56] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 4c021c1..eafac8f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 138 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-722f1deadb9046d6c3dd01a7ea4022c276f4675bf24b56cd6cab6fe7f7b82d26.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-376a9a6c6be14b4fd16c9f62d7bafef25df560ffe79ca7085fd0ba8d70540267.yml openapi_spec_hash: 6b119a3213e324f3826453deca57f10b -config_hash: 2cb79865930afed7a1592e2c19b61397 +config_hash: 21a91c7ecc39df58cb3ee0779a5915ef From 9719c1799d606410c0460c73b081e97a91567c98 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 10 Aug 2026 23:46:52 +0000 Subject: [PATCH 32/56] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index eafac8f..8413cc3 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 138 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-376a9a6c6be14b4fd16c9f62d7bafef25df560ffe79ca7085fd0ba8d70540267.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-fa2ec1f359f9ed18ba4e00d8d1edb2a10a50c02b4efd9f20736cedbefb39cc47.yml openapi_spec_hash: 6b119a3213e324f3826453deca57f10b -config_hash: 21a91c7ecc39df58cb3ee0779a5915ef +config_hash: d5d02dfde2ea057fdcf5b26e3a921f7a From cea5a983cf1515646ba721903c201be498574aa5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 10 Aug 2026 23:50:13 +0000 Subject: [PATCH 33/56] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 8413cc3..037d39e 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 138 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-fa2ec1f359f9ed18ba4e00d8d1edb2a10a50c02b4efd9f20736cedbefb39cc47.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-ce051733263f4e5809b254dbe76a500eb4101355b294decac25dfd0b31509908.yml openapi_spec_hash: 6b119a3213e324f3826453deca57f10b -config_hash: d5d02dfde2ea057fdcf5b26e3a921f7a +config_hash: a541f8cf3cf7c3035a581a022d87dc0e From 8d7f06aa3ddbfddfc3fc9a25aef8e5cf73bf6c72 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 10 Aug 2026 23:50:33 +0000 Subject: [PATCH 34/56] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 037d39e..46aef62 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 138 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-ce051733263f4e5809b254dbe76a500eb4101355b294decac25dfd0b31509908.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-7e8b631015f94fd76c51f5c435bc3a22a858d1e40b89c0daf4ae8f1324412fa8.yml openapi_spec_hash: 6b119a3213e324f3826453deca57f10b -config_hash: a541f8cf3cf7c3035a581a022d87dc0e +config_hash: ef364b35e7dbd38ff92ce0ddaf429906 From 32960848aaa847bcd0b1b8cad01026a6e4213366 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 10 Aug 2026 23:50:52 +0000 Subject: [PATCH 35/56] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 46aef62..754d90b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 138 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-7e8b631015f94fd76c51f5c435bc3a22a858d1e40b89c0daf4ae8f1324412fa8.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-86aec85be1021cea51f9fc0bb95248276c8b85e3ac0fd810490114599ed7cf00.yml openapi_spec_hash: 6b119a3213e324f3826453deca57f10b -config_hash: ef364b35e7dbd38ff92ce0ddaf429906 +config_hash: 7fca08d9dae18513f5d7451cea11e34c From e114f18311f9362822f1410c7ac98e1c11ab7ac0 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 11 Aug 2026 02:43:42 +0000 Subject: [PATCH 36/56] feat(api): api update --- .stats.yml | 4 ++-- pkg/cmd/function.go | 39 ++++++++++++++++++++++++++++++++++--- pkg/cmd/function_test.go | 42 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 5 deletions(-) diff --git a/.stats.yml b/.stats.yml index 754d90b..484237c 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 138 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-86aec85be1021cea51f9fc0bb95248276c8b85e3ac0fd810490114599ed7cf00.yml -openapi_spec_hash: 6b119a3213e324f3826453deca57f10b +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-21db15ab8c7fc4a95f96e62e9ae84d4db9696f41adf62806c9be4043268b0fad.yml +openapi_spec_hash: f5928b2d6fc0d6f10ea63acac9dead60 config_hash: 7fca08d9dae18513f5d7451cea11e34c diff --git a/pkg/cmd/function.go b/pkg/cmd/function.go index 37d40db..1040368 100644 --- a/pkg/cmd/function.go +++ b/pkg/cmd/function.go @@ -39,6 +39,17 @@ var functionsCreate = cli.Command{ Name: "description", BodyPath: "description", }, + &requestflag.Flag[string]{ + Name: "entrypoint", + Usage: "Which file in `files` is the entry point. Defaults to `index.ts`.", + Default: "index.ts", + BodyPath: "entrypoint", + }, + &requestflag.Flag[map[string]any]{ + Name: "files", + Usage: "The project's source files, keyed by path relative to the project root (e.g. `index.ts`, `lib/orders.ts`). Imports between them are resolved when the function is built, so a function can be split across as many files as it needs.\n\nPaths must be relative and use forward slashes; `..`, `node_modules/` and `package.json` are rejected. npm packages are not uploaded here — declare them under `dependencies` and Zavu installs them. Limits: 200 files and 900,000 bytes for the whole tree.", + BodyPath: "files", + }, &requestflag.Flag[bool]{ Name: "http-enabled", Usage: "Whether to expose a public HTTPS URL for this function.", @@ -58,7 +69,7 @@ var functionsCreate = cli.Command{ }, &requestflag.Flag[string]{ Name: "source-code", - Usage: "TypeScript source code for the function entry point (max ~900KB).", + Usage: "Shortcut for a single-file function: exactly equivalent to sending `files` with one entry named after `entrypoint` (`index.ts` by default). Fully supported — use whichever fits. If both are sent, `files` wins.", BodyPath: "sourceCode", }, &requestflag.Flag[int64]{ @@ -102,6 +113,17 @@ var functionsUpdate = cli.Command{ Usage: "New dependency map (replaces existing dependencies).", BodyPath: "dependencies", }, + &requestflag.Flag[string]{ + Name: "entrypoint", + Usage: "Which file in `files` is the entry point. Defaults to `index.ts`.", + Default: "index.ts", + BodyPath: "entrypoint", + }, + &requestflag.Flag[map[string]any]{ + Name: "files", + Usage: "The project's source files, keyed by path relative to the project root (e.g. `index.ts`, `lib/orders.ts`). Imports between them are resolved when the function is built, so a function can be split across as many files as it needs.\n\nPaths must be relative and use forward slashes; `..`, `node_modules/` and `package.json` are rejected. npm packages are not uploaded here — declare them under `dependencies` and Zavu installs them. Limits: 200 files and 900,000 bytes for the whole tree.", + BodyPath: "files", + }, &requestflag.Flag[bool]{ Name: "http-enabled", Usage: "Expose the function on its public HTTPS URL, or take it down. Applies to the already-deployed function without redeploying; the URL is returned as `publicUrl`.", @@ -109,7 +131,7 @@ var functionsUpdate = cli.Command{ }, &requestflag.Flag[string]{ Name: "source-code", - Usage: "New source code for the draft (replaces it).", + Usage: "Shortcut for a single-file function: exactly equivalent to sending `files` with one entry named after `entrypoint` (`index.ts` by default). Fully supported — use whichever fits. If both are sent, `files` wins.", BodyPath: "sourceCode", }, }, @@ -147,9 +169,20 @@ var functionsDeploy = cli.Command{ Usage: "New dependency map (replaces existing dependencies).", BodyPath: "dependencies", }, + &requestflag.Flag[string]{ + Name: "entrypoint", + Usage: "Which file in `files` is the entry point. Defaults to `index.ts`.", + Default: "index.ts", + BodyPath: "entrypoint", + }, + &requestflag.Flag[map[string]any]{ + Name: "files", + Usage: "The project's source files, keyed by path relative to the project root (e.g. `index.ts`, `lib/orders.ts`). Imports between them are resolved when the function is built, so a function can be split across as many files as it needs.\n\nPaths must be relative and use forward slashes; `..`, `node_modules/` and `package.json` are rejected. npm packages are not uploaded here — declare them under `dependencies` and Zavu installs them. Limits: 200 files and 900,000 bytes for the whole tree.", + BodyPath: "files", + }, &requestflag.Flag[string]{ Name: "source-code", - Usage: "New source code to publish (replaces the draft).", + Usage: "Shortcut for a single-file function: exactly equivalent to sending `files` with one entry named after `entrypoint` (`index.ts` by default). Fully supported — use whichever fits. If both are sent, `files` wins.", BodyPath: "sourceCode", }, }, diff --git a/pkg/cmd/function_test.go b/pkg/cmd/function_test.go index 3931b36..6120b51 100644 --- a/pkg/cmd/function_test.go +++ b/pkg/cmd/function_test.go @@ -19,6 +19,8 @@ func TestFunctionsCreate(t *testing.T) { "--slug", "order-bot", "--dependencies", "{openai: ^4.20.0}", "--description", "Replies to order status questions on WhatsApp.", + "--entrypoint", "index.ts", + "--files", `{index.ts: "import { formatOrder } from './lib/orders';\n\nexport default async function handler(event) {\n return { statusCode: 200, body: formatOrder(event) };\n}\n", lib/orders.ts: "export function formatOrder(event) {\n return JSON.stringify(event);\n}\n"}`, "--http-enabled=true", "--memory-mb", "128", "--runtime", "nodejs24", @@ -35,6 +37,18 @@ func TestFunctionsCreate(t *testing.T) { "dependencies:\n" + " openai: ^4.20.0\n" + "description: Replies to order status questions on WhatsApp.\n" + + "entrypoint: index.ts\n" + + "files:\n" + + " index.ts: |\n" + + " import { formatOrder } from './lib/orders';\n" + + "\n" + + " export default async function handler(event) {\n" + + " return { statusCode: 200, body: formatOrder(event) };\n" + + " }\n" + + " lib/orders.ts: |\n" + + " export function formatOrder(event) {\n" + + " return JSON.stringify(event);\n" + + " }\n" + "httpEnabled: true\n" + "memoryMb: 128\n" + "runtime: nodejs24\n" + @@ -74,6 +88,8 @@ func TestFunctionsUpdate(t *testing.T) { "functions", "update", "--function-id", "functionId", "--dependencies", "{foo: string}", + "--entrypoint", "index.ts", + "--files", `{index.ts: "import { formatOrder } from './lib/orders';\n\nexport default async function handler(event) {\n return { statusCode: 200, body: formatOrder(event) };\n}\n", lib/orders.ts: "export function formatOrder(event) {\n return JSON.stringify(event);\n}\n"}`, "--http-enabled=true", "--source-code", "sourceCode", ) @@ -84,6 +100,18 @@ func TestFunctionsUpdate(t *testing.T) { pipeData := []byte("" + "dependencies:\n" + " foo: string\n" + + "entrypoint: index.ts\n" + + "files:\n" + + " index.ts: |\n" + + " import { formatOrder } from './lib/orders';\n" + + "\n" + + " export default async function handler(event) {\n" + + " return { statusCode: 200, body: formatOrder(event) };\n" + + " }\n" + + " lib/orders.ts: |\n" + + " export function formatOrder(event) {\n" + + " return JSON.stringify(event);\n" + + " }\n" + "httpEnabled: true\n" + "sourceCode: sourceCode\n") mocktest.TestRunMockTestWithPipeAndFlags( @@ -116,6 +144,8 @@ func TestFunctionsDeploy(t *testing.T) { "functions", "deploy", "--function-id", "functionId", "--dependencies", "{foo: string}", + "--entrypoint", "index.ts", + "--files", `{index.ts: "import { formatOrder } from './lib/orders';\n\nexport default async function handler(event) {\n return { statusCode: 200, body: formatOrder(event) };\n}\n", lib/orders.ts: "export function formatOrder(event) {\n return JSON.stringify(event);\n}\n"}`, "--source-code", "sourceCode", ) }) @@ -125,6 +155,18 @@ func TestFunctionsDeploy(t *testing.T) { pipeData := []byte("" + "dependencies:\n" + " foo: string\n" + + "entrypoint: index.ts\n" + + "files:\n" + + " index.ts: |\n" + + " import { formatOrder } from './lib/orders';\n" + + "\n" + + " export default async function handler(event) {\n" + + " return { statusCode: 200, body: formatOrder(event) };\n" + + " }\n" + + " lib/orders.ts: |\n" + + " export function formatOrder(event) {\n" + + " return JSON.stringify(event);\n" + + " }\n" + "sourceCode: sourceCode\n") mocktest.TestRunMockTestWithPipeAndFlags( t, pipeData, From fa4596b5e2a8f2520a36c6110399d376b308c83c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 11 Aug 2026 03:48:14 +0000 Subject: [PATCH 37/56] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 484237c..0b60fcc 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 138 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-21db15ab8c7fc4a95f96e62e9ae84d4db9696f41adf62806c9be4043268b0fad.yml -openapi_spec_hash: f5928b2d6fc0d6f10ea63acac9dead60 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-e4987140138afb7287e25f31dc8cff94c6b11975e460e0c19d54ec457d009a1b.yml +openapi_spec_hash: d20e1b8d0a9a7a65dc8a21e3a151111e config_hash: 7fca08d9dae18513f5d7451cea11e34c From e16ccefd975ef9e1335159a30e8f41714dbebe1f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 11 Aug 2026 03:54:39 +0000 Subject: [PATCH 38/56] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 0b60fcc..edddea6 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 138 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-e4987140138afb7287e25f31dc8cff94c6b11975e460e0c19d54ec457d009a1b.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-f94b2cae6c929ce34c51007444f7105c96b458c2cb9b07be2ce7d434adda4e66.yml openapi_spec_hash: d20e1b8d0a9a7a65dc8a21e3a151111e -config_hash: 7fca08d9dae18513f5d7451cea11e34c +config_hash: 942fad5624cf310889c6161a3b6bf725 From 513386cab68c5998e9cda3cf22d21e006a404f23 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 11 Aug 2026 03:54:58 +0000 Subject: [PATCH 39/56] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index edddea6..0b60fcc 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 138 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-f94b2cae6c929ce34c51007444f7105c96b458c2cb9b07be2ce7d434adda4e66.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-e4987140138afb7287e25f31dc8cff94c6b11975e460e0c19d54ec457d009a1b.yml openapi_spec_hash: d20e1b8d0a9a7a65dc8a21e3a151111e -config_hash: 942fad5624cf310889c6161a3b6bf725 +config_hash: 7fca08d9dae18513f5d7451cea11e34c From 246f57f9419a5a7d4e386534724c4907a3160d99 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 11 Aug 2026 04:31:22 +0000 Subject: [PATCH 40/56] feat(api): api update --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index a0b6e1e..f661d68 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/tidwall/pretty v1.2.1 github.com/urfave/cli-docs/v3 v3.0.0-alpha6 github.com/urfave/cli/v3 v3.3.2 - github.com/zavudev/sdk-go v0.3.0 + github.com/zavudev/sdk-go v0.4.0 golang.org/x/sys v0.38.0 ) diff --git a/go.sum b/go.sum index 2bc85b4..067c6b1 100644 --- a/go.sum +++ b/go.sum @@ -71,8 +71,8 @@ github.com/urfave/cli/v3 v3.3.2 h1:BYFVnhhZ8RqT38DxEYVFPPmGFTEf7tJwySTXsVRrS/o= github.com/urfave/cli/v3 v3.3.2/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= -github.com/zavudev/sdk-go v0.3.0 h1:lIPS6cTM0bQMbGG5+CjL3wV80+r6iLlWLnnv4BYRNXs= -github.com/zavudev/sdk-go v0.3.0/go.mod h1:ncdVbaf7JHHiUcpJu2JE3c1xion5lkWSAUCn5CnX3bo= +github.com/zavudev/sdk-go v0.4.0 h1:OpPmvyDZVLF8y7Gc6tHB3JHhqRPy8aZMzVB6cOaeSPE= +github.com/zavudev/sdk-go v0.4.0/go.mod h1:ncdVbaf7JHHiUcpJu2JE3c1xion5lkWSAUCn5CnX3bo= golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E= golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= From 04d898d756e7786b3241881f2748894a2998a41a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 11 Aug 2026 23:29:41 +0000 Subject: [PATCH 41/56] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 0b60fcc..d105c4b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 138 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-e4987140138afb7287e25f31dc8cff94c6b11975e460e0c19d54ec457d009a1b.yml -openapi_spec_hash: d20e1b8d0a9a7a65dc8a21e3a151111e +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-dd62b3849e15304da4087f73928025602737e10a414c87a6c2a5fe7059b9ea37.yml +openapi_spec_hash: 5a3d5cb61a7dd5d556a9f9abd9db01d4 config_hash: 7fca08d9dae18513f5d7451cea11e34c From 48798969f5e101f3ff5b033046012351dc96d10d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 14 Aug 2026 23:52:13 +0000 Subject: [PATCH 42/56] feat(api): api update --- .stats.yml | 4 ++-- pkg/cmd/message.go | 5 +++++ pkg/cmd/message_test.go | 14 +++++++++++++- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/.stats.yml b/.stats.yml index d105c4b..c8f40a3 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 138 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-dd62b3849e15304da4087f73928025602737e10a414c87a6c2a5fe7059b9ea37.yml -openapi_spec_hash: 5a3d5cb61a7dd5d556a9f9abd9db01d4 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-8ead699d165bc116549ef757ace827bf39add6e0e19afdc1653f0d0282947eae.yml +openapi_spec_hash: 5a35a5dab833f4d1e6deeea795e1abf2 config_hash: 7fca08d9dae18513f5d7451cea11e34c diff --git a/pkg/cmd/message.go b/pkg/cmd/message.go index 558bfa3..decd47c 100644 --- a/pkg/cmd/message.go +++ b/pkg/cmd/message.go @@ -294,6 +294,11 @@ var messagesSend = requestflag.WithInnerFlags(cli.Command{ Usage: "Message ID to react to.", InnerField: "reactToMessageId", }, + &requestflag.InnerFlag[map[string]any]{ + Name: "content.referral", + Usage: "Click-to-WhatsApp (CTWA) ad attribution: where an inbound conversation came from.\n\nWhatsApp only. Present on the **first inbound message** of a conversation opened from a Meta ad or post, and on no message after it — so store it when it arrives rather than expecting it again. Organic conversations never carry it.\n\nField names are camelCased to match the rest of this API; Meta sends them as snake_case (`ctwa_clid`, `source_id`, ...). Fields that do not apply are omitted: a `post` source has no click id, and an image ad has no `videoUrl`.", + InnerField: "referral", + }, &requestflag.InnerFlag[string]{ Name: "content.reply-to-from", Usage: "Sender of the quoted message (phone number in E.164 format).", diff --git a/pkg/cmd/message_test.go b/pkg/cmd/message_test.go index fdb2e27..4d0887d 100644 --- a/pkg/cmd/message_test.go +++ b/pkg/cmd/message_test.go @@ -74,7 +74,7 @@ func TestMessagesSend(t *testing.T) { "--to", "+56912345678", "--attachment", "{filename: invoice.pdf, content: content, content_id: logo, content_type: application/pdf, path: https://example.com}", "--channel", "auto", - "--content", "{buttons: [{id: id, title: title}], contacts: [{name: name, phones: [string]}], ctaDisplayText: See Dates, ctaHeaderMediaUrl: https://example.com, ctaHeaderText: ctaHeaderText, ctaHeaderType: text, ctaUrl: https://example.com/schedule, emoji: emoji, filename: invoice.pdf, footerText: Dates subject to change., latitude: 0, listButton: listButton, locationAddress: locationAddress, locationName: locationName, longitude: 0, mediaId: mediaId, mediaUrl: https://example.com/image.jpg, mimeType: image/jpeg, reactToMessageId: reactToMessageId, replyToFrom: replyToFrom, replyToMessageId: replyToMessageId, replyToMessageType: replyToMessageType, replyToProviderMessageId: replyToProviderMessageId, replyToText: replyToText, sections: [{rows: [{id: id, title: title, description: description}], title: title}], templateButtonVariables: {'0': abc-report-token}, templateHeaderVariables: {'1': Jorge y Laura}, templateId: templateId, templateVariables: {'1': John, '2': ORD-12345}}", + "--content", "{buttons: [{id: id, title: title}], contacts: [{name: name, phones: [string]}], ctaDisplayText: See Dates, ctaHeaderMediaUrl: https://example.com, ctaHeaderText: ctaHeaderText, ctaHeaderType: text, ctaUrl: https://example.com/schedule, emoji: emoji, filename: invoice.pdf, footerText: Dates subject to change., latitude: 0, listButton: listButton, locationAddress: locationAddress, locationName: locationName, longitude: 0, mediaId: mediaId, mediaUrl: https://example.com/image.jpg, mimeType: image/jpeg, reactToMessageId: reactToMessageId, referral: {body: body, ctwaClid: ARIzZm9vYmFyY3R3YWNsaWQ, headline: headline, imageUrl: https://example.com, mediaType: image, sourceId: '120210000000000000', sourceType: ad, sourceUrl: https://example.com, thumbnailUrl: https://example.com, videoUrl: https://example.com}, replyToFrom: replyToFrom, replyToMessageId: replyToMessageId, replyToMessageType: replyToMessageType, replyToProviderMessageId: replyToProviderMessageId, replyToText: replyToText, sections: [{rows: [{id: id, title: title, description: description}], title: title}], templateButtonVariables: {'0': abc-report-token}, templateHeaderVariables: {'1': Jorge y Laura}, templateId: templateId, templateVariables: {'1': John, '2': ORD-12345}}", "--fallback-enabled=true", "--html-body", "htmlBody", "--idempotency-key", "msg_01HZY4ZP7VQY2J3BRW7Z6G0QGE", @@ -123,6 +123,7 @@ func TestMessagesSend(t *testing.T) { "--content.media-url", "https://example.com/image.jpg", "--content.mime-type", "image/jpeg", "--content.react-to-message-id", "reactToMessageId", + "--content.referral", "{body: body, ctwaClid: ARIzZm9vYmFyY3R3YWNsaWQ, headline: headline, imageUrl: https://example.com, mediaType: image, sourceId: '120210000000000000', sourceType: ad, sourceUrl: https://example.com, thumbnailUrl: https://example.com, videoUrl: https://example.com}", "--content.reply-to-from", "replyToFrom", "--content.reply-to-message-id", "replyToMessageId", "--content.reply-to-message-type", "replyToMessageType", @@ -182,6 +183,17 @@ func TestMessagesSend(t *testing.T) { " mediaUrl: https://example.com/image.jpg\n" + " mimeType: image/jpeg\n" + " reactToMessageId: reactToMessageId\n" + + " referral:\n" + + " body: body\n" + + " ctwaClid: ARIzZm9vYmFyY3R3YWNsaWQ\n" + + " headline: headline\n" + + " imageUrl: https://example.com\n" + + " mediaType: image\n" + + " sourceId: '120210000000000000'\n" + + " sourceType: ad\n" + + " sourceUrl: https://example.com\n" + + " thumbnailUrl: https://example.com\n" + + " videoUrl: https://example.com\n" + " replyToFrom: replyToFrom\n" + " replyToMessageId: replyToMessageId\n" + " replyToMessageType: replyToMessageType\n" + From e500c71438733a0eea106680bf27154af36983bf Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 17 Aug 2026 20:34:46 +0000 Subject: [PATCH 43/56] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index c8f40a3..d1cb2f5 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 138 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-8ead699d165bc116549ef757ace827bf39add6e0e19afdc1653f0d0282947eae.yml -openapi_spec_hash: 5a35a5dab833f4d1e6deeea795e1abf2 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-a3e89fc19e2d04b767db8da79617311c572cc2a0273abf1f211c77999c164a71.yml +openapi_spec_hash: 920231951c3213b2a70bdddf6f4922fb config_hash: 7fca08d9dae18513f5d7451cea11e34c From d6d1e286c0240f5f1dc1536701ffa06114659cb2 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 19 Aug 2026 12:42:02 +0000 Subject: [PATCH 44/56] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index d1cb2f5..968d7bb 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 138 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-a3e89fc19e2d04b767db8da79617311c572cc2a0273abf1f211c77999c164a71.yml -openapi_spec_hash: 920231951c3213b2a70bdddf6f4922fb +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-5f801823f89d92d2c00f2bc33b30aa7b127f21b5f3fb38f9278f06ce1d79b869.yml +openapi_spec_hash: d1baedab2c021ab97b4aba0030180b29 config_hash: 7fca08d9dae18513f5d7451cea11e34c From bfdd0e45043c1e391af623d0737a0589bace4ebc Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 19 Aug 2026 13:07:03 +0000 Subject: [PATCH 45/56] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 968d7bb..ec5b810 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 138 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-5f801823f89d92d2c00f2bc33b30aa7b127f21b5f3fb38f9278f06ce1d79b869.yml -openapi_spec_hash: d1baedab2c021ab97b4aba0030180b29 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-9acbefe4fc4908c698c1165bed491d415687f0092e9df6ffb968f92e43b11961.yml +openapi_spec_hash: 410cb45b6fa3a8a1f3da3aba50accbdc config_hash: 7fca08d9dae18513f5d7451cea11e34c From 9a299ae7b231a95ed973e60d1f66dcaeec50422d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 19 Aug 2026 13:24:15 +0000 Subject: [PATCH 46/56] feat(api): manual updates --- .stats.yml | 6 +- pkg/cmd/agent.go | 744 ++++++++++++++++++ pkg/cmd/agent_test.go | 324 ++++++++ pkg/cmd/agentsender.go | 139 ++++ pkg/cmd/agentsender_test.go | 46 ++ pkg/cmd/agenttemplate.go | 120 +++ pkg/cmd/agenttemplate_test.go | 32 + pkg/cmd/call.go | 299 +++++++ pkg/cmd/call_test.go | 83 ++ pkg/cmd/cmd.go | 116 +++ pkg/cmd/conversation.go | 320 ++++++++ pkg/cmd/conversation_test.go | 65 ++ pkg/cmd/emaildomain.go | 274 +++++++ pkg/cmd/emaildomain_test.go | 78 ++ pkg/cmd/function.go | 187 +++++ pkg/cmd/function_test.go | 48 ++ pkg/cmd/functiongitlink.go | 334 ++++++++ pkg/cmd/functiongitlink_test.go | 107 +++ pkg/cmd/functiontrigger.go | 262 ++++++ pkg/cmd/functiontrigger_test.go | 88 +++ pkg/cmd/introspect.go | 61 ++ pkg/cmd/introspect_test.go | 28 + pkg/cmd/message.go | 57 ++ pkg/cmd/message_test.go | 12 + pkg/cmd/senderagentknowledgebasedocument.go | 162 ++++ .../senderagentknowledgebasedocument_test.go | 45 ++ pkg/cmd/senderagenttool.go | 76 ++ pkg/cmd/senderagenttool_test.go | 14 + pkg/cmd/senderagenttoolwebhook.go | 86 ++ pkg/cmd/senderagenttoolwebhook_test.go | 22 + pkg/cmd/sendertelegram.go | 125 +++ pkg/cmd/sendertelegram_test.go | 45 ++ pkg/cmd/template.go | 56 ++ pkg/cmd/template_test.go | 22 + pkg/cmd/url.go | 70 ++ pkg/cmd/url_test.go | 24 + 36 files changed, 4574 insertions(+), 3 deletions(-) create mode 100644 pkg/cmd/agent.go create mode 100644 pkg/cmd/agent_test.go create mode 100644 pkg/cmd/agentsender.go create mode 100644 pkg/cmd/agentsender_test.go create mode 100644 pkg/cmd/agenttemplate.go create mode 100644 pkg/cmd/agenttemplate_test.go create mode 100644 pkg/cmd/call.go create mode 100644 pkg/cmd/call_test.go create mode 100644 pkg/cmd/conversation.go create mode 100644 pkg/cmd/conversation_test.go create mode 100644 pkg/cmd/emaildomain.go create mode 100644 pkg/cmd/emaildomain_test.go create mode 100644 pkg/cmd/functiongitlink.go create mode 100644 pkg/cmd/functiongitlink_test.go create mode 100644 pkg/cmd/functiontrigger.go create mode 100644 pkg/cmd/functiontrigger_test.go create mode 100644 pkg/cmd/senderagenttoolwebhook.go create mode 100644 pkg/cmd/senderagenttoolwebhook_test.go create mode 100644 pkg/cmd/sendertelegram.go create mode 100644 pkg/cmd/sendertelegram_test.go diff --git a/.stats.yml b/.stats.yml index ec5b810..906e6fe 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 138 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-9acbefe4fc4908c698c1165bed491d415687f0092e9df6ffb968f92e43b11961.yml +configured_endpoints: 184 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-ba782c22cc1b237327afbf2aeffb453185cec9513d918b5a0fdc7e7233320c9b.yml openapi_spec_hash: 410cb45b6fa3a8a1f3da3aba50accbdc -config_hash: 7fca08d9dae18513f5d7451cea11e34c +config_hash: 261e1b852ca7f8364e4a283a3edd350a diff --git a/pkg/cmd/agent.go b/pkg/cmd/agent.go new file mode 100644 index 0000000..d803baf --- /dev/null +++ b/pkg/cmd/agent.go @@ -0,0 +1,744 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "context" + "fmt" + + "github.com/tidwall/gjson" + "github.com/urfave/cli/v3" + "github.com/zavudev/cli/internal/apiquery" + "github.com/zavudev/cli/internal/requestflag" + "github.com/zavudev/sdk-go" + "github.com/zavudev/sdk-go/option" +) + +var agentsCreate = requestflag.WithInnerFlags(cli.Command{ + Name: "create", + Usage: "Create an agent without a sender. It is created disabled; connect a sender and\nenable it when you are ready for it to answer.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "model", + Required: true, + BodyPath: "model", + }, + &requestflag.Flag[string]{ + Name: "name", + Required: true, + BodyPath: "name", + }, + &requestflag.Flag[string]{ + Name: "provider", + Usage: "LLM provider for the AI agent.", + Required: true, + BodyPath: "provider", + }, + &requestflag.Flag[string]{ + Name: "system-prompt", + Required: true, + BodyPath: "systemPrompt", + }, + &requestflag.Flag[int64]{ + Name: "context-window-messages", + Default: 10, + BodyPath: "contextWindowMessages", + }, + &requestflag.Flag[bool]{ + Name: "include-contact-metadata", + Default: true, + BodyPath: "includeContactMetadata", + }, + &requestflag.Flag[int64]{ + Name: "max-tokens", + BodyPath: "maxTokens", + }, + &requestflag.Flag[float64]{ + Name: "temperature", + BodyPath: "temperature", + }, + &requestflag.Flag[[]string]{ + Name: "trigger-on-channel", + Default: []string{"*"}, + BodyPath: "triggerOnChannels", + }, + &requestflag.Flag[[]string]{ + Name: "trigger-on-message-type", + Default: []string{"text"}, + BodyPath: "triggerOnMessageTypes", + }, + &requestflag.Flag[map[string]any]{ + Name: "voice", + Usage: "Voice Agent configuration on a sender's AI agent. Controls how the agent behaves on inbound and outbound phone calls through Zavu's managed voice pipeline (speech recognition, the agent's LLM, and speech synthesis, with real-time interruption handling). Requires the Voice Agents feature to be enabled for your team.", + BodyPath: "voice", + }, + }, + Action: handleAgentsCreate, + HideHelpCommand: true, +}, map[string][]requestflag.HasOuterFlag{ + "voice": { + &requestflag.InnerFlag[bool]{ + Name: "voice.enabled", + Usage: "Whether the agent handles voice calls. When false, the sender's number is not answered by the voice agent and outbound calls are rejected.", + InnerField: "enabled", + }, + &requestflag.InnerFlag[string]{ + Name: "voice.greeting", + Usage: "Opening line the agent speaks when the call connects. If omitted, the agent waits for the caller to speak first.", + InnerField: "greeting", + }, + &requestflag.InnerFlag[map[string]any]{ + Name: "voice.greetings", + Usage: "Greeting per language, keyed by language code. Used when the caller's language differs from the one `greeting` is written in.", + InnerField: "greetings", + }, + &requestflag.InnerFlag[bool]{ + Name: "voice.interruptible", + Usage: "Whether the caller can interrupt the agent while it is speaking (barge-in). When true, the agent stops talking as soon as the caller starts.", + InnerField: "interruptible", + }, + &requestflag.InnerFlag[string]{ + Name: "voice.language", + Usage: "BCP-47 language code used for both speech recognition and speech synthesis (e.g. `en`, `es`, `pt-BR`). Auto-detected from the recipient when omitted.", + InnerField: "language", + }, + &requestflag.InnerFlag[int64]{ + Name: "voice.max-call-duration-minutes", + Usage: "Hard limit on call length in minutes. The call ends automatically when reached.", + InnerField: "maxCallDurationMinutes", + }, + &requestflag.InnerFlag[int64]{ + Name: "voice.max-idle-seconds", + Usage: "How long the agent waits during silence before ending the call.", + InnerField: "maxIdleSeconds", + }, + &requestflag.InnerFlag[string]{ + Name: "voice.model", + Usage: "Model that runs the conversation, co-located in the voice network for lowest latency. Independent of the model used for text messaging. Derived from the agent's text model when omitted.", + InnerField: "model", + }, + &requestflag.InnerFlag[bool]{ + Name: "voice.record-calls", + Usage: "Whether the call audio is recorded.", + InnerField: "recordCalls", + }, + &requestflag.InnerFlag[string]{ + Name: "voice.stt-model", + Usage: "Speech-recognition model. Uses the default when omitted.", + InnerField: "sttModel", + }, + &requestflag.InnerFlag[string]{ + Name: "voice.stt-provider", + Usage: "Speech-recognition provider. Uses the default when omitted.", + InnerField: "sttProvider", + }, + &requestflag.InnerFlag[string]{ + Name: "voice.transfer-phone-number", + Usage: "E.164 phone number the agent can transfer the call to. When set, the agent is given a transfer tool it can use to hand the call to a human.", + InnerField: "transferPhoneNumber", + }, + &requestflag.InnerFlag[string]{ + Name: "voice.tts-provider", + Usage: "Speech-synthesis provider. Uses the default when omitted.", + InnerField: "ttsProvider", + }, + &requestflag.InnerFlag[string]{ + Name: "voice.tts-voice-id", + Usage: "Identifier of the synthesized voice that speaks. Choose from the voices available in the dashboard. Uses a neutral default when omitted.", + InnerField: "ttsVoiceId", + }, + &requestflag.InnerFlag[string]{ + Name: "voice.voicemail-action", + Usage: "What the agent does when an answering machine or voicemail is detected on an outbound call.", + InnerField: "voicemailAction", + }, + &requestflag.InnerFlag[string]{ + Name: "voice.voicemail-message", + Usage: "Message spoken when `voicemailAction` is `leave_message`. Falls back to `greeting` when omitted.", + InnerField: "voicemailMessage", + }, + &requestflag.InnerFlag[float64]{ + Name: "voice.voice-speed", + Usage: "Speech rate. 1.0 is natural. Only honoured by voices that support rate control; ignored by the others.", + InnerField: "voiceSpeed", + }, + }, +}) + +var agentsRetrieve = cli.Command{ + Name: "retrieve", + Usage: "Get an agent", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "agent-id", + Required: true, + PathParam: "agentId", + }, + }, + Action: handleAgentsRetrieve, + HideHelpCommand: true, +} + +var agentsUpdate = requestflag.WithInnerFlags(cli.Command{ + Name: "update", + Usage: "Update an agent", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "agent-id", + Required: true, + PathParam: "agentId", + }, + &requestflag.Flag[string]{ + Name: "api-key", + BodyPath: "apiKey", + }, + &requestflag.Flag[int64]{ + Name: "context-window-messages", + BodyPath: "contextWindowMessages", + }, + &requestflag.Flag[bool]{ + Name: "enabled", + BodyPath: "enabled", + }, + &requestflag.Flag[bool]{ + Name: "include-contact-metadata", + BodyPath: "includeContactMetadata", + }, + &requestflag.Flag[*int64]{ + Name: "max-tokens", + BodyPath: "maxTokens", + }, + &requestflag.Flag[string]{ + Name: "model", + BodyPath: "model", + }, + &requestflag.Flag[string]{ + Name: "name", + BodyPath: "name", + }, + &requestflag.Flag[string]{ + Name: "provider", + Usage: "LLM provider for the AI agent.", + BodyPath: "provider", + }, + &requestflag.Flag[string]{ + Name: "system-prompt", + BodyPath: "systemPrompt", + }, + &requestflag.Flag[*float64]{ + Name: "temperature", + BodyPath: "temperature", + }, + &requestflag.Flag[[]string]{ + Name: "trigger-on-channel", + BodyPath: "triggerOnChannels", + }, + &requestflag.Flag[[]string]{ + Name: "trigger-on-message-type", + BodyPath: "triggerOnMessageTypes", + }, + &requestflag.Flag[map[string]any]{ + Name: "voice", + Usage: "Voice Agent configuration. Patch this object to enable voice, change the greeting, or adjust call limits. Requires the Voice Agents feature to be enabled for your team.", + BodyPath: "voice", + }, + }, + Action: handleAgentsUpdate, + HideHelpCommand: true, +}, map[string][]requestflag.HasOuterFlag{ + "voice": { + &requestflag.InnerFlag[bool]{ + Name: "voice.enabled", + Usage: "Whether the agent handles voice calls. When false, the sender's number is not answered by the voice agent and outbound calls are rejected.", + InnerField: "enabled", + }, + &requestflag.InnerFlag[string]{ + Name: "voice.greeting", + Usage: "Opening line the agent speaks when the call connects. If omitted, the agent waits for the caller to speak first.", + InnerField: "greeting", + }, + &requestflag.InnerFlag[map[string]any]{ + Name: "voice.greetings", + Usage: "Greeting per language, keyed by language code. Used when the caller's language differs from the one `greeting` is written in.", + InnerField: "greetings", + }, + &requestflag.InnerFlag[bool]{ + Name: "voice.interruptible", + Usage: "Whether the caller can interrupt the agent while it is speaking (barge-in). When true, the agent stops talking as soon as the caller starts.", + InnerField: "interruptible", + }, + &requestflag.InnerFlag[string]{ + Name: "voice.language", + Usage: "BCP-47 language code used for both speech recognition and speech synthesis (e.g. `en`, `es`, `pt-BR`). Auto-detected from the recipient when omitted.", + InnerField: "language", + }, + &requestflag.InnerFlag[int64]{ + Name: "voice.max-call-duration-minutes", + Usage: "Hard limit on call length in minutes. The call ends automatically when reached.", + InnerField: "maxCallDurationMinutes", + }, + &requestflag.InnerFlag[int64]{ + Name: "voice.max-idle-seconds", + Usage: "How long the agent waits during silence before ending the call.", + InnerField: "maxIdleSeconds", + }, + &requestflag.InnerFlag[string]{ + Name: "voice.model", + Usage: "Model that runs the conversation, co-located in the voice network for lowest latency. Independent of the model used for text messaging. Derived from the agent's text model when omitted.", + InnerField: "model", + }, + &requestflag.InnerFlag[bool]{ + Name: "voice.record-calls", + Usage: "Whether the call audio is recorded.", + InnerField: "recordCalls", + }, + &requestflag.InnerFlag[string]{ + Name: "voice.stt-model", + Usage: "Speech-recognition model. Uses the default when omitted.", + InnerField: "sttModel", + }, + &requestflag.InnerFlag[string]{ + Name: "voice.stt-provider", + Usage: "Speech-recognition provider. Uses the default when omitted.", + InnerField: "sttProvider", + }, + &requestflag.InnerFlag[string]{ + Name: "voice.transfer-phone-number", + Usage: "E.164 phone number the agent can transfer the call to. When set, the agent is given a transfer tool it can use to hand the call to a human.", + InnerField: "transferPhoneNumber", + }, + &requestflag.InnerFlag[string]{ + Name: "voice.tts-provider", + Usage: "Speech-synthesis provider. Uses the default when omitted.", + InnerField: "ttsProvider", + }, + &requestflag.InnerFlag[string]{ + Name: "voice.tts-voice-id", + Usage: "Identifier of the synthesized voice that speaks. Choose from the voices available in the dashboard. Uses a neutral default when omitted.", + InnerField: "ttsVoiceId", + }, + &requestflag.InnerFlag[string]{ + Name: "voice.voicemail-action", + Usage: "What the agent does when an answering machine or voicemail is detected on an outbound call.", + InnerField: "voicemailAction", + }, + &requestflag.InnerFlag[string]{ + Name: "voice.voicemail-message", + Usage: "Message spoken when `voicemailAction` is `leave_message`. Falls back to `greeting` when omitted.", + InnerField: "voicemailMessage", + }, + &requestflag.InnerFlag[float64]{ + Name: "voice.voice-speed", + Usage: "Speech rate. 1.0 is natural. Only honoured by voices that support rate control; ignored by the others.", + InnerField: "voiceSpeed", + }, + }, +}) + +var agentsList = cli.Command{ + Name: "list", + Usage: "Every agent in the project, newest first — including agents that are not\nconnected to any sender yet, which the sender-scoped routes cannot reach. Each\nitem carries `senderIds`, the senders the agent answers on.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "cursor", + QueryPath: "cursor", + }, + &requestflag.Flag[int64]{ + Name: "limit", + Default: 50, + QueryPath: "limit", + }, + &requestflag.Flag[int64]{ + Name: "max-items", + Usage: "The maximum number of items to return (use -1 for unlimited).", + }, + }, + Action: handleAgentsList, + HideHelpCommand: true, +} + +var agentsDelete = cli.Command{ + Name: "delete", + Usage: "Delete an agent", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "agent-id", + Required: true, + PathParam: "agentId", + }, + }, + Action: handleAgentsDelete, + HideHelpCommand: true, +} + +var agentsListVoices = cli.Command{ + Name: "list-voices", + Usage: "The voices an agent can speak with, for `voice.ttsVoiceId`. Filter by `language`\nto get the ones that speak it; a voice can still be used with `language: auto`,\nwhere the agent follows the caller and keeps the chosen voice.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "language", + Usage: "BCP-47 tag (`en`, `es`, `pt-BR`). Omit, or pass `auto`, for every voice.", + QueryPath: "language", + }, + }, + Action: handleAgentsListVoices, + HideHelpCommand: true, +} + +var agentsTest = requestflag.WithInnerFlags(cli.Command{ + Name: "test", + Usage: "Run the agent's prompt, model and knowledge base against a message and return\nthe reply instead of delivering it. Writes nothing and charges nothing, so it is\nsafe to call repeatedly while iterating on a prompt.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "agent-id", + Required: true, + PathParam: "agentId", + }, + &requestflag.Flag[string]{ + Name: "message", + Usage: "What to say to the agent.", + Required: true, + BodyPath: "message", + }, + &requestflag.Flag[bool]{ + Name: "execute-tools", + Usage: "Run the tools the agent calls instead of reporting the choice and stopping.\n\nOff by default because a tool handler talks to the outside world: a rehearsal that charges a card is not a rehearsal. Turn it on to exercise the loop that actually matters — the model picks a tool, the handler answers, the model replies with the result — without sending a message to anyone. What ran comes back in `executedToolCalls`.", + Default: false, + BodyPath: "executeTools", + }, + &requestflag.Flag[[]map[string]any]{ + Name: "history", + Usage: "Prior turns, oldest first, to exercise multi-turn behaviour without persisting a thread. Trimmed to the agent's context window.", + BodyPath: "history", + }, + &requestflag.Flag[bool]{ + Name: "use-knowledge-base", + Usage: "Set false to skip retrieval and isolate prompt behaviour from the knowledge base.", + Default: true, + BodyPath: "useKnowledgeBase", + }, + }, + Action: handleAgentsTest, + HideHelpCommand: true, +}, map[string][]requestflag.HasOuterFlag{ + "history": { + &requestflag.InnerFlag[string]{ + Name: "history.content", + InnerField: "content", + }, + &requestflag.InnerFlag[string]{ + Name: "history.role", + Usage: `Allowed values: "user", "assistant".`, + InnerField: "role", + }, + }, +}) + +func handleAgentsCreate(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + params := zavudev.AgentNewParams{} + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Agents.New(ctx, params, options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "agents create", + Transform: transform, + }) +} + +func handleAgentsRetrieve(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("agent-id") && len(unusedArgs) > 0 { + cmd.Set("agent-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Agents.Get(ctx, cmd.Value("agent-id").(string), options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "agents retrieve", + Transform: transform, + }) +} + +func handleAgentsUpdate(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("agent-id") && len(unusedArgs) > 0 { + cmd.Set("agent-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + params := zavudev.AgentUpdateParams{} + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Agents.Update( + ctx, + cmd.Value("agent-id").(string), + params, + options..., + ) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "agents update", + Transform: transform, + }) +} + +func handleAgentsList(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + params := zavudev.AgentListParams{} + + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + if format == "raw" { + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Agents.List(ctx, params, options...) + if err != nil { + return err + } + obj := gjson.ParseBytes(res) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "agents list", + Transform: transform, + }) + } else { + iter := client.Agents.ListAutoPaging(ctx, params, options...) + maxItems := int64(-1) + if cmd.IsSet("max-items") { + maxItems = cmd.Value("max-items").(int64) + } + return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "agents list", + Transform: transform, + }) + } +} + +func handleAgentsDelete(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("agent-id") && len(unusedArgs) > 0 { + cmd.Set("agent-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + return client.Agents.Delete(ctx, cmd.Value("agent-id").(string), options...) +} + +func handleAgentsListVoices(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + params := zavudev.AgentListVoicesParams{} + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Agents.ListVoices(ctx, params, options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "agents list-voices", + Transform: transform, + }) +} + +func handleAgentsTest(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("agent-id") && len(unusedArgs) > 0 { + cmd.Set("agent-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + params := zavudev.AgentTestParams{} + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Agents.Test( + ctx, + cmd.Value("agent-id").(string), + params, + options..., + ) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "agents test", + Transform: transform, + }) +} diff --git a/pkg/cmd/agent_test.go b/pkg/cmd/agent_test.go new file mode 100644 index 0000000..391608e --- /dev/null +++ b/pkg/cmd/agent_test.go @@ -0,0 +1,324 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "testing" + + "github.com/zavudev/cli/internal/mocktest" + "github.com/zavudev/cli/internal/requestflag" +) + +func TestAgentsCreate(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "agents", "create", + "--model", "model", + "--name", "name", + "--provider", "openai", + "--system-prompt", "systemPrompt", + "--context-window-messages", "1", + "--include-contact-metadata=true", + "--max-tokens", "1", + "--temperature", "0", + "--trigger-on-channel", "string", + "--trigger-on-message-type", "string", + "--voice", "{enabled: true, greeting: 'Hi, thanks for calling Acme. How can I help you today?', greetings: {es: 'Hola, soy Atlas. Preguntame lo que quieras.'}, interruptible: true, language: en, maxCallDurationMinutes: 1, maxIdleSeconds: 5, model: openai/gpt-4o, recordCalls: true, sttModel: sttModel, sttProvider: sttProvider, transferPhoneNumber: '+14155551234', ttsProvider: ttsProvider, ttsVoiceId: aria, voicemailAction: hangup, voicemailMessage: voicemailMessage, voiceSpeed: 0.5}", + ) + }) + + t.Run("inner flags", func(t *testing.T) { + // Check that inner flags have been set up correctly + requestflag.CheckInnerFlags(agentsCreate) + + // Alternative argument passing style using inner flags + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "agents", "create", + "--model", "model", + "--name", "name", + "--provider", "openai", + "--system-prompt", "systemPrompt", + "--context-window-messages", "1", + "--include-contact-metadata=true", + "--max-tokens", "1", + "--temperature", "0", + "--trigger-on-channel", "string", + "--trigger-on-message-type", "string", + "--voice.enabled=true", + "--voice.greeting", "Hi, thanks for calling Acme. How can I help you today?", + "--voice.greetings", "{es: 'Hola, soy Atlas. Preguntame lo que quieras.'}", + "--voice.interruptible=true", + "--voice.language", "en", + "--voice.max-call-duration-minutes", "1", + "--voice.max-idle-seconds", "5", + "--voice.model", "openai/gpt-4o", + "--voice.record-calls=true", + "--voice.stt-model", "sttModel", + "--voice.stt-provider", "sttProvider", + "--voice.transfer-phone-number", "+14155551234", + "--voice.tts-provider", "ttsProvider", + "--voice.tts-voice-id", "aria", + "--voice.voicemail-action", "hangup", + "--voice.voicemail-message", "voicemailMessage", + "--voice.voice-speed", "0.5", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("" + + "model: model\n" + + "name: name\n" + + "provider: openai\n" + + "systemPrompt: systemPrompt\n" + + "contextWindowMessages: 1\n" + + "includeContactMetadata: true\n" + + "maxTokens: 1\n" + + "temperature: 0\n" + + "triggerOnChannels:\n" + + " - string\n" + + "triggerOnMessageTypes:\n" + + " - string\n" + + "voice:\n" + + " enabled: true\n" + + " greeting: Hi, thanks for calling Acme. How can I help you today?\n" + + " greetings:\n" + + " es: Hola, soy Atlas. Preguntame lo que quieras.\n" + + " interruptible: true\n" + + " language: en\n" + + " maxCallDurationMinutes: 1\n" + + " maxIdleSeconds: 5\n" + + " model: openai/gpt-4o\n" + + " recordCalls: true\n" + + " sttModel: sttModel\n" + + " sttProvider: sttProvider\n" + + " transferPhoneNumber: '+14155551234'\n" + + " ttsProvider: ttsProvider\n" + + " ttsVoiceId: aria\n" + + " voicemailAction: hangup\n" + + " voicemailMessage: voicemailMessage\n" + + " voiceSpeed: 0.5\n") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "agents", "create", + ) + }) +} + +func TestAgentsRetrieve(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "agents", "retrieve", + "--agent-id", "agentId", + ) + }) +} + +func TestAgentsUpdate(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "agents", "update", + "--agent-id", "agentId", + "--api-key", "apiKey", + "--context-window-messages", "1", + "--enabled=true", + "--include-contact-metadata=true", + "--max-tokens", "1", + "--model", "model", + "--name", "name", + "--provider", "openai", + "--system-prompt", "systemPrompt", + "--temperature", "0", + "--trigger-on-channel", "string", + "--trigger-on-message-type", "string", + "--voice", "{enabled: true, greeting: 'Hi, thanks for calling Acme. How can I help you today?', greetings: {es: 'Hola, soy Atlas. Preguntame lo que quieras.'}, interruptible: true, language: en, maxCallDurationMinutes: 1, maxIdleSeconds: 5, model: openai/gpt-4o, recordCalls: true, sttModel: sttModel, sttProvider: sttProvider, transferPhoneNumber: '+14155551234', ttsProvider: ttsProvider, ttsVoiceId: aria, voicemailAction: hangup, voicemailMessage: voicemailMessage, voiceSpeed: 0.5}", + ) + }) + + t.Run("inner flags", func(t *testing.T) { + // Check that inner flags have been set up correctly + requestflag.CheckInnerFlags(agentsUpdate) + + // Alternative argument passing style using inner flags + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "agents", "update", + "--agent-id", "agentId", + "--api-key", "apiKey", + "--context-window-messages", "1", + "--enabled=true", + "--include-contact-metadata=true", + "--max-tokens", "1", + "--model", "model", + "--name", "name", + "--provider", "openai", + "--system-prompt", "systemPrompt", + "--temperature", "0", + "--trigger-on-channel", "string", + "--trigger-on-message-type", "string", + "--voice.enabled=true", + "--voice.greeting", "Hi, thanks for calling Acme. How can I help you today?", + "--voice.greetings", "{es: 'Hola, soy Atlas. Preguntame lo que quieras.'}", + "--voice.interruptible=true", + "--voice.language", "en", + "--voice.max-call-duration-minutes", "1", + "--voice.max-idle-seconds", "5", + "--voice.model", "openai/gpt-4o", + "--voice.record-calls=true", + "--voice.stt-model", "sttModel", + "--voice.stt-provider", "sttProvider", + "--voice.transfer-phone-number", "+14155551234", + "--voice.tts-provider", "ttsProvider", + "--voice.tts-voice-id", "aria", + "--voice.voicemail-action", "hangup", + "--voice.voicemail-message", "voicemailMessage", + "--voice.voice-speed", "0.5", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("" + + "apiKey: apiKey\n" + + "contextWindowMessages: 1\n" + + "enabled: true\n" + + "includeContactMetadata: true\n" + + "maxTokens: 1\n" + + "model: model\n" + + "name: name\n" + + "provider: openai\n" + + "systemPrompt: systemPrompt\n" + + "temperature: 0\n" + + "triggerOnChannels:\n" + + " - string\n" + + "triggerOnMessageTypes:\n" + + " - string\n" + + "voice:\n" + + " enabled: true\n" + + " greeting: Hi, thanks for calling Acme. How can I help you today?\n" + + " greetings:\n" + + " es: Hola, soy Atlas. Preguntame lo que quieras.\n" + + " interruptible: true\n" + + " language: en\n" + + " maxCallDurationMinutes: 1\n" + + " maxIdleSeconds: 5\n" + + " model: openai/gpt-4o\n" + + " recordCalls: true\n" + + " sttModel: sttModel\n" + + " sttProvider: sttProvider\n" + + " transferPhoneNumber: '+14155551234'\n" + + " ttsProvider: ttsProvider\n" + + " ttsVoiceId: aria\n" + + " voicemailAction: hangup\n" + + " voicemailMessage: voicemailMessage\n" + + " voiceSpeed: 0.5\n") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "agents", "update", + "--agent-id", "agentId", + ) + }) +} + +func TestAgentsList(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "agents", "list", + "--max-items", "10", + "--cursor", "cursor", + "--limit", "100", + ) + }) +} + +func TestAgentsDelete(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "agents", "delete", + "--agent-id", "agentId", + ) + }) +} + +func TestAgentsListVoices(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "agents", "list-voices", + "--language", "es", + ) + }) +} + +func TestAgentsTest(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "agents", "test", + "--agent-id", "agentId", + "--message", "Where is order ORD-12345?", + "--execute-tools=true", + "--history", "{content: content, role: user}", + "--use-knowledge-base=true", + ) + }) + + t.Run("inner flags", func(t *testing.T) { + // Check that inner flags have been set up correctly + requestflag.CheckInnerFlags(agentsTest) + + // Alternative argument passing style using inner flags + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "agents", "test", + "--agent-id", "agentId", + "--message", "Where is order ORD-12345?", + "--execute-tools=true", + "--history.content", "content", + "--history.role", "user", + "--use-knowledge-base=true", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("" + + "message: Where is order ORD-12345?\n" + + "executeTools: true\n" + + "history:\n" + + " - content: content\n" + + " role: user\n" + + "useKnowledgeBase: true\n") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "agents", "test", + "--agent-id", "agentId", + ) + }) +} diff --git a/pkg/cmd/agentsender.go b/pkg/cmd/agentsender.go new file mode 100644 index 0000000..7ee9735 --- /dev/null +++ b/pkg/cmd/agentsender.go @@ -0,0 +1,139 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "context" + "fmt" + + "github.com/tidwall/gjson" + "github.com/urfave/cli/v3" + "github.com/zavudev/cli/internal/apiquery" + "github.com/zavudev/cli/internal/requestflag" + "github.com/zavudev/sdk-go" + "github.com/zavudev/sdk-go/option" +) + +var agentsSendersConnect = cli.Command{ + Name: "connect", + Usage: "Make the agent answer on this sender. An agent can serve several senders; a\nsender answers with at most one agent, so connecting one that is already in use\nreturns `400` naming the agent that holds it.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "agent-id", + Required: true, + PathParam: "agentId", + }, + &requestflag.Flag[string]{ + Name: "sender-id", + Usage: "Sender to connect.", + Required: true, + BodyPath: "senderId", + }, + }, + Action: handleAgentsSendersConnect, + HideHelpCommand: true, +} + +var agentsSendersDisconnect = cli.Command{ + Name: "disconnect", + Usage: "Stop the agent answering on this sender. The agent's primary sender is part of\nthe agent itself and cannot be disconnected here.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "agent-id", + Required: true, + PathParam: "agentId", + }, + &requestflag.Flag[string]{ + Name: "sender-id", + Required: true, + PathParam: "senderId", + }, + }, + Action: handleAgentsSendersDisconnect, + HideHelpCommand: true, +} + +func handleAgentsSendersConnect(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("agent-id") && len(unusedArgs) > 0 { + cmd.Set("agent-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + params := zavudev.AgentSenderConnectParams{} + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Agents.Senders.Connect( + ctx, + cmd.Value("agent-id").(string), + params, + options..., + ) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "agents:senders connect", + Transform: transform, + }) +} + +func handleAgentsSendersDisconnect(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("sender-id") && len(unusedArgs) > 0 { + cmd.Set("sender-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + params := zavudev.AgentSenderDisconnectParams{ + AgentID: cmd.Value("agent-id").(string), + } + + return client.Agents.Senders.Disconnect( + ctx, + cmd.Value("sender-id").(string), + params, + options..., + ) +} diff --git a/pkg/cmd/agentsender_test.go b/pkg/cmd/agentsender_test.go new file mode 100644 index 0000000..d2fea18 --- /dev/null +++ b/pkg/cmd/agentsender_test.go @@ -0,0 +1,46 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "testing" + + "github.com/zavudev/cli/internal/mocktest" +) + +func TestAgentsSendersConnect(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "agents:senders", "connect", + "--agent-id", "agentId", + "--sender-id", "senderId", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("senderId: senderId") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "agents:senders", "connect", + "--agent-id", "agentId", + ) + }) +} + +func TestAgentsSendersDisconnect(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "agents:senders", "disconnect", + "--agent-id", "agentId", + "--sender-id", "senderId", + ) + }) +} diff --git a/pkg/cmd/agenttemplate.go b/pkg/cmd/agenttemplate.go new file mode 100644 index 0000000..ab4bd26 --- /dev/null +++ b/pkg/cmd/agenttemplate.go @@ -0,0 +1,120 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "context" + "fmt" + + "github.com/tidwall/gjson" + "github.com/urfave/cli/v3" + "github.com/zavudev/cli/internal/apiquery" + "github.com/zavudev/cli/internal/requestflag" + "github.com/zavudev/sdk-go" + "github.com/zavudev/sdk-go/option" +) + +var agentTemplatesRetrieve = cli.Command{ + Name: "retrieve", + Usage: "Fetch a single factory agent fully rendered: the function files to scaffold (an\n`index.ts` that declares the agent with `defineAgent` and its skills with\n`defineTool`) plus the secrets it needs. This is what\n`npx zavudev agents pull ` writes to disk before `npx zavudev deploy`.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "template-id", + Required: true, + PathParam: "templateId", + }, + }, + Action: handleAgentTemplatesRetrieve, + HideHelpCommand: true, +} + +var agentTemplatesList = cli.Command{ + Name: "list", + Usage: "List the factory agents available to scaffold with `npx zavudev agents pull`.\nEach entry is a ready-made voice or text agent (system prompt, skills, and — for\nvoice agents — a co-located voice config).", + Suggest: true, + Flags: []cli.Flag{}, + Action: handleAgentTemplatesList, + HideHelpCommand: true, +} + +func handleAgentTemplatesRetrieve(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("template-id") && len(unusedArgs) > 0 { + cmd.Set("template-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.AgentTemplates.Get(ctx, cmd.Value("template-id").(string), options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "agent-templates retrieve", + Transform: transform, + }) +} + +func handleAgentTemplatesList(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.AgentTemplates.List(ctx, options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "agent-templates list", + Transform: transform, + }) +} diff --git a/pkg/cmd/agenttemplate_test.go b/pkg/cmd/agenttemplate_test.go new file mode 100644 index 0000000..5dff614 --- /dev/null +++ b/pkg/cmd/agenttemplate_test.go @@ -0,0 +1,32 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "testing" + + "github.com/zavudev/cli/internal/mocktest" +) + +func TestAgentTemplatesRetrieve(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "agent-templates", "retrieve", + "--template-id", "fermi", + ) + }) +} + +func TestAgentTemplatesList(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "agent-templates", "list", + ) + }) +} diff --git a/pkg/cmd/call.go b/pkg/cmd/call.go new file mode 100644 index 0000000..d2650fa --- /dev/null +++ b/pkg/cmd/call.go @@ -0,0 +1,299 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "context" + "fmt" + + "github.com/tidwall/gjson" + "github.com/urfave/cli/v3" + "github.com/zavudev/cli/internal/apiquery" + "github.com/zavudev/cli/internal/requestflag" + "github.com/zavudev/sdk-go" + "github.com/zavudev/sdk-go/option" +) + +var callsCreate = cli.Command{ + Name: "create", + Usage: "Place an outbound voice call answered by the voice agent configured on the\nsender. Zavu dials the recipient and runs the conversation through its managed\nvoice pipeline (speech recognition, the agent's LLM, and speech synthesis, with\nreal-time interruption handling).", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "to", + Usage: "Recipient phone number in E.164 format.", + Required: true, + BodyPath: "to", + }, + &requestflag.Flag[string]{ + Name: "greeting", + Usage: "Overrides the agent's configured greeting for this call only.", + BodyPath: "greeting", + }, + &requestflag.Flag[string]{ + Name: "language", + Usage: "Language the agent speaks on this call only, as a BCP-47 tag (`en`, `es`, `es-ES`, `pt-BR`), or `auto` to detect the caller's language and follow it. Overrides the agent's configured language for speech recognition, the agent's replies, and the synthesized voice. If the agent uses a custom voice you supplied, that voice is kept and only the language changes. When omitted, the agent's configured language is used.", + BodyPath: "language", + }, + &requestflag.Flag[int64]{ + Name: "max-duration-minutes", + Usage: "Overrides the agent's maximum call duration for this call only.", + BodyPath: "maxDurationMinutes", + }, + &requestflag.Flag[map[string]any]{ + Name: "metadata", + Usage: "Arbitrary metadata to associate with the call. Returned on the call object and included in voice webhooks.", + BodyPath: "metadata", + }, + &requestflag.Flag[string]{ + Name: "sender-id", + Usage: "Sender profile that places the call. Uses the project's default sender if omitted. The sender's agent must have voice enabled.", + BodyPath: "senderId", + }, + }, + Action: handleCallsCreate, + HideHelpCommand: true, +} + +var callsRetrieve = cli.Command{ + Name: "retrieve", + Usage: "Retrieve a single voice call, including its full transcript once the\nconversation has produced turns.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "call-id", + Required: true, + PathParam: "callId", + }, + }, + Action: handleCallsRetrieve, + HideHelpCommand: true, +} + +var callsList = cli.Command{ + Name: "list", + Usage: "List voice calls for this project, most recent first. Transcripts are omitted\nfrom the list; fetch a single call to get its transcript.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "cursor", + QueryPath: "cursor", + }, + &requestflag.Flag[string]{ + Name: "direction", + Usage: "Whether the call was placed by Zavu (outbound) or received from a caller (inbound).", + QueryPath: "direction", + }, + &requestflag.Flag[int64]{ + Name: "limit", + Default: 50, + QueryPath: "limit", + }, + &requestflag.Flag[string]{ + Name: "status", + Usage: "Lifecycle status of a voice call.\n- `queued`: outbound call created, not yet dialing.\n- `ringing`: dialing (outbound) or received and ringing (inbound).\n- `in_progress`: answered, the agent is connected.\n- `completed`: ended after a conversation.\n- `failed`: could not be completed.\n- `busy`: the line was busy.\n- `no_answer`: rang but was not answered.\n- `canceled`: canceled before it was answered.", + QueryPath: "status", + }, + &requestflag.Flag[int64]{ + Name: "max-items", + Usage: "The maximum number of items to return (use -1 for unlimited).", + }, + }, + Action: handleCallsList, + HideHelpCommand: true, +} + +var callsHangup = cli.Command{ + Name: "hangup", + Usage: "End an active voice call. The call must still be ringing or in progress. Not\navailable with test-mode API keys.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "call-id", + Required: true, + PathParam: "callId", + }, + }, + Action: handleCallsHangup, + HideHelpCommand: true, +} + +func handleCallsCreate(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + params := zavudev.CallNewParams{} + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Calls.New(ctx, params, options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "calls create", + Transform: transform, + }) +} + +func handleCallsRetrieve(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("call-id") && len(unusedArgs) > 0 { + cmd.Set("call-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Calls.Get(ctx, cmd.Value("call-id").(string), options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "calls retrieve", + Transform: transform, + }) +} + +func handleCallsList(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + params := zavudev.CallListParams{} + + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + if format == "raw" { + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Calls.List(ctx, params, options...) + if err != nil { + return err + } + obj := gjson.ParseBytes(res) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "calls list", + Transform: transform, + }) + } else { + iter := client.Calls.ListAutoPaging(ctx, params, options...) + maxItems := int64(-1) + if cmd.IsSet("max-items") { + maxItems = cmd.Value("max-items").(int64) + } + return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "calls list", + Transform: transform, + }) + } +} + +func handleCallsHangup(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("call-id") && len(unusedArgs) > 0 { + cmd.Set("call-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Calls.Hangup(ctx, cmd.Value("call-id").(string), options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "calls hangup", + Transform: transform, + }) +} diff --git a/pkg/cmd/call_test.go b/pkg/cmd/call_test.go new file mode 100644 index 0000000..531443d --- /dev/null +++ b/pkg/cmd/call_test.go @@ -0,0 +1,83 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "testing" + + "github.com/zavudev/cli/internal/mocktest" +) + +func TestCallsCreate(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "calls", "create", + "--to", "+56912345678", + "--greeting", "greeting", + "--language", "es-ES", + "--max-duration-minutes", "1", + "--metadata", "{foo: string}", + "--sender-id", "sender_12345", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("" + + "to: '+56912345678'\n" + + "greeting: greeting\n" + + "language: es-ES\n" + + "maxDurationMinutes: 1\n" + + "metadata:\n" + + " foo: string\n" + + "senderId: sender_12345\n") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "calls", "create", + ) + }) +} + +func TestCallsRetrieve(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "calls", "retrieve", + "--call-id", "callId", + ) + }) +} + +func TestCallsList(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "calls", "list", + "--max-items", "10", + "--cursor", "cursor", + "--direction", "inbound", + "--limit", "100", + "--status", "queued", + ) + }) +} + +func TestCallsHangup(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "calls", "hangup", + "--call-id", "callId", + ) + }) +} diff --git a/pkg/cmd/cmd.go b/pkg/cmd/cmd.go index d2f0a41..27002ce 100644 --- a/pkg/cmd/cmd.go +++ b/pkg/cmd/cmd.go @@ -91,6 +91,7 @@ func init() { Commands: []*cli.Command{ &messagesRetrieve, &messagesList, + &messagesListAttachments, &messagesReact, &messagesSend, &messagesShowTyping, @@ -106,6 +107,7 @@ func init() { &templatesList, &templatesDelete, &templatesSubmit, + &templatesSync, }, }, { @@ -168,9 +170,18 @@ func init() { &sendersAgentToolsUpdate, &sendersAgentToolsList, &sendersAgentToolsDelete, + &sendersAgentToolsListTestRuns, &sendersAgentToolsTest, }, }, + { + Name: "senders:agent:tools:webhook", + Category: "API RESOURCE", + Suggest: true, + Commands: []*cli.Command{ + &sendersAgentToolsWebhookRotateSecret, + }, + }, { Name: "senders:agent:knowledge-bases", Category: "API RESOURCE", @@ -191,6 +202,8 @@ func init() { &sendersAgentKnowledgeBasesDocumentsCreate, &sendersAgentKnowledgeBasesDocumentsList, &sendersAgentKnowledgeBasesDocumentsDelete, + &sendersAgentKnowledgeBasesDocumentsRetrieveDocument, + &sendersAgentKnowledgeBasesDocumentsUpdateDocument, }, }, { @@ -203,6 +216,15 @@ func init() { &sendersWhatsappSyncStartHistorySync, }, }, + { + Name: "senders:telegram", + Category: "API RESOURCE", + Suggest: true, + Commands: []*cli.Command{ + &sendersTelegramConnect, + &sendersTelegramDisconnect, + }, + }, { Name: "contacts", Category: "API RESOURCE", @@ -262,6 +284,7 @@ func init() { Category: "API RESOURCE", Suggest: true, Commands: []*cli.Command{ + &introspectValidateEmail, &introspectValidatePhone, }, }, @@ -318,6 +341,7 @@ func init() { Category: "API RESOURCE", Suggest: true, Commands: []*cli.Command{ + &urlsEscalate, &urlsListVerified, &urlsRetrieveDetails, &urlsSubmitForVerification, @@ -412,6 +436,9 @@ func init() { &functionsDelete, &functionsDeploy, &functionsGetDeployment, + &functionsListDeployments, + &functionsListEventTypes, + &functionsRollbackDeployment, &functionsTailLogs, }, }, @@ -425,6 +452,95 @@ func init() { &functionsSecretsUnset, }, }, + { + Name: "functions:triggers", + Category: "API RESOURCE", + Suggest: true, + Commands: []*cli.Command{ + &functionsTriggersCreate, + &functionsTriggersUpdate, + &functionsTriggersList, + &functionsTriggersDelete, + }, + }, + { + Name: "functions:git-link", + Category: "API RESOURCE", + Suggest: true, + Commands: []*cli.Command{ + &functionsGitLinkRetrieve, + &functionsGitLinkUpdate, + &functionsGitLinkDeployNow, + &functionsGitLinkLink, + &functionsGitLinkUnlink, + }, + }, + { + Name: "conversations", + Category: "API RESOURCE", + Suggest: true, + Commands: []*cli.Command{ + &conversationsRetrieve, + &conversationsList, + &conversationsListMessages, + &conversationsMarkAsRead, + }, + }, + { + Name: "calls", + Category: "API RESOURCE", + Suggest: true, + Commands: []*cli.Command{ + &callsCreate, + &callsRetrieve, + &callsList, + &callsHangup, + }, + }, + { + Name: "agent-templates", + Category: "API RESOURCE", + Suggest: true, + Commands: []*cli.Command{ + &agentTemplatesRetrieve, + &agentTemplatesList, + }, + }, + { + Name: "email-domains", + Category: "API RESOURCE", + Suggest: true, + Commands: []*cli.Command{ + &emailDomainsCreate, + &emailDomainsRetrieve, + &emailDomainsList, + &emailDomainsDelete, + &emailDomainsVerify, + }, + }, + { + Name: "agents", + Category: "API RESOURCE", + Suggest: true, + Commands: []*cli.Command{ + &agentsCreate, + &agentsRetrieve, + &agentsUpdate, + &agentsList, + &agentsDelete, + &agentsListVoices, + &agentsTest, + }, + }, + { + Name: "agents:senders", + Category: "API RESOURCE", + Suggest: true, + Commands: []*cli.Command{ + &agentsSendersConnect, + &agentsSendersDisconnect, + }, + }, { Name: "@manpages", Usage: "Generate documentation for 'man'", diff --git a/pkg/cmd/conversation.go b/pkg/cmd/conversation.go new file mode 100644 index 0000000..822b40e --- /dev/null +++ b/pkg/cmd/conversation.go @@ -0,0 +1,320 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "context" + "fmt" + + "github.com/tidwall/gjson" + "github.com/urfave/cli/v3" + "github.com/zavudev/cli/internal/apiquery" + "github.com/zavudev/cli/internal/requestflag" + "github.com/zavudev/sdk-go" + "github.com/zavudev/sdk-go/option" +) + +var conversationsRetrieve = cli.Command{ + Name: "retrieve", + Usage: "Get conversation", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "conversation-id", + Required: true, + PathParam: "conversationId", + }, + }, + Action: handleConversationsRetrieve, + HideHelpCommand: true, +} + +var conversationsList = cli.Command{ + Name: "list", + Usage: "List inbox threads, most recently active first. A conversation groups every\nmessage with one contact across channels, which is what you need to build an\ninbox: `GET /v1/messages` returns a flat log with no thread to hang it on.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "channel", + Usage: "Keep only threads that have carried this channel.", + QueryPath: "channel", + }, + &requestflag.Flag[string]{ + Name: "cursor", + Usage: "Opaque cursor from a previous response's `nextCursor`. Do not construct it.", + QueryPath: "cursor", + }, + &requestflag.Flag[int64]{ + Name: "limit", + Default: 50, + QueryPath: "limit", + }, + &requestflag.Flag[string]{ + Name: "search", + Usage: "Search threads by identity: phone number (any format — `+1 (555) 123-4567` and `15551234567` both match), email address (full or local part), WhatsApp group subject, WhatsApp username, or BSUID. Matching is by whole word, with prefix matching on the last term, so `mar` finds `maria@example.com` and `+1555` finds `+15551234567`; a fragment from the middle or end of a number (`4567`) does not match.\n\nIt does **not** search message bodies — only who the thread is with.\n\nResults come back ranked by relevance rather than by recency, so the usual \"most recently active first\" ordering does not apply while `q` is set. `senderId` and `channel` still narrow the results, and `cursor` paginates them as usual. An empty or whitespace-only `q` returns no items rather than the full list.", + QueryPath: "search", + }, + &requestflag.Flag[string]{ + Name: "sender-id", + Usage: "Keep only threads last handled by this sender.", + QueryPath: "senderId", + }, + &requestflag.Flag[int64]{ + Name: "max-items", + Usage: "The maximum number of items to return (use -1 for unlimited).", + }, + }, + Action: handleConversationsList, + HideHelpCommand: true, +} + +var conversationsListMessages = cli.Command{ + Name: "list-messages", + Usage: "Messages in this thread, newest first, across every channel it has carried.\nReply with `POST /v1/messages`, passing the conversation's `senderId` as the\n`Zavu-Sender` header so the answer leaves from the number the contact already\nknows.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "conversation-id", + Required: true, + PathParam: "conversationId", + }, + &requestflag.Flag[string]{ + Name: "cursor", + Usage: "Opaque cursor from a previous response's `nextCursor`.", + QueryPath: "cursor", + }, + &requestflag.Flag[int64]{ + Name: "limit", + Default: 50, + QueryPath: "limit", + }, + &requestflag.Flag[int64]{ + Name: "max-items", + Usage: "The maximum number of items to return (use -1 for unlimited).", + }, + }, + Action: handleConversationsListMessages, + HideHelpCommand: true, +} + +var conversationsMarkAsRead = cli.Command{ + Name: "mark-as-read", + Usage: "Reset the thread's `unreadCount` to zero. Marks the thread read in your own\ninbox only: it does not send a read receipt to the contact.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "conversation-id", + Required: true, + PathParam: "conversationId", + }, + }, + Action: handleConversationsMarkAsRead, + HideHelpCommand: true, +} + +func handleConversationsRetrieve(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("conversation-id") && len(unusedArgs) > 0 { + cmd.Set("conversation-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Conversations.Get(ctx, cmd.Value("conversation-id").(string), options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "conversations retrieve", + Transform: transform, + }) +} + +func handleConversationsList(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + params := zavudev.ConversationListParams{} + + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + if format == "raw" { + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Conversations.List(ctx, params, options...) + if err != nil { + return err + } + obj := gjson.ParseBytes(res) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "conversations list", + Transform: transform, + }) + } else { + iter := client.Conversations.ListAutoPaging(ctx, params, options...) + maxItems := int64(-1) + if cmd.IsSet("max-items") { + maxItems = cmd.Value("max-items").(int64) + } + return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "conversations list", + Transform: transform, + }) + } +} + +func handleConversationsListMessages(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("conversation-id") && len(unusedArgs) > 0 { + cmd.Set("conversation-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + params := zavudev.ConversationListMessagesParams{} + + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + if format == "raw" { + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Conversations.ListMessages( + ctx, + cmd.Value("conversation-id").(string), + params, + options..., + ) + if err != nil { + return err + } + obj := gjson.ParseBytes(res) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "conversations list-messages", + Transform: transform, + }) + } else { + iter := client.Conversations.ListMessagesAutoPaging( + ctx, + cmd.Value("conversation-id").(string), + params, + options..., + ) + maxItems := int64(-1) + if cmd.IsSet("max-items") { + maxItems = cmd.Value("max-items").(int64) + } + return ShowJSONIterator(iter, maxItems, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "conversations list-messages", + Transform: transform, + }) + } +} + +func handleConversationsMarkAsRead(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("conversation-id") && len(unusedArgs) > 0 { + cmd.Set("conversation-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Conversations.MarkAsRead(ctx, cmd.Value("conversation-id").(string), options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "conversations mark-as-read", + Transform: transform, + }) +} diff --git a/pkg/cmd/conversation_test.go b/pkg/cmd/conversation_test.go new file mode 100644 index 0000000..949809f --- /dev/null +++ b/pkg/cmd/conversation_test.go @@ -0,0 +1,65 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "testing" + + "github.com/zavudev/cli/internal/mocktest" +) + +func TestConversationsRetrieve(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "conversations", "retrieve", + "--conversation-id", "conversationId", + ) + }) +} + +func TestConversationsList(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "conversations", "list", + "--max-items", "10", + "--channel", "sms", + "--cursor", "cursor", + "--limit", "100", + "--search", "+56912345678", + "--sender-id", "senderId", + ) + }) +} + +func TestConversationsListMessages(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "conversations", "list-messages", + "--max-items", "10", + "--conversation-id", "conversationId", + "--cursor", "cursor", + "--limit", "100", + ) + }) +} + +func TestConversationsMarkAsRead(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "conversations", "mark-as-read", + "--conversation-id", "conversationId", + ) + }) +} diff --git a/pkg/cmd/emaildomain.go b/pkg/cmd/emaildomain.go new file mode 100644 index 0000000..578c923 --- /dev/null +++ b/pkg/cmd/emaildomain.go @@ -0,0 +1,274 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "context" + "fmt" + + "github.com/tidwall/gjson" + "github.com/urfave/cli/v3" + "github.com/zavudev/cli/internal/apiquery" + "github.com/zavudev/cli/internal/requestflag" + "github.com/zavudev/sdk-go" + "github.com/zavudev/sdk-go/option" +) + +var emailDomainsCreate = cli.Command{ + Name: "create", + Usage: "Add a domain to send email from. Returns the DNS records to publish (DKIM CNAMEs\nare required; SPF, DMARC, and MAIL FROM are recommended). Publish them at your\nDNS provider, then verify.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "domain", + Usage: "Bare domain, e.g. example.com.", + Required: true, + BodyPath: "domain", + }, + }, + Action: handleEmailDomainsCreate, + HideHelpCommand: true, +} + +var emailDomainsRetrieve = cli.Command{ + Name: "retrieve", + Usage: "Fetch a domain with its DNS records and current status.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "domain-id", + Required: true, + PathParam: "domainId", + }, + }, + Action: handleEmailDomainsRetrieve, + HideHelpCommand: true, +} + +var emailDomainsList = cli.Command{ + Name: "list", + Usage: "List email domains", + Suggest: true, + Flags: []cli.Flag{}, + Action: handleEmailDomainsList, + HideHelpCommand: true, +} + +var emailDomainsDelete = cli.Command{ + Name: "delete", + Usage: "Remove an email domain", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "domain-id", + Required: true, + PathParam: "domainId", + }, + }, + Action: handleEmailDomainsDelete, + HideHelpCommand: true, +} + +var emailDomainsVerify = cli.Command{ + Name: "verify", + Usage: "Re-check the domain's published DNS records and refresh its status.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "domain-id", + Required: true, + PathParam: "domainId", + }, + }, + Action: handleEmailDomainsVerify, + HideHelpCommand: true, +} + +func handleEmailDomainsCreate(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + params := zavudev.EmailDomainNewParams{} + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.EmailDomains.New(ctx, params, options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "email-domains create", + Transform: transform, + }) +} + +func handleEmailDomainsRetrieve(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("domain-id") && len(unusedArgs) > 0 { + cmd.Set("domain-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.EmailDomains.Get(ctx, cmd.Value("domain-id").(string), options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "email-domains retrieve", + Transform: transform, + }) +} + +func handleEmailDomainsList(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.EmailDomains.List(ctx, options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "email-domains list", + Transform: transform, + }) +} + +func handleEmailDomainsDelete(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("domain-id") && len(unusedArgs) > 0 { + cmd.Set("domain-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + return client.EmailDomains.Delete(ctx, cmd.Value("domain-id").(string), options...) +} + +func handleEmailDomainsVerify(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("domain-id") && len(unusedArgs) > 0 { + cmd.Set("domain-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.EmailDomains.Verify(ctx, cmd.Value("domain-id").(string), options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "email-domains verify", + Transform: transform, + }) +} diff --git a/pkg/cmd/emaildomain_test.go b/pkg/cmd/emaildomain_test.go new file mode 100644 index 0000000..fccee9a --- /dev/null +++ b/pkg/cmd/emaildomain_test.go @@ -0,0 +1,78 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "testing" + + "github.com/zavudev/cli/internal/mocktest" +) + +func TestEmailDomainsCreate(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "email-domains", "create", + "--domain", "example.com", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("domain: example.com") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "email-domains", "create", + ) + }) +} + +func TestEmailDomainsRetrieve(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "email-domains", "retrieve", + "--domain-id", "domainId", + ) + }) +} + +func TestEmailDomainsList(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "email-domains", "list", + ) + }) +} + +func TestEmailDomainsDelete(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "email-domains", "delete", + "--domain-id", "domainId", + ) + }) +} + +func TestEmailDomainsVerify(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "email-domains", "verify", + "--domain-id", "domainId", + ) + }) +} diff --git a/pkg/cmd/function.go b/pkg/cmd/function.go index 1040368..87fb74f 100644 --- a/pkg/cmd/function.go +++ b/pkg/cmd/function.go @@ -205,6 +205,56 @@ var functionsGetDeployment = cli.Command{ HideHelpCommand: true, } +var functionsListDeployments = cli.Command{ + Name: "list-deployments", + Usage: "List a function's deployment history, newest first. Source code is omitted;\nfetch a single deployment via GET /v1/functions/deployments/{deploymentId} for\nfull details.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "function-id", + Required: true, + PathParam: "functionId", + }, + &requestflag.Flag[int64]{ + Name: "limit", + Default: 20, + QueryPath: "limit", + }, + }, + Action: handleFunctionsListDeployments, + HideHelpCommand: true, +} + +var functionsListEventTypes = cli.Command{ + Name: "list-event-types", + Usage: "List the event types a function trigger can subscribe to. Includes the special\ntype `cron`, which fires on a schedule (see POST\n/v1/functions/{functionId}/triggers) rather than on a messaging event.", + Suggest: true, + Flags: []cli.Flag{}, + Action: handleFunctionsListEventTypes, + HideHelpCommand: true, +} + +var functionsRollbackDeployment = cli.Command{ + Name: "rollback-deployment", + Usage: "Re-deploy a previous version by copying its source, dependencies, and runtime\npin onto the function's draft, then deploying. Returns immediately with a\ndeployment ID — poll GET /v1/functions/deployments/{deploymentId} until status\nis active or failed. Secrets are not rolled back.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "function-id", + Required: true, + PathParam: "functionId", + }, + &requestflag.Flag[string]{ + Name: "deployment-id", + Usage: "ID of the deployment to roll back to.", + Required: true, + BodyPath: "deploymentId", + }, + }, + Action: handleFunctionsRollbackDeployment, + HideHelpCommand: true, +} + var functionsTailLogs = cli.Command{ Name: "tail-logs", Usage: "Fetch invocation logs for a function. Logs are paginated via `nextToken`. Pass\n`startTime` / `endTime` (Unix epoch milliseconds) to bound the window, or\n`filterPattern` to filter messages.", @@ -508,6 +558,143 @@ func handleFunctionsGetDeployment(ctx context.Context, cmd *cli.Command) error { }) } +func handleFunctionsListDeployments(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("function-id") && len(unusedArgs) > 0 { + cmd.Set("function-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + params := zavudev.FunctionListDeploymentsParams{} + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Functions.ListDeployments( + ctx, + cmd.Value("function-id").(string), + params, + options..., + ) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "functions list-deployments", + Transform: transform, + }) +} + +func handleFunctionsListEventTypes(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Functions.ListEventTypes(ctx, options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "functions list-event-types", + Transform: transform, + }) +} + +func handleFunctionsRollbackDeployment(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("function-id") && len(unusedArgs) > 0 { + cmd.Set("function-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + params := zavudev.FunctionRollbackDeploymentParams{} + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Functions.RollbackDeployment( + ctx, + cmd.Value("function-id").(string), + params, + options..., + ) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "functions rollback-deployment", + Transform: transform, + }) +} + func handleFunctionsTailLogs(ctx context.Context, cmd *cli.Command) error { client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) unusedArgs := cmd.Args().Slice() diff --git a/pkg/cmd/function_test.go b/pkg/cmd/function_test.go index 6120b51..bd8e9c3 100644 --- a/pkg/cmd/function_test.go +++ b/pkg/cmd/function_test.go @@ -189,6 +189,54 @@ func TestFunctionsGetDeployment(t *testing.T) { }) } +func TestFunctionsListDeployments(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "functions", "list-deployments", + "--function-id", "functionId", + "--limit", "100", + ) + }) +} + +func TestFunctionsListEventTypes(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "functions", "list-event-types", + ) + }) +} + +func TestFunctionsRollbackDeployment(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "functions", "rollback-deployment", + "--function-id", "functionId", + "--deployment-id", "fnd_abc123", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("deploymentId: fnd_abc123") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "functions", "rollback-deployment", + "--function-id", "functionId", + ) + }) +} + func TestFunctionsTailLogs(t *testing.T) { t.Skip("Mock server tests are disabled") t.Run("regular flags", func(t *testing.T) { diff --git a/pkg/cmd/functiongitlink.go b/pkg/cmd/functiongitlink.go new file mode 100644 index 0000000..fbc959a --- /dev/null +++ b/pkg/cmd/functiongitlink.go @@ -0,0 +1,334 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "context" + "fmt" + + "github.com/tidwall/gjson" + "github.com/urfave/cli/v3" + "github.com/zavudev/cli/internal/apiquery" + "github.com/zavudev/cli/internal/requestflag" + "github.com/zavudev/sdk-go" + "github.com/zavudev/sdk-go/option" +) + +var functionsGitLinkRetrieve = cli.Command{ + Name: "retrieve", + Usage: "The link and its last deploy. Never returns the webhook secret.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "function-id", + Required: true, + PathParam: "functionId", + }, + }, + Action: handleFunctionsGitLinkRetrieve, + HideHelpCommand: true, +} + +var functionsGitLinkUpdate = cli.Command{ + Name: "update", + Usage: "Change the branch, the root directory, or whether pushes deploy. Pass at least\none field. `rootDir: null` clears the subdirectory.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "function-id", + Required: true, + PathParam: "functionId", + }, + &requestflag.Flag[bool]{ + Name: "auto-deploy", + BodyPath: "autoDeploy", + }, + &requestflag.Flag[string]{ + Name: "branch", + BodyPath: "branch", + }, + &requestflag.Flag[*string]{ + Name: "root-dir", + BodyPath: "rootDir", + }, + }, + Action: handleFunctionsGitLinkUpdate, + HideHelpCommand: true, +} + +var functionsGitLinkDeployNow = cli.Command{ + Name: "deploy-now", + Usage: "Fetch the linked branch and deploy it without waiting for a push. Returns\nimmediately; follow the outcome with `GET /v1/functions/{functionId}/git-link`,\nwhose `lastStatus` and `lastError` describe the run.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "function-id", + Required: true, + PathParam: "functionId", + }, + }, + Action: handleFunctionsGitLinkDeployNow, + HideHelpCommand: true, +} + +var functionsGitLinkLink = cli.Command{ + Name: "link", + Usage: "Bind a repository to this function so every push to `branch` deploys it. A\nfunction holds at most one link; linking again returns 400.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "function-id", + Required: true, + PathParam: "functionId", + }, + &requestflag.Flag[string]{ + Name: "owner", + Required: true, + BodyPath: "owner", + }, + &requestflag.Flag[string]{ + Name: "repo", + Required: true, + BodyPath: "repo", + }, + &requestflag.Flag[bool]{ + Name: "auto-deploy", + Default: true, + BodyPath: "autoDeploy", + }, + &requestflag.Flag[string]{ + Name: "branch", + Default: "main", + BodyPath: "branch", + }, + &requestflag.Flag[string]{ + Name: "root-dir", + Usage: "Subdirectory holding the project, for monorepos.", + BodyPath: "rootDir", + }, + }, + Action: handleFunctionsGitLinkLink, + HideHelpCommand: true, +} + +var functionsGitLinkUnlink = cli.Command{ + Name: "unlink", + Usage: "Remove the link. The function and its deployments stay. A manual webhook left in\nthe repository stops being accepted, so remove it there too.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "function-id", + Required: true, + PathParam: "functionId", + }, + }, + Action: handleFunctionsGitLinkUnlink, + HideHelpCommand: true, +} + +func handleFunctionsGitLinkRetrieve(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("function-id") && len(unusedArgs) > 0 { + cmd.Set("function-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Functions.GitLink.Get(ctx, cmd.Value("function-id").(string), options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "functions:git-link retrieve", + Transform: transform, + }) +} + +func handleFunctionsGitLinkUpdate(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("function-id") && len(unusedArgs) > 0 { + cmd.Set("function-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + params := zavudev.FunctionGitLinkUpdateParams{} + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Functions.GitLink.Update( + ctx, + cmd.Value("function-id").(string), + params, + options..., + ) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "functions:git-link update", + Transform: transform, + }) +} + +func handleFunctionsGitLinkDeployNow(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("function-id") && len(unusedArgs) > 0 { + cmd.Set("function-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Functions.GitLink.DeployNow(ctx, cmd.Value("function-id").(string), options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "functions:git-link deploy-now", + Transform: transform, + }) +} + +func handleFunctionsGitLinkLink(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("function-id") && len(unusedArgs) > 0 { + cmd.Set("function-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + params := zavudev.FunctionGitLinkLinkParams{} + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Functions.GitLink.Link( + ctx, + cmd.Value("function-id").(string), + params, + options..., + ) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "functions:git-link link", + Transform: transform, + }) +} + +func handleFunctionsGitLinkUnlink(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("function-id") && len(unusedArgs) > 0 { + cmd.Set("function-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + return client.Functions.GitLink.Unlink(ctx, cmd.Value("function-id").(string), options...) +} diff --git a/pkg/cmd/functiongitlink_test.go b/pkg/cmd/functiongitlink_test.go new file mode 100644 index 0000000..aca8265 --- /dev/null +++ b/pkg/cmd/functiongitlink_test.go @@ -0,0 +1,107 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "testing" + + "github.com/zavudev/cli/internal/mocktest" +) + +func TestFunctionsGitLinkRetrieve(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "functions:git-link", "retrieve", + "--function-id", "functionId", + ) + }) +} + +func TestFunctionsGitLinkUpdate(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "functions:git-link", "update", + "--function-id", "functionId", + "--auto-deploy=false", + "--branch", "branch", + "--root-dir", "rootDir", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("" + + "autoDeploy: false\n" + + "branch: branch\n" + + "rootDir: rootDir\n") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "functions:git-link", "update", + "--function-id", "functionId", + ) + }) +} + +func TestFunctionsGitLinkDeployNow(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "functions:git-link", "deploy-now", + "--function-id", "functionId", + ) + }) +} + +func TestFunctionsGitLinkLink(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "functions:git-link", "link", + "--function-id", "functionId", + "--owner", "acme", + "--repo", "order-bot", + "--auto-deploy=true", + "--branch", "main", + "--root-dir", "apps/bot", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("" + + "owner: acme\n" + + "repo: order-bot\n" + + "autoDeploy: true\n" + + "branch: main\n" + + "rootDir: apps/bot\n") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "functions:git-link", "link", + "--function-id", "functionId", + ) + }) +} + +func TestFunctionsGitLinkUnlink(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "functions:git-link", "unlink", + "--function-id", "functionId", + ) + }) +} diff --git a/pkg/cmd/functiontrigger.go b/pkg/cmd/functiontrigger.go new file mode 100644 index 0000000..4794571 --- /dev/null +++ b/pkg/cmd/functiontrigger.go @@ -0,0 +1,262 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "context" + "fmt" + + "github.com/tidwall/gjson" + "github.com/urfave/cli/v3" + "github.com/zavudev/cli/internal/apiquery" + "github.com/zavudev/cli/internal/requestflag" + "github.com/zavudev/sdk-go" + "github.com/zavudev/sdk-go/option" +) + +var functionsTriggersCreate = cli.Command{ + Name: "create", + Usage: "Subscribe a function to one or more event types, optionally scoped to specific\nsenders. Provide eventTypes and senderIds (use null in senderIds for all\nsenders); a trigger is created for each event type and sender combination.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "function-id", + Required: true, + PathParam: "functionId", + }, + &requestflag.Flag[[]string]{ + Name: "event-type", + Usage: "Event types to subscribe to.", + Required: true, + BodyPath: "eventTypes", + }, + &requestflag.Flag[[]string]{ + Name: "sender-id", + Usage: "Senders to scope the triggers to. Use null for all senders.", + Required: true, + BodyPath: "senderIds", + }, + &requestflag.Flag[string]{ + Name: "cron", + Usage: "Required when eventTypes includes `cron`: a 5-field cron expression (minute hour day-of-month month day-of-week), evaluated in UTC.", + BodyPath: "cron", + }, + }, + Action: handleFunctionsTriggersCreate, + HideHelpCommand: true, +} + +var functionsTriggersUpdate = cli.Command{ + Name: "update", + Usage: "Enable or disable a trigger", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "trigger-id", + Required: true, + PathParam: "triggerId", + }, + &requestflag.Flag[bool]{ + Name: "active", + Required: true, + BodyPath: "active", + }, + }, + Action: handleFunctionsTriggersUpdate, + HideHelpCommand: true, +} + +var functionsTriggersList = cli.Command{ + Name: "list", + Usage: "List function triggers", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "function-id", + Required: true, + PathParam: "functionId", + }, + }, + Action: handleFunctionsTriggersList, + HideHelpCommand: true, +} + +var functionsTriggersDelete = cli.Command{ + Name: "delete", + Usage: "Delete a trigger", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "trigger-id", + Required: true, + PathParam: "triggerId", + }, + }, + Action: handleFunctionsTriggersDelete, + HideHelpCommand: true, +} + +func handleFunctionsTriggersCreate(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("function-id") && len(unusedArgs) > 0 { + cmd.Set("function-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + params := zavudev.FunctionTriggerNewParams{} + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Functions.Triggers.New( + ctx, + cmd.Value("function-id").(string), + params, + options..., + ) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "functions:triggers create", + Transform: transform, + }) +} + +func handleFunctionsTriggersUpdate(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("trigger-id") && len(unusedArgs) > 0 { + cmd.Set("trigger-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + params := zavudev.FunctionTriggerUpdateParams{} + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Functions.Triggers.Update( + ctx, + cmd.Value("trigger-id").(string), + params, + options..., + ) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "functions:triggers update", + Transform: transform, + }) +} + +func handleFunctionsTriggersList(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("function-id") && len(unusedArgs) > 0 { + cmd.Set("function-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Functions.Triggers.List(ctx, cmd.Value("function-id").(string), options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "functions:triggers list", + Transform: transform, + }) +} + +func handleFunctionsTriggersDelete(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("trigger-id") && len(unusedArgs) > 0 { + cmd.Set("trigger-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + return client.Functions.Triggers.Delete(ctx, cmd.Value("trigger-id").(string), options...) +} diff --git a/pkg/cmd/functiontrigger_test.go b/pkg/cmd/functiontrigger_test.go new file mode 100644 index 0000000..7bccddd --- /dev/null +++ b/pkg/cmd/functiontrigger_test.go @@ -0,0 +1,88 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "testing" + + "github.com/zavudev/cli/internal/mocktest" +) + +func TestFunctionsTriggersCreate(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "functions:triggers", "create", + "--function-id", "functionId", + "--event-type", "message.inbound", + "--sender-id", "null", + "--cron", "0 9 * * 1-5", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("" + + "eventTypes:\n" + + " - message.inbound\n" + + "senderIds:\n" + + " - null\n" + + "cron: 0 9 * * 1-5\n") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "functions:triggers", "create", + "--function-id", "functionId", + ) + }) +} + +func TestFunctionsTriggersUpdate(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "functions:triggers", "update", + "--trigger-id", "triggerId", + "--active=true", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("active: true") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "functions:triggers", "update", + "--trigger-id", "triggerId", + ) + }) +} + +func TestFunctionsTriggersList(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "functions:triggers", "list", + "--function-id", "functionId", + ) + }) +} + +func TestFunctionsTriggersDelete(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "functions:triggers", "delete", + "--trigger-id", "triggerId", + ) + }) +} diff --git a/pkg/cmd/introspect.go b/pkg/cmd/introspect.go index 7b3a980..752663b 100644 --- a/pkg/cmd/introspect.go +++ b/pkg/cmd/introspect.go @@ -14,6 +14,26 @@ import ( "github.com/zavudev/sdk-go/option" ) +var introspectValidateEmail = cli.Command{ + Name: "validate-email", + Usage: "Heuristic email validation to run before sending: catches invalid syntax, dead\ndomains (no MX/A records), disposable inboxes, role-based addresses (info@,\ncontacto@, sales@), and addresses already on your project's suppression list.\nUse it to clean a list before a broadcast and keep your bounce rate low.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "email", + Usage: "Single email address to validate.", + BodyPath: "email", + }, + &requestflag.Flag[[]string]{ + Name: "email", + Usage: "Batch of email addresses to validate (max 100).", + BodyPath: "emails", + }, + }, + Action: handleIntrospectValidateEmail, + HideHelpCommand: true, +} + var introspectValidatePhone = cli.Command{ Name: "validate-phone", Usage: "Validate a phone number and check if a WhatsApp conversation window is open.", @@ -29,6 +49,47 @@ var introspectValidatePhone = cli.Command{ HideHelpCommand: true, } +func handleIntrospectValidateEmail(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + params := zavudev.IntrospectValidateEmailParams{} + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Introspect.ValidateEmail(ctx, params, options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "introspect validate-email", + Transform: transform, + }) +} + func handleIntrospectValidatePhone(ctx context.Context, cmd *cli.Command) error { client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) unusedArgs := cmd.Args().Slice() diff --git a/pkg/cmd/introspect_test.go b/pkg/cmd/introspect_test.go index 99bc1fa..fc56fb6 100644 --- a/pkg/cmd/introspect_test.go +++ b/pkg/cmd/introspect_test.go @@ -8,6 +8,34 @@ import ( "github.com/zavudev/cli/internal/mocktest" ) +func TestIntrospectValidateEmail(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "introspect", "validate-email", + "--email", "maria@example.com", + "--email", "maria@example.com", + "--email", "info@deaddomain.example", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("" + + "email: maria@example.com\n" + + "emails:\n" + + " - maria@example.com\n" + + " - info@deaddomain.example\n") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "introspect", "validate-email", + ) + }) +} + func TestIntrospectValidatePhone(t *testing.T) { t.Skip("Mock server tests are disabled") t.Run("regular flags", func(t *testing.T) { diff --git a/pkg/cmd/message.go b/pkg/cmd/message.go index decd47c..1f0d6b8 100644 --- a/pkg/cmd/message.go +++ b/pkg/cmd/message.go @@ -66,6 +66,21 @@ var messagesList = cli.Command{ HideHelpCommand: true, } +var messagesListAttachments = cli.Command{ + Name: "list-attachments", + Usage: "List the stored file attachments for an email message and get a short-lived\nsigned `downloadUrl` for each. Works for both inbound emails (received via\n`message.inbound`) and outbound emails you sent with attachments. Messages\nwithout stored attachments (including SMS, WhatsApp, and other channels) return\nan empty list. Each `downloadUrl` is generated fresh per request and expires —\nfetch the file promptly and do not cache the URL.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "message-id", + Required: true, + PathParam: "messageId", + }, + }, + Action: handleMessagesListAttachments, + HideHelpCommand: true, +} + var messagesReact = cli.Command{ Name: "react", Usage: "Send an emoji reaction to an existing WhatsApp message. Reactions are only\nsupported for WhatsApp messages.", @@ -468,6 +483,48 @@ func handleMessagesList(ctx context.Context, cmd *cli.Command) error { } } +func handleMessagesListAttachments(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("message-id") && len(unusedArgs) > 0 { + cmd.Set("message-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Messages.ListAttachments(ctx, cmd.Value("message-id").(string), options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "messages list-attachments", + Transform: transform, + }) +} + func handleMessagesReact(ctx context.Context, cmd *cli.Command) error { client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) unusedArgs := cmd.Args().Slice() diff --git a/pkg/cmd/message_test.go b/pkg/cmd/message_test.go index 4d0887d..6fe513e 100644 --- a/pkg/cmd/message_test.go +++ b/pkg/cmd/message_test.go @@ -38,6 +38,18 @@ func TestMessagesList(t *testing.T) { }) } +func TestMessagesListAttachments(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "messages", "list-attachments", + "--message-id", "messageId", + ) + }) +} + func TestMessagesReact(t *testing.T) { t.Skip("Mock server tests are disabled") t.Run("regular flags", func(t *testing.T) { diff --git a/pkg/cmd/senderagentknowledgebasedocument.go b/pkg/cmd/senderagentknowledgebasedocument.go index abe6325..060b88c 100644 --- a/pkg/cmd/senderagentknowledgebasedocument.go +++ b/pkg/cmd/senderagentknowledgebasedocument.go @@ -102,6 +102,64 @@ var sendersAgentKnowledgeBasesDocumentsDelete = cli.Command{ HideHelpCommand: true, } +var sendersAgentKnowledgeBasesDocumentsRetrieveDocument = cli.Command{ + Name: "retrieve-document", + Usage: "Get a single document from a knowledge base.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "sender-id", + Required: true, + PathParam: "senderId", + }, + &requestflag.Flag[string]{ + Name: "kb-id", + Required: true, + PathParam: "kbId", + }, + &requestflag.Flag[string]{ + Name: "doc-id", + Required: true, + PathParam: "docId", + }, + }, + Action: handleSendersAgentKnowledgeBasesDocumentsRetrieveDocument, + HideHelpCommand: true, +} + +var sendersAgentKnowledgeBasesDocumentsUpdateDocument = cli.Command{ + Name: "update-document", + Usage: "Update a document's title or content. Updating content reprocesses the document\nfor RAG.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "sender-id", + Required: true, + PathParam: "senderId", + }, + &requestflag.Flag[string]{ + Name: "kb-id", + Required: true, + PathParam: "kbId", + }, + &requestflag.Flag[string]{ + Name: "doc-id", + Required: true, + PathParam: "docId", + }, + &requestflag.Flag[string]{ + Name: "content", + BodyPath: "content", + }, + &requestflag.Flag[string]{ + Name: "title", + BodyPath: "title", + }, + }, + Action: handleSendersAgentKnowledgeBasesDocumentsUpdateDocument, + HideHelpCommand: true, +} + func handleSendersAgentKnowledgeBasesDocumentsCreate(ctx context.Context, cmd *cli.Command) error { client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) unusedArgs := cmd.Args().Slice() @@ -257,3 +315,107 @@ func handleSendersAgentKnowledgeBasesDocumentsDelete(ctx context.Context, cmd *c options..., ) } + +func handleSendersAgentKnowledgeBasesDocumentsRetrieveDocument(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("doc-id") && len(unusedArgs) > 0 { + cmd.Set("doc-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + params := zavudev.SenderAgentKnowledgeBaseDocumentGetDocumentParams{ + SenderID: cmd.Value("sender-id").(string), + KBID: cmd.Value("kb-id").(string), + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Senders.Agent.KnowledgeBases.Documents.GetDocument( + ctx, + cmd.Value("doc-id").(string), + params, + options..., + ) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "senders:agent:knowledge-bases:documents retrieve-document", + Transform: transform, + }) +} + +func handleSendersAgentKnowledgeBasesDocumentsUpdateDocument(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("doc-id") && len(unusedArgs) > 0 { + cmd.Set("doc-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + params := zavudev.SenderAgentKnowledgeBaseDocumentUpdateDocumentParams{ + SenderID: cmd.Value("sender-id").(string), + KBID: cmd.Value("kb-id").(string), + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Senders.Agent.KnowledgeBases.Documents.UpdateDocument( + ctx, + cmd.Value("doc-id").(string), + params, + options..., + ) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "senders:agent:knowledge-bases:documents update-document", + Transform: transform, + }) +} diff --git a/pkg/cmd/senderagentknowledgebasedocument_test.go b/pkg/cmd/senderagentknowledgebasedocument_test.go index 629e0f2..ee9403d 100644 --- a/pkg/cmd/senderagentknowledgebasedocument_test.go +++ b/pkg/cmd/senderagentknowledgebasedocument_test.go @@ -66,3 +66,48 @@ func TestSendersAgentKnowledgeBasesDocumentsDelete(t *testing.T) { ) }) } + +func TestSendersAgentKnowledgeBasesDocumentsRetrieveDocument(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "senders:agent:knowledge-bases:documents", "retrieve-document", + "--sender-id", "senderId", + "--kb-id", "kbId", + "--doc-id", "docId", + ) + }) +} + +func TestSendersAgentKnowledgeBasesDocumentsUpdateDocument(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "senders:agent:knowledge-bases:documents", "update-document", + "--sender-id", "senderId", + "--kb-id", "kbId", + "--doc-id", "docId", + "--content", "content", + "--title", "title", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("" + + "content: content\n" + + "title: title\n") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "senders:agent:knowledge-bases:documents", "update-document", + "--sender-id", "senderId", + "--kb-id", "kbId", + "--doc-id", "docId", + ) + }) +} diff --git a/pkg/cmd/senderagenttool.go b/pkg/cmd/senderagenttool.go index f67bfad..e6c8d94 100644 --- a/pkg/cmd/senderagenttool.go +++ b/pkg/cmd/senderagenttool.go @@ -208,6 +208,31 @@ var sendersAgentToolsDelete = cli.Command{ HideHelpCommand: true, } +var sendersAgentToolsListTestRuns = cli.Command{ + Name: "list-test-runs", + Usage: "Recent runs of this tool triggered from the test endpoint, newest first. Covers\nmanual tests only: a tool called by an agent during a real conversation is not\nrecorded here.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "sender-id", + Required: true, + PathParam: "senderId", + }, + &requestflag.Flag[string]{ + Name: "tool-id", + Required: true, + PathParam: "toolId", + }, + &requestflag.Flag[int64]{ + Name: "limit", + Default: 20, + QueryPath: "limit", + }, + }, + Action: handleSendersAgentToolsListTestRuns, + HideHelpCommand: true, +} + var sendersAgentToolsTest = cli.Command{ Name: "test", Usage: "Run a tool with the parameters you supply and return what it answered.", @@ -487,6 +512,57 @@ func handleSendersAgentToolsDelete(ctx context.Context, cmd *cli.Command) error ) } +func handleSendersAgentToolsListTestRuns(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("tool-id") && len(unusedArgs) > 0 { + cmd.Set("tool-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + params := zavudev.SenderAgentToolListTestRunsParams{ + SenderID: cmd.Value("sender-id").(string), + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Senders.Agent.Tools.ListTestRuns( + ctx, + cmd.Value("tool-id").(string), + params, + options..., + ) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "senders:agent:tools list-test-runs", + Transform: transform, + }) +} + func handleSendersAgentToolsTest(ctx context.Context, cmd *cli.Command) error { client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) unusedArgs := cmd.Args().Slice() diff --git a/pkg/cmd/senderagenttool_test.go b/pkg/cmd/senderagenttool_test.go index 0bc643f..a621896 100644 --- a/pkg/cmd/senderagenttool_test.go +++ b/pkg/cmd/senderagenttool_test.go @@ -180,6 +180,20 @@ func TestSendersAgentToolsDelete(t *testing.T) { }) } +func TestSendersAgentToolsListTestRuns(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "senders:agent:tools", "list-test-runs", + "--sender-id", "senderId", + "--tool-id", "toolId", + "--limit", "100", + ) + }) +} + func TestSendersAgentToolsTest(t *testing.T) { t.Skip("Mock server tests are disabled") t.Run("regular flags", func(t *testing.T) { diff --git a/pkg/cmd/senderagenttoolwebhook.go b/pkg/cmd/senderagenttoolwebhook.go new file mode 100644 index 0000000..737dd9f --- /dev/null +++ b/pkg/cmd/senderagenttoolwebhook.go @@ -0,0 +1,86 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "context" + "fmt" + + "github.com/tidwall/gjson" + "github.com/urfave/cli/v3" + "github.com/zavudev/cli/internal/apiquery" + "github.com/zavudev/cli/internal/requestflag" + "github.com/zavudev/sdk-go" + "github.com/zavudev/sdk-go/option" +) + +var sendersAgentToolsWebhookRotateSecret = cli.Command{ + Name: "rotate-secret", + Usage: "Generate a new signing secret for this tool. The previous one stops working on\nthe next call, with no overlap, so update your endpoint first. The tool keeps\nits id, so flows that reference it by name are unaffected.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "sender-id", + Required: true, + PathParam: "senderId", + }, + &requestflag.Flag[string]{ + Name: "tool-id", + Required: true, + PathParam: "toolId", + }, + }, + Action: handleSendersAgentToolsWebhookRotateSecret, + HideHelpCommand: true, +} + +func handleSendersAgentToolsWebhookRotateSecret(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("tool-id") && len(unusedArgs) > 0 { + cmd.Set("tool-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + params := zavudev.SenderAgentToolWebhookRotateSecretParams{ + SenderID: cmd.Value("sender-id").(string), + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Senders.Agent.Tools.Webhook.RotateSecret( + ctx, + cmd.Value("tool-id").(string), + params, + options..., + ) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "senders:agent:tools:webhook rotate-secret", + Transform: transform, + }) +} diff --git a/pkg/cmd/senderagenttoolwebhook_test.go b/pkg/cmd/senderagenttoolwebhook_test.go new file mode 100644 index 0000000..e62e6b7 --- /dev/null +++ b/pkg/cmd/senderagenttoolwebhook_test.go @@ -0,0 +1,22 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "testing" + + "github.com/zavudev/cli/internal/mocktest" +) + +func TestSendersAgentToolsWebhookRotateSecret(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "senders:agent:tools:webhook", "rotate-secret", + "--sender-id", "senderId", + "--tool-id", "toolId", + ) + }) +} diff --git a/pkg/cmd/sendertelegram.go b/pkg/cmd/sendertelegram.go new file mode 100644 index 0000000..c219efe --- /dev/null +++ b/pkg/cmd/sendertelegram.go @@ -0,0 +1,125 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "context" + "fmt" + + "github.com/tidwall/gjson" + "github.com/urfave/cli/v3" + "github.com/zavudev/cli/internal/apiquery" + "github.com/zavudev/cli/internal/requestflag" + "github.com/zavudev/sdk-go" + "github.com/zavudev/sdk-go/option" +) + +var sendersTelegramConnect = cli.Command{ + Name: "connect", + Usage: "Connect a Telegram bot to a sender. Provide the bot token from @BotFather; Zavu\nvalidates it, registers the webhook, and routes the sender's Telegram messages\nthrough it.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "sender-id", + Required: true, + PathParam: "senderId", + }, + &requestflag.Flag[string]{ + Name: "bot-token", + Usage: "Bot token from @BotFather.", + Required: true, + BodyPath: "botToken", + }, + }, + Action: handleSendersTelegramConnect, + HideHelpCommand: true, +} + +var sendersTelegramDisconnect = cli.Command{ + Name: "disconnect", + Usage: "Disconnect Telegram from a sender and remove the webhook.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "sender-id", + Required: true, + PathParam: "senderId", + }, + }, + Action: handleSendersTelegramDisconnect, + HideHelpCommand: true, +} + +func handleSendersTelegramConnect(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("sender-id") && len(unusedArgs) > 0 { + cmd.Set("sender-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + params := zavudev.SenderTelegramConnectParams{} + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Senders.Telegram.Connect( + ctx, + cmd.Value("sender-id").(string), + params, + options..., + ) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "senders:telegram connect", + Transform: transform, + }) +} + +func handleSendersTelegramDisconnect(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("sender-id") && len(unusedArgs) > 0 { + cmd.Set("sender-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + return client.Senders.Telegram.Disconnect(ctx, cmd.Value("sender-id").(string), options...) +} diff --git a/pkg/cmd/sendertelegram_test.go b/pkg/cmd/sendertelegram_test.go new file mode 100644 index 0000000..2d07970 --- /dev/null +++ b/pkg/cmd/sendertelegram_test.go @@ -0,0 +1,45 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "testing" + + "github.com/zavudev/cli/internal/mocktest" +) + +func TestSendersTelegramConnect(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "senders:telegram", "connect", + "--sender-id", "senderId", + "--bot-token", "botToken", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("botToken: botToken") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "senders:telegram", "connect", + "--sender-id", "senderId", + ) + }) +} + +func TestSendersTelegramDisconnect(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "senders:telegram", "disconnect", + "--sender-id", "senderId", + ) + }) +} diff --git a/pkg/cmd/template.go b/pkg/cmd/template.go index ca11d01..88eac51 100644 --- a/pkg/cmd/template.go +++ b/pkg/cmd/template.go @@ -216,6 +216,21 @@ var templatesSubmit = cli.Command{ HideHelpCommand: true, } +var templatesSync = cli.Command{ + Name: "sync", + Usage: "Reconcile this project's templates against WhatsApp. Two things happen per\nconnected WhatsApp Business Account: templates that exist on Meta but not in\nZavu are imported (or linked to an existing template with the same name), and\nthe approval status of the templates Zavu already knows about is refreshed from\nMeta.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "sender-id", + Usage: "Sync only the WhatsApp Business Account attached to this sender. If omitted, every WhatsApp sender in the project is synced.", + BodyPath: "senderId", + }, + }, + Action: handleTemplatesSync, + HideHelpCommand: true, +} + func handleTemplatesCreate(ctx context.Context, cmd *cli.Command) error { client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) unusedArgs := cmd.Args().Slice() @@ -427,3 +442,44 @@ func handleTemplatesSubmit(ctx context.Context, cmd *cli.Command) error { Transform: transform, }) } + +func handleTemplatesSync(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + params := zavudev.TemplateSyncParams{} + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Templates.Sync(ctx, params, options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "templates sync", + Transform: transform, + }) +} diff --git a/pkg/cmd/template_test.go b/pkg/cmd/template_test.go index 217c4f4..cbba832 100644 --- a/pkg/cmd/template_test.go +++ b/pkg/cmd/template_test.go @@ -167,3 +167,25 @@ func TestTemplatesSubmit(t *testing.T) { ) }) } + +func TestTemplatesSync(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "templates", "sync", + "--sender-id", "sender_12345", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("senderId: sender_12345") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "templates", "sync", + ) + }) +} diff --git a/pkg/cmd/url.go b/pkg/cmd/url.go index e0b53db..2d4aa70 100644 --- a/pkg/cmd/url.go +++ b/pkg/cmd/url.go @@ -14,6 +14,27 @@ import ( "github.com/zavudev/sdk-go/option" ) +var urlsEscalate = cli.Command{ + Name: "escalate", + Usage: "Request manual review of a rejected URL. Only URLs in 'rejected' status can be\nescalated; the status then moves to 'escalated'.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "url-id", + Required: true, + PathParam: "urlId", + }, + &requestflag.Flag[string]{ + Name: "reason", + Usage: "Why the URL should be reviewed manually.", + Required: true, + BodyPath: "reason", + }, + }, + Action: handleURLsEscalate, + HideHelpCommand: true, +} + var urlsListVerified = cli.Command{ Name: "list-verified", Usage: "List URLs that have been verified for this project.", @@ -73,6 +94,55 @@ var urlsSubmitForVerification = cli.Command{ HideHelpCommand: true, } +func handleURLsEscalate(ctx context.Context, cmd *cli.Command) error { + client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("url-id") && len(unusedArgs) > 0 { + cmd.Set("url-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + params := zavudev.URLEscalateParams{} + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.URLs.Escalate( + ctx, + cmd.Value("url-id").(string), + params, + options..., + ) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "urls escalate", + Transform: transform, + }) +} + func handleURLsListVerified(ctx context.Context, cmd *cli.Command) error { client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) unusedArgs := cmd.Args().Slice() diff --git a/pkg/cmd/url_test.go b/pkg/cmd/url_test.go index a40bafb..1ad64f3 100644 --- a/pkg/cmd/url_test.go +++ b/pkg/cmd/url_test.go @@ -8,6 +8,30 @@ import ( "github.com/zavudev/cli/internal/mocktest" ) +func TestURLsEscalate(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "urls", "escalate", + "--url-id", "urlId", + "--reason", "This is our official landing page and was rejected in error.", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("reason: This is our official landing page and was rejected in error.") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "urls", "escalate", + "--url-id", "urlId", + ) + }) +} + func TestURLsListVerified(t *testing.T) { t.Skip("Mock server tests are disabled") t.Run("regular flags", func(t *testing.T) { From 40c48d9fc3b1c51c2d980617ee34d43569827262 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 20 Aug 2026 03:35:04 +0000 Subject: [PATCH 47/56] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 906e6fe..5417f7d 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 184 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-ba782c22cc1b237327afbf2aeffb453185cec9513d918b5a0fdc7e7233320c9b.yml -openapi_spec_hash: 410cb45b6fa3a8a1f3da3aba50accbdc +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-7b2243278dffd1b709f9ea780bc7584f0040597d2f2429d583e0c12ddce900f7.yml +openapi_spec_hash: e23f8e081ded5104745133cfd4d48a48 config_hash: 261e1b852ca7f8364e4a283a3edd350a From 036bc772adbb059b148c9737cfa43e50cea5b734 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 20 Aug 2026 16:19:10 +0000 Subject: [PATCH 48/56] feat(api): api update --- .stats.yml | 6 ++--- pkg/cmd/cmd.go | 1 - pkg/cmd/contact.go | 57 ++++++++++++----------------------------- pkg/cmd/contact_test.go | 16 +++--------- 4 files changed, 24 insertions(+), 56 deletions(-) diff --git a/.stats.yml b/.stats.yml index 5417f7d..618b580 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 184 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-7b2243278dffd1b709f9ea780bc7584f0040597d2f2429d583e0c12ddce900f7.yml -openapi_spec_hash: e23f8e081ded5104745133cfd4d48a48 +configured_endpoints: 183 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-30be71f9a3d5d52fd31a10576b5379ae0bda99f204ced399108c62b9d5bbf632.yml +openapi_spec_hash: 3431ff6e6857c1472891ec1c15c9f142 config_hash: 261e1b852ca7f8364e4a283a3edd350a diff --git a/pkg/cmd/cmd.go b/pkg/cmd/cmd.go index 27002ce..75183ac 100644 --- a/pkg/cmd/cmd.go +++ b/pkg/cmd/cmd.go @@ -235,7 +235,6 @@ func init() { &contactsUpdate, &contactsList, &contactsDelete, - &contactsDismissMergeSuggestion, &contactsMerge, &contactsRetrieveByPhone, }, diff --git a/pkg/cmd/contact.go b/pkg/cmd/contact.go index afccb55..1f1fa1d 100644 --- a/pkg/cmd/contact.go +++ b/pkg/cmd/contact.go @@ -98,6 +98,11 @@ var contactsUpdate = cli.Command{ Usage: "Preferred channel for this contact. Set to null to clear.", BodyPath: "defaultChannel", }, + &requestflag.Flag[*string]{ + Name: "display-name", + Usage: "Human-readable name for this contact. Set to null to clear it and fall back to the contact's identifier. Contacts created automatically from an inbound message have no display name until you set one.", + BodyPath: "displayName", + }, &requestflag.Flag[map[string]any]{ Name: "metadata", BodyPath: "metadata", @@ -114,6 +119,7 @@ var contactsList = cli.Command{ Flags: []cli.Flag{ &requestflag.Flag[string]{ Name: "cursor", + Usage: "Opaque cursor from a previous response's `nextCursor`. Do not construct it.", QueryPath: "cursor", }, &requestflag.Flag[int64]{ @@ -123,8 +129,19 @@ var contactsList = cli.Command{ }, &requestflag.Flag[string]{ Name: "phone-number", + Usage: "Exact match on the contact's primary phone number, in E.164.", QueryPath: "phoneNumber", }, + &requestflag.Flag[string]{ + Name: "search", + Usage: "Free-text match over the contact's name (`displayName` and the WhatsApp profile name), phone numbers and email addresses. Case- and accent-insensitive. A phone number matches on a trailing fragment too, so `5551234` finds `+14155551234`.\n\nContacts created automatically from an inbound message have no `displayName` — they are matched by their identifier until you set one with `PATCH /v1/contacts/{contactId}`.\n\nResults come back in relevance order rather than newest-first. `cursor` is opaque in both modes; pass back exactly what the previous response returned, and start a new pagination run when the search term changes.", + QueryPath: "search", + }, + &requestflag.Flag[[]string]{ + Name: "tag", + Usage: "Tag name. Repeatable: `?tag=vip&tag=chile` returns contacts carrying **every** tag given, not any of them — the same rule the dashboard filter applies.\n\nTags are matched by name, case-insensitively. An unknown tag returns 400 rather than being ignored, because a typo that silently matched every contact would be a worse answer than an error.", + QueryPath: "tag", + }, &requestflag.Flag[int64]{ Name: "max-items", Usage: "The maximum number of items to return (use -1 for unlimited).", @@ -149,21 +166,6 @@ var contactsDelete = cli.Command{ HideHelpCommand: true, } -var contactsDismissMergeSuggestion = cli.Command{ - Name: "dismiss-merge-suggestion", - Usage: "Dismiss the merge suggestion for a contact.", - Suggest: true, - Flags: []cli.Flag{ - &requestflag.Flag[string]{ - Name: "contact-id", - Required: true, - PathParam: "contactId", - }, - }, - Action: handleContactsDismissMergeSuggestion, - HideHelpCommand: true, -} - var contactsMerge = cli.Command{ Name: "merge", Usage: "Merge a source contact into this contact. All channels from the source contact\nwill be moved to the target contact, and the source contact will be marked as\nmerged.", @@ -412,31 +414,6 @@ func handleContactsDelete(ctx context.Context, cmd *cli.Command) error { return client.Contacts.Delete(ctx, cmd.Value("contact-id").(string), options...) } -func handleContactsDismissMergeSuggestion(ctx context.Context, cmd *cli.Command) error { - client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) - unusedArgs := cmd.Args().Slice() - if !cmd.IsSet("contact-id") && len(unusedArgs) > 0 { - cmd.Set("contact-id", unusedArgs[0]) - unusedArgs = unusedArgs[1:] - } - if len(unusedArgs) > 0 { - return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) - } - - options, err := flagOptions( - cmd, - apiquery.NestedQueryFormatBrackets, - apiquery.ArrayQueryFormatComma, - EmptyBody, - false, - ) - if err != nil { - return err - } - - return client.Contacts.DismissMergeSuggestion(ctx, cmd.Value("contact-id").(string), options...) -} - func handleContactsMerge(ctx context.Context, cmd *cli.Command) error { client := zavudev.NewClient(getDefaultRequestOptions(cmd)...) unusedArgs := cmd.Args().Slice() diff --git a/pkg/cmd/contact_test.go b/pkg/cmd/contact_test.go index da62200..fb63cd9 100644 --- a/pkg/cmd/contact_test.go +++ b/pkg/cmd/contact_test.go @@ -82,6 +82,7 @@ func TestContactsUpdate(t *testing.T) { "contacts", "update", "--contact-id", "contactId", "--default-channel", "sms", + "--display-name", "John Doe", "--metadata", "{foo: string}", ) }) @@ -90,6 +91,7 @@ func TestContactsUpdate(t *testing.T) { // Test piping YAML data over stdin pipeData := []byte("" + "defaultChannel: sms\n" + + "displayName: John Doe\n" + "metadata:\n" + " foo: string\n") mocktest.TestRunMockTestWithPipeAndFlags( @@ -112,6 +114,8 @@ func TestContactsList(t *testing.T) { "--cursor", "cursor", "--limit", "100", "--phone-number", "phoneNumber", + "--search", "search", + "--tag", "string", ) }) } @@ -128,18 +132,6 @@ func TestContactsDelete(t *testing.T) { }) } -func TestContactsDismissMergeSuggestion(t *testing.T) { - t.Skip("Mock server tests are disabled") - t.Run("regular flags", func(t *testing.T) { - mocktest.TestRunMockTestWithFlags( - t, - "--api-key", "string", - "contacts", "dismiss-merge-suggestion", - "--contact-id", "contactId", - ) - }) -} - func TestContactsMerge(t *testing.T) { t.Skip("Mock server tests are disabled") t.Run("regular flags", func(t *testing.T) { From 9890852702362db1dc22caddb327cdda28aecd89 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 20 Aug 2026 19:51:22 +0000 Subject: [PATCH 49/56] feat(api): api update --- .stats.yml | 4 ++-- pkg/cmd/number10dlcbrand.go | 2 +- pkg/cmd/number10dlccampaign.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.stats.yml b/.stats.yml index 618b580..f32ec4b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 183 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-30be71f9a3d5d52fd31a10576b5379ae0bda99f204ced399108c62b9d5bbf632.yml -openapi_spec_hash: 3431ff6e6857c1472891ec1c15c9f142 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-4cbd2b04d9be8798b6440cddaebc2165d90c058b424912ab80c766bcab22752f.yml +openapi_spec_hash: 0d325247718f16700baef5ada4936a34 config_hash: 261e1b852ca7f8364e4a283a3edd350a diff --git a/pkg/cmd/number10dlcbrand.go b/pkg/cmd/number10dlcbrand.go index 6511979..e521dd8 100644 --- a/pkg/cmd/number10dlcbrand.go +++ b/pkg/cmd/number10dlcbrand.go @@ -257,7 +257,7 @@ var number10dlcBrandsListUseCases = cli.Command{ var number10dlcBrandsSubmit = cli.Command{ Name: "submit", - Usage: "Submit a draft brand to The Campaign Registry (TCR) for vetting. The brand must\nbe in draft status. TCR's one-time $4 brand registration fee is charged from\nyour balance at submission (passed through at cost) and refunded if the carrier\nrejects the registration. A team that already paid this fee through the\ncompliance flow is not charged again. Campaign registration is billed separately\nwhen a campaign is submitted.", + Usage: "Submit a draft brand to The Campaign Registry (TCR) for vetting. The brand must\nbe in draft status, and the team must have an approved Business Verification\n(KYB) — carriers register a brand against a vetted legal entity, so submitting\nwithout one returns `403` with code `kyb_required`.", Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ diff --git a/pkg/cmd/number10dlccampaign.go b/pkg/cmd/number10dlccampaign.go index 496ca75..2318997 100644 --- a/pkg/cmd/number10dlccampaign.go +++ b/pkg/cmd/number10dlccampaign.go @@ -219,7 +219,7 @@ var number10dlcCampaignsDelete = cli.Command{ var number10dlcCampaignsSubmit = cli.Command{ Name: "submit", - Usage: "Submit a draft campaign for carrier review. The campaign must be in draft status\nand its brand must be verified. TCR's one-time registration fee is charged from\nyour balance at submission ($15 for standard use cases, $2 for LOW_VOLUME),\npassed through at cost and refunded if the carrier rejects it. Once approved,\nthe campaign's monthly TCR fee ($10 standard, $2 LOW_VOLUME) is charged from\nyour balance while the campaign is active — see registrationCostCents and\nmonthlyFeeCents on the campaign object.", + Usage: "The team must have an approved Business Verification (KYB): submitting without\none returns `403` with code `kyb_required`. Submit a draft campaign for carrier\nreview. The campaign must be in draft status and its brand must be verified.\nTCR's one-time registration fee is charged from your balance at submission ($15\nfor standard use cases, $2 for LOW_VOLUME), passed through at cost and refunded\nif the carrier rejects it. Once approved, the campaign's monthly TCR fee ($10\nstandard, $2 LOW_VOLUME) is charged from your balance while the campaign is\nactive — see registrationCostCents and monthlyFeeCents on the campaign object.", Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ From 49abd3f9d51224951bede93daf9697dbf278ef7d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 26 Aug 2026 12:16:06 +0000 Subject: [PATCH 50/56] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index f32ec4b..ed9df4a 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 183 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-4cbd2b04d9be8798b6440cddaebc2165d90c058b424912ab80c766bcab22752f.yml -openapi_spec_hash: 0d325247718f16700baef5ada4936a34 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-861b30c9e6830b4768809d1d84b325ca4f8f322da3229d92dbc9551b0200d86b.yml +openapi_spec_hash: c2f328bfd5c3d225bc96e9abe43a51cd config_hash: 261e1b852ca7f8364e4a283a3edd350a From cd9b03e8fa9fbf9ddd9d09fd90e6bb24f79b02a2 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 26 Aug 2026 12:38:33 +0000 Subject: [PATCH 51/56] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index ed9df4a..31aa0a9 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 183 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-861b30c9e6830b4768809d1d84b325ca4f8f322da3229d92dbc9551b0200d86b.yml -openapi_spec_hash: c2f328bfd5c3d225bc96e9abe43a51cd +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-b9de268c0618a8d5541bebde820c239719d088b9931f19a4a188a607a052ca58.yml +openapi_spec_hash: 78dde7ad15bcc3b85db07afa17fdab7f config_hash: 261e1b852ca7f8364e4a283a3edd350a From 5d07bc280e4ce77de003cfbe03077a7cb9543c06 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 26 Aug 2026 13:39:21 +0000 Subject: [PATCH 52/56] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 31aa0a9..a17d0c5 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 183 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-b9de268c0618a8d5541bebde820c239719d088b9931f19a4a188a607a052ca58.yml -openapi_spec_hash: 78dde7ad15bcc3b85db07afa17fdab7f +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-ceb3ca773122a62795ca60eb454f3023561a2df0363673ae4e8fa842bced7bc2.yml +openapi_spec_hash: 450f6c41ae300571671b45b28611d0ee config_hash: 261e1b852ca7f8364e4a283a3edd350a From 9bebd35fedb6c65aebb5fc0d66e2e85608831c32 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 29 Aug 2026 04:10:01 +0000 Subject: [PATCH 53/56] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index a17d0c5..6c58be4 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 183 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-ceb3ca773122a62795ca60eb454f3023561a2df0363673ae4e8fa842bced7bc2.yml -openapi_spec_hash: 450f6c41ae300571671b45b28611d0ee +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-3c2c3dadcbcc8bb98070da5fc5d671816152c2304f02905d5eaacae39aa3093a.yml +openapi_spec_hash: ef0d31201ff8b374aefddca6a6329ab6 config_hash: 261e1b852ca7f8364e4a283a3edd350a From 1801ccf05062274bc44026fbdd614416008b1cd3 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 31 Aug 2026 16:39:58 +0000 Subject: [PATCH 54/56] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 6c58be4..d9f73e8 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 183 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-3c2c3dadcbcc8bb98070da5fc5d671816152c2304f02905d5eaacae39aa3093a.yml -openapi_spec_hash: ef0d31201ff8b374aefddca6a6329ab6 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-fc96dae99e9649b4cfb99002b7dfee6480114b8dda4ff136541533c9c9069118.yml +openapi_spec_hash: 3c2c8da8c73cdd7e656b7f2c06cc01b4 config_hash: 261e1b852ca7f8364e4a283a3edd350a From 8f3ded7ce92478656e1bf174d4eb7eb8fe0758ae Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 8 Sep 2026 16:15:22 +0000 Subject: [PATCH 55/56] feat(api): api update --- .stats.yml | 4 ++-- pkg/cmd/phonenumber.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.stats.yml b/.stats.yml index d9f73e8..ddb1fe8 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 183 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-fc96dae99e9649b4cfb99002b7dfee6480114b8dda4ff136541533c9c9069118.yml -openapi_spec_hash: 3c2c8da8c73cdd7e656b7f2c06cc01b4 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-5d75bbdcac3a0b0a8b798c0b4ef0dacfbc64ad4a20605489dc46dda7c8d1d8d8.yml +openapi_spec_hash: bb242cfd8cc43354412164c2dceae89c config_hash: 261e1b852ca7f8364e4a283a3edd350a diff --git a/pkg/cmd/phonenumber.go b/pkg/cmd/phonenumber.go index d70fa19..453c6da 100644 --- a/pkg/cmd/phonenumber.go +++ b/pkg/cmd/phonenumber.go @@ -132,7 +132,7 @@ var phoneNumbersRequirements = cli.Command{ }, &requestflag.Flag[string]{ Name: "type", - Usage: `Allowed values: "local", "national", "tollFree".`, + Usage: "Type of phone number. `mobile` is stocked in countries where no geographic (`local`) or non-geographic (`national`) inventory exists, and in several markets it is the only type that can receive SMS.", QueryPath: "type", }, }, @@ -169,7 +169,7 @@ var phoneNumbersSearchAvailable = cli.Command{ }, &requestflag.Flag[string]{ Name: "type", - Usage: `Allowed values: "local", "national", "tollFree".`, + Usage: "Type of phone number. `mobile` is stocked in countries where no geographic (`local`) or non-geographic (`national`) inventory exists, and in several markets it is the only type that can receive SMS.", QueryPath: "type", }, }, From 004fb2ba20925defa839ad60eb60e7c863132355 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 8 Sep 2026 16:16:11 +0000 Subject: [PATCH 56/56] release: 0.5.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 44 +++++++++++++++++++++++++++++++++++ pkg/cmd/version.go | 2 +- 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index da59f99..2aca35a 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.4.0" + ".": "0.5.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index a289dc5..f7b1aa8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,49 @@ # Changelog +## 0.5.0 (2026-09-08) + +Full Changelog: [v0.4.0...v0.5.0](https://github.com/zavudev/cli/compare/v0.4.0...v0.5.0) + +### Features + +* **api:** api update ([8f3ded7](https://github.com/zavudev/cli/commit/8f3ded7ce92478656e1bf174d4eb7eb8fe0758ae)) +* **api:** api update ([9890852](https://github.com/zavudev/cli/commit/9890852702362db1dc22caddb327cdda28aecd89)) +* **api:** api update ([036bc77](https://github.com/zavudev/cli/commit/036bc772adbb059b148c9737cfa43e50cea5b734)) +* **api:** api update ([4879896](https://github.com/zavudev/cli/commit/48798969f5e101f3ff5b033046012351dc96d10d)) +* **api:** api update ([246f57f](https://github.com/zavudev/cli/commit/246f57f9419a5a7d4e386534724c4907a3160d99)) +* **api:** api update ([e114f18](https://github.com/zavudev/cli/commit/e114f18311f9362822f1410c7ac98e1c11ab7ac0)) +* **api:** api update ([d9200b0](https://github.com/zavudev/cli/commit/d9200b09fa86ca1c86f2ae140a2b2f22554840a9)) +* **api:** api update ([0a0f73f](https://github.com/zavudev/cli/commit/0a0f73f9f8a329e1370c2787c27904752e6ef801)) +* **api:** api update ([771f45e](https://github.com/zavudev/cli/commit/771f45e52871e2e8499b5477b9f19c04e4167a1d)) +* **api:** api update ([ace0081](https://github.com/zavudev/cli/commit/ace00816df72353a38c8d9427988c61b21631528)) +* **api:** api update ([00f5deb](https://github.com/zavudev/cli/commit/00f5deb659ce9dca56f617f547d31ef461bb0f7f)) +* **api:** api update ([6126b7e](https://github.com/zavudev/cli/commit/6126b7ed48909667c2f3b2979332602b7b5b154c)) +* **api:** api update ([bcb63ef](https://github.com/zavudev/cli/commit/bcb63ef6b1b4e3f5fad06d07650c34f8bd3450d3)) +* **api:** manual updates ([9a299ae](https://github.com/zavudev/cli/commit/9a299ae7b231a95ed973e60d1f66dcaeec50422d)) +* **api:** manual updates ([c40c4cb](https://github.com/zavudev/cli/commit/c40c4cb409948540bb7feef81b0ae11de056e7f5)) +* **cli:** add `--raw-output`/`-r` option to print raw (non-JSON) strings ([d764186](https://github.com/zavudev/cli/commit/d76418669cafbae10aedd4c7e7d42f3fa356cf20)) +* **cli:** alias parameters in data with `x-stainless-cli-data-alias` ([3711e06](https://github.com/zavudev/cli/commit/3711e0654e91a0345effe9b901f8b8635fbe0c62)) +* **cli:** send filename and content type when reading input from files ([82faa3c](https://github.com/zavudev/cli/commit/82faa3cc225b7e931602af05d49a1482d42d0fcd)) +* support passing path and query params over stdin ([b8f994d](https://github.com/zavudev/cli/commit/b8f994dc81af29fb1526cbe06cfc7704e886b573)) + + +### Bug Fixes + +* **cli:** correctly load zsh autocompletion ([a29fdc5](https://github.com/zavudev/cli/commit/a29fdc57fec69b50adf10f5ca9d76da5267c60cc)) +* flags for nullable body scalar fields are strictly typed ([f71454c](https://github.com/zavudev/cli/commit/f71454c4db5b528adceda8850a41857a80da27d7)) + + +### Chores + +* add documentation for ./scripts/link ([df7e71b](https://github.com/zavudev/cli/commit/df7e71b731758877a22074668862d865d71603eb)) +* **ci:** support manually triggering release workflow ([0c7441d](https://github.com/zavudev/cli/commit/0c7441d9ef8c2da629a88ba8fc750319ca4e6225)) +* **cli:** fall back to JSON when using default "explore" with non-TTY ([b9b7c60](https://github.com/zavudev/cli/commit/b9b7c6049f69268590f059883babc559076e5945)) +* **cli:** switch long lists of positional args over to param structs ([c97dd87](https://github.com/zavudev/cli/commit/c97dd8703f6ca089d3c29307eab81e23bec7c1d4)) +* **cli:** use `ShowJSONOpts` as argument to `formatJSON` instead of many positionals ([c0c6221](https://github.com/zavudev/cli/commit/c0c62210622f72686086063f315649eb8bc35ec4)) +* configure new SDK language ([f703781](https://github.com/zavudev/cli/commit/f7037818453dadaac1a379e1fce5d78179c93ef1)) +* **internal:** more robust bootstrap script ([873650f](https://github.com/zavudev/cli/commit/873650f94169cc8ee552d5dab9e426b6b1007fa8)) +* redact api-key headers in debug logs ([4a93079](https://github.com/zavudev/cli/commit/4a93079e546fcc9437520a3d9d72a1c8fc49a5e1)) + ## 0.4.0 (2026-04-12) Full Changelog: [v0.3.0...v0.4.0](https://github.com/zavudev/cli/compare/v0.3.0...v0.4.0) diff --git a/pkg/cmd/version.go b/pkg/cmd/version.go index b113456..86845a8 100644 --- a/pkg/cmd/version.go +++ b/pkg/cmd/version.go @@ -2,4 +2,4 @@ package cmd -const Version = "0.4.0" // x-release-please-version +const Version = "0.5.0" // x-release-please-version