diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 2aca35a..4208b5c 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.5.0" + ".": "0.6.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index ddb1fe8..1a193ff 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-5d75bbdcac3a0b0a8b798c0b4ef0dacfbc64ad4a20605489dc46dda7c8d1d8d8.yml -openapi_spec_hash: bb242cfd8cc43354412164c2dceae89c -config_hash: 261e1b852ca7f8364e4a283a3edd350a +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-5257b1c4e3b9259ae2df6cd9ac2d2a31a39ddb336f50baf0dadbcc865c1913f3.yml +openapi_spec_hash: 4103decc37e4053c1cba58ba9527a5e7 +config_hash: f6a05e2edf2cd1af3f762b8ac759b9a7 diff --git a/CHANGELOG.md b/CHANGELOG.md index f7b1aa8..c600679 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## 0.6.0 (2026-09-15) + +Full Changelog: [v0.5.0...v0.6.0](https://github.com/zavudev/cli/compare/v0.5.0...v0.6.0) + +### Features + +* **api:** api update ([78967ce](https://github.com/zavudev/cli/commit/78967ce99892dfa4438b0d89af1e93a1f73df408)) +* **api:** api update ([c090781](https://github.com/zavudev/cli/commit/c0907819c7645e321e49192c00653604b1d34f43)) +* **api:** api update ([5e87793](https://github.com/zavudev/cli/commit/5e87793117d3d0c4545b583a2d8d9744dee18645)) + ## 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) diff --git a/go.mod b/go.mod index f661d68..39295ff 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.4.0 + github.com/zavudev/sdk-go v0.5.0 golang.org/x/sys v0.38.0 ) diff --git a/go.sum b/go.sum index 067c6b1..e62809d 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.4.0 h1:OpPmvyDZVLF8y7Gc6tHB3JHhqRPy8aZMzVB6cOaeSPE= -github.com/zavudev/sdk-go v0.4.0/go.mod h1:ncdVbaf7JHHiUcpJu2JE3c1xion5lkWSAUCn5CnX3bo= +github.com/zavudev/sdk-go v0.5.0 h1:PfXs1NDhnMPI5t8TemrOIjrcavxJLpnLDugxOXCpp2s= +github.com/zavudev/sdk-go v0.5.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= diff --git a/pkg/cmd/address.go b/pkg/cmd/address.go index b21556a..825debb 100644 --- a/pkg/cmd/address.go +++ b/pkg/cmd/address.go @@ -16,7 +16,7 @@ import ( var addressesCreate = cli.Command{ Name: "create", - Usage: "Create a regulatory address for phone number purchases. Some countries require a\nverified address before phone numbers can be activated.", + Usage: "Create a regulatory address, to use as the value of an `address` requirement\nwhen buying a phone number. It is registered for review when it is created, with\nstatus `pending`.", Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ @@ -24,6 +24,18 @@ var addressesCreate = cli.Command{ Required: true, BodyPath: "countryCode", }, + &requestflag.Flag[string]{ + Name: "first-name", + Usage: "First name of the person the address is registered to.", + Required: true, + BodyPath: "firstName", + }, + &requestflag.Flag[string]{ + Name: "last-name", + Usage: "Last name of the person the address is registered to.", + Required: true, + BodyPath: "lastName", + }, &requestflag.Flag[string]{ Name: "locality", Required: true, @@ -45,20 +57,13 @@ var addressesCreate = cli.Command{ }, &requestflag.Flag[string]{ Name: "business-name", + Usage: "Business name, when the address belongs to a business. Defaults to the person's full name.", BodyPath: "businessName", }, &requestflag.Flag[string]{ Name: "extended-address", BodyPath: "extendedAddress", }, - &requestflag.Flag[string]{ - Name: "first-name", - BodyPath: "firstName", - }, - &requestflag.Flag[string]{ - Name: "last-name", - BodyPath: "lastName", - }, }, Action: handleAddressesCreate, HideHelpCommand: true, @@ -104,7 +109,7 @@ var addressesList = cli.Command{ var addressesDelete = cli.Command{ Name: "delete", - Usage: "Delete a regulatory address. Cannot delete addresses that are in use.", + Usage: "Delete a regulatory address from this project. Any address can be deleted,\nwhatever its status. Phone numbers already purchased with it are not affected,\nand neither is information already submitted for later purchases in its country.", Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ diff --git a/pkg/cmd/address_test.go b/pkg/cmd/address_test.go index 890d6c2..cb81c57 100644 --- a/pkg/cmd/address_test.go +++ b/pkg/cmd/address_test.go @@ -16,14 +16,14 @@ func TestAddressesCreate(t *testing.T) { "--api-key", "string", "addresses", "create", "--country-code", "DE", + "--first-name", "John", + "--last-name", "Doe", "--locality", "Berlin", "--postal-code", "10115", "--street-address", "123 Main St", "--administrative-area", "administrativeArea", "--business-name", "businessName", "--extended-address", "extendedAddress", - "--first-name", "John", - "--last-name", "Doe", ) }) @@ -31,14 +31,14 @@ func TestAddressesCreate(t *testing.T) { // Test piping YAML data over stdin pipeData := []byte("" + "countryCode: DE\n" + + "firstName: John\n" + + "lastName: Doe\n" + "locality: Berlin\n" + "postalCode: '10115'\n" + "streetAddress: 123 Main St\n" + "administrativeArea: administrativeArea\n" + "businessName: businessName\n" + - "extendedAddress: extendedAddress\n" + - "firstName: John\n" + - "lastName: Doe\n") + "extendedAddress: extendedAddress\n") mocktest.TestRunMockTestWithPipeAndFlags( t, pipeData, "--api-key", "string", diff --git a/pkg/cmd/phonenumber.go b/pkg/cmd/phonenumber.go index 453c6da..2a71377 100644 --- a/pkg/cmd/phonenumber.go +++ b/pkg/cmd/phonenumber.go @@ -46,7 +46,7 @@ var phoneNumbersUpdate = cli.Command{ }, &requestflag.Flag[*string]{ Name: "sender-id", - Usage: "Sender ID to assign the phone number to. Set to null to unassign.", + Usage: "Sender ID to assign the phone number to. Set to null to unassign. A number under regulatory review is recorded now and connected to the sender when approved; a rejected number is refused.", BodyPath: "senderId", }, }, @@ -71,7 +71,7 @@ var phoneNumbersList = cli.Command{ }, &requestflag.Flag[string]{ Name: "status", - Usage: `Allowed values: "active", "suspended", "pending".`, + Usage: "Billing state of an owned number, separate from `regulatoryStatus`. `pending` is legacy and is not written to numbers today. The SDKs carry `active`, `suspended` and `pending` only; `releasing` and `released` are returned by the REST API until their next release.", QueryPath: "status", }, &requestflag.Flag[int64]{ @@ -83,9 +83,9 @@ var phoneNumbersList = cli.Command{ HideHelpCommand: true, } -var phoneNumbersPurchase = cli.Command{ +var phoneNumbersPurchase = requestflag.WithInnerFlags(cli.Command{ Name: "purchase", - 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.", + 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`.", Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ @@ -99,10 +99,33 @@ var phoneNumbersPurchase = cli.Command{ Usage: "Optional custom name for the phone number.", BodyPath: "name", }, + &requestflag.Flag[[]map[string]any]{ + Name: "regulatory-requirement", + Usage: "Regulatory information, for numbers whose requirements list is not empty. Get the list with `GET /v1/phone-numbers/requirements?phoneNumber=...` and send one entry per requirement id, except `action` requirements, which take no value. Every required id must be present, once, and no unknown id may be sent; otherwise the purchase is refused with `400 invalid_request` before anything is charged.\n\nThe information is kept for your project under the number's country and `type`. A later purchase there may omit this field if what is kept still covers that number's requirements. Omit it for numbers without requirements.", + BodyPath: "regulatoryRequirements", + }, + &requestflag.Flag[string]{ + Name: "type", + 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.", + BodyPath: "type", + }, }, Action: handlePhoneNumbersPurchase, HideHelpCommand: true, -} +}, map[string][]requestflag.HasOuterFlag{ + "regulatory-requirement": { + &requestflag.InnerFlag[string]{ + Name: "regulatory-requirement.field-value", + Usage: "Depends on the requirement's `type`: the text itself for `textual`; for `address`, the `id` of an address created in this project with `POST /v1/addresses`; for `document`, the `id` of a document created with `POST /v1/documents`. An address or document from another project, or one rejected in review, is refused.", + InnerField: "fieldValue", + }, + &requestflag.InnerFlag[string]{ + Name: "regulatory-requirement.requirement-type", + Usage: "A `requirementTypes[].id` from `GET /v1/phone-numbers/requirements`. Each id may appear only once.", + InnerField: "requirementType", + }, + }, +}) var phoneNumbersRelease = cli.Command{ Name: "release", @@ -121,15 +144,19 @@ var phoneNumbersRelease = cli.Command{ var phoneNumbersRequirements = cli.Command{ Name: "requirements", - Usage: "Get regulatory requirements for purchasing phone numbers in a specific country.\nSome countries require additional documentation (addresses, identity documents)\nbefore phone numbers can be activated.", + Usage: "Get the regulatory information needed to buy a phone number, for one specific\nnumber or for a country and number type. Prefer `phoneNumber`: the response is\nthen exactly the list the purchase of that number validates against. Pass each\n`requirementTypes[].id` back as `requirementType` in `regulatoryRequirements` on\n`POST /v1/phone-numbers`.", Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ Name: "country-code", - Usage: "Two-letter ISO country code.", - Required: true, + Usage: "Two-letter ISO country code. Required unless `phoneNumber` is given.", QueryPath: "countryCode", }, + &requestflag.Flag[string]{ + Name: "phone-number", + Usage: "E.164 number from `GET /v1/phone-numbers/available`, with `+` encoded as `%2B`. Returns the requirements the purchase of that number checks. Takes precedence over `countryCode`.", + QueryPath: "phoneNumber", + }, &requestflag.Flag[string]{ Name: "type", 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.", diff --git a/pkg/cmd/phonenumber_test.go b/pkg/cmd/phonenumber_test.go index 9fdcb84..b19494b 100644 --- a/pkg/cmd/phonenumber_test.go +++ b/pkg/cmd/phonenumber_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/zavudev/cli/internal/mocktest" + "github.com/zavudev/cli/internal/requestflag" ) func TestPhoneNumbersRetrieve(t *testing.T) { @@ -71,6 +72,25 @@ func TestPhoneNumbersPurchase(t *testing.T) { "phone-numbers", "purchase", "--phone-number", "+15551234567", "--name", "Primary Line", + "--regulatory-requirement", "{fieldValue: jd7x2k3m4n5p6q7r8s9t0abc, requirementType: 8c5b1a2e-0f3d-4f5b-9a61-2c7e4d9b1f10}", + "--type", "local", + ) + }) + + t.Run("inner flags", func(t *testing.T) { + // Check that inner flags have been set up correctly + requestflag.CheckInnerFlags(phoneNumbersPurchase) + + // Alternative argument passing style using inner flags + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "phone-numbers", "purchase", + "--phone-number", "+15551234567", + "--name", "Primary Line", + "--regulatory-requirement.field-value", "jd7x2k3m4n5p6q7r8s9t0abc", + "--regulatory-requirement.requirement-type", "8c5b1a2e-0f3d-4f5b-9a61-2c7e4d9b1f10", + "--type", "local", ) }) @@ -78,7 +98,11 @@ func TestPhoneNumbersPurchase(t *testing.T) { // Test piping YAML data over stdin pipeData := []byte("" + "phoneNumber: '+15551234567'\n" + - "name: Primary Line\n") + "name: Primary Line\n" + + "regulatoryRequirements:\n" + + " - fieldValue: jd7x2k3m4n5p6q7r8s9t0abc\n" + + " requirementType: 8c5b1a2e-0f3d-4f5b-9a61-2c7e4d9b1f10\n" + + "type: local\n") mocktest.TestRunMockTestWithPipeAndFlags( t, pipeData, "--api-key", "string", @@ -107,6 +131,7 @@ func TestPhoneNumbersRequirements(t *testing.T) { "--api-key", "string", "phone-numbers", "requirements", "--country-code", "xx", + "--phone-number", "phoneNumber", "--type", "local", ) }) diff --git a/pkg/cmd/sender.go b/pkg/cmd/sender.go index d42f630..3d85689 100644 --- a/pkg/cmd/sender.go +++ b/pkg/cmd/sender.go @@ -41,7 +41,7 @@ var sendersCreate = cli.Command{ }, &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.", + Usage: "Enable inbound email receiving on this sender. Requires a verified inbound MX record on the domain; the request is ignored otherwise. Read `emailReceivingEnabled` back off the response to see whether it was applied — it comes back `false` when the MX has not verified.", BodyPath: "emailReceivingEnabled", }, &requestflag.Flag[bool]{ @@ -58,7 +58,7 @@ var sendersCreate = cli.Command{ }, &requestflag.Flag[string]{ Name: "phone-number", - 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.", + 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, one already attached to another sender, or one rejected in regulatory review returns 400 rather than creating a sender that cannot send. A number still under review is attached and starts carrying messages when it is approved. Omit for an email-only sender.", BodyPath: "phoneNumber", }, &requestflag.Flag[bool]{ @@ -133,7 +133,7 @@ var sendersUpdate = cli.Command{ }, &requestflag.Flag[bool]{ Name: "email-receiving-enabled", - Usage: "Enable or disable inbound email receiving for this sender.", + Usage: "Enable or disable inbound email receiving for this sender. Enabling requires a verified inbound MX record on the domain; the request is ignored otherwise, and `emailReceivingEnabled` comes back `false` on the response. Disabling always applies.", BodyPath: "emailReceivingEnabled", }, &requestflag.Flag[bool]{ diff --git a/pkg/cmd/version.go b/pkg/cmd/version.go index 86845a8..c1f2884 100644 --- a/pkg/cmd/version.go +++ b/pkg/cmd/version.go @@ -2,4 +2,4 @@ package cmd -const Version = "0.5.0" // x-release-please-version +const Version = "0.6.0" // x-release-please-version