From 6a02a70eef8767ff2257c44ac2f95eb42ca0d04b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2026 20:38:04 +0000 Subject: [PATCH 01/23] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 1c40aac..d76d554 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 236 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/increase%2Fincrease-57310f0e57b0996b79f938ef778b40e54f5b62cefb78c3e6405cefacfa704c66.yml -openapi_spec_hash: c6d65e9ebf76cb81a5395b8ab4251a95 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/increase%2Fincrease-1537e933795625528b93b601e286a513458f9aad05160ee018383e6b29986956.yml +openapi_spec_hash: f34d800917618bf012b466c1f88e8eca config_hash: 4945e03affdf289484733306e4797f81 From 44a8c166b0bc930afa2eba1e1244b468696bd5f6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2026 20:49:04 +0000 Subject: [PATCH 02/23] 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 21214fa..355be7f 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/Increase/increase-cli go 1.25 require ( - github.com/Increase/increase-go v0.524.0 + github.com/Increase/increase-go v0.525.0 github.com/charmbracelet/bubbles v0.21.0 github.com/charmbracelet/bubbletea v1.3.6 github.com/charmbracelet/lipgloss v1.1.0 diff --git a/go.sum b/go.sum index 4ae596a..7710597 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Increase/increase-go v0.524.0 h1:zTFXedEOuVxTBNV6LLXY5FaCYJrP1BGp1s0cuhLVZ68= -github.com/Increase/increase-go v0.524.0/go.mod h1:GOsK6Rh57nWpcfykcdHSIccIQJc7Fq0qitViNGbS4RM= +github.com/Increase/increase-go v0.525.0 h1:x/6GVzidcpunk4ow8ZYNbS17S0DMdm0hqUnWcnRonBc= +github.com/Increase/increase-go v0.525.0/go.mod h1:GOsK6Rh57nWpcfykcdHSIccIQJc7Fq0qitViNGbS4RM= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8= From f89ad7a65ce470107f747082a05d5111027a426e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2026 22:53:36 +0000 Subject: [PATCH 03/23] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index d76d554..6f4195b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 236 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/increase%2Fincrease-1537e933795625528b93b601e286a513458f9aad05160ee018383e6b29986956.yml -openapi_spec_hash: f34d800917618bf012b466c1f88e8eca +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/increase%2Fincrease-63d432438e53e7ef1697b512a4f93fbc62faaceba725ccd8d56036206517deec.yml +openapi_spec_hash: 7de476081771994688a1c04508a3ebea config_hash: 4945e03affdf289484733306e4797f81 From b802215033456369742d20124d3077a12e61bc8d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2026 23:22:00 +0000 Subject: [PATCH 04/23] feat(api): api update --- .stats.yml | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.stats.yml b/.stats.yml index 6f4195b..7b45f5b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 236 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/increase%2Fincrease-63d432438e53e7ef1697b512a4f93fbc62faaceba725ccd8d56036206517deec.yml -openapi_spec_hash: 7de476081771994688a1c04508a3ebea +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/increase%2Fincrease-ec66c42c6badee269e15e5f4b9e9881b2ab484d31b4426d5364390794bf72b14.yml +openapi_spec_hash: d8651196e5ffce19bee74f276d900d64 config_hash: 4945e03affdf289484733306e4797f81 diff --git a/go.mod b/go.mod index 355be7f..a9d932c 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/Increase/increase-cli go 1.25 require ( - github.com/Increase/increase-go v0.525.0 + github.com/Increase/increase-go v0.526.0 github.com/charmbracelet/bubbles v0.21.0 github.com/charmbracelet/bubbletea v1.3.6 github.com/charmbracelet/lipgloss v1.1.0 diff --git a/go.sum b/go.sum index 7710597..c38a953 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Increase/increase-go v0.525.0 h1:x/6GVzidcpunk4ow8ZYNbS17S0DMdm0hqUnWcnRonBc= -github.com/Increase/increase-go v0.525.0/go.mod h1:GOsK6Rh57nWpcfykcdHSIccIQJc7Fq0qitViNGbS4RM= +github.com/Increase/increase-go v0.526.0 h1:0IsdGjy3qV80zRU8N4sFdLklw/JK5Tg/M2Y+MqVz7I8= +github.com/Increase/increase-go v0.526.0/go.mod h1:GOsK6Rh57nWpcfykcdHSIccIQJc7Fq0qitViNGbS4RM= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8= From a75f6ba67337b21db18d53f3726b3be54047b6a5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2026 23:27:51 +0000 Subject: [PATCH 05/23] 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 a9d932c..f701739 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/Increase/increase-cli go 1.25 require ( - github.com/Increase/increase-go v0.526.0 + github.com/Increase/increase-go v0.527.0 github.com/charmbracelet/bubbles v0.21.0 github.com/charmbracelet/bubbletea v1.3.6 github.com/charmbracelet/lipgloss v1.1.0 diff --git a/go.sum b/go.sum index c38a953..ee1eff9 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Increase/increase-go v0.526.0 h1:0IsdGjy3qV80zRU8N4sFdLklw/JK5Tg/M2Y+MqVz7I8= -github.com/Increase/increase-go v0.526.0/go.mod h1:GOsK6Rh57nWpcfykcdHSIccIQJc7Fq0qitViNGbS4RM= +github.com/Increase/increase-go v0.527.0 h1:BL7MBp302/lpC520T6tb0DHhyG8A483G8aWL06XC6to= +github.com/Increase/increase-go v0.527.0/go.mod h1:GOsK6Rh57nWpcfykcdHSIccIQJc7Fq0qitViNGbS4RM= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8= From 9445bc1d6dc5c611e1aa90d126e4340c3b62de45 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 31 Mar 2026 13:33:54 +0000 Subject: [PATCH 06/23] chore(tests): bump steady to v0.20.1 --- scripts/mock | 6 +++--- scripts/test | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/mock b/scripts/mock index f99ce05..01313f2 100755 --- a/scripts/mock +++ b/scripts/mock @@ -22,9 +22,9 @@ echo "==> Starting mock server with URL ${URL}" # Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stdy/cli@0.19.7 -- steady --version + npm exec --package=@stdy/cli@0.20.1 -- steady --version - npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=dots --validator-form-object-format=dots "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.20.1 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=dots --validator-form-object-format=dots "$URL" &> .stdy.log & # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=dots --validator-form-object-format=dots "$URL" + npm exec --package=@stdy/cli@0.20.1 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=dots --validator-form-object-format=dots "$URL" fi diff --git a/scripts/test b/scripts/test index fd9e712..8d25456 100755 --- a/scripts/test +++ b/scripts/test @@ -46,7 +46,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.7 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=dots --validator-form-object-format=dots${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.20.1 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=dots --validator-form-object-format=dots${NC}" echo exit 1 From 7e566bbb3900457f626c4e9af9db990f75d41ef7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 31 Mar 2026 15:27:26 +0000 Subject: [PATCH 07/23] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 7b45f5b..6f1916c 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 236 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/increase%2Fincrease-ec66c42c6badee269e15e5f4b9e9881b2ab484d31b4426d5364390794bf72b14.yml -openapi_spec_hash: d8651196e5ffce19bee74f276d900d64 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/increase%2Fincrease-83b14e7fc19ffc252017eb4d438bfefde36b3a92a235ed669838d2fadcfdf4e8.yml +openapi_spec_hash: 581503d1dea135d37c180f632120c0b5 config_hash: 4945e03affdf289484733306e4797f81 From ec2fc1e9a4e0510f30b06632e5f0ce3d6e1ef284 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 31 Mar 2026 15:51:31 +0000 Subject: [PATCH 08/23] chore(internal): codegen related update --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index f701739..11ac156 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/Increase/increase-cli go 1.25 require ( - github.com/Increase/increase-go v0.527.0 + github.com/Increase/increase-go v0.528.0 github.com/charmbracelet/bubbles v0.21.0 github.com/charmbracelet/bubbletea v1.3.6 github.com/charmbracelet/lipgloss v1.1.0 diff --git a/go.sum b/go.sum index ee1eff9..8d8f782 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Increase/increase-go v0.527.0 h1:BL7MBp302/lpC520T6tb0DHhyG8A483G8aWL06XC6to= -github.com/Increase/increase-go v0.527.0/go.mod h1:GOsK6Rh57nWpcfykcdHSIccIQJc7Fq0qitViNGbS4RM= +github.com/Increase/increase-go v0.528.0 h1:wzUryVldkJ7HV6RB2I3SdSsPaxpvH9/lz2gpRu0aA9Y= +github.com/Increase/increase-go v0.528.0/go.mod h1:GOsK6Rh57nWpcfykcdHSIccIQJc7Fq0qitViNGbS4RM= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8= From 50914c66bc316e863f6cbc89c3ef6fb070c10fd7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 31 Mar 2026 18:29:27 +0000 Subject: [PATCH 09/23] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 6f1916c..d5a9493 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 236 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/increase%2Fincrease-83b14e7fc19ffc252017eb4d438bfefde36b3a92a235ed669838d2fadcfdf4e8.yml -openapi_spec_hash: 581503d1dea135d37c180f632120c0b5 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/increase%2Fincrease-c8283ca199163ab2079f2cafac18348f54e96e9049864f39cd7e89958e80db0d.yml +openapi_spec_hash: a4f32a1c462a8ad1380f6a7a1d7b75ec config_hash: 4945e03affdf289484733306e4797f81 From 49ec4a16ff861bd0fa104c75a4d63bf40c506847 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 31 Mar 2026 19:33:21 +0000 Subject: [PATCH 10/23] chore(internal): codegen related update --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 11ac156..27c49d5 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/Increase/increase-cli go 1.25 require ( - github.com/Increase/increase-go v0.528.0 + github.com/Increase/increase-go v0.529.0 github.com/charmbracelet/bubbles v0.21.0 github.com/charmbracelet/bubbletea v1.3.6 github.com/charmbracelet/lipgloss v1.1.0 diff --git a/go.sum b/go.sum index 8d8f782..01ba8fe 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Increase/increase-go v0.528.0 h1:wzUryVldkJ7HV6RB2I3SdSsPaxpvH9/lz2gpRu0aA9Y= -github.com/Increase/increase-go v0.528.0/go.mod h1:GOsK6Rh57nWpcfykcdHSIccIQJc7Fq0qitViNGbS4RM= +github.com/Increase/increase-go v0.529.0 h1:2br1Fa68WNRuesiP6m/gFZEihpdsCQEjKYoua7sLTP0= +github.com/Increase/increase-go v0.529.0/go.mod h1:GOsK6Rh57nWpcfykcdHSIccIQJc7Fq0qitViNGbS4RM= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8= From 5a976f142db86f917109f88b06bfd2fc7f0c6e1a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 31 Mar 2026 19:34:18 +0000 Subject: [PATCH 11/23] chore(tests): bump steady to v0.20.2 --- scripts/mock | 6 +++--- scripts/test | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/mock b/scripts/mock index 01313f2..1aea7aa 100755 --- a/scripts/mock +++ b/scripts/mock @@ -22,9 +22,9 @@ echo "==> Starting mock server with URL ${URL}" # Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stdy/cli@0.20.1 -- steady --version + npm exec --package=@stdy/cli@0.20.2 -- steady --version - npm exec --package=@stdy/cli@0.20.1 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=dots --validator-form-object-format=dots "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.20.2 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=dots --validator-form-object-format=dots "$URL" &> .stdy.log & # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.20.1 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=dots --validator-form-object-format=dots "$URL" + npm exec --package=@stdy/cli@0.20.2 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=dots --validator-form-object-format=dots "$URL" fi diff --git a/scripts/test b/scripts/test index 8d25456..e8d6b07 100755 --- a/scripts/test +++ b/scripts/test @@ -46,7 +46,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.20.1 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=dots --validator-form-object-format=dots${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.20.2 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=dots --validator-form-object-format=dots${NC}" echo exit 1 From ff538ca7d15fd6f362c169b2f89c3dc9e25a64bc Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 1 Apr 2026 16:14:51 +0000 Subject: [PATCH 12/23] fix: handle empty data set using `--format explore` --- internal/jsonview/explorer.go | 4 ++++ internal/jsonview/explorer_test.go | 37 ++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 internal/jsonview/explorer_test.go diff --git a/internal/jsonview/explorer.go b/internal/jsonview/explorer.go index 055541e..ea900bc 100644 --- a/internal/jsonview/explorer.go +++ b/internal/jsonview/explorer.go @@ -406,6 +406,10 @@ func (v *JSONViewer) navigateForward() (tea.Model, tea.Cmd) { return v, nil } + if len(tableView.rowData) < 1 { + return v, nil + } + cursor := tableView.table.Cursor() selected := tableView.rowData[cursor] if !v.canNavigateInto(selected) { diff --git a/internal/jsonview/explorer_test.go b/internal/jsonview/explorer_test.go new file mode 100644 index 0000000..3f0e751 --- /dev/null +++ b/internal/jsonview/explorer_test.go @@ -0,0 +1,37 @@ +package jsonview + +import ( + "testing" + + "github.com/charmbracelet/bubbles/help" + "github.com/tidwall/gjson" +) + +func TestNavigateForward_EmptyRowData(t *testing.T) { + // An empty JSON array produces a TableView with no rows. + emptyArray := gjson.Parse("[]") + view, err := newTableView("", emptyArray, false) + if err != nil { + t.Fatalf("newTableView: %v", err) + } + + viewer := &JSONViewer{ + stack: []JSONView{view}, + root: "test", + help: help.New(), + } + + // Should return without panicking despite the empty data set. + model, cmd := viewer.navigateForward() + if model != viewer { + t.Error("expected same viewer model returned") + } + if cmd != nil { + t.Error("expected nil cmd") + } + + // Stack should remain unchanged (no new view pushed). + if len(viewer.stack) != 1 { + t.Errorf("expected stack length 1, got %d", len(viewer.stack)) + } +} From 67340d0d26af534f125883c29686fbc5f48470ad Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 1 Apr 2026 17:28:09 +0000 Subject: [PATCH 13/23] fix: use `RawJSON` when iterating items with `--format explore` in the CLI --- internal/jsonview/explorer.go | 34 ++++++++++++++++++++-- internal/jsonview/explorer_test.go | 45 ++++++++++++++++++++++-------- pkg/cmd/cmdutil.go | 6 ++-- 3 files changed, 68 insertions(+), 17 deletions(-) diff --git a/internal/jsonview/explorer.go b/internal/jsonview/explorer.go index ea900bc..836bb2c 100644 --- a/internal/jsonview/explorer.go +++ b/internal/jsonview/explorer.go @@ -1,6 +1,7 @@ package jsonview import ( + "bytes" "encoding/json" "errors" "fmt" @@ -309,6 +310,10 @@ func ExploreJSON(title string, json gjson.Result) error { return err } +type hasRawJSON interface { + RawJSON() string +} + // ExploreJSONStream explores JSON data loaded incrementally via an iterator func ExploreJSONStream[T any](title string, it Iterator[T]) error { anyIt := genericToAnyIterator(it) @@ -327,12 +332,12 @@ func ExploreJSONStream[T any](title string, it Iterator[T]) error { return err } - // Convert items to JSON array - jsonBytes, err := json.Marshal(items) + arrayJSONBytes, err := marshalItemsToJSONArray(items) if err != nil { return err } - arrayJSON := gjson.ParseBytes(jsonBytes) + + arrayJSON := gjson.ParseBytes(arrayJSONBytes) view, err := newTableView("", arrayJSON, false) if err != nil { return err @@ -352,6 +357,29 @@ func ExploreJSONStream[T any](title string, it Iterator[T]) error { return err } +func marshalItemsToJSONArray(items []any) ([]byte, error) { + var buf bytes.Buffer + buf.WriteByte('[') + + for i, item := range items { + if i > 0 { + buf.WriteByte(',') + } + if hasRaw, ok := item.(hasRawJSON); ok { + buf.WriteString(hasRaw.RawJSON()) + } else { + jsonData, err := json.Marshal(item) + if err != nil { + return nil, err + } + buf.Write(jsonData) + } + } + + buf.WriteByte(']') + return buf.Bytes(), nil +} + func (v *JSONViewer) current() JSONView { return v.stack[len(v.stack)-1] } func (v *JSONViewer) Init() tea.Cmd { return nil } diff --git a/internal/jsonview/explorer_test.go b/internal/jsonview/explorer_test.go index 3f0e751..c559254 100644 --- a/internal/jsonview/explorer_test.go +++ b/internal/jsonview/explorer_test.go @@ -5,15 +5,15 @@ import ( "github.com/charmbracelet/bubbles/help" "github.com/tidwall/gjson" + + "github.com/stretchr/testify/require" ) func TestNavigateForward_EmptyRowData(t *testing.T) { // An empty JSON array produces a TableView with no rows. emptyArray := gjson.Parse("[]") view, err := newTableView("", emptyArray, false) - if err != nil { - t.Fatalf("newTableView: %v", err) - } + require.NoError(t, err) viewer := &JSONViewer{ stack: []JSONView{view}, @@ -23,15 +23,38 @@ func TestNavigateForward_EmptyRowData(t *testing.T) { // Should return without panicking despite the empty data set. model, cmd := viewer.navigateForward() - if model != viewer { - t.Error("expected same viewer model returned") - } - if cmd != nil { - t.Error("expected nil cmd") - } + require.Equal(t, model, viewer, "expected same viewer model returned") + require.Nil(t, cmd) // Stack should remain unchanged (no new view pushed). - if len(viewer.stack) != 1 { - t.Errorf("expected stack length 1, got %d", len(viewer.stack)) + require.Equal(t, 1, len(viewer.stack), "expected stack length 1, got %d", len(viewer.stack)) +} + +// rawJSONItem implements HasRawJSON, returning pre-built JSON. +type rawJSONItem struct { + raw string +} + +func (r rawJSONItem) RawJSON() string { return r.raw } + +func TestMarshalItemsToJSONArray_WithHasRawJSON(t *testing.T) { + items := []any{ + rawJSONItem{raw: `{"id":1,"name":"alice"}`}, + rawJSONItem{raw: `{"id":2,"name":"bob"}`}, } + + got, err := marshalItemsToJSONArray(items) + require.NoError(t, err) + require.JSONEq(t, `[{"id":1,"name":"alice"},{"id":2,"name":"bob"}]`, string(got)) +} + +func TestMarshalItemsToJSONArray_WithoutHasRawJSON(t *testing.T) { + items := []any{ + map[string]any{"id": 1, "name": "alice"}, + map[string]any{"id": 2, "name": "bob"}, + } + + got, err := marshalItemsToJSONArray(items) + require.NoError(t, err) + require.JSONEq(t, `[{"id":1,"name":"alice"},{"id":2,"name":"bob"}]`, string(got)) } diff --git a/pkg/cmd/cmdutil.go b/pkg/cmd/cmdutil.go index 8987478..e3492ac 100644 --- a/pkg/cmd/cmdutil.go +++ b/pkg/cmd/cmdutil.go @@ -387,7 +387,7 @@ func countTerminalLines(data []byte, terminalWidth int) int { return bytes.Count([]byte(wrap.String(string(data), terminalWidth)), []byte("\n")) } -type HasRawJSON interface { +type hasRawJSON interface { RawJSON() string } @@ -413,7 +413,7 @@ func ShowJSONIterator[T any](stdout *os.File, title string, iter jsonview.Iterat for itemsToDisplay != 0 && iter.Next() { item := iter.Current() var obj gjson.Result - if hasRaw, ok := any(item).(HasRawJSON); ok { + if hasRaw, ok := any(item).(hasRawJSON); ok { obj = gjson.Parse(hasRaw.RawJSON()) } else { jsonData, err := json.Marshal(item) @@ -460,7 +460,7 @@ func ShowJSONIterator[T any](stdout *os.File, title string, iter jsonview.Iterat } item := iter.Current() var obj gjson.Result - if hasRaw, ok := any(item).(HasRawJSON); ok { + if hasRaw, ok := any(item).(hasRawJSON); ok { obj = gjson.Parse(hasRaw.RawJSON()) } else { jsonData, err := json.Marshal(item) From cc21dffca52f9ff543a3a38555742a7ed3cd926a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 2 Apr 2026 13:52:40 +0000 Subject: [PATCH 14/23] feat: binary-only parameters become CLI flags that take filenames only --- internal/requestflag/requestflag.go | 10 +++ pkg/cmd/file.go | 9 +-- pkg/cmd/file_test.go | 10 ++- pkg/cmd/flagoptions.go | 96 +++++++++++++++++++++++++++++ 4 files changed, 118 insertions(+), 7 deletions(-) diff --git a/internal/requestflag/requestflag.go b/internal/requestflag/requestflag.go index 32c13f5..bdef64f 100644 --- a/internal/requestflag/requestflag.go +++ b/internal/requestflag/requestflag.go @@ -43,6 +43,11 @@ type Flag[ // parameters. Const bool + // FileInput, when true, indicates that the flag value is always treated as a file path. The file is read + // automatically without requiring the "@" prefix. This is used for parameters with `type: string, format: + // binary` in the OpenAPI spec. + FileInput bool + // 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 @@ -59,6 +64,7 @@ type InRequest interface { GetHeaderPath() string GetBodyPath() string IsBodyRoot() bool + IsFileInput() bool } func (f Flag[T]) GetQueryPath() string { @@ -77,6 +83,10 @@ func (f Flag[T]) IsBodyRoot() bool { return f.BodyRoot } +func (f Flag[T]) IsFileInput() bool { + return f.FileInput +} + // The values that will be sent in different parts of a request. type RequestContents struct { Queries map[string]any diff --git a/pkg/cmd/file.go b/pkg/cmd/file.go index c21a674..589a955 100644 --- a/pkg/cmd/file.go +++ b/pkg/cmd/file.go @@ -21,10 +21,11 @@ var filesCreate = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "file", - Usage: "The file contents. This should follow the specifications of [RFC 7578](https://datatracker.ietf.org/doc/html/rfc7578) which defines file transfers for the multipart/form-data protocol.", - Required: true, - BodyPath: "file", + Name: "file", + Usage: "The file contents. This should follow the specifications of [RFC 7578](https://datatracker.ietf.org/doc/html/rfc7578) which defines file transfers for the multipart/form-data protocol.", + Required: true, + BodyPath: "file", + FileInput: true, }, &requestflag.Flag[string]{ Name: "purpose", diff --git a/pkg/cmd/file_test.go b/pkg/cmd/file_test.go index 8f03c21..e432719 100644 --- a/pkg/cmd/file_test.go +++ b/pkg/cmd/file_test.go @@ -3,6 +3,7 @@ package cmd import ( + "strings" "testing" "github.com/Increase/increase-cli/internal/mocktest" @@ -15,18 +16,21 @@ func TestFilesCreate(t *testing.T) { t, "--api-key", "string", "files", "create", - "--file", "Example data", + "--file", mocktest.TestFile(t, "Example data"), "--purpose", "check_image_front", "--description", "x", ) }) t.Run("piping data", func(t *testing.T) { + testFile := mocktest.TestFile(t, "Example data") // Test piping YAML data over stdin - pipeData := []byte("" + + pipeDataStr := "" + "file: Example data\n" + "purpose: check_image_front\n" + - "description: x\n") + "description: x\n" + pipeDataStr = strings.ReplaceAll(pipeDataStr, "Example data", testFile) + pipeData := []byte(pipeDataStr) mocktest.TestRunMockTestWithPipeAndFlags( t, pipeData, "--api-key", "string", diff --git a/pkg/cmd/flagoptions.go b/pkg/cmd/flagoptions.go index e50adc8..2d84005 100644 --- a/pkg/cmd/flagoptions.go +++ b/pkg/cmd/flagoptions.go @@ -98,6 +98,21 @@ func embedFilesValue(v reflect.Value, embedStyle FileEmbedStyle) (reflect.Value, return result, nil case reflect.String: + // FilePathValue is always treated as a file path without needing the "@" prefix. + // These only appear on binary upload parameters (multipart/octet-stream), which + // always use EmbedIOReader. + if v.Type() == reflect.TypeOf(FilePathValue("")) { + s := v.String() + if s == "" { + return v, nil + } + content, err := os.ReadFile(s) + if err != nil { + return v, err + } + return reflect.ValueOf(string(content)), nil + } + s := v.String() if literal, ok := strings.CutPrefix(s, "\\@"); ok { // Allow for escaped @ signs if you don't want them to be treated as files @@ -258,6 +273,12 @@ func flagOptions( } } + // For flags marked as FileInput (type: string, format: binary), the value is always + // a file path. Wrap with FilePathValue so embedFiles reads the file automatically + // without requiring the user to type the "@" prefix. This handles both values set + // via explicit CLI flags and values that arrived via piped YAML/JSON data. + wrapFileInputValues(cmd, &requestContents) + // Embed files passed as "@file.jpg" in the request body, headers, and query: embedStyle := EmbedText if bodyType == ApplicationOctetStream || bodyType == MultipartFormEncoded { @@ -371,3 +392,78 @@ func flagOptions( return options, nil } + +// FilePathValue is a string wrapper that marks a value as a file path whose contents should be read +// and embedded in the request. Unlike a regular string, embedFilesValue always treats a FilePathValue +// as a file path without needing the "@" prefix. +type FilePathValue string + +// 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 +// CLI flags and values that arrived via piped YAML/JSON data. +func wrapFileInputValues(cmd *cli.Command, contents *requestflag.RequestContents) { + bodyMap, _ := contents.Body.(map[string]any) + + for _, flag := range cmd.Flags { + inReq, ok := flag.(requestflag.InRequest) + if !ok || !inReq.IsFileInput() || inReq.IsBodyRoot() { + continue + } + + // Wrap values set via explicit CLI flags. + if flag.IsSet() { + if wrapped, changed := wrapFileInputValue(flag.Get()); changed { + if bodyPath := inReq.GetBodyPath(); bodyPath != "" { + if bodyMap != nil { + bodyMap[bodyPath] = wrapped + } + } else if queryPath := inReq.GetQueryPath(); queryPath != "" { + contents.Queries[queryPath] = wrapped + } else if headerPath := inReq.GetHeaderPath(); headerPath != "" { + contents.Headers[headerPath] = wrapped + } + } + } + + // Wrap values that arrived via piped YAML/JSON data in the body map. + if bodyPath := inReq.GetBodyPath(); bodyPath != "" && bodyMap != nil { + if value, exists := bodyMap[bodyPath]; exists { + if wrapped, changed := wrapFileInputValue(value); changed { + bodyMap[bodyPath] = wrapped + } + } + } + } +} + +func wrapFileInputValue(value any) (any, bool) { + switch v := value.(type) { + case string: + if v == "" { + return value, false + } + return FilePathValue(v), true + + case []string: + result := make([]any, len(v)) + for i, s := range v { + result[i] = FilePathValue(s) + } + return result, true + + case []any: + result := make([]any, len(v)) + for i, elem := range v { + if s, ok := elem.(string); ok { + result[i] = FilePathValue(s) + } else { + result[i] = elem + } + } + return result, true + + default: + return value, false + } +} From 1cad891105e3deb9ebfc00ad34093d51506c947f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 2 Apr 2026 20:19:14 +0000 Subject: [PATCH 15/23] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index d5a9493..3e05b57 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 236 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/increase%2Fincrease-c8283ca199163ab2079f2cafac18348f54e96e9049864f39cd7e89958e80db0d.yml -openapi_spec_hash: a4f32a1c462a8ad1380f6a7a1d7b75ec +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/increase%2Fincrease-e3103e9bb8480e11581841c322e0bd0255b64413d401873c96cd00a3e6d6c3f7.yml +openapi_spec_hash: 962e1efe27066cf84b405a6695dd8288 config_hash: 4945e03affdf289484733306e4797f81 From 2ca37dccd872b339cc77201cf1e7b349dd0efd19 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 2 Apr 2026 20:26:56 +0000 Subject: [PATCH 16/23] 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 27c49d5..c830b87 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/Increase/increase-cli go 1.25 require ( - github.com/Increase/increase-go v0.529.0 + github.com/Increase/increase-go v0.530.0 github.com/charmbracelet/bubbles v0.21.0 github.com/charmbracelet/bubbletea v1.3.6 github.com/charmbracelet/lipgloss v1.1.0 diff --git a/go.sum b/go.sum index 01ba8fe..8ada46a 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Increase/increase-go v0.529.0 h1:2br1Fa68WNRuesiP6m/gFZEihpdsCQEjKYoua7sLTP0= -github.com/Increase/increase-go v0.529.0/go.mod h1:GOsK6Rh57nWpcfykcdHSIccIQJc7Fq0qitViNGbS4RM= +github.com/Increase/increase-go v0.530.0 h1:+//+kfAX1kIneepn07Mt/bc2Ao70V08cJGY2Gd9/plY= +github.com/Increase/increase-go v0.530.0/go.mod h1:GOsK6Rh57nWpcfykcdHSIccIQJc7Fq0qitViNGbS4RM= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8= From a6ec976ce1a9846354377f719e6926493f52f63e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 2 Apr 2026 21:08:38 +0000 Subject: [PATCH 17/23] feat: better error message if scheme forgotten in CLI `*_BASE_URL`/`--base-url` --- cmd/increase/main.go | 7 +++++++ pkg/cmd/cmd.go | 3 +++ pkg/cmd/cmdutil.go | 9 +++++++++ pkg/cmd/cmdutil_test.go | 39 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 58 insertions(+) diff --git a/cmd/increase/main.go b/cmd/increase/main.go index bf86971..fc9127c 100644 --- a/cmd/increase/main.go +++ b/cmd/increase/main.go @@ -23,6 +23,13 @@ func main() { prepareForAutocomplete(app) } + if baseURL, ok := os.LookupEnv("INCREASE_BASE_URL"); ok { + if err := cmd.ValidateBaseURL(baseURL, "INCREASE_BASE_URL"); err != nil { + fmt.Fprintf(os.Stderr, "%s\n", err.Error()) + os.Exit(1) + } + } + if err := app.Run(context.Background(), os.Args); err != nil { exitCode := 1 diff --git a/pkg/cmd/cmd.go b/pkg/cmd/cmd.go index 9b2d5cc..8e00825 100644 --- a/pkg/cmd/cmd.go +++ b/pkg/cmd/cmd.go @@ -39,6 +39,9 @@ func init() { Name: "base-url", DefaultText: "url", Usage: "Override the base URL for API requests", + Validator: func(baseURL string) error { + return ValidateBaseURL(baseURL, "--base-url") + }, }, &cli.StringFlag{ Name: "format", diff --git a/pkg/cmd/cmdutil.go b/pkg/cmd/cmdutil.go index e3492ac..7b745c0 100644 --- a/pkg/cmd/cmdutil.go +++ b/pkg/cmd/cmdutil.go @@ -29,6 +29,15 @@ import ( var OutputFormats = []string{"auto", "explore", "json", "jsonl", "pretty", "raw", "yaml"} +// ValidateBaseURL checks that a base URL is correctly prefixed with a protocol scheme and produces a better +// error message than the person would see otherwise if it doesn't. +func ValidateBaseURL(value, source string) error { + if value != "" && !strings.HasPrefix(value, "http://") && !strings.HasPrefix(value, "https://") { + return fmt.Errorf("%s %q is missing a scheme (expected http:// or https://)", source, value) + } + return nil +} + func getDefaultRequestOptions(cmd *cli.Command) []option.RequestOption { opts := []option.RequestOption{ option.WithHeader("User-Agent", fmt.Sprintf("Increase/CLI %s", Version)), diff --git a/pkg/cmd/cmdutil_test.go b/pkg/cmd/cmdutil_test.go index 0a46fd1..8487408 100644 --- a/pkg/cmd/cmdutil_test.go +++ b/pkg/cmd/cmdutil_test.go @@ -125,3 +125,42 @@ func TestCreateDownloadFile(t *testing.T) { assert.Equal(t, "passwd", filepath.Base(file.Name())) }) } + +func TestValidateBaseURL(t *testing.T) { + t.Parallel() + + t.Run("ValidHTTPS", func(t *testing.T) { + t.Parallel() + + require.NoError(t, ValidateBaseURL("https://api.example.com", "--base-url")) + }) + + t.Run("ValidHTTP", func(t *testing.T) { + t.Parallel() + + require.NoError(t, ValidateBaseURL("http://localhost:8080", "--base-url")) + }) + + t.Run("Empty", func(t *testing.T) { + t.Parallel() + + require.NoError(t, ValidateBaseURL("", "MY_BASE_URL")) + }) + + t.Run("MissingScheme", func(t *testing.T) { + t.Parallel() + + err := ValidateBaseURL("localhost:8080", "MY_BASE_URL") + require.Error(t, err) + assert.Contains(t, err.Error(), "MY_BASE_URL") + assert.Contains(t, err.Error(), "missing a scheme") + }) + + t.Run("HostOnly", func(t *testing.T) { + t.Parallel() + + err := ValidateBaseURL("api.example.com", "--base-url") + require.Error(t, err) + assert.Contains(t, err.Error(), "--base-url") + }) +} From 3fb3ad900950561ac7a6a45f50be97a680af0962 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 2 Apr 2026 21:21:40 +0000 Subject: [PATCH 18/23] feat: allow `-` as value representing stdin to binary-only file parameters in CLIs --- pkg/cmd/flagoptions.go | 103 +++++++++++++++++++++++++++++++++--- pkg/cmd/flagoptions_test.go | 91 ++++++++++++++++++++++++++++++- 2 files changed, 184 insertions(+), 10 deletions(-) diff --git a/pkg/cmd/flagoptions.go b/pkg/cmd/flagoptions.go index 2d84005..0c9e5d3 100644 --- a/pkg/cmd/flagoptions.go +++ b/pkg/cmd/flagoptions.go @@ -40,12 +40,48 @@ const ( EmbedIOReader ) -func embedFiles(obj any, embedStyle FileEmbedStyle) (any, error) { +// onceStdinReader wraps an io.Reader that can only be consumed once, used to ensure stdin is read by at most +// one parameter (or only for a body root parameter or only for YAML parameter input). If reason is set, stdin +// is unavailable and read() returns an error explaining why. +type onceStdinReader struct { + stdinReader io.Reader + failureReason string +} + +func (o *onceStdinReader) read() (io.Reader, error) { + if o.failureReason != "" { + return nil, fmt.Errorf("cannot read from stdin: %s", o.failureReason) + } + if o.stdinReader == nil { + return nil, fmt.Errorf("stdin has already been read by another parameter; it can only be read once") + } + r := o.stdinReader + o.stdinReader = nil + return r, nil +} + +func (o *onceStdinReader) readAll() ([]byte, error) { + r, err := o.read() + if err != nil { + return nil, err + } + return io.ReadAll(r) +} + +func isStdinPath(s string) bool { + switch s { + case "-", "/dev/fd/0", "/dev/stdin": + return true + } + return false +} + +func embedFiles(obj any, embedStyle FileEmbedStyle, stdin *onceStdinReader) (any, error) { if obj == nil { return obj, nil } v := reflect.ValueOf(obj) - result, err := embedFilesValue(v, embedStyle) + result, err := embedFilesValue(v, embedStyle, stdin) if err != nil { return nil, err } @@ -53,7 +89,7 @@ func embedFiles(obj any, embedStyle FileEmbedStyle) (any, error) { } // Replace "@file.txt" with the file's contents inside a value -func embedFilesValue(v reflect.Value, embedStyle FileEmbedStyle) (reflect.Value, error) { +func embedFilesValue(v reflect.Value, embedStyle FileEmbedStyle, stdin *onceStdinReader) (reflect.Value, error) { // Unwrap interface values to get the concrete type if v.Kind() == reflect.Interface { if v.IsNil() { @@ -74,7 +110,7 @@ func embedFilesValue(v reflect.Value, embedStyle FileEmbedStyle) (reflect.Value, for iter.Next() { key := iter.Key() val := iter.Value() - newVal, err := embedFilesValue(val, embedStyle) + newVal, err := embedFilesValue(val, embedStyle, stdin) if err != nil { return reflect.Value{}, err } @@ -89,7 +125,7 @@ func embedFilesValue(v reflect.Value, embedStyle FileEmbedStyle) (reflect.Value, // Use `[]any` to allow for types to change when embedding files result := reflect.MakeSlice(reflect.TypeOf([]any{}), v.Len(), v.Len()) for i := 0; i < v.Len(); i++ { - newVal, err := embedFilesValue(v.Index(i), embedStyle) + newVal, err := embedFilesValue(v.Index(i), embedStyle, stdin) if err != nil { return reflect.Value{}, err } @@ -106,6 +142,13 @@ func embedFilesValue(v reflect.Value, embedStyle FileEmbedStyle) (reflect.Value, if s == "" { return v, nil } + if isStdinPath(s) { + content, err := stdin.readAll() + if err != nil { + return v, err + } + return reflect.ValueOf(string(content)), nil + } content, err := os.ReadFile(s) if err != nil { return v, err @@ -123,6 +166,13 @@ func embedFilesValue(v reflect.Value, embedStyle FileEmbedStyle) (reflect.Value, if filename, ok := strings.CutPrefix(s, "@data://"); ok { // The "@data://" prefix is for files you explicitly want to upload // as base64-encoded (even if the file itself is plain text) + if isStdinPath(filename) { + content, err := stdin.readAll() + if err != nil { + return v, err + } + return reflect.ValueOf(base64.StdEncoding.EncodeToString(content)), nil + } content, err := os.ReadFile(filename) if err != nil { return v, err @@ -132,12 +182,29 @@ func embedFilesValue(v reflect.Value, embedStyle FileEmbedStyle) (reflect.Value, // The "@file://" prefix is for files that you explicitly want to // upload as a string literal with backslash escapes (not base64 // encoded) + if isStdinPath(filename) { + content, err := stdin.readAll() + if err != nil { + return v, err + } + return reflect.ValueOf(string(content)), nil + } content, err := os.ReadFile(filename) if err != nil { return v, err } return reflect.ValueOf(string(content)), nil } else if filename, ok := strings.CutPrefix(s, "@"); ok { + if isStdinPath(filename) { + content, err := stdin.readAll() + if err != nil { + return v, err + } + if isUTF8TextFile(content) { + return reflect.ValueOf(string(content)), nil + } + return reflect.ValueOf(base64.StdEncoding.EncodeToString(content)), nil + } content, err := os.ReadFile(filename) if err != nil { // If the string is "@username", it's probably supposed to be a @@ -175,6 +242,14 @@ func embedFilesValue(v reflect.Value, embedStyle FileEmbedStyle) (reflect.Value, expectsFile = strings.Contains(filename, ".") || strings.Contains(filename, "/") } + if isStdinPath(filename) { + r, err := stdin.read() + if err != nil { + return v, err + } + return reflect.ValueOf(io.NopCloser(r)), nil + } + file, err := os.Open(filename) if err != nil { if !expectsFile { @@ -234,6 +309,7 @@ func flagOptions( requestContents := requestflag.ExtractRequestContents(cmd) + stdinConsumedByPipe := false if (bodyType == MultipartFormEncoded || bodyType == ApplicationJSON) && !ignoreStdin && isInputPiped() { pipeData, err := io.ReadAll(os.Stdin) if err != nil { @@ -241,6 +317,7 @@ func flagOptions( } if len(pipeData) > 0 { + stdinConsumedByPipe = true var bodyData any if err := yaml.Unmarshal(pipeData, &bodyData); err != nil { return nil, fmt.Errorf("Failed to parse piped data as YAML/JSON:\n%w", err) @@ -279,24 +356,34 @@ func flagOptions( // via explicit CLI flags and values that arrived via piped YAML/JSON data. wrapFileInputValues(cmd, &requestContents) + // Determine stdin availability for FileInput params that use "-". + var stdinReader onceStdinReader + if ignoreStdin { + stdinReader = onceStdinReader{failureReason: "stdin is already being used for the request body"} + } else if stdinConsumedByPipe { + stdinReader = onceStdinReader{failureReason: "stdin was already consumed by piped YAML/JSON input"} + } else { + stdinReader = onceStdinReader{stdinReader: os.Stdin} + } + // Embed files passed as "@file.jpg" in the request body, headers, and query: embedStyle := EmbedText if bodyType == ApplicationOctetStream || bodyType == MultipartFormEncoded { embedStyle = EmbedIOReader } - if embedded, err := embedFiles(requestContents.Body, embedStyle); err != nil { + if embedded, err := embedFiles(requestContents.Body, embedStyle, &stdinReader); err != nil { return nil, err } else { requestContents.Body = embedded } - if headersWithFiles, err := embedFiles(requestContents.Headers, EmbedText); err != nil { + if headersWithFiles, err := embedFiles(requestContents.Headers, EmbedText, &stdinReader); err != nil { return nil, err } else { requestContents.Headers = headersWithFiles.(map[string]any) } - if queriesWithFiles, err := embedFiles(requestContents.Queries, EmbedText); err != nil { + if queriesWithFiles, err := embedFiles(requestContents.Queries, EmbedText, &stdinReader); err != nil { return nil, err } else { requestContents.Queries = queriesWithFiles.(map[string]any) diff --git a/pkg/cmd/flagoptions_test.go b/pkg/cmd/flagoptions_test.go index e5dad4b..9a7fe3b 100644 --- a/pkg/cmd/flagoptions_test.go +++ b/pkg/cmd/flagoptions_test.go @@ -2,8 +2,10 @@ package cmd import ( "encoding/base64" + "io" "os" "path/filepath" + "strings" "testing" "github.com/stretchr/testify/assert" @@ -11,6 +13,7 @@ import ( ) func TestIsUTF8TextFile(t *testing.T) { + tests := []struct { content []byte expected bool @@ -32,6 +35,7 @@ func TestIsUTF8TextFile(t *testing.T) { } func TestEmbedFiles(t *testing.T) { + // Create temporary directory for test files tmpDir := t.TempDir() @@ -216,7 +220,8 @@ func TestEmbedFiles(t *testing.T) { for _, tt := range tests { t.Run(tt.name+" text", func(t *testing.T) { - got, err := embedFiles(tt.input, EmbedText) + + got, err := embedFiles(tt.input, EmbedText, nil) if tt.wantErr { assert.Error(t, err) } else { @@ -226,7 +231,8 @@ func TestEmbedFiles(t *testing.T) { }) t.Run(tt.name+" io.Reader", func(t *testing.T) { - _, err := embedFiles(tt.input, EmbedIOReader) + + _, err := embedFiles(tt.input, EmbedIOReader, nil) if tt.wantErr { assert.Error(t, err) } else { @@ -236,9 +242,90 @@ func TestEmbedFiles(t *testing.T) { } } +func TestEmbedFilesStdin(t *testing.T) { + + t.Run("FilePathValueDash", func(t *testing.T) { + + stdin := &onceStdinReader{stdinReader: strings.NewReader("stdin content")} + + withEmbedded, err := embedFiles(map[string]any{"file": FilePathValue("-")}, EmbedText, stdin) + require.NoError(t, err) + require.Equal(t, map[string]any{"file": "stdin content"}, withEmbedded) + }) + + t.Run("FilePathValueDevStdin", func(t *testing.T) { + + stdin := &onceStdinReader{stdinReader: strings.NewReader("stdin content")} + + withEmbedded, err := embedFiles(map[string]any{"file": FilePathValue("/dev/stdin")}, EmbedText, stdin) + require.NoError(t, err) + require.Equal(t, map[string]any{"file": "stdin content"}, withEmbedded) + }) + + t.Run("MultipleFilePathValueDashesError", func(t *testing.T) { + + stdin := &onceStdinReader{stdinReader: strings.NewReader("stdin content")} + + _, err := embedFiles(map[string]any{ + "file1": FilePathValue("-"), + "file2": FilePathValue("-"), + }, EmbedText, stdin) + require.Error(t, err) + require.Contains(t, err.Error(), "already been read") + }) + + t.Run("FilePathValueDashUnavailableStdin", func(t *testing.T) { + + stdin := &onceStdinReader{failureReason: "stdin is already being used for the request body"} + + _, err := embedFiles(map[string]any{"file": FilePathValue("-")}, EmbedText, stdin) + require.Error(t, err) + require.Contains(t, err.Error(), "cannot read from stdin") + require.Contains(t, err.Error(), "request body") + }) + + t.Run("AtDashEmbedText", func(t *testing.T) { + + stdin := &onceStdinReader{stdinReader: strings.NewReader("piped content")} + + withEmbedded, err := embedFiles(map[string]any{"data": "@-"}, EmbedText, stdin) + require.NoError(t, err) + require.Equal(t, map[string]any{"data": "piped content"}, withEmbedded) + }) + + t.Run("AtDashEmbedIOReader", func(t *testing.T) { + + stdin := &onceStdinReader{stdinReader: strings.NewReader("piped content")} + + withEmbedded, err := embedFiles(map[string]any{"data": "@-"}, EmbedIOReader, stdin) + require.NoError(t, err) + + withEmbeddedMap := withEmbedded.(map[string]any) + r := withEmbeddedMap["data"].(io.ReadCloser) + + content, err := io.ReadAll(r) + require.NoError(t, err) + require.Equal(t, "piped content", string(content)) + }) + + t.Run("FilePathValueRealFile", func(t *testing.T) { + + tmpDir := t.TempDir() + writeTestFile(t, tmpDir, "test.txt", "file content") + + stdin := &onceStdinReader{stdinReader: strings.NewReader("unused stdin")} + + withEmbedded, err := embedFiles(map[string]any{"file": FilePathValue(filepath.Join(tmpDir, "test.txt"))}, EmbedText, stdin) + require.NoError(t, err) + require.Equal(t, map[string]any{"file": "file content"}, withEmbedded) + }) +} + func writeTestFile(t *testing.T, dir, filename, content string) { t.Helper() + path := filepath.Join(dir, filename) + err := os.WriteFile(path, []byte(content), 0644) require.NoError(t, err, "failed to write test file %s", path) } From dc10b4d70b8a31fe785d8a6123136f666f06e3ad Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 2 Apr 2026 22:44:56 +0000 Subject: [PATCH 19/23] chore: switch some CLI Go tests from `os.Chdir` to `t.Chdir` --- pkg/cmd/cmdutil_test.go | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/pkg/cmd/cmdutil_test.go b/pkg/cmd/cmdutil_test.go index 8487408..550c995 100644 --- a/pkg/cmd/cmdutil_test.go +++ b/pkg/cmd/cmdutil_test.go @@ -67,10 +67,7 @@ func TestWriteBinaryResponse(t *testing.T) { func TestCreateDownloadFile(t *testing.T) { t.Run("creates file with filename from header", func(t *testing.T) { - tmpDir := t.TempDir() - oldWd, _ := os.Getwd() - os.Chdir(tmpDir) - defer os.Chdir(oldWd) + t.Chdir(t.TempDir()) resp := &http.Response{ Header: http.Header{ @@ -96,10 +93,7 @@ func TestCreateDownloadFile(t *testing.T) { }) t.Run("creates temp file when no header", func(t *testing.T) { - tmpDir := t.TempDir() - oldWd, _ := os.Getwd() - os.Chdir(tmpDir) - defer os.Chdir(oldWd) + t.Chdir(t.TempDir()) resp := &http.Response{Header: http.Header{}} file, err := createDownloadFile(resp, []byte("test content")) @@ -109,10 +103,7 @@ func TestCreateDownloadFile(t *testing.T) { }) t.Run("prevents directory traversal", func(t *testing.T) { - tmpDir := t.TempDir() - oldWd, _ := os.Getwd() - os.Chdir(tmpDir) - defer os.Chdir(oldWd) + t.Chdir(t.TempDir()) resp := &http.Response{ Header: http.Header{ From 28d2401e462235b81e59f0f21a241b9485b2c850 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 3 Apr 2026 00:55:46 +0000 Subject: [PATCH 20/23] feat(api): api update --- .stats.yml | 4 +-- pkg/cmd/card.go | 64 ++++++++++++++++++++++++++++++++++++++++++++ pkg/cmd/card_test.go | 58 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 124 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 3e05b57..5c2b865 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 236 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/increase%2Fincrease-e3103e9bb8480e11581841c322e0bd0255b64413d401873c96cd00a3e6d6c3f7.yml -openapi_spec_hash: 962e1efe27066cf84b405a6695dd8288 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/increase%2Fincrease-31046e3fe86098c429a87dc861cf42dae0252314abf90021a804e748f9c16417.yml +openapi_spec_hash: 78fe78704879172326e842c27ee09a3a config_hash: 4945e03affdf289484733306e4797f81 diff --git a/pkg/cmd/card.go b/pkg/cmd/card.go index 467e76f..3a605ad 100644 --- a/pkg/cmd/card.go +++ b/pkg/cmd/card.go @@ -26,6 +26,11 @@ var cardsCreate = requestflag.WithInnerFlags(cli.Command{ Required: true, BodyPath: "account_id", }, + &requestflag.Flag[map[string]any]{ + Name: "authorization-controls", + Usage: "Controls that restrict how this card can be used.", + BodyPath: "authorization_controls", + }, &requestflag.Flag[map[string]any]{ Name: "billing-address", Usage: "The card's billing address.", @@ -50,6 +55,33 @@ var cardsCreate = requestflag.WithInnerFlags(cli.Command{ Action: handleCardsCreate, HideHelpCommand: true, }, map[string][]requestflag.HasOuterFlag{ + "authorization-controls": { + &requestflag.InnerFlag[map[string]any]{ + Name: "authorization-controls.maximum-authorization-count", + Usage: "Limits the number of authorizations that can be approved on this card.", + InnerField: "maximum_authorization_count", + }, + &requestflag.InnerFlag[map[string]any]{ + Name: "authorization-controls.merchant-acceptor-identifier", + Usage: "Restricts which Merchant Acceptor IDs are allowed or blocked for authorizations on this card.", + InnerField: "merchant_acceptor_identifier", + }, + &requestflag.InnerFlag[map[string]any]{ + Name: "authorization-controls.merchant-category-code", + Usage: "Restricts which Merchant Category Codes are allowed or blocked for authorizations on this card.", + InnerField: "merchant_category_code", + }, + &requestflag.InnerFlag[map[string]any]{ + Name: "authorization-controls.merchant-country", + Usage: "Restricts which merchant countries are allowed or blocked for authorizations on this card.", + InnerField: "merchant_country", + }, + &requestflag.InnerFlag[[]map[string]any]{ + Name: "authorization-controls.spending-limits", + Usage: "Spending limits for this card. The most restrictive limit is applied if multiple limits match.", + InnerField: "spending_limits", + }, + }, "billing-address": { &requestflag.InnerFlag[string]{ Name: "billing-address.city", @@ -121,6 +153,11 @@ var cardsUpdate = requestflag.WithInnerFlags(cli.Command{ Usage: "The card identifier.", Required: true, }, + &requestflag.Flag[map[string]any]{ + Name: "authorization-controls", + Usage: "Controls that restrict how this card can be used.", + BodyPath: "authorization_controls", + }, &requestflag.Flag[map[string]any]{ Name: "billing-address", Usage: "The card's updated billing address.", @@ -150,6 +187,33 @@ var cardsUpdate = requestflag.WithInnerFlags(cli.Command{ Action: handleCardsUpdate, HideHelpCommand: true, }, map[string][]requestflag.HasOuterFlag{ + "authorization-controls": { + &requestflag.InnerFlag[map[string]any]{ + Name: "authorization-controls.maximum-authorization-count", + Usage: "Limits the number of authorizations that can be approved on this card.", + InnerField: "maximum_authorization_count", + }, + &requestflag.InnerFlag[map[string]any]{ + Name: "authorization-controls.merchant-acceptor-identifier", + Usage: "Restricts which Merchant Acceptor IDs are allowed or blocked for authorizations on this card.", + InnerField: "merchant_acceptor_identifier", + }, + &requestflag.InnerFlag[map[string]any]{ + Name: "authorization-controls.merchant-category-code", + Usage: "Restricts which Merchant Category Codes are allowed or blocked for authorizations on this card.", + InnerField: "merchant_category_code", + }, + &requestflag.InnerFlag[map[string]any]{ + Name: "authorization-controls.merchant-country", + Usage: "Restricts which merchant countries are allowed or blocked for authorizations on this card.", + InnerField: "merchant_country", + }, + &requestflag.InnerFlag[[]map[string]any]{ + Name: "authorization-controls.spending-limits", + Usage: "Spending limits for this card. The most restrictive limit is applied if multiple limits match.", + InnerField: "spending_limits", + }, + }, "billing-address": { &requestflag.InnerFlag[string]{ Name: "billing-address.city", diff --git a/pkg/cmd/card_test.go b/pkg/cmd/card_test.go index 354e929..f6f1a3f 100644 --- a/pkg/cmd/card_test.go +++ b/pkg/cmd/card_test.go @@ -16,6 +16,7 @@ func TestCardsCreate(t *testing.T) { "--api-key", "string", "cards", "create", "--account-id", "account_in71c4amph0vgo2qllky", + "--authorization-controls", "{maximum_authorization_count: {all_time: 0}, merchant_acceptor_identifier: {allowed: [{identifier: x}], blocked: [{identifier: x}]}, merchant_category_code: {allowed: [{code: xxxx}], blocked: [{code: xxxx}]}, merchant_country: {allowed: [{country: xx}], blocked: [{country: xx}]}, spending_limits: [{interval: all_time, settlement_amount: 0, merchant_category_codes: [{code: x}]}]}", "--billing-address", "{city: x, line1: x, postal_code: x, state: x, line2: x}", "--description", "Card for Ian Crease", "--digital-wallet", "{digital_card_profile_id: digital_card_profile_id, email: dev@stainless.com, phone: x}", @@ -33,6 +34,11 @@ func TestCardsCreate(t *testing.T) { "--api-key", "string", "cards", "create", "--account-id", "account_in71c4amph0vgo2qllky", + "--authorization-controls.maximum-authorization-count", "{all_time: 0}", + "--authorization-controls.merchant-acceptor-identifier", "{allowed: [{identifier: x}], blocked: [{identifier: x}]}", + "--authorization-controls.merchant-category-code", "{allowed: [{code: xxxx}], blocked: [{code: xxxx}]}", + "--authorization-controls.merchant-country", "{allowed: [{country: xx}], blocked: [{country: xx}]}", + "--authorization-controls.spending-limits", "[{interval: all_time, settlement_amount: 0, merchant_category_codes: [{code: x}]}]", "--billing-address.city", "x", "--billing-address.line1", "x", "--billing-address.postal-code", "x", @@ -50,6 +56,29 @@ func TestCardsCreate(t *testing.T) { // Test piping YAML data over stdin pipeData := []byte("" + "account_id: account_in71c4amph0vgo2qllky\n" + + "authorization_controls:\n" + + " maximum_authorization_count:\n" + + " all_time: 0\n" + + " merchant_acceptor_identifier:\n" + + " allowed:\n" + + " - identifier: x\n" + + " blocked:\n" + + " - identifier: x\n" + + " merchant_category_code:\n" + + " allowed:\n" + + " - code: xxxx\n" + + " blocked:\n" + + " - code: xxxx\n" + + " merchant_country:\n" + + " allowed:\n" + + " - country: xx\n" + + " blocked:\n" + + " - country: xx\n" + + " spending_limits:\n" + + " - interval: all_time\n" + + " settlement_amount: 0\n" + + " merchant_category_codes:\n" + + " - code: x\n" + "billing_address:\n" + " city: x\n" + " line1: x\n" + @@ -88,6 +117,7 @@ func TestCardsUpdate(t *testing.T) { "--api-key", "string", "cards", "update", "--card-id", "card_oubs0hwk5rn6knuecxg2", + "--authorization-controls", "{maximum_authorization_count: {all_time: 0}, merchant_acceptor_identifier: {allowed: [{identifier: x}], blocked: [{identifier: x}]}, merchant_category_code: {allowed: [{code: xxxx}], blocked: [{code: xxxx}]}, merchant_country: {allowed: [{country: xx}], blocked: [{country: xx}]}, spending_limits: [{interval: all_time, settlement_amount: 0, merchant_category_codes: [{code: x}]}]}", "--billing-address", "{city: x, line1: x, postal_code: x, state: x, line2: x}", "--description", "New description", "--digital-wallet", "{digital_card_profile_id: digital_card_profile_id, email: dev@stainless.com, phone: x}", @@ -106,6 +136,11 @@ func TestCardsUpdate(t *testing.T) { "--api-key", "string", "cards", "update", "--card-id", "card_oubs0hwk5rn6knuecxg2", + "--authorization-controls.maximum-authorization-count", "{all_time: 0}", + "--authorization-controls.merchant-acceptor-identifier", "{allowed: [{identifier: x}], blocked: [{identifier: x}]}", + "--authorization-controls.merchant-category-code", "{allowed: [{code: xxxx}], blocked: [{code: xxxx}]}", + "--authorization-controls.merchant-country", "{allowed: [{country: xx}], blocked: [{country: xx}]}", + "--authorization-controls.spending-limits", "[{interval: all_time, settlement_amount: 0, merchant_category_codes: [{code: x}]}]", "--billing-address.city", "x", "--billing-address.line1", "x", "--billing-address.postal-code", "x", @@ -123,6 +158,29 @@ func TestCardsUpdate(t *testing.T) { t.Run("piping data", func(t *testing.T) { // Test piping YAML data over stdin pipeData := []byte("" + + "authorization_controls:\n" + + " maximum_authorization_count:\n" + + " all_time: 0\n" + + " merchant_acceptor_identifier:\n" + + " allowed:\n" + + " - identifier: x\n" + + " blocked:\n" + + " - identifier: x\n" + + " merchant_category_code:\n" + + " allowed:\n" + + " - code: xxxx\n" + + " blocked:\n" + + " - code: xxxx\n" + + " merchant_country:\n" + + " allowed:\n" + + " - country: xx\n" + + " blocked:\n" + + " - country: xx\n" + + " spending_limits:\n" + + " - interval: all_time\n" + + " settlement_amount: 0\n" + + " merchant_category_codes:\n" + + " - code: x\n" + "billing_address:\n" + " city: x\n" + " line1: x\n" + From 6076806e52396ce87d6245f07eb8c52d5035ece5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 3 Apr 2026 01:22:31 +0000 Subject: [PATCH 21/23] 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 c830b87..a4f6455 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/Increase/increase-cli go 1.25 require ( - github.com/Increase/increase-go v0.530.0 + github.com/Increase/increase-go v0.531.0 github.com/charmbracelet/bubbles v0.21.0 github.com/charmbracelet/bubbletea v1.3.6 github.com/charmbracelet/lipgloss v1.1.0 diff --git a/go.sum b/go.sum index 8ada46a..260a762 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Increase/increase-go v0.530.0 h1:+//+kfAX1kIneepn07Mt/bc2Ao70V08cJGY2Gd9/plY= -github.com/Increase/increase-go v0.530.0/go.mod h1:GOsK6Rh57nWpcfykcdHSIccIQJc7Fq0qitViNGbS4RM= +github.com/Increase/increase-go v0.531.0 h1:7DFDBbxXOUkx+SuP+beny2bSEJT3pdACeEEXZa/Mfc8= +github.com/Increase/increase-go v0.531.0/go.mod h1:GOsK6Rh57nWpcfykcdHSIccIQJc7Fq0qitViNGbS4RM= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8= From a030582e292dfd5dbf1dec8cdb5dcda445008729 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 3 Apr 2026 01:55:58 +0000 Subject: [PATCH 22/23] chore: mark all CLI-related tests in Go with `t.Parallel()` --- internal/apiform/form_test.go | 4 ++ internal/apiquery/query_test.go | 4 ++ internal/autocomplete/autocomplete_test.go | 40 +++++++++++++++ internal/jsonview/explorer_test.go | 6 +++ internal/requestflag/innerflag_test.go | 28 +++++++++++ internal/requestflag/requestflag_test.go | 58 ++++++++++++++++++++++ pkg/cmd/flagoptions_test.go | 12 +++++ 7 files changed, 152 insertions(+) diff --git a/internal/apiform/form_test.go b/internal/apiform/form_test.go index 2cf5bdd..f68cfd1 100644 --- a/internal/apiform/form_test.go +++ b/internal/apiform/form_test.go @@ -85,8 +85,12 @@ var tests = map[string]struct { } func TestEncode(t *testing.T) { + t.Parallel() + for name, test := range tests { t.Run(name, func(t *testing.T) { + t.Parallel() + buf := bytes.NewBuffer(nil) writer := multipart.NewWriter(buf) writer.SetBoundary("xxx") diff --git a/internal/apiquery/query_test.go b/internal/apiquery/query_test.go index 8bee784..3791ec9 100644 --- a/internal/apiquery/query_test.go +++ b/internal/apiquery/query_test.go @@ -6,6 +6,8 @@ import ( ) func TestEncode(t *testing.T) { + t.Parallel() + tests := map[string]struct { val any settings QuerySettings @@ -114,6 +116,8 @@ func TestEncode(t *testing.T) { for name, test := range tests { t.Run(name, func(t *testing.T) { + t.Parallel() + query := map[string]any{"query": test.val} values, err := MarshalWithSettings(query, test.settings) if err != nil { diff --git a/internal/autocomplete/autocomplete_test.go b/internal/autocomplete/autocomplete_test.go index 3e8aa33..2338924 100644 --- a/internal/autocomplete/autocomplete_test.go +++ b/internal/autocomplete/autocomplete_test.go @@ -8,6 +8,8 @@ import ( ) func TestGetCompletions_EmptyArgs(t *testing.T) { + t.Parallel() + root := &cli.Command{ Commands: []*cli.Command{ {Name: "generate", Usage: "Generate SDK"}, @@ -26,6 +28,8 @@ func TestGetCompletions_EmptyArgs(t *testing.T) { } func TestGetCompletions_SubcommandPrefix(t *testing.T) { + t.Parallel() + root := &cli.Command{ Commands: []*cli.Command{ {Name: "generate", Usage: "Generate SDK"}, @@ -43,6 +47,8 @@ func TestGetCompletions_SubcommandPrefix(t *testing.T) { } func TestGetCompletions_HiddenCommand(t *testing.T) { + t.Parallel() + root := &cli.Command{ Commands: []*cli.Command{ {Name: "visible", Usage: "Visible command"}, @@ -57,6 +63,8 @@ func TestGetCompletions_HiddenCommand(t *testing.T) { } func TestGetCompletions_NestedSubcommand(t *testing.T) { + t.Parallel() + root := &cli.Command{ Commands: []*cli.Command{ { @@ -79,6 +87,8 @@ func TestGetCompletions_NestedSubcommand(t *testing.T) { } func TestGetCompletions_FlagCompletion(t *testing.T) { + t.Parallel() + root := &cli.Command{ Commands: []*cli.Command{ { @@ -102,6 +112,8 @@ func TestGetCompletions_FlagCompletion(t *testing.T) { } func TestGetCompletions_ShortFlagCompletion(t *testing.T) { + t.Parallel() + root := &cli.Command{ Commands: []*cli.Command{ { @@ -123,6 +135,8 @@ func TestGetCompletions_ShortFlagCompletion(t *testing.T) { } func TestGetCompletions_FileFlagBehavior(t *testing.T) { + t.Parallel() + root := &cli.Command{ Commands: []*cli.Command{ { @@ -142,6 +156,8 @@ func TestGetCompletions_FileFlagBehavior(t *testing.T) { } func TestGetCompletions_NonBoolFlagValue(t *testing.T) { + t.Parallel() + root := &cli.Command{ Commands: []*cli.Command{ { @@ -161,6 +177,8 @@ func TestGetCompletions_NonBoolFlagValue(t *testing.T) { } func TestGetCompletions_BoolFlagDoesNotBlockCompletion(t *testing.T) { + t.Parallel() + root := &cli.Command{ Commands: []*cli.Command{ { @@ -185,6 +203,8 @@ func TestGetCompletions_BoolFlagDoesNotBlockCompletion(t *testing.T) { } func TestGetCompletions_ColonCommands_NoColonTyped(t *testing.T) { + t.Parallel() + root := &cli.Command{ Commands: []*cli.Command{ {Name: "config:get", Usage: "Get config value"}, @@ -202,6 +222,8 @@ func TestGetCompletions_ColonCommands_NoColonTyped(t *testing.T) { } func TestGetCompletions_ColonCommands_ColonTyped_Bash(t *testing.T) { + t.Parallel() + root := &cli.Command{ Commands: []*cli.Command{ {Name: "config:get", Usage: "Get config value"}, @@ -221,6 +243,8 @@ func TestGetCompletions_ColonCommands_ColonTyped_Bash(t *testing.T) { } func TestGetCompletions_ColonCommands_ColonTyped_Zsh(t *testing.T) { + t.Parallel() + root := &cli.Command{ Commands: []*cli.Command{ {Name: "config:get", Usage: "Get config value"}, @@ -240,6 +264,8 @@ func TestGetCompletions_ColonCommands_ColonTyped_Zsh(t *testing.T) { } func TestGetCompletions_BashStyleColonCompletion(t *testing.T) { + t.Parallel() + root := &cli.Command{ Commands: []*cli.Command{ {Name: "config:get", Usage: "Get config value"}, @@ -257,6 +283,8 @@ func TestGetCompletions_BashStyleColonCompletion(t *testing.T) { } func TestGetCompletions_BashStyleColonCompletion_NoMatch(t *testing.T) { + t.Parallel() + root := &cli.Command{ Commands: []*cli.Command{ {Name: "config:get", Usage: "Get config value"}, @@ -271,6 +299,8 @@ func TestGetCompletions_BashStyleColonCompletion_NoMatch(t *testing.T) { } func TestGetCompletions_ZshStyleColonCompletion(t *testing.T) { + t.Parallel() + root := &cli.Command{ Commands: []*cli.Command{ {Name: "config:get", Usage: "Get config value"}, @@ -287,6 +317,8 @@ func TestGetCompletions_ZshStyleColonCompletion(t *testing.T) { } func TestGetCompletions_MixedColonAndRegularCommands(t *testing.T) { + t.Parallel() + root := &cli.Command{ Commands: []*cli.Command{ {Name: "generate", Usage: "Generate SDK"}, @@ -305,6 +337,8 @@ func TestGetCompletions_MixedColonAndRegularCommands(t *testing.T) { } func TestGetCompletions_FlagWithBoolFlagSkipsValue(t *testing.T) { + t.Parallel() + root := &cli.Command{ Commands: []*cli.Command{ { @@ -329,6 +363,8 @@ func TestGetCompletions_FlagWithBoolFlagSkipsValue(t *testing.T) { } func TestGetCompletions_MultipleFlagsBeforeSubcommand(t *testing.T) { + t.Parallel() + root := &cli.Command{ Commands: []*cli.Command{ { @@ -353,6 +389,8 @@ func TestGetCompletions_MultipleFlagsBeforeSubcommand(t *testing.T) { } func TestGetCompletions_CommandAliases(t *testing.T) { + t.Parallel() + root := &cli.Command{ Commands: []*cli.Command{ {Name: "generate", Aliases: []string{"gen", "g"}, Usage: "Generate SDK"}, @@ -372,6 +410,8 @@ func TestGetCompletions_CommandAliases(t *testing.T) { } func TestGetCompletions_AllFlagsWhenNoPrefix(t *testing.T) { + t.Parallel() + root := &cli.Command{ Commands: []*cli.Command{ { diff --git a/internal/jsonview/explorer_test.go b/internal/jsonview/explorer_test.go index c559254..67ee730 100644 --- a/internal/jsonview/explorer_test.go +++ b/internal/jsonview/explorer_test.go @@ -10,6 +10,8 @@ import ( ) func TestNavigateForward_EmptyRowData(t *testing.T) { + t.Parallel() + // An empty JSON array produces a TableView with no rows. emptyArray := gjson.Parse("[]") view, err := newTableView("", emptyArray, false) @@ -38,6 +40,8 @@ type rawJSONItem struct { func (r rawJSONItem) RawJSON() string { return r.raw } func TestMarshalItemsToJSONArray_WithHasRawJSON(t *testing.T) { + t.Parallel() + items := []any{ rawJSONItem{raw: `{"id":1,"name":"alice"}`}, rawJSONItem{raw: `{"id":2,"name":"bob"}`}, @@ -49,6 +53,8 @@ func TestMarshalItemsToJSONArray_WithHasRawJSON(t *testing.T) { } func TestMarshalItemsToJSONArray_WithoutHasRawJSON(t *testing.T) { + t.Parallel() + items := []any{ map[string]any{"id": 1, "name": "alice"}, map[string]any{"id": 2, "name": "bob"}, diff --git a/internal/requestflag/innerflag_test.go b/internal/requestflag/innerflag_test.go index 3f204c9..133e8b4 100644 --- a/internal/requestflag/innerflag_test.go +++ b/internal/requestflag/innerflag_test.go @@ -8,6 +8,8 @@ import ( ) func TestInnerFlagSet(t *testing.T) { + t.Parallel() + tests := []struct { name string flagType string @@ -27,6 +29,8 @@ func TestInnerFlagSet(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() + outerFlag := &Flag[map[string]any]{ Name: "test-flag", } @@ -81,6 +85,8 @@ func TestInnerFlagSet(t *testing.T) { } func TestInnerFlagValidator(t *testing.T) { + t.Parallel() + outerFlag := &Flag[map[string]any]{Name: "test-flag"} innerFlag := &InnerFlag[int64]{ @@ -105,6 +111,8 @@ func TestInnerFlagValidator(t *testing.T) { } func TestWithInnerFlags(t *testing.T) { + t.Parallel() + outerFlag := &Flag[map[string]any]{Name: "outer"} innerFlag := &InnerFlag[string]{ Name: "outer.baz", @@ -126,6 +134,8 @@ func TestWithInnerFlags(t *testing.T) { } func TestInnerFlagTypeNames(t *testing.T) { + t.Parallel() + tests := []struct { name string flag cli.DocGenerationFlag @@ -143,6 +153,8 @@ func TestInnerFlagTypeNames(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() + typeName := tt.flag.TypeName() assert.Equal(t, tt.expected, typeName, "Expected type name %q, got %q", tt.expected, typeName) }) @@ -150,8 +162,12 @@ func TestInnerFlagTypeNames(t *testing.T) { } func TestInnerYamlHandling(t *testing.T) { + t.Parallel() + // Test with map value t.Run("Parse YAML to map", func(t *testing.T) { + t.Parallel() + outerFlag := &Flag[map[string]any]{Name: "outer"} innerFlag := &InnerFlag[map[string]any]{ Name: "outer.baz", @@ -176,6 +192,8 @@ func TestInnerYamlHandling(t *testing.T) { // Test with invalid YAML t.Run("Parse invalid YAML", func(t *testing.T) { + t.Parallel() + outerFlag := &Flag[map[string]any]{Name: "outer"} innerFlag := &InnerFlag[map[string]any]{ Name: "outer.baz", @@ -190,6 +208,8 @@ func TestInnerYamlHandling(t *testing.T) { // Test setting inner flags on a map multiple times t.Run("Set inner flags on map multiple times", func(t *testing.T) { + t.Parallel() + outerFlag := &Flag[map[string]any]{Name: "outer"} // Set first inner flag @@ -219,6 +239,8 @@ func TestInnerYamlHandling(t *testing.T) { // Test setting YAML and then an inner flag t.Run("Set YAML and then inner flag", func(t *testing.T) { + t.Parallel() + outerFlag := &Flag[map[string]any]{Name: "outer"} // First set the outer flag with YAML @@ -246,7 +268,11 @@ func TestInnerYamlHandling(t *testing.T) { } func TestInnerFlagWithSliceType(t *testing.T) { + t.Parallel() + t.Run("Setting inner flags on slice of maps", func(t *testing.T) { + t.Parallel() + outerFlag := &Flag[[]map[string]any]{Name: "outer"} // Set first inner flag (should create first item) @@ -284,6 +310,8 @@ func TestInnerFlagWithSliceType(t *testing.T) { }) t.Run("Appending to existing slice", func(t *testing.T) { + t.Parallel() + // Initialize with existing items outerFlag := &Flag[[]map[string]any]{Name: "outer"} err := outerFlag.Set(outerFlag.Name, `{name: initial}`) diff --git a/internal/requestflag/requestflag_test.go b/internal/requestflag/requestflag_test.go index 9751904..0e86e07 100644 --- a/internal/requestflag/requestflag_test.go +++ b/internal/requestflag/requestflag_test.go @@ -11,6 +11,8 @@ import ( ) func TestDateValueParse(t *testing.T) { + t.Parallel() + tests := []struct { name string input string @@ -56,6 +58,8 @@ func TestDateValueParse(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() + var d DateValue err := d.Parse(tt.input) @@ -70,6 +74,8 @@ func TestDateValueParse(t *testing.T) { } func TestDateTimeValueParse(t *testing.T) { + t.Parallel() + tests := []struct { name string input string @@ -119,6 +125,8 @@ func TestDateTimeValueParse(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() + var d DateTimeValue err := d.Parse(tt.input) @@ -136,6 +144,8 @@ func TestDateTimeValueParse(t *testing.T) { } func TestTimeValueParse(t *testing.T) { + t.Parallel() + tests := []struct { name string input string @@ -181,6 +191,8 @@ func TestTimeValueParse(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() + var tv TimeValue err := tv.Parse(tt.input) @@ -195,7 +207,11 @@ func TestTimeValueParse(t *testing.T) { } func TestRequestParams(t *testing.T) { + t.Parallel() + t.Run("map body type", func(t *testing.T) { + t.Parallel() + // Create a mock command with flags cmd := &cli.Command{ Name: "test", @@ -283,6 +299,8 @@ func TestRequestParams(t *testing.T) { }) t.Run("non-map body type", func(t *testing.T) { + t.Parallel() + // Create a mock command with flags cmd := &cli.Command{ Name: "test", @@ -304,6 +322,8 @@ func TestRequestParams(t *testing.T) { } func TestFlagSet(t *testing.T) { + t.Parallel() + strFlag := &Flag[string]{ Name: "string-flag", Default: "default-string", @@ -327,38 +347,52 @@ func TestFlagSet(t *testing.T) { // Test initialization and setting t.Run("PreParse initialization", func(t *testing.T) { + t.Parallel() + assert.NoError(t, strFlag.PreParse()) assert.True(t, strFlag.applied) assert.Equal(t, "default-string", strFlag.Get()) }) t.Run("Set string flag", func(t *testing.T) { + t.Parallel() + assert.NoError(t, strFlag.Set("string-flag", "new-value")) assert.Equal(t, "new-value", strFlag.Get()) assert.True(t, strFlag.IsSet()) }) t.Run("Set int flag with valid value", func(t *testing.T) { + t.Parallel() + assert.NoError(t, superstitiousIntFlag.Set("int-flag", "100")) assert.Equal(t, int64(100), superstitiousIntFlag.Get()) assert.True(t, superstitiousIntFlag.IsSet()) }) t.Run("Set int flag with invalid value", func(t *testing.T) { + t.Parallel() + assert.Error(t, superstitiousIntFlag.Set("int-flag", "not-an-int")) }) t.Run("Set int flag with validator failing", func(t *testing.T) { + t.Parallel() + assert.Error(t, superstitiousIntFlag.Set("int-flag", "13")) }) t.Run("Set bool flag", func(t *testing.T) { + t.Parallel() + assert.NoError(t, boolFlag.Set("bool-flag", "true")) assert.Equal(t, true, boolFlag.Get()) assert.True(t, boolFlag.IsSet()) }) t.Run("Set slice flag with multiple values", func(t *testing.T) { + t.Parallel() + sliceFlag := &Flag[[]int64]{ Name: "slice-flag", Default: []int64{}, @@ -381,6 +415,8 @@ func TestFlagSet(t *testing.T) { }) t.Run("Set slice flag with a nonempty default", func(t *testing.T) { + t.Parallel() + sliceFlag := &Flag[[]int64]{ Name: "slice-flag", Default: []int64{99, 100}, @@ -400,6 +436,8 @@ func TestFlagSet(t *testing.T) { } func TestParseTimeWithFormats(t *testing.T) { + t.Parallel() + tests := []struct { name string input string @@ -439,6 +477,8 @@ func TestParseTimeWithFormats(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() + got, err := parseTimeWithFormats(tt.input, tt.formats) if tt.wantErr { @@ -452,8 +492,12 @@ func TestParseTimeWithFormats(t *testing.T) { } func TestYamlHandling(t *testing.T) { + t.Parallel() + // Test with any value t.Run("Parse YAML to any", func(t *testing.T) { + t.Parallel() + cv := &cliValue[any]{} err := cv.Set("name: test\nvalue: 42\n") assert.NoError(t, err) @@ -478,6 +522,8 @@ func TestYamlHandling(t *testing.T) { // Test with array t.Run("Parse YAML array", func(t *testing.T) { + t.Parallel() + cv := &cliValue[any]{} err := cv.Set("- item1\n- item2\n- item3\n") assert.NoError(t, err) @@ -495,6 +541,8 @@ func TestYamlHandling(t *testing.T) { }) t.Run("Parse @file.txt as YAML", func(t *testing.T) { + t.Parallel() + flag := &Flag[any]{ Name: "file-flag", Default: nil, @@ -507,6 +555,8 @@ func TestYamlHandling(t *testing.T) { }) t.Run("Parse @file.txt list as YAML", func(t *testing.T) { + t.Parallel() + flag := &Flag[[]any]{ Name: "file-flag", Default: nil, @@ -520,6 +570,8 @@ func TestYamlHandling(t *testing.T) { }) t.Run("Parse identifiers as YAML", func(t *testing.T) { + t.Parallel() + tests := []string{ "hello", "e4e355fa-b03b-4c57-a73d-25c9733eec79", @@ -555,6 +607,8 @@ func TestYamlHandling(t *testing.T) { // Test with invalid YAML t.Run("Parse invalid YAML", func(t *testing.T) { + t.Parallel() + invalidYaml := `[not closed` cv := &cliValue[any]{} err := cv.Set(invalidYaml) @@ -563,6 +617,8 @@ func TestYamlHandling(t *testing.T) { } func TestFlagTypeNames(t *testing.T) { + t.Parallel() + tests := []struct { name string flag cli.DocGenerationFlag @@ -583,6 +639,8 @@ func TestFlagTypeNames(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() + typeName := tt.flag.TypeName() assert.Equal(t, tt.expected, typeName, "Expected type name %q, got %q", tt.expected, typeName) }) diff --git a/pkg/cmd/flagoptions_test.go b/pkg/cmd/flagoptions_test.go index 9a7fe3b..039b9ff 100644 --- a/pkg/cmd/flagoptions_test.go +++ b/pkg/cmd/flagoptions_test.go @@ -13,6 +13,7 @@ import ( ) func TestIsUTF8TextFile(t *testing.T) { + t.Parallel() tests := []struct { content []byte @@ -35,6 +36,7 @@ func TestIsUTF8TextFile(t *testing.T) { } func TestEmbedFiles(t *testing.T) { + t.Parallel() // Create temporary directory for test files tmpDir := t.TempDir() @@ -220,6 +222,7 @@ func TestEmbedFiles(t *testing.T) { for _, tt := range tests { t.Run(tt.name+" text", func(t *testing.T) { + t.Parallel() got, err := embedFiles(tt.input, EmbedText, nil) if tt.wantErr { @@ -231,6 +234,7 @@ func TestEmbedFiles(t *testing.T) { }) t.Run(tt.name+" io.Reader", func(t *testing.T) { + t.Parallel() _, err := embedFiles(tt.input, EmbedIOReader, nil) if tt.wantErr { @@ -243,8 +247,10 @@ func TestEmbedFiles(t *testing.T) { } func TestEmbedFilesStdin(t *testing.T) { + t.Parallel() t.Run("FilePathValueDash", func(t *testing.T) { + t.Parallel() stdin := &onceStdinReader{stdinReader: strings.NewReader("stdin content")} @@ -254,6 +260,7 @@ func TestEmbedFilesStdin(t *testing.T) { }) t.Run("FilePathValueDevStdin", func(t *testing.T) { + t.Parallel() stdin := &onceStdinReader{stdinReader: strings.NewReader("stdin content")} @@ -263,6 +270,7 @@ func TestEmbedFilesStdin(t *testing.T) { }) t.Run("MultipleFilePathValueDashesError", func(t *testing.T) { + t.Parallel() stdin := &onceStdinReader{stdinReader: strings.NewReader("stdin content")} @@ -275,6 +283,7 @@ func TestEmbedFilesStdin(t *testing.T) { }) t.Run("FilePathValueDashUnavailableStdin", func(t *testing.T) { + t.Parallel() stdin := &onceStdinReader{failureReason: "stdin is already being used for the request body"} @@ -285,6 +294,7 @@ func TestEmbedFilesStdin(t *testing.T) { }) t.Run("AtDashEmbedText", func(t *testing.T) { + t.Parallel() stdin := &onceStdinReader{stdinReader: strings.NewReader("piped content")} @@ -294,6 +304,7 @@ func TestEmbedFilesStdin(t *testing.T) { }) t.Run("AtDashEmbedIOReader", func(t *testing.T) { + t.Parallel() stdin := &onceStdinReader{stdinReader: strings.NewReader("piped content")} @@ -309,6 +320,7 @@ func TestEmbedFilesStdin(t *testing.T) { }) t.Run("FilePathValueRealFile", func(t *testing.T) { + t.Parallel() tmpDir := t.TempDir() writeTestFile(t, tmpDir, "test.txt", "file content") From 246c1221b4301713c3ab778680a4712b18413668 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 3 Apr 2026 01:56:14 +0000 Subject: [PATCH 23/23] release: 0.1.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 35 +++++++++++++++++++++++++++++++++++ pkg/cmd/version.go | 2 +- 3 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 CHANGELOG.md diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 1332969..3d2ac0b 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.0.1" + ".": "0.1.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..1b28ccb --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,35 @@ +# Changelog + +## 0.1.0 (2026-04-03) + +Full Changelog: [v0.0.1...v0.1.0](https://github.com/Increase/increase-cli/compare/v0.0.1...v0.1.0) + +### Features + +* allow `-` as value representing stdin to binary-only file parameters in CLIs ([3fb3ad9](https://github.com/Increase/increase-cli/commit/3fb3ad900950561ac7a6a45f50be97a680af0962)) +* **api:** api update ([6076806](https://github.com/Increase/increase-cli/commit/6076806e52396ce87d6245f07eb8c52d5035ece5)) +* **api:** api update ([28d2401](https://github.com/Increase/increase-cli/commit/28d2401e462235b81e59f0f21a241b9485b2c850)) +* **api:** api update ([2ca37dc](https://github.com/Increase/increase-cli/commit/2ca37dccd872b339cc77201cf1e7b349dd0efd19)) +* **api:** api update ([a75f6ba](https://github.com/Increase/increase-cli/commit/a75f6ba67337b21db18d53f3726b3be54047b6a5)) +* **api:** api update ([b802215](https://github.com/Increase/increase-cli/commit/b802215033456369742d20124d3077a12e61bc8d)) +* **api:** api update ([44a8c16](https://github.com/Increase/increase-cli/commit/44a8c166b0bc930afa2eba1e1244b468696bd5f6)) +* **api:** api update ([b9c0732](https://github.com/Increase/increase-cli/commit/b9c073243aa91a766a7db4a604401f52f4431c38)) +* better error message if scheme forgotten in CLI `*_BASE_URL`/`--base-url` ([a6ec976](https://github.com/Increase/increase-cli/commit/a6ec976ce1a9846354377f719e6926493f52f63e)) +* binary-only parameters become CLI flags that take filenames only ([cc21dff](https://github.com/Increase/increase-cli/commit/cc21dffca52f9ff543a3a38555742a7ed3cd926a)) + + +### Bug Fixes + +* fix for off-by-one error in pagination logic ([d5b8428](https://github.com/Increase/increase-cli/commit/d5b842883d1201fef0db3f2adb768fddbf53c27f)) +* handle empty data set using `--format explore` ([ff538ca](https://github.com/Increase/increase-cli/commit/ff538ca7d15fd6f362c169b2f89c3dc9e25a64bc)) +* use `RawJSON` when iterating items with `--format explore` in the CLI ([67340d0](https://github.com/Increase/increase-cli/commit/67340d0d26af534f125883c29686fbc5f48470ad)) + + +### Chores + +* **internal:** codegen related update ([49ec4a1](https://github.com/Increase/increase-cli/commit/49ec4a16ff861bd0fa104c75a4d63bf40c506847)) +* **internal:** codegen related update ([ec2fc1e](https://github.com/Increase/increase-cli/commit/ec2fc1e9a4e0510f30b06632e5f0ce3d6e1ef284)) +* mark all CLI-related tests in Go with `t.Parallel()` ([a030582](https://github.com/Increase/increase-cli/commit/a030582e292dfd5dbf1dec8cdb5dcda445008729)) +* switch some CLI Go tests from `os.Chdir` to `t.Chdir` ([dc10b4d](https://github.com/Increase/increase-cli/commit/dc10b4d70b8a31fe785d8a6123136f666f06e3ad)) +* **tests:** bump steady to v0.20.1 ([9445bc1](https://github.com/Increase/increase-cli/commit/9445bc1d6dc5c611e1aa90d126e4340c3b62de45)) +* **tests:** bump steady to v0.20.2 ([5a976f1](https://github.com/Increase/increase-cli/commit/5a976f142db86f917109f88b06bfd2fc7f0c6e1a)) diff --git a/pkg/cmd/version.go b/pkg/cmd/version.go index 1f71453..9bb8168 100644 --- a/pkg/cmd/version.go +++ b/pkg/cmd/version.go @@ -2,4 +2,4 @@ package cmd -const Version = "0.0.1" // x-release-please-version +const Version = "0.1.0" // x-release-please-version