Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
0875d6c
feat: binary-only parameters become CLI flags that take filenames only
stainless-app[bot] Apr 2, 2026
5edec04
feat: better error message if scheme forgotten in CLI `*_BASE_URL`/`-…
stainless-app[bot] Apr 2, 2026
7da15e2
feat: allow `-` as value representing stdin to binary-only file param…
stainless-app[bot] Apr 2, 2026
ba52bbe
chore: switch some CLI Go tests from `os.Chdir` to `t.Chdir`
stainless-app[bot] Apr 2, 2026
7d1a565
chore: mark all CLI-related tests in Go with `t.Parallel()`
stainless-app[bot] Apr 3, 2026
6073189
chore: modify CLI tests to inject stdout so mutating `os.Stdout` isn'…
stainless-app[bot] Apr 3, 2026
87613ec
fix: fall back to main branch if linking fails in CI
stainless-app[bot] Apr 6, 2026
575418c
fix: fix quoting typo
stainless-app[bot] Apr 6, 2026
c913d27
chore(cli): let `--format raw` be used in conjunction with `--transform`
stainless-app[bot] Apr 8, 2026
c64d07c
chore(cli): additional test cases for `ShowJSONIterator`
stainless-app[bot] Apr 9, 2026
547c3e1
fix: fix for failing to drop invalid module replace in link script
stainless-app[bot] Apr 9, 2026
6d33e1a
chore: add documentation for ./scripts/link
stainless-app[bot] Apr 13, 2026
3b62e8d
chore(cli): fall back to JSON when using default "explore" with non-TTY
stainless-app[bot] Apr 14, 2026
2a1a9d8
chore(ci): remove release-doctor workflow
stainless-app[bot] Apr 14, 2026
5a62637
feat(cli): alias parameters in data with `x-stainless-cli-data-alias`
stainless-app[bot] Apr 14, 2026
804d5ec
release: 0.2.0
stainless-app[bot] Apr 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 0 additions & 20 deletions .github/workflows/release-doctor.yml

This file was deleted.

2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "0.1.0"
".": "0.2.0"
}
30 changes: 30 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,35 @@
# Changelog

## 0.2.0 (2026-04-14)

Full Changelog: [v0.1.0...v0.2.0](https://github.com/dedalus-labs/dedalus-cli/compare/v0.1.0...v0.2.0)

### Features

* allow `-` as value representing stdin to binary-only file parameters in CLIs ([7da15e2](https://github.com/dedalus-labs/dedalus-cli/commit/7da15e2f6c1d715fc55e26fa2bd7be95eba5ff0e))
* better error message if scheme forgotten in CLI `*_BASE_URL`/`--base-url` ([5edec04](https://github.com/dedalus-labs/dedalus-cli/commit/5edec044b3dda53824fa2981ffe94b557fc3ce65))
* binary-only parameters become CLI flags that take filenames only ([0875d6c](https://github.com/dedalus-labs/dedalus-cli/commit/0875d6c6e2426509185f56022f763b2927b07af2))
* **cli:** alias parameters in data with `x-stainless-cli-data-alias` ([5a62637](https://github.com/dedalus-labs/dedalus-cli/commit/5a6263723604bb1a35fbc53c5f99e7fd7fd0463a))


### Bug Fixes

* fall back to main branch if linking fails in CI ([87613ec](https://github.com/dedalus-labs/dedalus-cli/commit/87613ecfe5030a1a76be6dcfdb29161ff58a2550))
* fix for failing to drop invalid module replace in link script ([547c3e1](https://github.com/dedalus-labs/dedalus-cli/commit/547c3e146cf035cee1b466a85a06529c0117edf2))
* fix quoting typo ([575418c](https://github.com/dedalus-labs/dedalus-cli/commit/575418c4b4a58aa2496fef880d038c59da9e7fb8))


### Chores

* add documentation for ./scripts/link ([6d33e1a](https://github.com/dedalus-labs/dedalus-cli/commit/6d33e1af76dc27b3c70c252b1d28894050b0a7dc))
* **ci:** remove release-doctor workflow ([2a1a9d8](https://github.com/dedalus-labs/dedalus-cli/commit/2a1a9d8c0b94a6c06c2ab828c1c8a6fbf1382f39))
* **cli:** additional test cases for `ShowJSONIterator` ([c64d07c](https://github.com/dedalus-labs/dedalus-cli/commit/c64d07ce2e2819b489ee60e500379bb3d3b4fc67))
* **cli:** fall back to JSON when using default "explore" with non-TTY ([3b62e8d](https://github.com/dedalus-labs/dedalus-cli/commit/3b62e8d1b6b6305b4cb36aec45abf4f88d3b6a2c))
* **cli:** let `--format raw` be used in conjunction with `--transform` ([c913d27](https://github.com/dedalus-labs/dedalus-cli/commit/c913d2739ca2f58b5498e7aea3b644b4068a1fe8))
* mark all CLI-related tests in Go with `t.Parallel()` ([7d1a565](https://github.com/dedalus-labs/dedalus-cli/commit/7d1a565862ae3190b48e40ff892073eca20a5480))
* modify CLI tests to inject stdout so mutating `os.Stdout` isn't necessary ([6073189](https://github.com/dedalus-labs/dedalus-cli/commit/6073189d85a64ac3d53c0f8b08899a2b617740b8))
* switch some CLI Go tests from `os.Chdir` to `t.Chdir` ([ba52bbe](https://github.com/dedalus-labs/dedalus-cli/commit/ba52bbea1667ea208f169c8b1b768e714ad3eeb7))

## 0.1.0 (2026-04-02)

Full Changelog: [v0.0.4...v0.1.0](https://github.com/dedalus-labs/dedalus-cli/compare/v0.0.4...v0.1.0)
Expand Down
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,23 @@ base64-encoding). Note that absolute paths will begin with `@file://` or
```bash
dedalus <command> --arg @data://file.txt
```

