Skip to main content

Configuration

Configuration, scripting and debugging.

2 min read
View MarkdownEdit on GitHub

Configuration

Three things can be set, and each has the same three sources. First match wins:

flagenvironmentfile
API key--api-key <key>CODESPAR_API_KEY~/.codespar/config.json
Project--project <id>CODESPAR_PROJECTidem
API base URL--base-url <url>CODESPAR_BASE_URLidem

The file is written by codespar login, which prompts for an API key and saves it. It does not open a browser.

# One project for one command
codespar servers list --project proj_abc123

# Or for the whole shell
export CODESPAR_PROJECT=proj_abc123

There is no codespar config command. The file is plain JSON at ~/.codespar/config.json, so editing it or deleting it is the whole of configuration management, and deleting it is how you reset the CLI.

Scripting

--json is global: it works on any command and forces machine-readable output even in a TTY. Human-readable messages go to stderr, so stdout stays clean for a pipe.

# Servers in Brazil, as a newline-delimited list of ids
codespar servers list --country BR --json | jq -r '.[].id'

What a script gets when a command fails

Every command exits 0 on success and 1 on failure. The one exception is a bug inside the CLI, which exits 2 and prints a stack trace to stderr.

With --json, a failure is a document too. It goes to stdout, while the human line stays on stderr:

stdout
{
  "error": {
    "kind": "cli",
    "message": "Not logged in. Run `codespar login` or set CODESPAR_API_KEY."
  }
}
stderr
✗ Not logged in. Run `codespar login` or set CODESPAR_API_KEY.

kind is there so a script can branch without reading prose:

kindwhat happenedexitalso carries
clithe CLI refused before the API answered: no key, an --input that is not JSON, an argument the command itself rejects1
apithe API answered with an error1status, body, and code when the API sends one
timeouta request or a stream passed its --timeout1
internala bug in the CLI2the stack trace, on stderr
stdout
{
  "error": {
    "kind": "api",
    "message": "GET /v1/whoami → 401: unauthorized",
    "status": 401,
    "code": "unauthorized",
    "body": {
      "error": "unauthorized"
    }
  }
}

One failure never becomes a document: the argument parser runs before --json is read, so a missing argument, an unknown option or an unknown command writes one line to stderr and leaves stdout empty.

stderr
error: missing required argument 'consumer'

So a script reads the exit code first and parses stdout only after it knows the command got far enough to write one:

if out=$(codespar wallet "$consumer" --json); then
  echo "$out" | jq -r '.currencies[].available_minor'
elif [ -n "$out" ]; then
  echo "$out" | jq -r '.error.message' >&2
else
  # Empty stdout on a failure means the parser refused the line before --json
  # was read, and the reason is on stderr. Piping an empty string into jq
  # prints nothing, so a `//` fallback there never fires.
  echo "check the arguments" >&2
fi

Debugging

--base-url is the flag that answers "which API did this actually reach", and codespar whoami answers the other four questions at once: whether the key is valid, which organization and project it is bound to, which environment it acts in, and which scopes it holds.

codespar whoami --json

When a command answers nothing where you expected rows, the usual cause is a live key reading a test project or the reverse. whoami shows which, and the key prefix (csk_test_ or csk_live_) picks the environment on the same host.

Next steps

Configuration | CodeSpar