Files
sim/packages/sim-cli
Waleed 6bbabbbac6 fix(cli): close follow-up gaps (#7137)
* chore: run the orphaned migration-safety test, and stop publishing real-looking ids

One of the eight script tests was reachable from no entrypoint, so it had
never run in CI — it passes, it was simply never invoked. This is the second
time that hand-maintained list has drifted from the files beside it; the
audit runner's own header records the first. The guard against a third is a
`check:*` script rather than a test, because the runner derives its list
from that namespace and so picks the guard up by name — a test would have
had to be hand-added to the very list it guards.

A published spec sat outside the generator's manifest and so outside its
drift check, and carried six example ids with the texture of real generated
ones rather than the pandigital placeholders the rest of the repo uses. The
new check globs the directory instead of reading the manifest, since the
manifest is what omitted the file. The one pre-existing borderline id is
allowlisted with a reason: loosening the threshold to admit it would have
hidden one of the six.

* fix(cli): close the gaps black-box testing the shipped CLI found

`sim profiles <anything>` still exited 0, so a probe reading the exit code
to ask whether a command exists was told yes — the one group the earlier
guard missed. The exemption was written for commands that are both a group
and a leaf, but only `files restore` takes an operand; `profiles` takes
none. Registering its listing as a default subcommand puts it back among
the pure dispatchers the existing guard already covers, so the guard itself
did not need widening.

Three commands refuse a workspace API key and said nothing, while their
menu siblings said so — reading as though they accept one. They are
hand-written, so they never reached the code that appends the note. That
note now comes from a helper taking the operation, so a command names the
operation it invokes and the two cannot disagree, and a test fails if a
hand-written command ever calls a restricted operation without it.

A blank numeric value in a request body still became a real zero, the same
coercion already fixed for query strings: the guard keyed off the slot when
the distinction is the field's declared type. Twenty-one fields across
fifteen operations were affected. An empty body string still clears a
description.

Blank values for the root endpoint, workspace and profile flags fell back
to what was configured instead of being refused, and a whitespace workspace
was accepted verbatim. A hand-written profile name carrying padding listed
as reachable but resolved to defaults rather than erroring. Two schema
descriptions named request fields that no flag spells, and a rejected value
was echoed unredacted by four messages while their siblings redacted it.

A write now re-emits a section header it was not asked to touch byte for
byte. The blank-line normalisation around it is left alone: making the
writer position-faithful is a change to its model, not a fix.

* fix(scripts): match example uuids case-insensitively in the spec audit

The pattern only recognised lowercase hex, so an uppercase id in a
published spec was never examined and the audit reported success without
having looked at it.

Matching case-insensitively is not enough on its own: hex is
case-insensitive, so a mixed-case id counts `A` and `a` as two digits and
reports twenty distinct ones rather than sixteen. That inflated count
clears the threshold the texture test uses to recognise a hand-authored
placeholder, so a real id could have passed for one. The allowlist is an
exact-string lookup and would likewise have missed an uppercase spelling of
an entry. Both checks and the lookup now take a normalised id, while the
finding still reports the spelling as it appears in the file.

* fix(cli): stop a refusal being swallowed, and gate example ids by name

A blank root flag was refused everywhere except `profiles`, where the catch
that lets a broken profile still list absorbed it and the command exited 0
after printing the table. The refusal now carries its own error class, which
is what the listing rethrows on — the two are distinguished by type rather
than by matching message text, and a genuinely broken profile still lists.

The unknown-profile message redacted the name the caller typed but not the
suggestion or the list of configured names beside it, which come from the
same file and are equally attacker-influenced once it has been hand-edited.
Those are redacted now, as is every other message in these two files that
quotes a name read out of the config, and the profile listing flattens the
names it renders the way it already flattened the error column.

The example-id audit judged a uuid by its digit texture, on the premise that
a real one essentially never looks hand-authored. Measured against ten
million generated ids, 0.81% of them do — one in 124, where this change
alone replaced six. Requiring each digit exactly twice takes that to zero
but rejects all fourteen placeholders now in the specs, so it is no cheaper
than the alternative. The audit now holds the eighteen ids the specs
actually use, which is one file rather than the twenty-seven a reserved
format would touch, and a new id fails until someone lists it — which is the
review the check exists to force.

* fix(scripts): match the uuid sentinels exactly rather than by shape

Accepting any id built from at most two distinct hex digits let something
through that was never on the approved list. A generated id essentially
never has that shape, so the practical risk was small — but this check had
just stopped being a shape test and become a list, and a structural
exception is the one thing that undoes that. The two ids it exists for are
the nil and max sentinels, and both are matched by value now.
2026-08-26 17:53:39 -07:00
..

Sim CLI

sim is the command-line client for Sim, a workspace for building, deploying, and managing AI agents and workflows.

Use the CLI to work with an existing Sim account or self-hosted deployment from your terminal. You can run workflows, inspect logs, query tables, manage files and knowledge bases, and configure workspace resources. The CLI does not install or run Sim itself; see the self-hosting guide if you need to set up a Sim deployment.

Install

The CLI requires Node.js 20 or newer.

npm install --global sim
sim --version

You can also run a command without installing the package globally:

npx sim --help

Get started

Sign in to the default profile:

sim login

The CLI opens a browser and prints a pairing code. Confirm that the code in the browser matches the one in your terminal, approve the login, and choose a workspace. The selected workspace becomes the default for this profile.

The login stores a personal API key locally. It does not start a local callback server, so the same flow works over SSH and in containers. Use sim login --no-browser when the browser is on another machine.

Check the active profile and verify that its endpoint, API key, and workspace work together:

sim whoami

This also reports whether the active key is personal or workspace-scoped. Some administrative and deployment operations require a personal key.

Then list and run workflows:

sim workflows list
sim workflows run <workflowId> --input '{"ticketId":"T-4821"}'

A workflow must be deployed before it can run:

sim workflows deploy <workflowId>

Workflow, knowledge-base, and workspace IDs are UUIDs. Table IDs start with tbl_; file IDs start with wf_. Despite the prefix, wf_ identifies a file, not a workflow.

Profiles

A profile is a named CLI configuration. It determines:

  • which Sim deployment to use
  • which API key to authenticate with
  • which workspace to target by default
  • how command output is formatted

If you do not specify a profile, the CLI uses default. Select another profile with --profile, its short form -P, or SIM_PROFILE:

sim workflows list --profile production
sim -P production logs list
SIM_PROFILE=production sim tables list

Unknown profile names fail with the configured profile list and a suggested match when available. login and configure are the exceptions because they can create a new profile.

There are two common ways to create profiles.

Use one login with several workspaces

After sim login, create another profile that shares the active profile's API key but has its own default workspace:

sim workspaces list
sim profile add acme --workspace <workspaceId>
sim --profile acme whoami

If you omit --workspace in an interactive terminal, the CLI asks you to choose one. The new profile stores an auth_profile reference to the active login; it does not copy the API key.

Use a separate account or deployment

Run login with a new profile name. Add --endpoint when the profile should use a self-hosted or local deployment:

sim login --profile work
sim login --profile local --endpoint http://localhost:3000

Each of these profiles stores its own API key. The endpoint selected during login is saved with the profile.

View and change profiles

sim profiles
sim configure --profile work
sim configure --profile work --set-workspace <workspaceId>
sim configure --profile work --set-output json
sim configure --profile local --set-endpoint http://localhost:3000
sim whoami --profile work

sim profiles marks the active profile with *. Running sim configure with no setting flags prints the saved settings for that profile.

Non-secret settings are stored in ~/.sim/config. API keys are stored separately in ~/.sim/credentials, which is written with 0600 permissions. Set SIM_CONFIG_DIR to use a different directory.

For each setting, the CLI uses the first available value in this order:

  1. command-line flag
  2. environment variable
  3. selected profile
  4. built-in default

sim whoami shows both the resolved values and where each one came from.

Useful commands

Run --help at any level to see the available subcommands and flags:

sim --help
sim workflows --help
sim tables rows query --help

The commands you will use most often are:

Task Command
Ask Sim about the workspace sim chat "Which workflows failed today?"
List or inspect workflows sim workflows list, sim workflows get <workflowId>
Deploy or run a workflow sim workflows deploy <workflowId>, sim workflows run <workflowId>
Follow a workflow run sim workflows run <workflowId> --follow
Inspect workflow runs sim workflows runs list --workflow <workflowId>
Find errors sim logs list --level error, sim logs follow
Inspect a run trace sim logs get <runId> --trace
Work with tables sim tables list, sim tables rows query <tableId>
Import a CSV sim tables import ./data.csv
Upload or download files sim files upload ./report.pdf, sim files get <fileId>
Search knowledge bases sim knowledge search --query "refund policy" --kb <knowledgeBaseId>
Upload a knowledge document sim knowledge documents upload <knowledgeBaseId> ./handbook.pdf
Manage integration credentials sim credentials --help
Manage workspace secrets sim secrets list, sim secrets set <name>

Commands follow this general shape:

sim <resource> [sub-resource] <verb> [arguments] [options]

Many plural top-level resource names also accept a singular spelling, so sim workflow get <workflowId> and sim workflows get <workflowId> are equivalent. Not every group has a singular alias; sim --help shows the exact aliases. knowledge also has the kb alias.

For workflows, tables, files, and knowledge bases, list returns resources only. ls [path] returns the resources and direct child folders at a path:

sim workflows ls /Support
sim files ls /Reports

See the command reference for every command, argument, and flag.

JSON input and output

Human-readable tables are the default. Use JSON or YAML when another program will consume the result, and text for tab-separated shell output:

sim workflows list --output json
sim logs list --output json | jq -r '.[].runId'
SIM_OUTPUT=yaml sim tables get <tableId>
sim configure --set-output json

JSON-valued options accept inline JSON, a file prefixed with @, or stdin with @-:

sim workflows run <workflowId> --input '{"customerId":"cus_123"}'
sim workflows run <workflowId> --input @input.json
printf '%s' '{"customerId":"cus_123"}' | sim workflows run <workflowId> --input @-

List-valued options use the same @file and @- forms, with one value per line. Destructive commands require an explicit selector and --yes; they do not default to deleting every resource when a selector is missing.

For secret values, prefer a prompt, file, or stdin so the value does not appear in shell history or the process list:

sim secrets set API_KEY --scope workspace
sim secrets set API_KEY --scope workspace --value @secret.txt
printf '%s' "$API_KEY" | sim secrets set API_KEY --scope workspace --value @-

CI and automation

In CI, use an API key instead of sim login:

export SIM_API_KEY="sim_..."
export SIM_WORKSPACE="<workspaceId>"

sim workflows run <workflowId> --input @input.json --output json

Create and revoke API keys in Sim under Settings → API keys, and store them in your CI provider's secret store. sim logout only removes a stored key from the current machine; it does not revoke the key.

The main environment variables are:

Variable Purpose
SIM_PROFILE Profile to use
SIM_ENDPOINT Sim deployment URL
SIM_API_KEY API key, usually for CI
SIM_WORKSPACE Workspace to target
SIM_OUTPUT table, json, yaml, or text
SIM_CONFIG_DIR Directory containing CLI config and credentials
SIM_TIMEOUT_SECONDS Per-request timeout; 0 waits indefinitely
SIM_DEBUG Print request diagnostics to stderr

Documentation

License

Apache-2.0