## Linking different Go SDK versions

You can link the CLI against a different version of the Dedalus Go SDK
for development purposes using the `./scripts/link` script.

To link to a specific version from a repository (version can be a branch,
git tag, or commit hash):

```bash
./scripts/link github.com/org/repo@version
```

To link to a local copy of the SDK:

```bash
./scripts/link ../path/to/dedalus-go
```

If you run the link script without any arguments, it will default to `../dedalus-go`.
17 changes: 0 additions & 17 deletions bin/check-release-environment

This file was deleted.

9 changes: 8 additions & 1 deletion cmd/dedalus/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ func main() {
prepareForAutocomplete(app)
}

if baseURL, ok := os.LookupEnv("DEDALUS_BASE_URL"); ok {
if err := cmd.ValidateBaseURL(baseURL, "DEDALUS_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

Expand All @@ -36,7 +43,7 @@ func main() {
fmt.Fprintf(os.Stderr, "%s %q: %d %s\n", apierr.Request.Method, apierr.Request.URL, apierr.Response.StatusCode, http.StatusText(apierr.Response.StatusCode))
format := app.String("format-error")
json := gjson.Parse(apierr.RawJSON())
show_err := cmd.ShowJSON(os.Stdout, "Error", json, format, app.String("transform-error"))
show_err := cmd.ShowJSON(os.Stdout, os.Stderr, "Error", json, format, app.IsSet("format-error"), app.String("transform-error"))
if show_err != nil {
// Just print the original error:
fmt.Fprintf(os.Stderr, "%s\n", err.Error())
Expand Down
4 changes: 4 additions & 0 deletions internal/apiform/form_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
4 changes: 4 additions & 0 deletions internal/apiquery/query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
)

func TestEncode(t *testing.T) {
t.Parallel()

tests := map[string]struct {
val any
settings QuerySettings
Expand Down Expand Up @@ -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 {
Expand Down
40 changes: 40 additions & 0 deletions internal/autocomplete/autocomplete_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
)

func TestGetCompletions_EmptyArgs(t *testing.T) {
t.Parallel()

root := &cli.Command{
Commands: []*cli.Command{
{Name: "generate", Usage: "Generate SDK"},
Expand All @@ -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"},
Expand All @@ -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"},
Expand All @@ -57,6 +63,8 @@ func TestGetCompletions_HiddenCommand(t *testing.T) {
}

func TestGetCompletions_NestedSubcommand(t *testing.T) {
t.Parallel()

root := &cli.Command{
Commands: []*cli.Command{
{
Expand All @@ -79,6 +87,8 @@ func TestGetCompletions_NestedSubcommand(t *testing.T) {
}

func TestGetCompletions_FlagCompletion(t *testing.T) {
t.Parallel()

root := &cli.Command{
Commands: []*cli.Command{
{
Expand All @@ -102,6 +112,8 @@ func TestGetCompletions_FlagCompletion(t *testing.T) {
}

func TestGetCompletions_ShortFlagCompletion(t *testing.T) {
t.Parallel()

root := &cli.Command{
Commands: []*cli.Command{
{
Expand All @@ -123,6 +135,8 @@ func TestGetCompletions_ShortFlagCompletion(t *testing.T) {
}

func TestGetCompletions_FileFlagBehavior(t *testing.T) {
t.Parallel()

root := &cli.Command{
Commands: []*cli.Command{
{
Expand All @@ -142,6 +156,8 @@ func TestGetCompletions_FileFlagBehavior(t *testing.T) {
}

func TestGetCompletions_NonBoolFlagValue(t *testing.T) {
t.Parallel()

root := &cli.Command{
Commands: []*cli.Command{
{
Expand All @@ -161,6 +177,8 @@ func TestGetCompletions_NonBoolFlagValue(t *testing.T) {
}

func TestGetCompletions_BoolFlagDoesNotBlockCompletion(t *testing.T) {
t.Parallel()

root := &cli.Command{
Commands: []*cli.Command{
{
Expand All @@ -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"},
Expand All @@ -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"},
Expand All @@ -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"},
Expand All @@ -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"},
Expand All @@ -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"},
Expand All @@ -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"},
Expand All @@ -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"},
Expand All @@ -305,6 +337,8 @@ func TestGetCompletions_MixedColonAndRegularCommands(t *testing.T) {
}

func TestGetCompletions_FlagWithBoolFlagSkipsValue(t *testing.T) {
t.Parallel()

root := &cli.Command{
Commands: []*cli.Command{
{
Expand All @@ -329,6 +363,8 @@ func TestGetCompletions_FlagWithBoolFlagSkipsValue(t *testing.T) {
}

func TestGetCompletions_MultipleFlagsBeforeSubcommand(t *testing.T) {
t.Parallel()

root := &cli.Command{
Commands: []*cli.Command{
{
Expand All @@ -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"},
Expand All @@ -372,6 +410,8 @@ func TestGetCompletions_CommandAliases(t *testing.T) {
}

func TestGetCompletions_AllFlagsWhenNoPrefix(t *testing.T) {
t.Parallel()

root := &cli.Command{
Commands: []*cli.Command{
{
Expand Down
6 changes: 6 additions & 0 deletions internal/jsonview/explorer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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"}`},
Expand All @@ -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"},
Expand Down
19 changes: 17 additions & 2 deletions internal/requestflag/innerflag.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,29 @@ type InnerFlag[
Aliases []string // aliases that are allowed for this flag
Validator func(T) error // custom function to validate this flag value

OuterFlag cli.Flag // The flag on which this inner flag will set values
InnerField string // The inner field which this flag will set
OuterFlag cli.Flag // The flag on which this inner flag will set values
InnerField string // The inner field which this flag will set
DataAliases []string // alternate names recognized in YAML values passed as the outer flag
}

// GetDataAliases returns the aliases recognized when parsing inner field keys from piped or flag YAML.
func (f *InnerFlag[T]) GetDataAliases() []string {
return f.DataAliases
}

// GetInnerField returns the API field name that this inner flag sets on its outer flag's value.
// For example, the flag --parent.foo targeting a parameter whose OpenAPI property name is "foo"
// would return "foo". This is distinct from the flag's CLI name and from any DataAliases entries.
func (f *InnerFlag[T]) GetInnerField() string {
return f.InnerField
}

type HasOuterFlag interface {
cli.Flag
SetOuterFlag(cli.Flag)
GetOuterFlag() cli.Flag
GetInnerField() string
GetDataAliases() []string
}

func (f *InnerFlag[T]) SetOuterFlag(flag cli.Flag) {
Expand Down
Loading
Loading