mirror of
https://github.com/simstudioai/sim.git
synced 2026-10-30 19:32:46 +08:00
docs(cli): add a CLI docs section generated from the command tree (#6762)
* docs(cli): add a CLI section, generated from the command tree
The `sim` CLI shipped with no coverage in the docs site. Adds a fourth
top-level tab for it, and moves Academy last.
The command reference is generated. `sim` exposes 147 leaf commands across
33 groups, most of them derived at runtime from the v2 route contracts, so a
hand-written reference would be wrong the week after it was written. The
generator walks the command tree `buildProgram()` hands to commander — the
same tree the terminal parses — rather than re-deriving it from the contract,
which would be a second implementation free to describe commands nobody can
invoke. `check:cli-docs` is a zero-arg `check:*` script, so the existing audit
runner picks it up and stale pages fail CI.
Generating against the real tree surfaced a collision it had been hiding:
`bulkUpdateKnowledgeDocuments` and `updateKnowledgeDocument` both derived to
`sim knowledge documents update`. Commander resolves a duplicate to the first
match, so the bulk form shadowed the single-document one and its flags were
unreachable while still appearing in `--help`. The bulk form is now
`batch-update`, matching how `tables rows batch-delete`/`batch-update` already
handle the same REST overload, and the generator fails on any duplicate path
so the next one cannot land silently.
Five hand-written guides cover install, auth, configuration, output formats,
and scripting. Also corrects two commands in the package README that do not
exist as documented (`tables columns <tableId>`, and `--sort score:desc`,
which is JSON).
* docs(cli): document every flag from the contracts, add troubleshooting and a single-page reference
The command reference was structurally complete but said almost nothing: 223 of
377 flags rendered as "Set sort by" because the CLI only ever read flag help
from its own contract overrides, and fell back to restating the flag name.
The prose already existed. The v2 route contracts carry 931 `.describe()` calls
and the OpenAPI specs publish all of them — 327 parameters and 282 body
properties, 100% coverage — but the generated operation table dropped every one,
carrying only a per-operation summary. It now carries the field descriptions,
the path-parameter descriptions, and positional help, so `--help` and the docs
explain a flag the same way the API reference does. Placeholder descriptions are
now zero, and 147/147 commands, 377/377 flags and 130/130 arguments are
documented.
`check:cli-docs` fails on a request field with no `.describe()` rather than
letting it render as documentation that says nothing.
Also in this pass:
- Commands are root-level sidebar entries under a Commands heading rather than
a folder, and headings are the command's description, so the table of
contents distinguishes entries at the first word instead of repeating
"sim knowledge documents …" fourteen times. A guard fails the build if two
descriptions on a page collide, since they would share an anchor.
- A single-page `Complete reference` carrying all 147 commands, for in-page
search and for agents fetching `/cli/reference.mdx`. It keys on exact command
paths because descriptions are only unique within a group.
- A troubleshooting page, with every message copied from the source.
- Table columns are sized by a local component; the flag column was starved
while descriptions kept most of the row empty.
- The prerelease install channels are dropped from the docs and the package
README, which is what npm renders.
* fix(docs): match the CLI tab by path segment, and escape backslashes before pipes
`pathname.includes('/cli')` also matches `/integrations/clickup` and
`/integrations/clickhouse`, so both existing integration pages lit the CLI tab
and unlit Documentation. Matching is now per path segment. Anchoring to the
start would not work either — a non-default locale prefixes the path, as in
`/ja/cli` — so the segment is matched wherever it sits.
Table cells now double a backslash before escaping pipes. A value ending in one
turned `a\` + `|` into `a\\|`, which the table parser reads as an escaped
backslash followed by an unescaped pipe, splitting the cell early. Nothing in
the command surface contains a backslash today, so this was latent rather than
visible.
The reference page's global options table is two-column and was being wrapped in
`CommandTable`, which sizes the second column for the `Required` cell of the
three-column tables and crushed the description into 5.5rem. It now matches the
overview page, which leaves that table unsized.
This commit is contained in:
@@ -113,14 +113,21 @@ export default async function Page(props: { params: Promise<{ slug?: string[]; l
|
||||
// Academy lessons are video-first: drop the "On this page" TOC and go full
|
||||
// width so the lesson hero/video gets the room (chapters live in-page instead).
|
||||
const isAcademy = slug?.[0] === 'academy'
|
||||
const isCli = slug?.[0] === 'cli'
|
||||
|
||||
const pageTreeRecord = source.pageTree as Record<string, Root>
|
||||
const pageTree = pageTreeRecord[lang] ?? pageTreeRecord.en ?? Object.values(pageTreeRecord)[0]
|
||||
const rawNeighbours = pageTree ? findNeighbour(pageTree, page.url) : null
|
||||
// Academy and API Reference are self-contained sections; keep prev/next inside
|
||||
// the section instead of spilling into the main documentation tree. Match both
|
||||
// the section's pages (`/<slug>/...`) and its index (`/<slug>`).
|
||||
const sectionSlug = isApiReference ? 'api-reference' : isAcademy ? 'academy' : null
|
||||
// Academy, API Reference, and CLI are self-contained sections; keep prev/next
|
||||
// inside the section instead of spilling into the main documentation tree.
|
||||
// Match both the section's pages (`/<slug>/...`) and its index (`/<slug>`).
|
||||
const sectionSlug = isApiReference
|
||||
? 'api-reference'
|
||||
: isAcademy
|
||||
? 'academy'
|
||||
: isCli
|
||||
? 'cli'
|
||||
: null
|
||||
const inSection = (url?: string) =>
|
||||
url != null && (url.includes(`/${sectionSlug}/`) || url.endsWith(`/${sectionSlug}`))
|
||||
const neighbours = sectionSlug
|
||||
|
||||
@@ -8,23 +8,53 @@ import { SimWordmark } from '@/components/ui/sim-logo'
|
||||
import { ThemeToggle } from '@/components/ui/theme-toggle'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
/**
|
||||
* Sections that own a tab, in reading order: the main docs, then the two
|
||||
* reference surfaces, then Academy. `Documentation` matches by exclusion, so
|
||||
* every section listed here is one it must not claim.
|
||||
*/
|
||||
const SECTION_TABS = ['api-reference', 'academy', 'cli'] as const
|
||||
|
||||
/**
|
||||
* Whether a pathname is inside a section, matched by whole path segment.
|
||||
*
|
||||
* A substring test is wrong: `/integrations/clickup` and
|
||||
* `/integrations/clickhouse` both contain `/cli`, which lit the CLI tab and
|
||||
* unlit Documentation on two existing integration pages. Anchoring to the start
|
||||
* is also wrong, because a non-default locale prefixes the path (`/ja/cli`), so
|
||||
* the segment can sit anywhere.
|
||||
*/
|
||||
function isInSection(pathname: string, section: string): boolean {
|
||||
return (
|
||||
pathname === `/${section}` ||
|
||||
pathname.endsWith(`/${section}`) ||
|
||||
pathname.includes(`/${section}/`)
|
||||
)
|
||||
}
|
||||
|
||||
const NAV_TABS = [
|
||||
{
|
||||
label: 'Documentation',
|
||||
href: '/introduction',
|
||||
match: (p: string) => !p.includes('/api-reference') && !p.includes('/academy'),
|
||||
external: false,
|
||||
},
|
||||
{
|
||||
label: 'Academy',
|
||||
href: '/academy',
|
||||
match: (p: string) => p.includes('/academy'),
|
||||
match: (p: string) => !SECTION_TABS.some((section) => isInSection(p, section)),
|
||||
external: false,
|
||||
},
|
||||
{
|
||||
label: 'API Reference',
|
||||
href: '/api-reference/getting-started',
|
||||
match: (p: string) => p.includes('/api-reference'),
|
||||
match: (p: string) => isInSection(p, 'api-reference'),
|
||||
external: false,
|
||||
},
|
||||
{
|
||||
label: 'CLI',
|
||||
href: '/cli',
|
||||
match: (p: string) => isInSection(p, 'cli'),
|
||||
external: false,
|
||||
},
|
||||
{
|
||||
label: 'Academy',
|
||||
href: '/academy',
|
||||
match: (p: string) => isInSection(p, 'academy'),
|
||||
external: false,
|
||||
},
|
||||
] as const
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import type { ReactNode } from 'react'
|
||||
|
||||
interface CommandTableProps {
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
/**
|
||||
* Column sizing for the generated CLI reference tables.
|
||||
*
|
||||
* Auto layout gives a column width in proportion to its content, which is
|
||||
* backwards here: descriptions are sentences and flags are short, so the flag
|
||||
* column collapsed until `--enabled-filter <value>` wrapped across three lines
|
||||
* while the description beside it kept most of the row empty. A fixed layout
|
||||
* with explicit widths reserves the space the flag actually needs.
|
||||
*
|
||||
* Cells align to the top because a wrapped four-line description would
|
||||
* otherwise float its flag into the middle of the row, away from the line it
|
||||
* belongs to.
|
||||
*/
|
||||
export function CommandTable({ children }: CommandTableProps) {
|
||||
return (
|
||||
<div
|
||||
className={[
|
||||
'[&_table]:w-full [&_table]:table-fixed',
|
||||
'[&_th:nth-child(1)]:w-[30%] [&_th:nth-child(2)]:w-[5.5rem]',
|
||||
'[&_td]:align-top [&_th]:align-bottom',
|
||||
// Long flags and dotted paths have no spaces to break on.
|
||||
'[&_td:nth-child(1)_code]:break-words',
|
||||
].join(' ')}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
---
|
||||
title: Audit Logs
|
||||
description: Manage audit logs — every subcommand, argument, and flag
|
||||
---
|
||||
|
||||
import { CommandTable } from '@/components/ui/command-table'
|
||||
|
||||
`sim audit-logs` is also spelled `sim audit-log`.
|
||||
|
||||
Every command below also accepts the [global options](/cli/commands#global-options).
|
||||
|
||||
## Get audit log
|
||||
|
||||
```bash
|
||||
sim audit-logs get <id> [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `id` | Yes | Audit-log entry identifier. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--organization <value>` | Yes | Organization ID (personal API key required). |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## List audit logs
|
||||
|
||||
```bash
|
||||
sim audit-logs list [options]
|
||||
```
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--action <value>` | No | Filter by exact action name. |
|
||||
| `--resource-type <value>` | No | Filter by resource type. Accepts a comma-separated set; members are trimmed and deduplicated, and member order affects neither the result nor the cursor. |
|
||||
| `--resource-id <value>` | No | Filter by exact resource identifier. |
|
||||
| `--start-date <value>` | No | Only include runs started at or after this UTC ISO 8601 timestamp, e.g. `2026-08-06T00:00:00Z`. A date without a time, or a timestamp carrying a UTC offset instead of `Z`, is rejected, as is year `0000`, which names no storable instant. |
|
||||
| `--end-date <value>` | No | Only include runs started at or before this UTC ISO 8601 timestamp, e.g. `2026-08-06T00:00:00Z`. A date without a time, or a timestamp carrying a UTC offset instead of `Z`, is rejected, as is year `0000`, which names no storable instant. |
|
||||
| `--include-departed` | No | Include actions by users who have left the organization. |
|
||||
| `--no-include-departed` | No | Send --include-departed as false. |
|
||||
| `--limit <n>` | No | Maximum items to return (0 for everything). Defaults to `100`. |
|
||||
| `--organization <value>` | Yes | Organization ID (personal API key required). |
|
||||
| `--actor-email <value>` | No | Filter by actor email address. |
|
||||
| `--all-workspaces` | No | Do not filter to the configured workspace (personal API key required for account-wide access). |
|
||||
|
||||
</CommandTable>
|
||||
@@ -0,0 +1,167 @@
|
||||
---
|
||||
title: Authentication
|
||||
description: Sign in from the terminal, authenticate CI with an API key, and keep several accounts side by side
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout'
|
||||
|
||||
The CLI authenticates with a Sim API key. On a workstation, `sim login` mints and
|
||||
stores one for you. In CI, you supply one through the environment and nothing
|
||||
touches the filesystem.
|
||||
|
||||
## Signing in
|
||||
|
||||
```bash
|
||||
sim login
|
||||
```
|
||||
|
||||
The terminal prints a pairing code and a URL:
|
||||
|
||||
```
|
||||
Pairing code: K7M2-P9XT
|
||||
Confirm this code matches what the browser shows before approving.
|
||||
|
||||
https://sim.ai/cli/auth?request=…&scope=platform
|
||||
Waiting for approval…
|
||||
|
||||
✓ Logged in. Key stored in /Users/you/.sim/credentials
|
||||
Personal key, defaulting to ws_abc123. Override per command with --workspace.
|
||||
```
|
||||
|
||||
This is the same browser handoff shape as `gh auth login`. Nothing redeemable
|
||||
crosses the browser leg, and there is no loopback listener — so it works over
|
||||
SSH and inside containers.
|
||||
|
||||
<Callout type="warn">
|
||||
Confirm the pairing code in your terminal matches the one the browser shows
|
||||
before you approve. That check is what binds the approval to *your* terminal.
|
||||
</Callout>
|
||||
|
||||
| Option | What it does |
|
||||
| --- | --- |
|
||||
| `--no-browser` | Print the URL instead of opening a browser |
|
||||
| `--scope <scope>` | Key space to mint from: `platform` (default) or `copilot` |
|
||||
| `-y, --yes` | Overwrite an existing profile without prompting |
|
||||
|
||||
### Picking a workspace
|
||||
|
||||
The approval page is where you choose the workspace — the terminal has no key
|
||||
yet, so it cannot list them for you.
|
||||
|
||||
`sim login` issues a **personal** key. The workspace you pick becomes the
|
||||
profile's default `workspace`; it does **not** restrict the key to that
|
||||
workspace. Target another workspace the key can reach with `--workspace`:
|
||||
|
||||
```bash
|
||||
sim workflows list --workspace ws_other
|
||||
```
|
||||
|
||||
`sim login --workspace <id>` preselects a workspace in the picker, and
|
||||
re-logging into an existing profile preselects the one already configured.
|
||||
|
||||
## Checking who you are
|
||||
|
||||
```bash
|
||||
sim whoami
|
||||
```
|
||||
|
||||
This prints the resolved endpoint, workspace, output format, and account — and
|
||||
which source each value came from. Reach for it first whenever a command targets
|
||||
something you did not expect.
|
||||
|
||||
## Signing out
|
||||
|
||||
```bash
|
||||
sim logout # remove the stored key
|
||||
sim logout --all # remove the profile entirely, including its settings
|
||||
```
|
||||
|
||||
<Callout type="warn">
|
||||
`sim logout` removes the key from disk but does **not** revoke it. Revoke keys in
|
||||
Sim under **Settings → API keys**.
|
||||
</Callout>
|
||||
|
||||
## Authenticating CI
|
||||
|
||||
Skip `sim login` entirely. Set the key and workspace in the environment and the
|
||||
CLI never reads or writes a config file:
|
||||
|
||||
```bash
|
||||
export SIM_API_KEY="sim_…"
|
||||
export SIM_WORKSPACE="ws_abc123"
|
||||
|
||||
sim workflows run wf_7Yb2 --input '{"source":"nightly"}' --output json
|
||||
```
|
||||
|
||||
Create the key in Sim under **Settings → API keys**. Store it as a secret in your
|
||||
CI provider — never commit it.
|
||||
|
||||
<Callout type="info">
|
||||
`SIM_CONFIG_DIR` relocates both files if you do need them somewhere other than
|
||||
`~/.sim` — a container image, or a runner with no writable home directory.
|
||||
</Callout>
|
||||
|
||||
### GitHub Actions
|
||||
|
||||
```yaml title=".github/workflows/nightly.yml"
|
||||
jobs:
|
||||
digest:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
- run: npm install --global sim
|
||||
- run: sim workflows run wf_7Yb2 --output json
|
||||
env:
|
||||
SIM_API_KEY: ${{ secrets.SIM_API_KEY }}
|
||||
SIM_WORKSPACE: ${{ vars.SIM_WORKSPACE }}
|
||||
```
|
||||
|
||||
## Several accounts at once
|
||||
|
||||
Each profile holds one identity and one set of defaults, so a production account
|
||||
and a local stack can coexist without re-authenticating:
|
||||
|
||||
```bash
|
||||
sim login --profile dev --endpoint http://localhost:3000
|
||||
sim login --profile prod
|
||||
|
||||
sim workflows list --profile dev
|
||||
sim workflows list --profile prod
|
||||
```
|
||||
|
||||
See [Configuration](/cli/configuration) for how profiles are stored and resolved.
|
||||
|
||||
## Self-hosted and non-production deployments
|
||||
|
||||
Point the CLI at any Sim deployment with `--endpoint`, then sign in against it:
|
||||
|
||||
```bash
|
||||
sim login --profile local --endpoint http://localhost:3000
|
||||
```
|
||||
|
||||
Save it so you do not have to repeat the flag:
|
||||
|
||||
```bash
|
||||
sim configure --set-endpoint http://localhost:3000 --profile local
|
||||
```
|
||||
|
||||
## Where the key is stored
|
||||
|
||||
Keys live in `~/.sim/credentials`, written with `0600` permissions, kept apart
|
||||
from the non-secret `~/.sim/config` so the two can be handled differently — you
|
||||
can commit `config` to a dotfiles repo, and never `credentials`.
|
||||
|
||||
```ini title="~/.sim/credentials"
|
||||
[default]
|
||||
api_key = sim_…
|
||||
|
||||
[dev]
|
||||
api_key = sim_…
|
||||
```
|
||||
|
||||
## Organization audit logs
|
||||
|
||||
`sim audit-logs` requires a **personal** API key — the kind `sim login` issues.
|
||||
A workspace-scoped key cannot read organization-level audit logs.
|
||||
@@ -0,0 +1,45 @@
|
||||
---
|
||||
title: Billing
|
||||
description: Manage billing — every subcommand, argument, and flag
|
||||
---
|
||||
|
||||
import { CommandTable } from '@/components/ui/command-table'
|
||||
|
||||
Every command below also accepts the [global options](/cli/commands#global-options).
|
||||
|
||||
## Show billing status and current-period credit usage
|
||||
|
||||
```bash
|
||||
sim billing status [options]
|
||||
```
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--all-workspaces` | No | Do not filter to the configured workspace (personal API key required for account-wide access). |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## List credit usage events
|
||||
|
||||
```bash
|
||||
sim billing logs [options]
|
||||
```
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--source <value>` | No | Filter by usage source; sim-chat combines Copilot and workspace chat. Accepted values: `workflow`, `wand`, `sim-chat`, `mcp_copilot`, `mothership_block`, `knowledge-base`, `voice-input`, `enrichment`, `voice-output`. |
|
||||
| `--period <value>` | No | Billing period. Accepted values: `1d`, `7d`, `30d`, `all`, `custom`. |
|
||||
| `--start-date <value>` | No | Custom period start (ISO 8601). |
|
||||
| `--end-date <value>` | No | Custom period end (ISO 8601). |
|
||||
| `--limit <n>` | No | Maximum items to return (0 for everything). Defaults to `100`. |
|
||||
| `--all-workspaces` | No | Do not filter to the configured workspace (personal API key required for account-wide access). |
|
||||
|
||||
</CommandTable>
|
||||
@@ -0,0 +1,112 @@
|
||||
---
|
||||
title: Overview
|
||||
description: Global options, and every sim command group
|
||||
---
|
||||
|
||||
import { CommandTable } from '@/components/ui/command-table'
|
||||
|
||||
Every `sim` command follows the same shape:
|
||||
|
||||
```bash
|
||||
sim <resource> [sub-resource] <verb> [arguments] [options]
|
||||
```
|
||||
|
||||
Resource groups are plural, and each one also accepts its singular spelling —
|
||||
`sim workflow get` and `sim workflows get` are the same command. `knowledge`
|
||||
additionally answers to `kb`.
|
||||
|
||||
## Global options
|
||||
|
||||
These apply to every command, and may be written before or after it.
|
||||
|
||||
| Option | Description |
|
||||
| --- | --- |
|
||||
| `-P, --profile <name>` | Profile to use (env: SIM_PROFILE). |
|
||||
| `--endpoint <url>` | Sim deployment to talk to (env: SIM_ENDPOINT). |
|
||||
| `-w, --workspace <id>` | Workspace to target (env: SIM_WORKSPACE). |
|
||||
| `--output <format>` | Output format for this command. Accepted values: `table`, `json`, `yaml`, `text`. |
|
||||
|
||||
## Command groups
|
||||
|
||||
| Group | Description |
|
||||
| --- | --- |
|
||||
| [`sim audit-logs`](/cli/audit-logs) | Manage audit logs |
|
||||
| [`sim billing`](/cli/billing) | Manage billing |
|
||||
| [`sim credentials`](/cli/credentials) | Manage credentials |
|
||||
| [`sim custom-tools`](/cli/custom-tools) | Manage custom tools |
|
||||
| [`sim files`](/cli/files) | Manage files |
|
||||
| [`sim knowledge`](/cli/knowledge) | Manage knowledge |
|
||||
| [`sim logs`](/cli/logs) | Manage logs |
|
||||
| [`sim mcp-servers`](/cli/mcp-servers) | Manage mcp servers |
|
||||
| [`sim secrets`](/cli/secrets) | Manage secrets |
|
||||
| [`sim skills`](/cli/skills) | Manage skills |
|
||||
| [`sim tables`](/cli/tables) | Manage tables |
|
||||
| [`sim workflows`](/cli/workflows) | Manage workflows |
|
||||
| [`sim workspaces`](/cli/workspaces) | Manage workspaces |
|
||||
|
||||
## Authorize this terminal and store an API key for the profile
|
||||
|
||||
```bash
|
||||
sim login [options]
|
||||
```
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--scope <scope>` | No | Key space to mint from: platform or copilot. Defaults to `platform`. |
|
||||
| `--no-browser` | No | Print the URL instead of opening a browser. |
|
||||
| `-y, --yes` | No | Overwrite an existing profile without prompting. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Remove the profile's stored API key
|
||||
|
||||
```bash
|
||||
sim logout [options]
|
||||
```
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--all` | No | Remove the profile entirely, including its settings. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Show the resolved profile and where each setting came from
|
||||
|
||||
```bash
|
||||
sim whoami
|
||||
```
|
||||
|
||||
## List the profiles defined in the config and credentials files
|
||||
|
||||
```bash
|
||||
sim profiles
|
||||
```
|
||||
|
||||
Also available as `sim profile`.
|
||||
|
||||
## Set a profile's endpoint, default workspace, or output format
|
||||
|
||||
```bash
|
||||
sim configure [options]
|
||||
```
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--set-endpoint <url>` | No | Sim deployment to talk to. |
|
||||
| `--set-workspace <id>` | No | Default workspace for workspace-scoped commands. |
|
||||
| `--set-output <format>` | No | Default output format (table \| json \| yaml \| text). |
|
||||
| `--unset <key...>` | No | Remove settings (endpoint, workspace, output). |
|
||||
|
||||
</CommandTable>
|
||||
@@ -0,0 +1,147 @@
|
||||
---
|
||||
title: Configuration
|
||||
description: Profiles, config files, environment variables, and how each setting is resolved
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout'
|
||||
|
||||
The CLI has four settings: which **endpoint** to talk to, which **API key** to
|
||||
use, which **workspace** to target, and which **output format** to print. Each
|
||||
one resolves independently, so you can save a default and still override it for
|
||||
a single command.
|
||||
|
||||
## Profiles
|
||||
|
||||
A profile is one identity plus one set of defaults. Profiles work like the AWS
|
||||
CLI, which is what lets a production account and a local stack sit side by side
|
||||
without re-authenticating.
|
||||
|
||||
Select one with `-P`, `--profile`, or `SIM_PROFILE`:
|
||||
|
||||
```bash
|
||||
sim workflows list --profile dev
|
||||
SIM_PROFILE=dev sim workflows list
|
||||
```
|
||||
|
||||
The profile is named `default` when you do not pick one.
|
||||
|
||||
```bash
|
||||
sim profiles # list them; * marks the active one
|
||||
```
|
||||
|
||||
## Setting defaults
|
||||
|
||||
```bash
|
||||
sim configure --set-endpoint http://localhost:3000 --profile dev
|
||||
sim configure --set-workspace ws_local --profile dev
|
||||
sim configure --set-output json
|
||||
```
|
||||
|
||||
| Option | What it sets |
|
||||
| --- | --- |
|
||||
| `--set-endpoint <url>` | The Sim deployment to talk to |
|
||||
| `--set-workspace <id>` | Default workspace for workspace-scoped commands |
|
||||
| `--set-output <format>` | Default output format: `table`, `json`, `yaml`, or `text` |
|
||||
| `--unset <key...>` | Remove settings — `endpoint`, `workspace`, or `output` |
|
||||
|
||||
Run `sim configure` with no flags to print the profile's stored settings.
|
||||
|
||||
<Callout type="info">
|
||||
API keys are deliberately **not** settable through `sim configure`. Use
|
||||
[`sim login`](/cli/authentication), or `SIM_API_KEY` for CI.
|
||||
</Callout>
|
||||
|
||||
## Where settings come from
|
||||
|
||||
Each setting resolves independently, and the first match wins:
|
||||
|
||||
| Rank | Source |
|
||||
| --- | --- |
|
||||
| 1 | Command-line flag — `--endpoint`, `--workspace`, `--output` |
|
||||
| 2 | Environment — `SIM_ENDPOINT`, `SIM_API_KEY`, `SIM_WORKSPACE`, `SIM_OUTPUT` |
|
||||
| 3 | `~/.sim/config` and `~/.sim/credentials`, for the selected profile |
|
||||
| 4 | Built-in default — `https://sim.ai` and `table` |
|
||||
|
||||
Because they resolve independently, a saved profile still supplies the workspace
|
||||
when you override only the output format.
|
||||
|
||||
`sim whoami` prints the winning source for each setting, which is usually the
|
||||
fastest way to explain a surprising result:
|
||||
|
||||
```bash
|
||||
sim whoami
|
||||
```
|
||||
|
||||
## The files
|
||||
|
||||
Non-secret settings live in `~/.sim/config`. It is safe to commit to a dotfiles
|
||||
repo:
|
||||
|
||||
```ini title="~/.sim/config"
|
||||
[default]
|
||||
endpoint = https://sim.ai
|
||||
workspace = ws_abc123
|
||||
output = table
|
||||
|
||||
[profile dev]
|
||||
endpoint = http://localhost:3000
|
||||
workspace = ws_local
|
||||
```
|
||||
|
||||
Keys live in `~/.sim/credentials`, written `0600`:
|
||||
|
||||
```ini title="~/.sim/credentials"
|
||||
[default]
|
||||
api_key = sim_…
|
||||
|
||||
[dev]
|
||||
api_key = sim_…
|
||||
```
|
||||
|
||||
<Callout type="info">
|
||||
The section-naming asymmetry — `[profile dev]` in config, `[dev]` in credentials
|
||||
— is the AWS convention, kept so existing habits and tooling carry over. The
|
||||
`default` profile is spelled `[default]` in both.
|
||||
</Callout>
|
||||
|
||||
## Environment variables
|
||||
|
||||
| Variable | Effect |
|
||||
| --- | --- |
|
||||
| `SIM_PROFILE` | Profile to use |
|
||||
| `SIM_ENDPOINT` | Deployment to talk to |
|
||||
| `SIM_API_KEY` | API key — skips `sim login` entirely |
|
||||
| `SIM_WORKSPACE` | Workspace to target |
|
||||
| `SIM_OUTPUT` | Output format |
|
||||
| `SIM_CONFIG_DIR` | Relocate both files away from `~/.sim` |
|
||||
| `SIM_CONFIG_FILE` | Relocate only the config file |
|
||||
| `SIM_CREDENTIALS_FILE` | Relocate only the credentials file |
|
||||
|
||||
For CI, set `SIM_API_KEY` and `SIM_WORKSPACE` and nothing needs to touch the
|
||||
filesystem at all.
|
||||
|
||||
## Choosing a workspace
|
||||
|
||||
Workspace-scoped commands need a workspace. Supply it per command, save it to
|
||||
the profile, or set it in the environment:
|
||||
|
||||
```bash
|
||||
sim tables list --workspace ws_other
|
||||
sim configure --set-workspace ws_abc123
|
||||
export SIM_WORKSPACE=ws_abc123
|
||||
```
|
||||
|
||||
Without one, the command fails and tells you how to set it. A few commands —
|
||||
`sim billing status`, `sim billing logs`, and `sim audit-logs list` — accept
|
||||
`--all-workspaces` to drop the filter instead. It cannot be combined with
|
||||
`--workspace`.
|
||||
|
||||
## Repairing a bad setting
|
||||
|
||||
An invalid `output` value fails with the list of accepted formats. Because a
|
||||
higher-priority source still wins, you can repair a profile without editing the
|
||||
file by hand:
|
||||
|
||||
```bash
|
||||
sim --output table configure --set-output json
|
||||
```
|
||||
@@ -0,0 +1,144 @@
|
||||
---
|
||||
title: Credentials
|
||||
description: Manage credentials — every subcommand, argument, and flag
|
||||
---
|
||||
|
||||
import { CommandTable } from '@/components/ui/command-table'
|
||||
|
||||
`sim credentials` is also spelled `sim credential`.
|
||||
|
||||
Every command below also accepts the [global options](/cli/commands#global-options).
|
||||
|
||||
## Disconnect credential
|
||||
|
||||
```bash
|
||||
sim credentials delete <credentialId> [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `credentialId` | Yes | Credential to disconnect. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `-y, --yes` | No | Skip the confirmation. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## List credential providers
|
||||
|
||||
```bash
|
||||
sim credentials providers list [options]
|
||||
```
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--search <value>` | No | Case-insensitive substring match against the credential provider name. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## List credentials
|
||||
|
||||
```bash
|
||||
sim credentials list [options]
|
||||
```
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--type <value>` | No | Restrict results to this credential type. Accepted values: `oauth`, `service_account`. |
|
||||
| `--provider-id <value>` | No | Restrict results to credentials for this integration provider. |
|
||||
| `--search <value>` | No | Case-insensitive substring match against the credential display name. |
|
||||
| `--sort-by <value>` | No | Field used to sort the result. Accepted values: `displayName`, `createdAt`, `updatedAt`. |
|
||||
| `--sort-order <value>` | No | Sort direction. Accepted values: `asc`, `desc`. |
|
||||
| `--limit <n>` | No | Maximum items to return (0 for everything). Defaults to `100`. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Create a service-account credential using its discovered provider schema
|
||||
|
||||
```bash
|
||||
sim credentials create <providerId> [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `providerId` | Yes | Service-account provider to create a credential for |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--name <displayName>` | Yes | Name shown for the credential in Sim. |
|
||||
| `--credentials <json\|@file>` | Yes | Provider credentials as JSON (or @path / @- to read a file or stdin). |
|
||||
| `--description <description>` | No | Optional credential description. |
|
||||
| `--id <credentialId>` | No | Client-generated credential ID when provider discovery requires it. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Create a short-lived link for connecting an OAuth provider
|
||||
|
||||
```bash
|
||||
sim credentials connect <providerId> [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `providerId` | Yes | OAuth provider to connect |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--name <displayName>` | Yes | Name shown for the new credential in Sim. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Create a short-lived link for reconnecting an OAuth credential
|
||||
|
||||
```bash
|
||||
sim credentials reconnect <credentialId>
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `credentialId` | Yes | Existing OAuth credential to re-authorize |
|
||||
|
||||
</CommandTable>
|
||||
@@ -0,0 +1,117 @@
|
||||
---
|
||||
title: Custom Tools
|
||||
description: Manage custom tools — every subcommand, argument, and flag
|
||||
---
|
||||
|
||||
import { CommandTable } from '@/components/ui/command-table'
|
||||
|
||||
`sim custom-tools` is also spelled `sim custom-tool`.
|
||||
|
||||
Every command below also accepts the [global options](/cli/commands#global-options).
|
||||
|
||||
## Create custom tool
|
||||
|
||||
```bash
|
||||
sim custom-tools create [options]
|
||||
```
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--title <value>` | Yes | Display title, unique within the workspace. |
|
||||
| `--schema <json\|@file>` | Yes | OpenAI function schema: {"type":"function","function":{"name":"...","parameters":{"type":"object","properties":{}}}} (JSON, or @path / @- to read a file or stdin). |
|
||||
| `--code <value>` | Yes | Tool implementation executed in the sandboxed function runtime. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Delete custom tool
|
||||
|
||||
```bash
|
||||
sim custom-tools delete <id> [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `id` | Yes | Unique custom tool identifier. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `-y, --yes` | No | Skip the confirmation. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Get custom tool
|
||||
|
||||
```bash
|
||||
sim custom-tools get <id>
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `id` | Yes | Unique custom tool identifier. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## List custom tools
|
||||
|
||||
```bash
|
||||
sim custom-tools list [options]
|
||||
```
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--search <value>` | No | Case-insensitive substring match against the tool title. |
|
||||
| `--sort-by <value>` | No | Field used to sort the result. Accepted values: `title`, `createdAt`, `updatedAt`. |
|
||||
| `--sort-order <value>` | No | Sort direction. Accepted values: `asc`, `desc`. |
|
||||
| `--limit <n>` | No | Maximum items to return (0 for everything). Defaults to `100`. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Update custom tool
|
||||
|
||||
```bash
|
||||
sim custom-tools update <id> [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `id` | Yes | Unique custom tool identifier. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--title <value>` | No | New display title for the tool. |
|
||||
| `--schema <json\|@file>` | No | OpenAI function schema: {"type":"function","function":{"name":"...","parameters":{"type":"object","properties":{}}}} (JSON, or @path / @- to read a file or stdin). |
|
||||
| `--code <value>` | No | Replacement tool implementation. |
|
||||
|
||||
</CommandTable>
|
||||
@@ -0,0 +1,433 @@
|
||||
---
|
||||
title: Files
|
||||
description: Manage files — every subcommand, argument, and flag
|
||||
---
|
||||
|
||||
import { CommandTable } from '@/components/ui/command-table'
|
||||
|
||||
`sim files` is also spelled `sim file`.
|
||||
|
||||
Every command below also accepts the [global options](/cli/commands#global-options).
|
||||
|
||||
## Delete several files at once
|
||||
|
||||
```bash
|
||||
sim files batch-delete [options]
|
||||
```
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--file-ids <value...>` | Yes | File identifiers to update. (space-separated, or @path / @- with one value per line). |
|
||||
| `-y, --yes` | No | Skip the confirmation. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Create file
|
||||
|
||||
```bash
|
||||
sim files create [options]
|
||||
```
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--name <value>` | Yes | File name, including its extension. Path separators and dot segments are rejected. |
|
||||
| `--content-type <value>` | No | MIME type. When omitted, it is inferred from the file extension. |
|
||||
| `--folder <value>` | No | Folder path; the leading / is optional. |
|
||||
| `--content <value>` | No | Initial file content. Omit or send an empty string for a zero-byte file. The 70,000,000-character bound guards the JSON envelope; the decoded bytes must be at most 50 MiB, and a longer base64 payload is rejected with `413`. Use an upload session for anything larger. |
|
||||
| `--encoding <value>` | No | Encoding of the content field. Accepted values: `utf-8`, `base64`. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Create a file folder at a path
|
||||
|
||||
```bash
|
||||
sim files folders create <path>
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `path` | Yes | Folder path; the leading / is optional |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Delete folder
|
||||
|
||||
```bash
|
||||
sim files folders delete <path> [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `path` | Yes | Folder path; the leading / is optional |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--recursive` | No | Delete the folder and its descendants. |
|
||||
| `-y, --yes` | No | Skip the confirmation. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## List folders
|
||||
|
||||
```bash
|
||||
sim files folders list [options]
|
||||
```
|
||||
|
||||
Also available as `sim files folders ls`.
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--parent <value>` | No | Direct parent folder path. |
|
||||
| `--search <value>` | No | Case-insensitive substring match against the folder name. |
|
||||
| `--sort-by <value>` | No | Field used to sort the result. Sorting by `name` is case-sensitive and follows the storage collation, so do not rely on a case-insensitive order. Accepted values: `name`, `createdAt`, `updatedAt`. |
|
||||
| `--sort-order <value>` | No | Sort direction. Accepted values: `asc`, `desc`. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Rename or move a file folder
|
||||
|
||||
```bash
|
||||
sim files folders move <path> <destination>
|
||||
```
|
||||
|
||||
Also available as `sim files folders mv`.
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `path` | Yes | Folder path; the leading / is optional |
|
||||
| `destination` | Yes | Folder path; the leading / is optional |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Delete file
|
||||
|
||||
```bash
|
||||
sim files delete <fileId> [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `fileId` | Yes | File identifier. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `-y, --yes` | No | Skip the confirmation. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Show file metadata and sharing status
|
||||
|
||||
```bash
|
||||
sim files describe <fileId> [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `fileId` | Yes | File identifier. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--scope <value>` | No | Which lifecycle set to read from: `active` (default) resolves live files only and returns `404` for a file a `DELETE` soft-deleted; `archived` also resolves soft-deleted files, so metadata stays readable before `POST /files/{fileId}/restore`. Authorization is identical for both. Accepted values: `active`, `archived`. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Show a file’s share settings
|
||||
|
||||
```bash
|
||||
sim files share get <fileId>
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `fileId` | Yes | File identifier. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Enable or disable sharing for a file
|
||||
|
||||
```bash
|
||||
sim files share set <fileId> [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `fileId` | Yes | File identifier. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--is-active <true\|false>` | Yes | Whether the share should resolve. Disabling preserves the token and the whole access configuration, so re-enabling restores the share as it was; enabling rewrites the credentials the resulting mode does not use. Accepted values: `true`, `false`. |
|
||||
| `--auth-type <value>` | No | How access to the share is gated. The stored mode is kept when omitted. Enabling `public` clears the stored password and empties `allowedEmails`; `password` empties `allowedEmails`; `email` and `sso` clear the stored password. Accepted values: `public`, `password`, `email`, `sso`. |
|
||||
| `--password <value>` | No | Password for a password-gated share. Kept when omitted; enabling `password` with neither a supplied nor a stored password is a 400. |
|
||||
| `--allowed-emails <value...>` | No | Allowed addresses or `@domain` patterns for email and SSO shares. Kept when omitted; enabling `email` or `sso` with an empty resulting list is a 400. (space-separated, or @path / @- with one value per line). |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## List files
|
||||
|
||||
```bash
|
||||
sim files list [options]
|
||||
```
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--folder <value>` | No | Folder path; the leading / is optional. |
|
||||
| `--scope <value>` | No | Which lifecycle set to list: `active` (default) for live files, `archived` for files a `DELETE` soft-deleted. `folderPath` resolves against active folders only, so pairing it with `scope=archived` returns an empty page when the containing folder was archived too. Accepted values: `active`, `archived`. |
|
||||
| `--search <value>` | No | Case-insensitive substring match against the file name. |
|
||||
| `--sort-by <value>` | No | Field used to sort the result. Sorting by `name` is case-sensitive and follows the storage collation, so do not rely on a case-insensitive order. Accepted values: `name`, `size`, `uploadedAt`, `updatedAt`. |
|
||||
| `--sort-order <value>` | No | Sort direction. Accepted values: `asc`, `desc`. |
|
||||
| `--limit <n>` | No | Maximum items to return (0 for everything). Defaults to `100`. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Move files into another folder
|
||||
|
||||
```bash
|
||||
sim files move [options]
|
||||
```
|
||||
|
||||
Also available as `sim files mv`.
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--file-ids <value...>` | Yes | File identifiers to update. (space-separated, or @path / @- with one value per line). |
|
||||
| `--to <value>` | No | Destination folder path; omit for root. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Rename a file
|
||||
|
||||
```bash
|
||||
sim files rename <fileId> [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `fileId` | Yes | File identifier. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--name <value>` | Yes | New file name, including its extension. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Restore file
|
||||
|
||||
```bash
|
||||
sim files restore create <fileId>
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `fileId` | Yes | File identifier. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Replace a file’s contents
|
||||
|
||||
```bash
|
||||
sim files set-content <fileId> [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `fileId` | Yes | File identifier. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--content <value>` | Yes | Complete replacement content for the file. The 70,000,000-character bound guards the JSON envelope; the decoded bytes must be at most 50 MiB, and a longer base64 payload is rejected with `413`. |
|
||||
| `--encoding <value>` | No | Content encoding. Accepted values: `utf-8`, `base64`. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Upload a file to the workspace
|
||||
|
||||
```bash
|
||||
sim files upload <path> [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `path` | Yes | Local file to upload |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--folder <path>` | No | Destination folder path (defaults to /). |
|
||||
| `--name <name>` | No | Store it under a different name. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Get a file’s content
|
||||
|
||||
```bash
|
||||
sim files get <fileId> [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `fileId` | Yes | File whose content to read |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `-o, --output-file <path>` | No | Write content to a file instead of stdout. |
|
||||
| `--force` | No | Overwrite --output-file if it already exists. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## List file resources and child folders together
|
||||
|
||||
```bash
|
||||
sim files ls [path] [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `path` | No | Folder path to list; defaults to the root folder |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--search <text>` | No | Filter folders and resources by name. |
|
||||
| `--limit <n>` | No | Maximum combined items to return (0 for everything). Defaults to `100`. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Create a file directory at a path
|
||||
|
||||
```bash
|
||||
sim files mkdir <path>
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `path` | Yes | Folder path to create; the leading / is optional |
|
||||
|
||||
</CommandTable>
|
||||
@@ -0,0 +1,162 @@
|
||||
---
|
||||
title: Sim CLI
|
||||
description: Drive workflows, tables, files, knowledge bases, and logs from your shell
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout'
|
||||
import { Step, Steps } from 'fumadocs-ui/components/steps'
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
|
||||
|
||||
`sim` is the command line for Sim. Sign in once, then run workflows, query tables,
|
||||
move files, search knowledge bases, and read run logs from the terminal. Every
|
||||
command has a `--output json` mode, so results pipe cleanly into `jq`, cron jobs,
|
||||
CI pipelines, and any other tool you already use.
|
||||
|
||||
## Install
|
||||
|
||||
<Tabs items={['npm', 'pnpm', 'bun', 'yarn']}>
|
||||
<Tab value="npm">
|
||||
```bash
|
||||
npm install --global sim
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="pnpm">
|
||||
```bash
|
||||
pnpm add --global sim
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="bun">
|
||||
```bash
|
||||
bun add --global sim
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="yarn">
|
||||
```bash
|
||||
yarn global add sim
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
The CLI needs **Node.js 20 or newer**. Verify the install:
|
||||
|
||||
```bash
|
||||
sim --version
|
||||
```
|
||||
|
||||
Prefer using Sim as a library? See the [TypeScript](/api-reference/typescript)
|
||||
and [Python](/api-reference/python) SDKs, or call the
|
||||
[HTTP API](/api-reference/getting-started) directly.
|
||||
|
||||
## Your first command
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>
|
||||
### Sign in
|
||||
|
||||
```bash
|
||||
sim login
|
||||
```
|
||||
|
||||
The terminal prints a pairing code and a URL. Approve it in the browser, pick a
|
||||
workspace, and the key comes back over the CLI's own connection. Nothing
|
||||
redeemable crosses the browser leg and there is no loopback listener, so this
|
||||
works over SSH and inside containers.
|
||||
|
||||
See [Authentication](/cli/authentication) for CI keys, multiple accounts, and
|
||||
self-hosted deployments.
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Check what you are pointed at
|
||||
|
||||
```bash
|
||||
sim whoami
|
||||
```
|
||||
|
||||
This prints the resolved endpoint, workspace, and output format — and **where
|
||||
each one came from**. It is the fastest way to explain a surprising result.
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### List your workflows
|
||||
|
||||
```bash
|
||||
sim workflows list
|
||||
```
|
||||
|
||||
```
|
||||
ID NAME FOLDER DEPLOYED RUNS LAST RUN
|
||||
wf_7Yb2 Refund triage /Support yes 412 2026-08-15 14:02:11
|
||||
wf_9Kd4 Weekly digest /Reporting no 18 2026-08-11 09:00:04
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Run one
|
||||
|
||||
```bash
|
||||
sim workflows run wf_7Yb2 --input '{"ticketId":"T-4821"}'
|
||||
```
|
||||
|
||||
A workflow must be deployed before it can be run. Deploy from the editor, or
|
||||
with `sim workflows deploy <id>`.
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
## How commands are shaped
|
||||
|
||||
Every command reads the same way:
|
||||
|
||||
```bash
|
||||
sim <resource> [sub-resource] <verb> [arguments] [options]
|
||||
```
|
||||
|
||||
```bash
|
||||
sim workflows list
|
||||
sim tables rows query tbl_123 --limit 50
|
||||
sim knowledge documents upload kb_123 ./handbook.pdf
|
||||
```
|
||||
|
||||
Resource groups are plural, and each also accepts its singular spelling —
|
||||
`sim workflow get` and `sim workflows get` are the same command. `knowledge`
|
||||
also answers to `kb`.
|
||||
|
||||
Every command accepts `--help`, at any depth:
|
||||
|
||||
```bash
|
||||
sim --help
|
||||
sim tables --help
|
||||
sim tables rows query --help
|
||||
```
|
||||
|
||||
## What you can do
|
||||
|
||||
| Group | What it covers |
|
||||
| --- | --- |
|
||||
| [`workflows`](/cli/workflows) | Run, deploy, roll back, import, export, and organize workflows |
|
||||
| [`logs`](/cli/logs) | Read run diagnostics, including the full trace tree |
|
||||
| [`tables`](/cli/tables) | Query, insert, update, and import rows; manage columns and views |
|
||||
| [`files`](/cli/files) | Upload, download, share, and organize workspace files |
|
||||
| [`knowledge`](/cli/knowledge) | Search knowledge bases and manage their documents and tags |
|
||||
| [`skills`](/cli/skills) | Manage agent skills |
|
||||
| [`mcp-servers`](/cli/mcp-servers) | Manage MCP server connections and their tools |
|
||||
| [`custom-tools`](/cli/custom-tools) | Manage custom tool definitions |
|
||||
| [`credentials`](/cli/credentials) | Connect, reconnect, and disconnect integration credentials |
|
||||
| [`secrets`](/cli/secrets) | Set and remove workspace secrets |
|
||||
| [`billing`](/cli/billing) | Check plan status and credit usage |
|
||||
| [`audit-logs`](/cli/audit-logs) | Read organization audit logs |
|
||||
| [`workspaces`](/cli/workspaces) | Inspect the active workspace and its members |
|
||||
|
||||
The [command reference](/cli/commands) documents every subcommand, argument, and
|
||||
flag, and is generated from the CLI itself.
|
||||
|
||||
## Where to go next
|
||||
|
||||
- [Authentication](/cli/authentication) — signing in, API keys for CI, and multiple accounts
|
||||
- [Configuration](/cli/configuration) — profiles, config files, environment variables, and precedence
|
||||
- [Output formats](/cli/output) — `table`, `json`, `yaml`, and `text`, and when to use each
|
||||
- [Scripting](/cli/scripting) — piping, file inputs, exit codes, and automation recipes
|
||||
- [Troubleshooting](/cli/troubleshooting) — what each error means, and how to resolve it
|
||||
- [Command reference](/cli/commands) — every command, argument, and flag
|
||||
@@ -0,0 +1,488 @@
|
||||
---
|
||||
title: Knowledge
|
||||
description: Manage knowledge — every subcommand, argument, and flag
|
||||
---
|
||||
|
||||
import { CommandTable } from '@/components/ui/command-table'
|
||||
|
||||
`sim knowledge` is also spelled `sim kb`.
|
||||
|
||||
Every command below also accepts the [global options](/cli/commands#global-options).
|
||||
|
||||
## Enable or disable every matching document
|
||||
|
||||
```bash
|
||||
sim knowledge documents batch-update <knowledgeBaseId> [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `knowledgeBaseId` | Yes | Unique knowledge base identifier. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--operation <value>` | Yes | Whether the selected documents become enabled or disabled for search. Accepted values: `enable`, `disable`. |
|
||||
| `--document <value...>` | No | Documents to update, by identifier. (space-separated, or @path / @- with one value per line). |
|
||||
| `--select-all` | No | Apply to every document in the knowledge base. |
|
||||
| `--enabled-filter <value>` | No | With `selectAll`, restrict the update to documents in this state. Accepted values: `all`, `enabled`, `disabled`. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Delete document
|
||||
|
||||
```bash
|
||||
sim knowledge documents delete <knowledgeBaseId> <documentId> [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `knowledgeBaseId` | Yes | Unique knowledge base identifier. |
|
||||
| `documentId` | Yes | Unique knowledge document identifier. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `-y, --yes` | No | Skip the confirmation. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Get document
|
||||
|
||||
```bash
|
||||
sim knowledge documents get <knowledgeBaseId> <documentId>
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `knowledgeBaseId` | Yes | Unique knowledge base identifier. |
|
||||
| `documentId` | Yes | Unique knowledge document identifier. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## List documents
|
||||
|
||||
```bash
|
||||
sim knowledge documents list <knowledgeBaseId> [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `knowledgeBaseId` | Yes | Unique knowledge base identifier. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--limit <n>` | No | Maximum items to return (0 for everything). Defaults to `100`. |
|
||||
| `--search <value>` | No | Case-insensitive substring match against the document filename. |
|
||||
| `--enabled-filter <value>` | No | Filter by whether documents are enabled for search. Accepted values: `all`, `enabled`, `disabled`. |
|
||||
| `--sort-by <value>` | No | Field used to sort the result. Sorting by `filename` is case-sensitive and follows the storage collation, so do not rely on a case-insensitive order. Accepted values: `filename`, `fileSize`, `tokenCount`, `chunkCount`, `uploadedAt`, `processingStatus`, `enabled`. |
|
||||
| `--sort-order <value>` | No | Sort direction. Accepted values: `asc`, `desc`. |
|
||||
| `--tag-filters <value>` | No | A JSON-encoded array of at most 10 tag filters, using the same display-name shape as knowledge search: `[{"tagName":"category","operator":"eq","value":"billing"}]`. Every filter must hold, including two that name the same tag. A name that is not defined in this knowledge base is rejected, never ignored. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Update document
|
||||
|
||||
```bash
|
||||
sim knowledge documents update <id> <documentId> [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `id` | Yes | Unique knowledge base identifier. |
|
||||
| `documentId` | Yes | Unique knowledge document identifier. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--filename <value>` | No | New filename for the document. |
|
||||
| `--enabled` | No | Whether the document participates in search. Disabling keeps it indexed. |
|
||||
| `--no-enabled` | No | Send --enabled as false. |
|
||||
| `--tag1 <value>` | No | New value for tag slot 1. |
|
||||
| `--tag2 <value>` | No | New value for tag slot 2. |
|
||||
| `--tag3 <value>` | No | New value for tag slot 3. |
|
||||
| `--tag4 <value>` | No | New value for tag slot 4. |
|
||||
| `--tag5 <value>` | No | New value for tag slot 5. |
|
||||
| `--tag6 <value>` | No | New value for tag slot 6. |
|
||||
| `--tag7 <value>` | No | New value for tag slot 7. |
|
||||
| `--number1 <value>` | No | New value for number tag slot 1. |
|
||||
| `--number2 <value>` | No | New value for number tag slot 2. |
|
||||
| `--number3 <value>` | No | New value for number tag slot 3. |
|
||||
| `--number4 <value>` | No | New value for number tag slot 4. |
|
||||
| `--number5 <value>` | No | New value for number tag slot 5. |
|
||||
| `--date1 <value>` | No | New value for date tag slot 1, formatted YYYY-MM-DD. |
|
||||
| `--date2 <value>` | No | New value for date tag slot 2, formatted YYYY-MM-DD. |
|
||||
| `--boolean1` | No | New value for boolean tag slot 1. |
|
||||
| `--no-boolean1` | No | Send --boolean1 as false. |
|
||||
| `--boolean2` | No | New value for boolean tag slot 2. |
|
||||
| `--no-boolean2` | No | Send --boolean2 as false. |
|
||||
| `--boolean3` | No | New value for boolean tag slot 3. |
|
||||
| `--no-boolean3` | No | Send --boolean3 as false. |
|
||||
| `--retry-processing` | No | Requeue a failed or stuck document for processing. Send it alone — no other field may accompany it — and it answers with a queue acknowledgement rather than the document. |
|
||||
| `--no-retry-processing` | No | Send --retry-processing as false. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Upload a document to a knowledge base
|
||||
|
||||
```bash
|
||||
sim knowledge documents upload <knowledgeBaseId> <path> [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `knowledgeBaseId` | Yes | Knowledge base to upload into |
|
||||
| `path` | Yes | Local file to upload |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--name <name>` | No | Store it under a different name. |
|
||||
| `--tag <value...>` | No | Document tags, in tag1 through tag7 order. |
|
||||
| `--recipe <name>` | No | Document processing recipe. |
|
||||
| `--lang <code>` | No | Document language code. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Create knowledge base
|
||||
|
||||
```bash
|
||||
sim knowledge create [options]
|
||||
```
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--name <value>` | Yes | Human-readable knowledge base name. |
|
||||
| `--description <value>` | No | Optional knowledge base description. |
|
||||
| `--chunking-config <json\|@file>` | No | Chunking configuration; defaults are applied when omitted. (JSON, or @path / @- to read a file or stdin). |
|
||||
| `--folder <value>` | No | Folder path; the leading / is optional. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Create a knowledge folder at a path
|
||||
|
||||
```bash
|
||||
sim knowledge folders create <path>
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `path` | Yes | Folder path; the leading / is optional |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Delete folder
|
||||
|
||||
```bash
|
||||
sim knowledge folders delete <path> [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `path` | Yes | Folder path; the leading / is optional |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--recursive` | No | Delete the folder and its descendants. |
|
||||
| `-y, --yes` | No | Skip the confirmation. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## List folders
|
||||
|
||||
```bash
|
||||
sim knowledge folders list [options]
|
||||
```
|
||||
|
||||
Also available as `sim knowledge folders ls`.
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--parent <value>` | No | Direct parent folder path. |
|
||||
| `--search <value>` | No | Case-insensitive substring match against the folder name. |
|
||||
| `--sort-by <value>` | No | Field used to sort the result. Sorting by `name` is case-sensitive and follows the storage collation, so do not rely on a case-insensitive order. Accepted values: `name`, `createdAt`, `updatedAt`. |
|
||||
| `--sort-order <value>` | No | Sort direction. Accepted values: `asc`, `desc`. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Rename or move a knowledge folder
|
||||
|
||||
```bash
|
||||
sim knowledge folders move <path> <destination>
|
||||
```
|
||||
|
||||
Also available as `sim knowledge folders mv`.
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `path` | Yes | Folder path; the leading / is optional |
|
||||
| `destination` | Yes | Folder path; the leading / is optional |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Delete knowledge base
|
||||
|
||||
```bash
|
||||
sim knowledge delete <id> [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `id` | Yes | Unique knowledge base identifier. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `-y, --yes` | No | Skip the confirmation. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Get knowledge base
|
||||
|
||||
```bash
|
||||
sim knowledge get <id>
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `id` | Yes | Unique knowledge base identifier. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## List knowledge bases
|
||||
|
||||
```bash
|
||||
sim knowledge list [options]
|
||||
```
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--folder <value>` | No | Folder path; the leading / is optional. |
|
||||
| `--search <value>` | No | Case-insensitive substring match against the resource name. |
|
||||
| `--sort-by <value>` | No | Field used to sort the result. Sorting by `name` is case-sensitive and follows the storage collation, so do not rely on a case-insensitive order. Accepted values: `name`, `createdAt`, `updatedAt`. |
|
||||
| `--sort-order <value>` | No | Sort direction. Accepted values: `asc`, `desc`. |
|
||||
| `--limit <n>` | No | Maximum items to return (0 for everything). Defaults to `100`. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## List tags
|
||||
|
||||
```bash
|
||||
sim knowledge tags list <id>
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `id` | Yes | Unique knowledge base identifier. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Search knowledge
|
||||
|
||||
```bash
|
||||
sim knowledge search [options]
|
||||
```
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--kb <value...>` | Yes | Knowledge base ID (repeatable) (space-separated, or @path / @- with one value per line). |
|
||||
| `--query <value>` | No | Text to search for. |
|
||||
| `--top-k <value>` | No | Maximum number of search results to return. Must be a whole number between 1 and 100; the boundary schema only bounds the range, so a fractional value is admitted here and then rejected with 400 during search. |
|
||||
| `--tag-filters <json\|@file>` | No | Tag filters as [{"tagName":"...","operator":"...","value":"..."}] (JSON, or @path / @- to read a file or stdin). |
|
||||
| `--search-mode <value>` | No | Search algorithm. Accepted values: `vector`, `hybrid`. |
|
||||
| `--reranker-enabled` | No | Re-order retrieved chunks with a reranking model before truncating to `topK`. Ignored for a tag-only search, and billed as an additional search unit. Reranking is best-effort — a provider failure falls back to vector ordering, so check `rerankerStatus` on the response. |
|
||||
| `--no-reranker-enabled` | No | Send --reranker-enabled as false. |
|
||||
| `--reranker-model <value>` | No | Reranking model to use when `rerankerEnabled` is true. Defaults to `rerank-v4.0-fast`. Accepted values: `rerank-v4.0-pro`, `rerank-v4.0-fast`, `rerank-v3.5`. |
|
||||
| `--reranker-input-count <value>` | No | How many candidate chunks to retrieve before reranking. Defaults to four times `topK`, capped at 100. A larger pool costs more retrieval work but gives the reranker more to choose from. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Update knowledge base
|
||||
|
||||
```bash
|
||||
sim knowledge update <id> [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `id` | Yes | Unique knowledge base identifier. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--name <value>` | No | New knowledge base name. |
|
||||
| `--description <value>` | No | New knowledge base description. |
|
||||
| `--chunking-config <json\|@file>` | No | New document chunking configuration. (JSON, or @path / @- to read a file or stdin). |
|
||||
| `--folder <value>` | No | Folder path; the leading / is optional. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Move a knowledge base to a folder
|
||||
|
||||
```bash
|
||||
sim knowledge mv <id> <folder>
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `id` | Yes | Unique knowledge base identifier. |
|
||||
| `folder` | Yes | Folder path; the leading / is optional |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## List knowledge resources and child folders together
|
||||
|
||||
```bash
|
||||
sim knowledge ls [path] [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `path` | No | Folder path to list; defaults to the root folder |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--search <text>` | No | Filter folders and resources by name. |
|
||||
| `--limit <n>` | No | Maximum combined items to return (0 for everything). Defaults to `100`. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Create a knowledge directory at a path
|
||||
|
||||
```bash
|
||||
sim knowledge mkdir <path>
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `path` | Yes | Folder path to create; the leading / is optional |
|
||||
|
||||
</CommandTable>
|
||||
@@ -0,0 +1,68 @@
|
||||
---
|
||||
title: Logs
|
||||
description: Manage logs — every subcommand, argument, and flag
|
||||
---
|
||||
|
||||
import { CommandTable } from '@/components/ui/command-table'
|
||||
|
||||
`sim logs` is also spelled `sim log`.
|
||||
|
||||
Every command below also accepts the [global options](/cli/commands#global-options).
|
||||
|
||||
## Show run diagnostics
|
||||
|
||||
```bash
|
||||
sim logs get <runId> [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `runId` | Yes | Unique workflow run identifier. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--trace` | No | Show expanded trace spans with inputs, outputs, errors, timing, and cost. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## List logs
|
||||
|
||||
```bash
|
||||
sim logs list [options]
|
||||
```
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--workflow <value...>` | No | Comma-separated workflow identifiers to include. An empty entry is rejected. (space-separated, or @path / @- with one value per line). |
|
||||
| `--trigger <value...>` | No | Comma-separated trigger types to include. An empty entry is rejected. Values are matched exactly and are case-sensitive — every recorded trigger is lowercase, so `API` matches nothing while `api` matches. The vocabulary is open: it covers the core trigger types (`manual`, `api`, `schedule`, `chat`, `webhook`, `mcp`, `copilot`, `workflow`, `custom_block`) and the provider id of any webhook trigger (`slack`, `gmail`, `github`, …), so an unrecognized member is not rejected — it selects no runs. The literal value `all` is a sentinel that disables this filter entirely, so a list containing it returns runs of every trigger type; no real trigger type is named `all`. (space-separated, or @path / @- with one value per line). |
|
||||
| `--level <value>` | No | Severity level to include. Accepted values: `info`, `error`. |
|
||||
| `--start-date <value>` | No | Only include runs started at or after this UTC ISO 8601 timestamp, e.g. `2026-08-06T00:00:00Z`. A date without a time, or a timestamp carrying a UTC offset instead of `Z`, is rejected, as is year `0000`, which names no storable instant. |
|
||||
| `--end-date <value>` | No | Only include runs started at or before this UTC ISO 8601 timestamp, e.g. `2026-08-06T00:00:00Z`. A date without a time, or a timestamp carrying a UTC offset instead of `Z`, is rejected, as is year `0000`, which names no storable instant. |
|
||||
| `--min-duration-ms <value>` | No | Minimum total execution duration in milliseconds. Whole milliseconds from 0 to 2147483647; the stored duration is a 32-bit integer, so a fractional or out-of-range bound is rejected. |
|
||||
| `--max-duration-ms <value>` | No | Maximum total execution duration in milliseconds. Whole milliseconds from 0 to 2147483647; the stored duration is a 32-bit integer, so a fractional or out-of-range bound is rejected. |
|
||||
| `--min-cost <value>` | No | Minimum execution cost in USD, from 0 to 1000000. A run is never charged a negative amount, so a negative bound is rejected rather than treated as a filter that matches every run. |
|
||||
| `--max-cost <value>` | No | Maximum execution cost in USD, from 0 to 1000000. A run is never charged a negative amount, so a negative bound is rejected rather than treated as a filter that matches every run. |
|
||||
| `--model <value>` | No | AI model used during execution. |
|
||||
| `--details <value>` | No | Response detail level. Accepted values: `basic`, `full`. |
|
||||
| `--include-trace-spans` | No | Include trace spans in JSON or YAML output (implies full detail). |
|
||||
| `--include-final-output` | No | Include final output in JSON or YAML output (implies full detail). |
|
||||
| `--limit <n>` | No | Maximum items to return (0 for everything). Defaults to `100`. |
|
||||
| `--order <value>` | No | Sort direction by execution start time. This list is sortable only by execution start time, so it takes `order` in place of `sortBy`/`sortOrder`, which it rejects. Accepted values: `asc`, `desc`. |
|
||||
| `--run-id <value>` | No | Exact run identifier to match. |
|
||||
| `--folder <value...>` | No | Folder path; the leading / is optional (space-separated, or @path / @- with one value per line). |
|
||||
|
||||
</CommandTable>
|
||||
@@ -0,0 +1,162 @@
|
||||
---
|
||||
title: MCP Servers
|
||||
description: Manage mcp servers — every subcommand, argument, and flag
|
||||
---
|
||||
|
||||
import { CommandTable } from '@/components/ui/command-table'
|
||||
|
||||
`sim mcp-servers` is also spelled `sim mcp-server`.
|
||||
|
||||
Every command below also accepts the [global options](/cli/commands#global-options).
|
||||
|
||||
## Create MCP server
|
||||
|
||||
```bash
|
||||
sim mcp-servers create [options]
|
||||
```
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--name <value>` | Yes | Server display name. |
|
||||
| `--description <value>` | No | Optional server description. |
|
||||
| `--transport <value>` | No | Transport used to communicate with the server. Applied server-side as `streamable-http` when omitted on create. Accepted values: `streamable-http`. |
|
||||
| `--url <value>` | Yes | Absolute HTTP or HTTPS endpoint URL without `{{ENV_VAR}}` references. It determines server identity and is immutable: delete and recreate the server to change endpoints. |
|
||||
| `--auth-type <value>` | No | Authentication method. When omitted, and no `headers` are sent, registration probes the endpoint once to classify it, falling back to `headers` when the probe fails or the server does not advertise OAuth. A server publishing RFC 9728 metadata is therefore stored as `oauth`, and headers configured afterwards will not authenticate — send this field explicitly to pin the method. Accepted values: `none`, `headers`, `oauth`. |
|
||||
| `--headers <json\|@file>` | No | Write-only request headers sent to the server. Replaced wholesale rather than merged on update: sending this field drops every stored header it does not repeat. (JSON, or @path / @- to read a file or stdin). |
|
||||
| `--timeout <value>` | No | Per-request timeout in milliseconds. Applied server-side as 30000 when omitted on create. |
|
||||
| `--retries <value>` | No | Number of retries per request. Applied server-side as 3 when omitted on create. |
|
||||
| `--enabled` | No | Whether the server tools are available to workflows. Applied server-side as true when omitted on create. |
|
||||
| `--no-enabled` | No | Send --enabled as false. |
|
||||
| `--oauth-client-id <value>` | No | Pre-registered OAuth client identifier. Changing it on update revokes the stored OAuth grant and forces reauthorization. |
|
||||
| `--oauth-client-secret <value>` | No | Write-only pre-registered OAuth client secret. Sending it on update as null or a new value revokes the stored OAuth grant and forces reauthorization, as does switching away from OAuth authentication. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Delete MCP server
|
||||
|
||||
```bash
|
||||
sim mcp-servers delete <id> [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `id` | Yes | Unique MCP server identifier. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `-y, --yes` | No | Skip the confirmation. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Get MCP server
|
||||
|
||||
```bash
|
||||
sim mcp-servers get <id>
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `id` | Yes | Unique MCP server identifier. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## List MCP servers
|
||||
|
||||
```bash
|
||||
sim mcp-servers list [options]
|
||||
```
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--search <value>` | No | Case-insensitive substring match against the server name. |
|
||||
| `--sort-by <value>` | No | Field used to sort the result. Sorting by `name` is case-sensitive and follows the storage collation, so do not rely on a case-insensitive order. Accepted values: `name`, `createdAt`, `updatedAt`. |
|
||||
| `--sort-order <value>` | No | Sort direction. Accepted values: `asc`, `desc`. |
|
||||
| `--limit <n>` | No | Maximum items to return (0 for everything). Defaults to `100`. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## List MCP server tools
|
||||
|
||||
```bash
|
||||
sim mcp-servers tools list <id> [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `id` | Yes | Unique MCP server identifier. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--refresh` | No | Bypass the short-lived per-workspace tool cache and reconnect under your own credentials. A cached result reflects whichever workspace member last ran discovery, so this is the only way to pick up a tool added since then; it costs a live round trip. |
|
||||
| `--no-refresh` | No | Send --refresh as false. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Update MCP server
|
||||
|
||||
```bash
|
||||
sim mcp-servers update <id> [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `id` | Yes | Unique MCP server identifier. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--name <value>` | No | Server display name. |
|
||||
| `--description <value>` | No | Optional server description. |
|
||||
| `--transport <value>` | No | Transport used to communicate with the server. Applied server-side as `streamable-http` when omitted on create. Accepted values: `streamable-http`. |
|
||||
| `--url <value>` | No | Immutable server URL. When provided, it must equal the current URL; use delete and create to change endpoints. |
|
||||
| `--auth-type <value>` | No | Authentication method. When omitted, and no `headers` are sent, registration probes the endpoint once to classify it, falling back to `headers` when the probe fails or the server does not advertise OAuth. A server publishing RFC 9728 metadata is therefore stored as `oauth`, and headers configured afterwards will not authenticate — send this field explicitly to pin the method. Accepted values: `none`, `headers`, `oauth`. |
|
||||
| `--headers <json\|@file>` | No | Write-only request headers sent to the server. Replaced wholesale rather than merged on update: sending this field drops every stored header it does not repeat. (JSON, or @path / @- to read a file or stdin). |
|
||||
| `--timeout <value>` | No | Per-request timeout in milliseconds. Applied server-side as 30000 when omitted on create. |
|
||||
| `--retries <value>` | No | Number of retries per request. Applied server-side as 3 when omitted on create. |
|
||||
| `--enabled` | No | Whether the server tools are available to workflows. Applied server-side as true when omitted on create. |
|
||||
| `--no-enabled` | No | Send --enabled as false. |
|
||||
| `--oauth-client-id <value>` | No | Pre-registered OAuth client identifier. Changing it on update revokes the stored OAuth grant and forces reauthorization. |
|
||||
| `--oauth-client-secret <value>` | No | Write-only pre-registered OAuth client secret. Sending it on update as null or a new value revokes the stored OAuth grant and forces reauthorization, as does switching away from OAuth authentication. |
|
||||
|
||||
</CommandTable>
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"title": "CLI",
|
||||
"root": true,
|
||||
"pages": [
|
||||
"---Sim CLI---",
|
||||
"index",
|
||||
"authentication",
|
||||
"configuration",
|
||||
"output",
|
||||
"scripting",
|
||||
"troubleshooting",
|
||||
"---Commands---",
|
||||
"commands",
|
||||
"audit-logs",
|
||||
"billing",
|
||||
"credentials",
|
||||
"custom-tools",
|
||||
"files",
|
||||
"knowledge",
|
||||
"logs",
|
||||
"mcp-servers",
|
||||
"secrets",
|
||||
"skills",
|
||||
"tables",
|
||||
"workflows",
|
||||
"workspaces",
|
||||
"reference"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
---
|
||||
title: Output formats
|
||||
description: table, json, yaml, and text — what each is for, and how to select one
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout'
|
||||
|
||||
Every command renders through the same four formats. Pick one per command with
|
||||
`--output`, save a profile default with `sim configure --set-output`, or set
|
||||
`SIM_OUTPUT` ambiently for CI.
|
||||
|
||||
| Format | For |
|
||||
| --- | --- |
|
||||
| `table` | reading (default) |
|
||||
| `json` | piping into `jq` |
|
||||
| `yaml` | piping into anything that reads YAML |
|
||||
| `text` | shell loops — tab-separated, no header, no colour |
|
||||
|
||||
```bash
|
||||
sim --output json tables get tbl_123 # before the command
|
||||
sim tables get tbl_123 --output json # or after; both work
|
||||
sim configure --set-output json # for this profile, from now on
|
||||
SIM_OUTPUT=yaml sim logs list > logs.yaml # for one invocation or a whole job
|
||||
```
|
||||
|
||||
## Raw values versus rendered cells
|
||||
|
||||
`json` and `yaml` emit the API's **raw** values, not the table's formatting — a
|
||||
duration stays `1500`, not `"1.5s"`. Switching format never changes the data,
|
||||
only how it is displayed.
|
||||
|
||||
`text` uses the rendered cells, because it exists for shell plumbing rather than
|
||||
parsing.
|
||||
|
||||
<Callout type="info">
|
||||
An absent value prints as an em-dash (`—`) in `table` and as an **empty field**
|
||||
in `text`, so emptiness tests downstream behave as you would expect.
|
||||
</Callout>
|
||||
|
||||
## table
|
||||
|
||||
The default. Uppercase dim headers, one row per record, values formatted for
|
||||
reading — timestamps without milliseconds, sizes as `4.2 MB`, booleans as
|
||||
`yes`/`no`, costs as `$0.0142`.
|
||||
|
||||
Long cells are clipped so rows stay on one line. When you need the untruncated
|
||||
value, switch to `json`.
|
||||
|
||||
```bash
|
||||
sim files list
|
||||
```
|
||||
|
||||
## json
|
||||
|
||||
```bash
|
||||
sim logs list --level error --output json | jq -r '.[].runId'
|
||||
sim tables rows query tbl_123 --output json | jq '.[] | select(.score > 10)'
|
||||
```
|
||||
|
||||
## yaml
|
||||
|
||||
```bash
|
||||
sim workflows get wf_123 --output yaml
|
||||
sim logs get run_123 --output yaml > run.yaml
|
||||
```
|
||||
|
||||
## text
|
||||
|
||||
Tab-separated, no header, no colour — built for `read` loops:
|
||||
|
||||
```bash
|
||||
SIM_OUTPUT=text sim files list | while IFS=$'\t' read -r id name folder size type uploader uploaded; do
|
||||
echo "$id $name"
|
||||
done
|
||||
```
|
||||
|
||||
## Reading a run in detail
|
||||
|
||||
`sim logs get` keeps its default human output concise. Add `--trace` for the
|
||||
expanded recursive trace — span inputs, outputs, errors, timing, and cost:
|
||||
|
||||
```bash
|
||||
sim logs get run_123 --trace
|
||||
```
|
||||
|
||||
`json` and `yaml` always carry the complete structured response, so `--trace` is
|
||||
a no-op there — the data is already present:
|
||||
|
||||
```bash
|
||||
sim logs get run_123 --output json | jq '.traceSpans'
|
||||
sim logs list --include-trace-spans --output json
|
||||
```
|
||||
|
||||
## Commands that ignore the format
|
||||
|
||||
`sim profiles` and `sim configure`'s listing mode always print for humans — they
|
||||
report on your local configuration rather than on API data.
|
||||
|
||||
`sim workflows export` is the opposite case: it emits a document rather than a
|
||||
record to look at, so it prints raw JSON — or YAML when the profile says so —
|
||||
whatever the display format is. That is what makes it round-trip:
|
||||
|
||||
```bash
|
||||
sim workflows export wf_123 > wf.json
|
||||
sim workflows import --workflow @wf.json
|
||||
```
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,178 @@
|
||||
---
|
||||
title: Scripting
|
||||
description: File and stdin inputs, list flags, pagination, exit codes, and automation recipes
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout'
|
||||
|
||||
The CLI is built to be driven by other programs. Everything below applies to
|
||||
every command.
|
||||
|
||||
## Reading input from files and stdin
|
||||
|
||||
Any flag that takes JSON or a list also accepts `@path` to read a file, or `@-`
|
||||
to read stdin.
|
||||
|
||||
```bash
|
||||
sim workflows import --workflow @wf.json
|
||||
sim tables rows query tbl_123 --filter @filter.json
|
||||
cat wf.json | sim workflows import --workflow @-
|
||||
```
|
||||
|
||||
This keeps large payloads out of your shell history and out of the argument
|
||||
length limit.
|
||||
|
||||
## List flags
|
||||
|
||||
Primitive lists take space-separated values. With `@`, the file supplies one
|
||||
value per line:
|
||||
|
||||
```bash
|
||||
sim files mv --file-ids file_1 file_2 --to Archive
|
||||
sim files mv --file-ids @file-ids.txt --to Archive
|
||||
printf 'file_1\nfile_2\n' | sim files mv --file-ids @- --to Archive
|
||||
```
|
||||
|
||||
Arrays of objects stay JSON inputs, because they cannot be flattened to a list
|
||||
without losing structure.
|
||||
|
||||
## Filtering table rows
|
||||
|
||||
`--filter` takes the same predicate tree the API uses: `all` (AND) or `any` (OR)
|
||||
groups of `{field, op, value}` conditions, nestable. It is JSON because the
|
||||
grammar is a tree, and there is no honest flag encoding for one.
|
||||
|
||||
```bash
|
||||
sim tables rows query tbl_123 \
|
||||
--filter '{"all":[{"field":"status","op":"eq","value":"open"},
|
||||
{"field":"score","op":"gt","value":10}]}' \
|
||||
--limit 50
|
||||
```
|
||||
|
||||
Operators: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `in`, `nin`, `contains`,
|
||||
`ncontains`, `startsWith`, `endsWith`, `like`, `ilike`, `nlike`, `nilike`,
|
||||
`isEmpty`, `isNotEmpty`, `isNull`, `isNotNull`.
|
||||
|
||||
`--sort` is also JSON, an ordered list of keys:
|
||||
|
||||
```bash
|
||||
sim tables rows query tbl_123 --sort '[{"field":"createdAt","direction":"desc"}]'
|
||||
```
|
||||
|
||||
Row columns are discovered at runtime from the returned data, unioned across the
|
||||
page, so a sparse row does not hide a column.
|
||||
|
||||
## Pagination
|
||||
|
||||
List commands page automatically up to `--limit`, which defaults to `100`. Pass
|
||||
`--limit 0` to fetch everything:
|
||||
|
||||
```bash
|
||||
sim logs list --limit 0 --output json > all-logs.json
|
||||
```
|
||||
|
||||
## Destructive commands
|
||||
|
||||
Deletions require an explicit selector **and** `--yes`. There is no "delete
|
||||
everything" default:
|
||||
|
||||
```bash
|
||||
sim tables rows batch-delete tbl_123 --row row_1 row_2 --yes
|
||||
sim files delete file_123 --yes
|
||||
```
|
||||
|
||||
Without `--yes` the command explains what it would have destroyed and stops.
|
||||
|
||||
<Callout type="warn">
|
||||
`batch-delete` and `batch-update` also carry the standard `--limit` default of
|
||||
`100`. Set `--limit 0` when you intend to affect every matching row.
|
||||
</Callout>
|
||||
|
||||
## Exit codes
|
||||
|
||||
| Code | Meaning |
|
||||
| --- | --- |
|
||||
| `0` | Success |
|
||||
| `1` | Anything else — API error, bad configuration, invalid arguments, or a missing `--yes` |
|
||||
|
||||
Errors print one line to stderr, prefixed `Error:`, plus the API's error code and
|
||||
validation details when it supplies them. Failures are safe to branch on:
|
||||
|
||||
```bash
|
||||
if ! sim workflows run wf_7Yb2 --output json > result.json; then
|
||||
echo "run failed" >&2
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
<Callout type="info">
|
||||
An unexpected error keeps its stack trace on purpose — that is a bug in the CLI,
|
||||
and hiding it behind a friendly message would make it unreportable. Please
|
||||
[open an issue](https://github.com/simstudioai/sim/issues) if you see one.
|
||||
</Callout>
|
||||
|
||||
## Selecting workflow output
|
||||
|
||||
`--select-output` takes `blockName.field` selectors. Fields that a run did not
|
||||
produce are simply omitted:
|
||||
|
||||
```bash
|
||||
sim workflows run wf_7Yb2 --select-output agent_1.content --output json
|
||||
```
|
||||
|
||||
## Polling a long run
|
||||
|
||||
Start the run asynchronously, then poll its status:
|
||||
|
||||
```bash
|
||||
run_id=$(sim workflows run wf_7Yb2 --async --output json | jq -r '.runId')
|
||||
|
||||
until sim workflows runs get "$run_id" --workflow wf_7Yb2 --output json \
|
||||
| jq -e '.status | IN("completed","failed","cancelled")' > /dev/null; do
|
||||
sleep 5
|
||||
done
|
||||
|
||||
sim logs get "$run_id" --trace
|
||||
```
|
||||
|
||||
`workflows runs get` is the lightweight status resource; `logs get` is the full
|
||||
diagnostic one. For a paused run, the status includes the context ID that
|
||||
`sim workflows runs resume` needs.
|
||||
|
||||
## Working with folders
|
||||
|
||||
Every folder-backed resource — `workflows`, `tables`, `files`, `knowledge` —
|
||||
shares the same path commands:
|
||||
|
||||
```bash
|
||||
sim tables ls Reports
|
||||
sim tables mkdir Reports/Quarterly
|
||||
sim tables folders mv Reports/Quarterly Archive/Quarterly
|
||||
sim tables folders delete Archive --recursive --yes
|
||||
```
|
||||
|
||||
`ls` is a directory view: it combines the resources at its path with that
|
||||
folder's direct child folders, and never includes deeper descendants. Its `ref`
|
||||
column is the resource ID or canonical folder path to pass to the next command.
|
||||
Use `list` when you want resources only, or `folders ls` for folders only.
|
||||
|
||||
The leading `/` is optional on input; the API returns the canonical
|
||||
leading-slash form.
|
||||
|
||||
## A nightly job, end to end
|
||||
|
||||
```bash title="nightly-digest.sh"
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
export SIM_API_KEY="${SIM_API_KEY:?missing}"
|
||||
export SIM_WORKSPACE="${SIM_WORKSPACE:?missing}"
|
||||
export SIM_OUTPUT=json
|
||||
|
||||
run_id=$(sim workflows run wf_7Yb2 --input '{"source":"nightly"}' | jq -r '.runId')
|
||||
|
||||
if [ "$(sim workflows runs get "$run_id" --workflow wf_7Yb2 | jq -r '.status')" != "completed" ]; then
|
||||
sim logs get "$run_id" >&2
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
@@ -0,0 +1,84 @@
|
||||
---
|
||||
title: Secrets
|
||||
description: Manage secrets — every subcommand, argument, and flag
|
||||
---
|
||||
|
||||
import { CommandTable } from '@/components/ui/command-table'
|
||||
|
||||
`sim secrets` is also spelled `sim secret`.
|
||||
|
||||
Every command below also accepts the [global options](/cli/commands#global-options).
|
||||
|
||||
## Delete secret
|
||||
|
||||
```bash
|
||||
sim secrets delete <name> [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `name` | Yes | Secret to create, replace, or delete. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--scope <value>` | Yes | Whether the secret belongs to the workspace or to the caller. A personal secret belongs to the caller across every workspace, not to one workspace. Accepted values: `workspace`, `personal`. |
|
||||
| `-y, --yes` | No | Skip the confirmation. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## List secrets
|
||||
|
||||
```bash
|
||||
sim secrets list [options]
|
||||
```
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--scope <value>` | No | Restrict results to one ownership scope. Accepted values: `workspace`, `personal`. |
|
||||
| `--search <value>` | No | Case-insensitive substring match against the secret name. |
|
||||
| `--sort-by <value>` | No | Field used to sort the result. Sorting by `name` is case-sensitive and follows the storage collation, so do not rely on a case-insensitive order. Accepted values: `name`, `createdAt`, `updatedAt`. |
|
||||
| `--sort-order <value>` | No | Sort direction. Accepted values: `asc`, `desc`. |
|
||||
| `--limit <n>` | No | Maximum items to return (0 for everything). Defaults to `100`. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Create or replace a named secret
|
||||
|
||||
```bash
|
||||
sim secrets set <name> [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `name` | Yes | Secret name, as referenced in workflows |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--scope <scope>` | Yes | Secret ownership scope. Accepted values: `workspace`, `personal`. |
|
||||
| `--value <value>` | No | Secret value; visible to shell history when supplied directly. |
|
||||
|
||||
</CommandTable>
|
||||
@@ -0,0 +1,117 @@
|
||||
---
|
||||
title: Skills
|
||||
description: Manage skills — every subcommand, argument, and flag
|
||||
---
|
||||
|
||||
import { CommandTable } from '@/components/ui/command-table'
|
||||
|
||||
`sim skills` is also spelled `sim skill`.
|
||||
|
||||
Every command below also accepts the [global options](/cli/commands#global-options).
|
||||
|
||||
## Create skill
|
||||
|
||||
```bash
|
||||
sim skills create [options]
|
||||
```
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--name <value>` | Yes | Kebab-case name, unique within the workspace and not reserved by a built-in skill. |
|
||||
| `--description <value>` | Yes | One-line summary of when the skill applies. |
|
||||
| `--content <value>` | Yes | Skill body containing the instructions given to the agent. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Delete skill
|
||||
|
||||
```bash
|
||||
sim skills delete <id> [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `id` | Yes | Unique skill identifier. A built-in skill is `builtin-` followed by its name, for example `builtin-research`. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `-y, --yes` | No | Skip the confirmation. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Get skill
|
||||
|
||||
```bash
|
||||
sim skills get <id>
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `id` | Yes | Unique skill identifier. A built-in skill is `builtin-` followed by its name, for example `builtin-research`. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## List skills
|
||||
|
||||
```bash
|
||||
sim skills list [options]
|
||||
```
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--search <value>` | No | Case-insensitive substring match against the skill name. |
|
||||
| `--sort-by <value>` | No | Field used to sort the result. Sorting by `name` is case-sensitive and follows the storage collation, so do not rely on a case-insensitive order. Accepted values: `name`, `createdAt`, `updatedAt`. |
|
||||
| `--sort-order <value>` | No | Sort direction. Accepted values: `asc`, `desc`. |
|
||||
| `--limit <n>` | No | Maximum items to return (0 for everything). Defaults to `100`. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Update skill
|
||||
|
||||
```bash
|
||||
sim skills update <id> [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `id` | Yes | Unique skill identifier. A built-in skill is `builtin-` followed by its name, for example `builtin-research`. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--name <value>` | No | New kebab-case skill name. |
|
||||
| `--description <value>` | No | New one-line summary of when the skill applies. |
|
||||
| `--content <value>` | No | Replacement skill body. |
|
||||
|
||||
</CommandTable>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,244 @@
|
||||
---
|
||||
title: Troubleshooting
|
||||
description: What the CLI's errors mean, and the fastest way to resolve each one
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout'
|
||||
|
||||
Every error the CLI can explain prints one line to stderr, prefixed `Error:`, and
|
||||
exits `1`. Where the API supplies an error code or validation details, those
|
||||
follow on dimmed lines.
|
||||
|
||||
<Callout type="info">
|
||||
Start with `sim whoami`. Most surprises are a command running against a different
|
||||
profile, endpoint, or workspace than you assumed, and `whoami` prints the winning
|
||||
value for each setting **and where it came from**.
|
||||
</Callout>
|
||||
|
||||
## Authentication
|
||||
|
||||
### `Not logged in on profile "default". Run: sim login --profile default`
|
||||
|
||||
No API key resolved for the profile. Either sign in, or supply a key through the
|
||||
environment:
|
||||
|
||||
```bash
|
||||
sim login
|
||||
# or, for CI
|
||||
export SIM_API_KEY="sim_…"
|
||||
```
|
||||
|
||||
Remember that the key is per profile. If you signed in as `dev` and are running
|
||||
without `--profile dev`, the default profile is still unauthenticated.
|
||||
|
||||
### An error ending in `— run: sim login --profile <name>`
|
||||
|
||||
The API rejected the key with a `401`. The key was revoked, or it belongs to a
|
||||
different deployment than the endpoint you are pointed at. Sign in again:
|
||||
|
||||
```bash
|
||||
sim login --profile <name>
|
||||
```
|
||||
|
||||
Check the endpoint first if you did not expect this — a key minted against a
|
||||
local stack will not authenticate against production:
|
||||
|
||||
```bash
|
||||
sim whoami --profile <name>
|
||||
```
|
||||
|
||||
### `Timed out waiting for browser approval.`
|
||||
|
||||
`sim login` waits 15 minutes for you to approve the pairing code. Run it again.
|
||||
If the browser never opened, use the printed URL directly:
|
||||
|
||||
```bash
|
||||
sim login --no-browser
|
||||
```
|
||||
|
||||
### `Profile "default" already exists. Re-run with --yes to overwrite it.`
|
||||
|
||||
Signing in again over a profile that already holds a key. This is a guard, not a
|
||||
failure — confirm you mean to replace it:
|
||||
|
||||
```bash
|
||||
sim login --yes
|
||||
```
|
||||
|
||||
## Workspace
|
||||
|
||||
### `No workspace set for profile "default". Pass --workspace, or run: sim configure --profile default --set-workspace <id>`
|
||||
|
||||
The command is workspace-scoped and no workspace resolved. Set one for the
|
||||
profile, pass it per command, or export it:
|
||||
|
||||
```bash
|
||||
sim configure --set-workspace ws_abc123
|
||||
sim tables list --workspace ws_abc123
|
||||
export SIM_WORKSPACE=ws_abc123
|
||||
```
|
||||
|
||||
The workspace ID is in the Sim URL: `https://sim.ai/workspace/{workspaceId}/…`.
|
||||
|
||||
<Callout type="warn">
|
||||
`sim login` sets the profile's default workspace to whichever one you picked on
|
||||
the approval page. It does **not** limit the key to that workspace — use
|
||||
`--workspace` to reach any other workspace the key can access.
|
||||
</Callout>
|
||||
|
||||
### A command returns rows from the wrong workspace
|
||||
|
||||
The workspace resolved from a higher-priority source than you expected. `--workspace`
|
||||
beats `SIM_WORKSPACE`, which beats the profile. Confirm with `sim whoami`.
|
||||
|
||||
`sim billing status`, `sim billing logs`, and `sim audit-logs list` also accept
|
||||
`--all-workspaces`, which drops the filter entirely and cannot be combined with
|
||||
`--workspace`.
|
||||
|
||||
## Connectivity
|
||||
|
||||
### `Could not reach https://sim.ai: <reason>`
|
||||
|
||||
The request never got a response — DNS, TLS, a proxy, or a self-hosted stack that
|
||||
is not running. Check the endpoint the CLI actually used:
|
||||
|
||||
```bash
|
||||
sim whoami
|
||||
```
|
||||
|
||||
For a local deployment, confirm it is up and that the endpoint has the right port
|
||||
and scheme:
|
||||
|
||||
```bash
|
||||
sim configure --set-endpoint http://localhost:3000 --profile local
|
||||
```
|
||||
|
||||
### `Request cancelled.`
|
||||
|
||||
The request was aborted, usually by `Ctrl-C` or a CI job timeout. Re-run it.
|
||||
|
||||
## Arguments and flags
|
||||
|
||||
### `error: missing required argument '<name>'`
|
||||
|
||||
The CLI prints an `Example:` line beneath showing the full invocation. `--help`
|
||||
gives the complete signature at any depth:
|
||||
|
||||
```bash
|
||||
sim tables rows query --help
|
||||
```
|
||||
|
||||
### `error: option '--x <value>' argument 'y' is invalid. Allowed choices are …`
|
||||
|
||||
The value is outside the accepted set. The accepted values are in `--help` and in
|
||||
this section's [command reference](/cli/commands).
|
||||
|
||||
### `<message> Re-run with --yes to confirm.`
|
||||
|
||||
A destructive command needs explicit confirmation. The message says what would be
|
||||
destroyed — read it, then repeat the command with `--yes`:
|
||||
|
||||
```bash
|
||||
sim files delete file_123 --yes
|
||||
```
|
||||
|
||||
There is no "delete everything" default: deletions require an explicit selector
|
||||
**and** `--yes`.
|
||||
|
||||
### `--limit must be a non-negative number`
|
||||
|
||||
Use `0` for "everything" rather than a negative number:
|
||||
|
||||
```bash
|
||||
sim logs list --limit 0
|
||||
```
|
||||
|
||||
### `--input @- reads stdin, but nothing is piped in`
|
||||
|
||||
`@-` reads from stdin, so something has to be piped to it. Either pipe a value or
|
||||
point at a file:
|
||||
|
||||
```bash
|
||||
cat wf.json | sim workflows import --workflow @-
|
||||
sim workflows import --workflow @wf.json
|
||||
```
|
||||
|
||||
### A JSON flag rejects a value that looks like valid JSON
|
||||
|
||||
Your shell probably consumed the quotes. Wrap the whole value in single quotes,
|
||||
or read it from a file:
|
||||
|
||||
```bash
|
||||
sim tables rows query tbl_123 --filter '{"all":[{"field":"status","op":"eq","value":"open"}]}'
|
||||
sim tables rows query tbl_123 --filter @filter.json
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
### `Error: … output …` naming the accepted formats
|
||||
|
||||
A stored or exported output format is not one of `table`, `json`, `yaml`, or
|
||||
`text`. A higher-priority source still wins, so you can repair the profile
|
||||
without editing the file:
|
||||
|
||||
```bash
|
||||
sim --output table configure --set-output json
|
||||
```
|
||||
|
||||
### A value looks truncated
|
||||
|
||||
`table` clips long cells so rows stay on one line. The data is not truncated —
|
||||
switch to a machine format to see it in full:
|
||||
|
||||
```bash
|
||||
sim logs get run_123 --output json
|
||||
```
|
||||
|
||||
### `sim profiles` ignores `--output`
|
||||
|
||||
`sim profiles` and `sim configure`'s listing mode always print for humans; they
|
||||
report on your local configuration rather than on API data.
|
||||
|
||||
## Files
|
||||
|
||||
### `<path> already exists. Pass --force to overwrite it, or choose another output path.`
|
||||
|
||||
`sim files get -o` will not overwrite an existing file by accident:
|
||||
|
||||
```bash
|
||||
sim files get file_123 -o ./report.csv --force
|
||||
```
|
||||
|
||||
### `sim files get` refuses to print to the terminal
|
||||
|
||||
Writing arbitrary binary to an interactive terminal can corrupt it, so non-text
|
||||
content must go to a file or a pipe:
|
||||
|
||||
```bash
|
||||
sim files get file_123 -o ./image.png
|
||||
sim files get file_123 | shasum
|
||||
```
|
||||
|
||||
## Secrets
|
||||
|
||||
### `Interactive secret input requires a terminal. Pass --value instead.`
|
||||
|
||||
`sim secrets set` prompts with masked input when it can. In CI there is no
|
||||
terminal, so supply the value directly — from a CI secret, not a literal:
|
||||
|
||||
```bash
|
||||
sim secrets set MY_KEY --scope workspace --value "$MY_KEY"
|
||||
```
|
||||
|
||||
## Something else
|
||||
|
||||
An unexpected error keeps its stack trace on purpose — that is a bug in the CLI,
|
||||
not a message meant for you. Please
|
||||
[open an issue](https://github.com/simstudioai/sim/issues) with the command you
|
||||
ran and the trace.
|
||||
|
||||
Include the version:
|
||||
|
||||
```bash
|
||||
sim --version
|
||||
```
|
||||
@@ -0,0 +1,566 @@
|
||||
---
|
||||
title: Workflows
|
||||
description: Manage workflows — every subcommand, argument, and flag
|
||||
---
|
||||
|
||||
import { CommandTable } from '@/components/ui/command-table'
|
||||
|
||||
`sim workflows` is also spelled `sim workflow`.
|
||||
|
||||
Every command below also accepts the [global options](/cli/commands#global-options).
|
||||
|
||||
## Cancel a running workflow run
|
||||
|
||||
```bash
|
||||
sim workflows runs cancel <runId> [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `runId` | Yes | Unique workflow run identifier. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--workflow <workflowId>` | Yes | Workflow ID. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Show run status
|
||||
|
||||
```bash
|
||||
sim workflows runs get <runId> [options]
|
||||
```
|
||||
|
||||
Show run status (requested outputs are included in JSON or YAML output)
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `runId` | Yes | Unique workflow run identifier. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--workflow <workflowId>` | Yes | Workflow ID. |
|
||||
| `--include-output` | No | Include the final output in JSON or YAML output. |
|
||||
| `--select-output <value...>` | No | Include blockName.field values in JSON or YAML output (e.g. agent_1.content) (space-separated, or @path / @- with one value per line). |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## List runs for a workflow
|
||||
|
||||
```bash
|
||||
sim workflows runs list [options]
|
||||
```
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--workflow <workflowId>` | Yes | Workflow ID. |
|
||||
| `--status <value>` | No | Filter by run status. Accepted values: `pending`, `running`, `completed`, `failed`, `cancelled`, `paused`. |
|
||||
| `--trigger <value>` | No | Filter by trigger type. |
|
||||
| `--start-date <value>` | No | Only include runs started at or after this UTC ISO 8601 timestamp, e.g. `2026-08-06T00:00:00Z`. A date without a time, or a timestamp carrying a UTC offset instead of `Z`, is rejected, as is year `0000`, which names no storable instant. |
|
||||
| `--end-date <value>` | No | Only include runs started at or before this UTC ISO 8601 timestamp, e.g. `2026-08-06T00:00:00Z`. A date without a time, or a timestamp carrying a UTC offset instead of `Z`, is rejected, as is year `0000`, which names no storable instant. |
|
||||
| `--limit <n>` | No | Maximum items to return (0 for everything). Defaults to `100`. |
|
||||
| `--order <value>` | No | Sort direction by run start time. This list is sortable only by run start time, so it takes `order` in place of `sortBy`/`sortOrder`, which it rejects. Accepted values: `asc`, `desc`. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Resume a paused run
|
||||
|
||||
```bash
|
||||
sim workflows runs resume <runId> [options]
|
||||
```
|
||||
|
||||
Resume a paused run (output is included in JSON or YAML output)
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `runId` | Yes | Unique workflow run identifier. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--workflow <workflowId>` | Yes | Workflow ID. |
|
||||
| `--context <value>` | Yes | Pause context ID returned by run status. |
|
||||
| `--input <json\|@file>` | No | Resume input as JSON (JSON, or @path / @- to read a file or stdin). |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Create workflow
|
||||
|
||||
```bash
|
||||
sim workflows create [options]
|
||||
```
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--name <value>` | Yes | Workflow name. |
|
||||
| `--description <value>` | No | Optional workflow description. |
|
||||
| `--folder <value>` | No | Folder path; the leading / is optional. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Create a workflow folder at a path
|
||||
|
||||
```bash
|
||||
sim workflows folders create <path>
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `path` | Yes | Folder path; the leading / is optional |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Delete workflow folder
|
||||
|
||||
```bash
|
||||
sim workflows folders delete <path> [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `path` | Yes | Folder path; the leading / is optional |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--recursive` | No | Delete the folder and its descendants. |
|
||||
| `-y, --yes` | No | Skip the confirmation. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## List workflow folders
|
||||
|
||||
```bash
|
||||
sim workflows folders list [options]
|
||||
```
|
||||
|
||||
Also available as `sim workflows folders ls`.
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--parent <value>` | No | Direct parent folder path. |
|
||||
| `--search <value>` | No | Case-insensitive substring match against the folder name. |
|
||||
| `--sort-by <value>` | No | Field used to sort the result. Sorting by `name` is case-sensitive and follows the storage collation, so do not rely on a case-insensitive order. Accepted values: `name`, `createdAt`, `updatedAt`. |
|
||||
| `--sort-order <value>` | No | Sort direction. Accepted values: `asc`, `desc`. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Rename or move a workflow folder
|
||||
|
||||
```bash
|
||||
sim workflows folders move <path> <destination>
|
||||
```
|
||||
|
||||
Also available as `sim workflows folders mv`.
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `path` | Yes | Folder path; the leading / is optional |
|
||||
| `destination` | Yes | Folder path; the leading / is optional |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Delete workflow
|
||||
|
||||
```bash
|
||||
sim workflows delete <id> [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `id` | Yes | Unique workflow identifier. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `-y, --yes` | No | Skip the confirmation. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Deploy workflow
|
||||
|
||||
```bash
|
||||
sim workflows deploy <id> [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `id` | Yes | Unique workflow identifier. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--name <value>` | No | Optional label for the deployment version. |
|
||||
| `--description <value>` | No | Optional release note for the deployment version. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Run a deployed workflow
|
||||
|
||||
```bash
|
||||
sim workflows run <id> [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `id` | Yes | Unique workflow identifier. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--input <json\|@file>` | No | Trigger input as JSON (JSON, or @path / @- to read a file or stdin). |
|
||||
| `--async` | No | Queue the run and return immediately. |
|
||||
| `--execution-timeout-seconds <value>` | No | Requested server-side timeout for an asynchronous run, in seconds. An upper bound, not the effective timeout: the run uses the smaller of this value and the plan's execution timeout, so requesting more than the plan allows silently yields the plan timeout. Rejected with `400` unless `async` is true. |
|
||||
| `--select-output <value...>` | No | Return blockName.field values (e.g. agent_1.content); missing fields are omitted (space-separated, or @path / @- with one value per line). |
|
||||
| `--include-file-base64` | No | Inline eligible output files as base64 content. Rejected when `async` is true. |
|
||||
| `--no-include-file-base64` | No | Send --include-file-base64 as false. |
|
||||
| `--base64-max-bytes <value>` | No | Maximum total bytes of file content to inline as base64. Rejected when `async` is true. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Print a workflow as a portable JSON document
|
||||
|
||||
```bash
|
||||
sim workflows export <id>
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `id` | Yes | Unique workflow identifier. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Get workflow
|
||||
|
||||
```bash
|
||||
sim workflows get <id>
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `id` | Yes | Unique workflow identifier. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Get workflow deployment
|
||||
|
||||
```bash
|
||||
sim workflows deployment list <id>
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `id` | Yes | Unique workflow identifier. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Get workflow version
|
||||
|
||||
```bash
|
||||
sim workflows versions get <id> <version>
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `id` | Yes | Unique workflow identifier. |
|
||||
| `version` | Yes | Numeric deployment version. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## List workflow versions
|
||||
|
||||
```bash
|
||||
sim workflows versions list <id> [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `id` | Yes | Unique workflow identifier. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--limit <n>` | No | Maximum items to return (0 for everything). Defaults to `100`. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Import workflow
|
||||
|
||||
```bash
|
||||
sim workflows import [options]
|
||||
```
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--workflow <json\|@file>` | Yes | Workflow export object, bare workflow state, or JSON string containing either form. (JSON, or @path / @- to read a file or stdin). |
|
||||
| `--folder <value>` | No | Folder path; the leading / is optional. |
|
||||
| `--name <value>` | No | Override for the imported workflow name. |
|
||||
| `--description <value>` | No | Override for the imported workflow description. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## List workflows
|
||||
|
||||
```bash
|
||||
sim workflows list [options]
|
||||
```
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--folder <value>` | No | Folder path; the leading / is optional. |
|
||||
| `--deployed-only` | No | Return only workflows with an active deployment when true. |
|
||||
| `--no-deployed-only` | No | Send --deployed-only as false. |
|
||||
| `--limit <n>` | No | Maximum items to return (0 for everything). Defaults to `100`. |
|
||||
| `--search <value>` | No | Case-insensitive substring match against the resource name. |
|
||||
| `--sort-by <value>` | No | Field used to sort the result. Sorting by `name` is case-sensitive and follows the storage collation, so do not rely on a case-insensitive order. Accepted values: `position`, `name`, `createdAt`, `updatedAt`, `runCount`. |
|
||||
| `--sort-order <value>` | No | Sort direction. Accepted values: `asc`, `desc`. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Rollback workflow
|
||||
|
||||
```bash
|
||||
sim workflows rollback <id> [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `id` | Yes | Unique workflow identifier. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--version <value>` | No | Deployment version to reactivate. Omit to select the previous active version. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Take a workflow out of deployment
|
||||
|
||||
```bash
|
||||
sim workflows undeploy <id>
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `id` | Yes | Unique workflow identifier. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Update workflow
|
||||
|
||||
```bash
|
||||
sim workflows update <id> [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `id` | Yes | Unique workflow identifier. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--name <value>` | No | Replacement workflow name. |
|
||||
| `--description <value>` | No | Replacement workflow description; null clears it. |
|
||||
| `--folder <value>` | No | Folder path; the leading / is optional. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Move a workflow to a folder
|
||||
|
||||
```bash
|
||||
sim workflows mv <id> <folder>
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `id` | Yes | Unique workflow identifier. |
|
||||
| `folder` | Yes | Folder path; the leading / is optional |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## List workflow resources and child folders together
|
||||
|
||||
```bash
|
||||
sim workflows ls [path] [options]
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `path` | No | Folder path to list; defaults to the root folder |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--search <text>` | No | Filter folders and resources by name. |
|
||||
| `--limit <n>` | No | Maximum combined items to return (0 for everything). Defaults to `100`. |
|
||||
|
||||
</CommandTable>
|
||||
|
||||
## Create a workflow directory at a path
|
||||
|
||||
```bash
|
||||
sim workflows mkdir <path>
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Argument | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `path` | Yes | Folder path to create; the leading / is optional |
|
||||
|
||||
</CommandTable>
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
title: Workspaces
|
||||
description: Manage workspaces — every subcommand, argument, and flag
|
||||
---
|
||||
|
||||
import { CommandTable } from '@/components/ui/command-table'
|
||||
|
||||
`sim workspaces` is also spelled `sim workspace`.
|
||||
|
||||
Every command below also accepts the [global options](/cli/commands#global-options).
|
||||
|
||||
## Get workspace
|
||||
|
||||
```bash
|
||||
sim workspaces get
|
||||
```
|
||||
|
||||
## List workspace members
|
||||
|
||||
```bash
|
||||
sim workspaces members [options]
|
||||
```
|
||||
|
||||
**Options**
|
||||
|
||||
<CommandTable>
|
||||
|
||||
| Option | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| `--limit <n>` | No | Maximum items to return (0 for everything). Defaults to `100`. |
|
||||
|
||||
</CommandTable>
|
||||
@@ -32,6 +32,8 @@
|
||||
"check:openapi": "bun run scripts/check-openapi.ts",
|
||||
"generate:cli-api": "bun run scripts/generate-v2-cli-api.ts",
|
||||
"check:cli-api": "bun run scripts/generate-v2-cli-api.ts --check",
|
||||
"generate:cli-docs": "bun run scripts/generate-cli-docs.ts",
|
||||
"check:cli-docs": "bun run scripts/generate-cli-docs.ts --check",
|
||||
"check:cron-parity": "bun run scripts/check-cron-parity.ts",
|
||||
"check:api-validation:strict": "bun run scripts/check-api-validation-contracts.ts --check --enforce-boundary-baseline",
|
||||
"check:realtime-prune": "bun run scripts/check-realtime-prune-graph.ts",
|
||||
|
||||
@@ -8,12 +8,7 @@ sim login
|
||||
sim workflows list
|
||||
```
|
||||
|
||||
Prerelease channels track the corresponding Sim environments:
|
||||
|
||||
```bash
|
||||
npm install --global sim@staging # staging
|
||||
npm install --global sim@dev # dev
|
||||
```
|
||||
Full documentation: **https://docs.sim.ai/cli**
|
||||
|
||||
## Profiles
|
||||
|
||||
@@ -108,6 +103,10 @@ Settings → API keys.
|
||||
|
||||
## Commands
|
||||
|
||||
The commands below are the common ones. The complete reference — every group,
|
||||
subcommand, argument, and flag, generated from this package — is at
|
||||
[docs.sim.ai/cli/commands](https://docs.sim.ai/cli/commands).
|
||||
|
||||
Plural resource names are canonical, but every plural top-level resource group
|
||||
also accepts its singular form: for example, `sim table list`,
|
||||
`sim file get`, and `sim workflow get` are equivalent to their plural
|
||||
@@ -142,7 +141,7 @@ sim tables list [--folder <path>]
|
||||
sim tables get <tableId>
|
||||
sim tables update <tableId> [--name <name>] [--description <text>] [--folder <path>]
|
||||
sim tables mv <tableId> <folder>
|
||||
sim tables columns <tableId>
|
||||
sim tables columns create|update|delete|run <tableId>
|
||||
sim tables rows list <tableId> [--limit <n>]
|
||||
sim tables rows create <tableId> --data <json|@file>
|
||||
sim tables rows create <tableId> --rows <json|@file>
|
||||
@@ -173,6 +172,8 @@ sim knowledge search --query <text> --kb <id>… [--search-mode vector|hybrid]
|
||||
sim knowledge documents list <knowledgeBaseId> [--search <text>]
|
||||
sim knowledge documents get <knowledgeBaseId> <documentId>
|
||||
sim knowledge documents upload <knowledgeBaseId> <path> [--tag <value>...]
|
||||
sim knowledge documents update <knowledgeBaseId> <documentId> [--filename <name>] [--enabled]
|
||||
sim knowledge documents batch-update <knowledgeBaseId> --operation enable|disable
|
||||
sim knowledge documents delete <knowledgeBaseId> <documentId> --yes
|
||||
|
||||
sim billing status [--all-workspaces]
|
||||
@@ -249,9 +250,12 @@ grammar is a tree; there's no honest flag encoding for it.
|
||||
sim tables rows query tbl_123 \
|
||||
--filter '{"all":[{"field":"status","op":"eq","value":"open"},
|
||||
{"field":"score","op":"gt","value":10}]}' \
|
||||
--sort score:desc --limit 50
|
||||
--sort '[{"field":"score","direction":"desc"}]' --limit 50
|
||||
```
|
||||
|
||||
`--sort` is JSON for the same reason: it is an ordered list of keys, each with a
|
||||
`field` and a `direction` of `asc` or `desc`.
|
||||
|
||||
Row columns are discovered at runtime from the returned data, unioned across the
|
||||
page so a sparse row doesn't hide a column.
|
||||
|
||||
|
||||
@@ -162,7 +162,8 @@ export function attachCredentialCommands(program: Command): void {
|
||||
if (!credentials) throw new Error('The generated credentials command group is missing')
|
||||
|
||||
credentials
|
||||
.command('create <providerId>')
|
||||
.command('create')
|
||||
.argument('<providerId>', 'Service-account provider to create a credential for')
|
||||
.description('Create a service-account credential using its discovered provider schema')
|
||||
.requiredOption('--name <displayName>', 'Name shown for the credential in Sim')
|
||||
.requiredOption(
|
||||
@@ -179,7 +180,8 @@ export function attachCredentialCommands(program: Command): void {
|
||||
)
|
||||
|
||||
credentials
|
||||
.command('connect <providerId>')
|
||||
.command('connect')
|
||||
.argument('<providerId>', 'OAuth provider to connect')
|
||||
.description('Create a short-lived link for connecting an OAuth provider')
|
||||
.requiredOption('--name <displayName>', 'Name shown for the new credential in Sim')
|
||||
.action(async (providerId: string, options: { name: string }, command: Command) =>
|
||||
@@ -187,7 +189,8 @@ export function attachCredentialCommands(program: Command): void {
|
||||
)
|
||||
|
||||
credentials
|
||||
.command('reconnect <credentialId>')
|
||||
.command('reconnect')
|
||||
.argument('<credentialId>', 'Existing OAuth credential to re-authorize')
|
||||
.description('Create a short-lived link for reconnecting an OAuth credential')
|
||||
.action((credentialId: string, _options: unknown, command: Command) =>
|
||||
createConnectionLink(command, { credentialId })
|
||||
|
||||
@@ -174,7 +174,8 @@ export function isTerminalSafeContentType(contentType: string | null): boolean {
|
||||
|
||||
export function attachFileGet(files: Command): void {
|
||||
files
|
||||
.command('get <fileId>')
|
||||
.command('get')
|
||||
.argument('<fileId>', 'File whose content to read')
|
||||
.description('Get a file’s content')
|
||||
.option('-o, --output-file <path>', 'Write content to a file instead of stdout')
|
||||
.option('--force', 'Overwrite --output-file if it already exists')
|
||||
|
||||
@@ -8,7 +8,8 @@ import { printProtocolResult } from './result'
|
||||
|
||||
export function attachFileUpload(files: Command): void {
|
||||
files
|
||||
.command('upload <path>')
|
||||
.command('upload')
|
||||
.argument('<path>', 'Local file to upload')
|
||||
.description('Upload a file to the workspace')
|
||||
.option('--folder <path>', 'Destination folder path (defaults to /)')
|
||||
.option('--name <name>', 'Store it under a different name')
|
||||
|
||||
@@ -37,7 +37,9 @@ function uploadMetadata(options: KnowledgeDocumentUploadOptions): Record<string,
|
||||
|
||||
export function attachKnowledgeDocumentUpload(documents: Command): void {
|
||||
documents
|
||||
.command('upload <knowledgeBaseId> <path>')
|
||||
.command('upload')
|
||||
.argument('<knowledgeBaseId>', 'Knowledge base to upload into')
|
||||
.argument('<path>', 'Local file to upload')
|
||||
.description('Upload a document to a knowledge base')
|
||||
.option('--name <name>', 'Store it under a different name')
|
||||
.option('--tag <value...>', 'Document tags, in tag1 through tag7 order')
|
||||
|
||||
@@ -153,7 +153,8 @@ export function attachResourceDirectoryCommands(
|
||||
config: ResourceDirectoryConfig
|
||||
): void {
|
||||
group
|
||||
.command('ls [path]')
|
||||
.command('ls')
|
||||
.argument('[path]', 'Folder path to list; defaults to the root folder')
|
||||
.allowExcessArguments(false)
|
||||
.description(`List ${config.kind} resources and child folders together`)
|
||||
.option('--search <text>', 'Filter folders and resources by name')
|
||||
@@ -181,7 +182,8 @@ export function attachResourceDirectoryCommands(
|
||||
})
|
||||
|
||||
group
|
||||
.command('mkdir <path>')
|
||||
.command('mkdir')
|
||||
.argument('<path>', 'Folder path to create; the leading / is optional')
|
||||
.allowExcessArguments(false)
|
||||
.description(`Create a ${config.kind} directory at a path`)
|
||||
.action(async (path: string, _options: Record<string, never>, command: Command) => {
|
||||
|
||||
@@ -94,7 +94,8 @@ function validateTargetOptions(options: ImportOptions): boolean {
|
||||
|
||||
export function attachTableImport(tables: Command): void {
|
||||
tables
|
||||
.command('import [path]')
|
||||
.command('import')
|
||||
.argument('[path]', 'Local CSV file to import; omit when using --file-id')
|
||||
.description('Import a CSV, into a new table by default')
|
||||
.option(
|
||||
'--name <name>',
|
||||
|
||||
@@ -53,7 +53,8 @@ export function attachSecretCommands(program: Command): void {
|
||||
if (!secrets) throw new Error('The generated secrets command group is missing')
|
||||
|
||||
secrets
|
||||
.command('set <name>')
|
||||
.command('set')
|
||||
.argument('<name>', 'Secret name, as referenced in workflows')
|
||||
.description('Create or replace a named secret')
|
||||
.addOption(
|
||||
new Option('--scope <scope>', 'Secret ownership scope')
|
||||
|
||||
@@ -115,6 +115,20 @@ export const CLI_CONTRACT: CliContract = {
|
||||
},
|
||||
confirm: 'This updates every matching row and cannot be undone.',
|
||||
},
|
||||
// Same overload one level down: PATCH `/documents` is the bulk form of PATCH
|
||||
// `/documents/[documentId]`. Both derived to `knowledge documents update`, and
|
||||
// because commander resolves a duplicate name to the first registered match,
|
||||
// the bulk form silently shadowed the single-document one — its flags were
|
||||
// unreachable from the terminal.
|
||||
bulkUpdateKnowledgeDocuments: {
|
||||
command: 'knowledge documents batch-update',
|
||||
describe: 'Enable or disable every matching document',
|
||||
pathArgumentNames: KNOWLEDGE_DOCUMENT_PATH_ARGUMENTS,
|
||||
flags: {
|
||||
documentIds: { name: 'document', list: true },
|
||||
selectAll: { boolean: true, describe: 'Apply to every document in the knowledge base' },
|
||||
},
|
||||
},
|
||||
// `DELETE /workflows/[id]/deploy` is an undeploy, not a delete.
|
||||
undeployWorkflow: {
|
||||
command: 'workflows undeploy',
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,79 +1,10 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { readFileSync } from 'node:fs'
|
||||
import chalk from 'chalk'
|
||||
import { Command, Option } from 'commander'
|
||||
import { loginCommand, logoutCommand, profilesCommand, whoamiCommand } from './commands/auth'
|
||||
import { configureCommand } from './commands/configure'
|
||||
import { attachCredentialCommands } from './commands/credentials'
|
||||
import { attachProtocolCommands } from './commands/protocol/index'
|
||||
import { attachSecretCommands } from './commands/secrets'
|
||||
import { OUTPUT_FORMATS, ProfileConfigError } from './config/index'
|
||||
import { ProfileConfigError } from './config/index'
|
||||
import { formatApiErrorDetails, SimApiError } from './http/client'
|
||||
import { sanitize } from './output/render'
|
||||
import { buildGeneratedCommands } from './runtime/build'
|
||||
|
||||
const program = new Command()
|
||||
|
||||
function readPackageVersion(): string {
|
||||
const metadata: unknown = JSON.parse(
|
||||
readFileSync(new URL('../package.json', import.meta.url), 'utf8')
|
||||
)
|
||||
if (
|
||||
typeof metadata !== 'object' ||
|
||||
metadata === null ||
|
||||
!('version' in metadata) ||
|
||||
typeof metadata.version !== 'string'
|
||||
) {
|
||||
throw new Error('CLI package metadata is missing a valid version')
|
||||
}
|
||||
return metadata.version
|
||||
}
|
||||
|
||||
program
|
||||
.name('sim')
|
||||
.description('Talk to the Sim API from your terminal')
|
||||
.version(readPackageVersion())
|
||||
.option('-P, --profile <name>', 'Profile to use (env: SIM_PROFILE)')
|
||||
.option('--endpoint <url>', 'Sim deployment to talk to (env: SIM_ENDPOINT)')
|
||||
.option('-w, --workspace <id>', 'Workspace to target (env: SIM_WORKSPACE)')
|
||||
.addOption(
|
||||
new Option('--output <format>', 'Output format for this command').choices([...OUTPUT_FORMATS])
|
||||
)
|
||||
|
||||
program.addCommand(loginCommand())
|
||||
program.addCommand(logoutCommand())
|
||||
program.addCommand(whoamiCommand())
|
||||
program.addCommand(profilesCommand())
|
||||
program.addCommand(configureCommand())
|
||||
|
||||
for (const command of buildGeneratedCommands()) {
|
||||
program.addCommand(command)
|
||||
}
|
||||
|
||||
attachCredentialCommands(program)
|
||||
attachProtocolCommands(program)
|
||||
attachSecretCommands(program)
|
||||
|
||||
program.addHelpText(
|
||||
'after',
|
||||
`
|
||||
Profiles work like the AWS CLI: settings live in ~/.sim/config, keys in
|
||||
~/.sim/credentials (0600). Select one with -P, --profile, or SIM_PROFILE.
|
||||
|
||||
Examples:
|
||||
$ sim login Authorize the default profile
|
||||
$ sim login --profile dev --endpoint http://localhost:3000
|
||||
$ sim workflows list
|
||||
$ sim logs list --level error --limit 20
|
||||
$ sim --output json tables get tbl_123 Override output for one command
|
||||
$ sim configure --set-output json Save a profile output default
|
||||
$ sim knowledge search --query "refund policy" --kb kb_123
|
||||
$ sim workflows export wf_123 > wf.json JSON flags read files with @
|
||||
$ sim workflows import --workflow @wf.json
|
||||
$ sim whoami --profile dev
|
||||
`
|
||||
)
|
||||
import { buildProgram } from './program'
|
||||
|
||||
/**
|
||||
* Anything the CLI can explain prints as one line and exits 1. An unexpected
|
||||
@@ -82,7 +13,7 @@ Examples:
|
||||
*/
|
||||
async function main() {
|
||||
try {
|
||||
await program.parseAsync(process.argv)
|
||||
await buildProgram().parseAsync(process.argv)
|
||||
} catch (error) {
|
||||
if (error instanceof ProfileConfigError) {
|
||||
console.error(chalk.red(`Error: ${sanitize(error.message)}`))
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { Command, Option } from 'commander'
|
||||
import { loginCommand, logoutCommand, profilesCommand, whoamiCommand } from './commands/auth'
|
||||
import { configureCommand } from './commands/configure'
|
||||
import { attachCredentialCommands } from './commands/credentials'
|
||||
import { attachProtocolCommands } from './commands/protocol/index'
|
||||
import { attachSecretCommands } from './commands/secrets'
|
||||
import { OUTPUT_FORMATS } from './config/index'
|
||||
import { buildGeneratedCommands } from './runtime/build'
|
||||
|
||||
/** Root program description, shared by `--help` and the generated docs. */
|
||||
export const PROGRAM_DESCRIPTION = 'Talk to the Sim API from your terminal'
|
||||
|
||||
export const HELP_EPILOGUE = `
|
||||
Profiles work like the AWS CLI: settings live in ~/.sim/config, keys in
|
||||
~/.sim/credentials (0600). Select one with -P, --profile, or SIM_PROFILE.
|
||||
|
||||
Examples:
|
||||
$ sim login Authorize the default profile
|
||||
$ sim login --profile dev --endpoint http://localhost:3000
|
||||
$ sim workflows list
|
||||
$ sim logs list --level error --limit 20
|
||||
$ sim --output json tables get tbl_123 Override output for one command
|
||||
$ sim configure --set-output json Save a profile output default
|
||||
$ sim knowledge search --query "refund policy" --kb kb_123
|
||||
$ sim workflows export wf_123 > wf.json JSON flags read files with @
|
||||
$ sim workflows import --workflow @wf.json
|
||||
$ sim whoami --profile dev
|
||||
`
|
||||
|
||||
function readPackageVersion(): string {
|
||||
const metadata: unknown = JSON.parse(
|
||||
readFileSync(new URL('../package.json', import.meta.url), 'utf8')
|
||||
)
|
||||
if (
|
||||
typeof metadata !== 'object' ||
|
||||
metadata === null ||
|
||||
!('version' in metadata) ||
|
||||
typeof metadata.version !== 'string'
|
||||
) {
|
||||
throw new Error('CLI package metadata is missing a valid version')
|
||||
}
|
||||
return metadata.version
|
||||
}
|
||||
|
||||
/**
|
||||
* Assemble the complete command tree.
|
||||
*
|
||||
* Kept separate from the entrypoint so the documentation generator can walk the
|
||||
* same tree the terminal parses. A generator that rebuilt the surface from the
|
||||
* contract instead would be a second implementation of `buildGeneratedCommands`,
|
||||
* free to drift from the one users actually run.
|
||||
*
|
||||
* `version` is optional because the generator reads the package metadata itself
|
||||
* and the emitted pages must not carry a version that goes stale on every
|
||||
* release.
|
||||
*/
|
||||
export function buildProgram(options: { version?: boolean } = {}): Command {
|
||||
const program = new Command()
|
||||
|
||||
program.name('sim').description(PROGRAM_DESCRIPTION)
|
||||
|
||||
if (options.version !== false) program.version(readPackageVersion())
|
||||
|
||||
program
|
||||
.option('-P, --profile <name>', 'Profile to use (env: SIM_PROFILE)')
|
||||
.option('--endpoint <url>', 'Sim deployment to talk to (env: SIM_ENDPOINT)')
|
||||
.option('-w, --workspace <id>', 'Workspace to target (env: SIM_WORKSPACE)')
|
||||
.addOption(
|
||||
new Option('--output <format>', 'Output format for this command').choices([...OUTPUT_FORMATS])
|
||||
)
|
||||
|
||||
program.addCommand(loginCommand())
|
||||
program.addCommand(logoutCommand())
|
||||
program.addCommand(whoamiCommand())
|
||||
program.addCommand(profilesCommand())
|
||||
program.addCommand(configureCommand())
|
||||
|
||||
for (const command of buildGeneratedCommands()) {
|
||||
program.addCommand(command)
|
||||
}
|
||||
|
||||
attachCredentialCommands(program)
|
||||
attachProtocolCommands(program)
|
||||
attachSecretCommands(program)
|
||||
|
||||
program.addHelpText('after', HELP_EPILOGUE)
|
||||
|
||||
return program
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import { V2_OPERATIONS, type V2OperationName } from '../generated/v2-api'
|
||||
import { deriveCommandPath } from './derive'
|
||||
import { executeOperation } from './execute'
|
||||
import { addOperationOptions } from './options'
|
||||
import { flagNameFor, isProfileWorkspacePath, PROFILE_INJECTED_FIELD } from './request'
|
||||
import { flagNameFor, flagSpecFor, isProfileWorkspacePath, PROFILE_INJECTED_FIELD } from './request'
|
||||
import type { OperationSpec } from './types'
|
||||
|
||||
const GROUP_ALIASES: Readonly<Record<string, string>> = {
|
||||
@@ -95,7 +95,10 @@ function configureOperation(
|
||||
|
||||
for (const param of operationSpec.pathParams) {
|
||||
if (spec.pathFlags?.[param] || isProfileWorkspacePath(spec, param)) continue
|
||||
command.argument(`<${spec.pathArgumentNames?.[param] ?? param}>`)
|
||||
command.argument(
|
||||
`<${spec.pathArgumentNames?.[param] ?? param}>`,
|
||||
operationSpec.pathParamDocs?.[param]
|
||||
)
|
||||
}
|
||||
|
||||
if (spec.allWorkspaces) {
|
||||
@@ -111,7 +114,12 @@ function configureOperation(
|
||||
if (spec.requestFields && !spec.requestFields.includes(field)) {
|
||||
throw new Error(`${operation}.${field} is positional but not exposed`)
|
||||
}
|
||||
command.argument(`<${flagNameFor(operation, field)}>`)
|
||||
// A field promoted to a positional keeps the prose it would have carried as
|
||||
// a flag; the promotion changes where the value is typed, not what it means.
|
||||
command.argument(
|
||||
`<${flagNameFor(operation, field)}>`,
|
||||
flagSpecFor(operation, field).describe ?? descriptor.describe
|
||||
)
|
||||
}
|
||||
|
||||
if (spec.requestFields) {
|
||||
|
||||
@@ -13,6 +13,30 @@ import type { OperationSpec } from './types'
|
||||
|
||||
export const DEFAULT_LIMIT = 100
|
||||
|
||||
/**
|
||||
* Help text for one flag, best source first.
|
||||
*
|
||||
* The CLI contract wins, because an entry there exists precisely to say
|
||||
* something the schema cannot — that `workflowIds` is really a list, or that
|
||||
* `conflictTarget` reads better as `--on`. Otherwise the field's own
|
||||
* `.describe()` from the route contract carries through: it is the same prose
|
||||
* the OpenAPI specs publish, so the terminal and the API reference explain a
|
||||
* field the same way instead of diverging.
|
||||
*
|
||||
* `Set <name>` remains as a last resort for a field that documents itself
|
||||
* nowhere. It is not documentation — it restates the flag name — so it is worth
|
||||
* treating a fallback that shows up in `--help` as a missing `.describe()` on
|
||||
* the contract rather than as finished work.
|
||||
*/
|
||||
function describeField(
|
||||
flag: { describe?: string },
|
||||
descriptor: FieldSpec,
|
||||
name: string,
|
||||
field: string
|
||||
): string {
|
||||
return flag.describe ?? descriptor.describe ?? `Set ${name.replaceAll('-', ' ') || field}`
|
||||
}
|
||||
|
||||
function addFieldOption(
|
||||
command: Command,
|
||||
operation: V2OperationName,
|
||||
@@ -36,21 +60,24 @@ function addFieldOption(
|
||||
return
|
||||
}
|
||||
|
||||
const documented = describeField(flag, descriptor, name, field)
|
||||
|
||||
if (descriptor.kind === 'boolean' || flag.boolean) {
|
||||
if (descriptor.required) {
|
||||
command.addOption(
|
||||
new Option(
|
||||
`${short}--${name} <true|false>`,
|
||||
`${flag.describe ?? `Set ${field}`} (required)`
|
||||
)
|
||||
new Option(`${short}--${name} <true|false>`, `${documented} (required)`)
|
||||
.choices(['true', 'false'])
|
||||
.makeOptionMandatory()
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
command.option(`${short}--${name}`, flag.describe ?? `Set ${field}`)
|
||||
if (!flag.boolean) command.option(`--no-${name}`, `Set ${field} to false`)
|
||||
command.option(`${short}--${name}`, documented)
|
||||
// The twin exists to send an explicit `false`. Restating the positive
|
||||
// flag's prose here inverts its meaning ("Return only deployed workflows"
|
||||
// on the flag that stops doing exactly that), so it names its counterpart
|
||||
// instead and lets the reader look up one description, not two.
|
||||
if (!flag.boolean) command.option(`--no-${name}`, `Send --${name} as false`)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -58,7 +85,7 @@ function addFieldOption(
|
||||
const wantsJson = takesJson(descriptor, flag)
|
||||
const placeholder = takesList ? '<value...>' : wantsJson ? '<json|@file>' : '<value>'
|
||||
const choices = flag.choices ?? descriptor.values
|
||||
const describe = `${flag.describe ?? `Set ${name.replaceAll('-', ' ')}`}${
|
||||
const describe = `${documented}${
|
||||
takesList
|
||||
? ' (space-separated, or @path / @- with one value per line)'
|
||||
: wantsJson
|
||||
@@ -91,7 +118,7 @@ export function addOperationOptions(
|
||||
command.addOption(
|
||||
new Option(
|
||||
`${short}--${name} <${flag.placeholder ?? 'value'}>`,
|
||||
`${flag.describe ?? `Set ${name.replaceAll('-', ' ')}`} (required)`
|
||||
`${flag.describe ?? operationSpec.pathParamDocs?.[param] ?? `Set ${name.replaceAll('-', ' ')}`} (required)`
|
||||
).makeOptionMandatory()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ export interface FieldSpec {
|
||||
required?: boolean
|
||||
values?: readonly string[]
|
||||
default?: unknown
|
||||
/** The field's `.describe()` from the route contract, used as `--help` text. */
|
||||
describe?: string
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,6 +5,8 @@ export interface OperationSpec {
|
||||
method: NonNullable<RequestOptions['method']>
|
||||
path: string
|
||||
pathParams: readonly string[]
|
||||
/** `.describe()` per path parameter, used as positional-argument help. */
|
||||
pathParamDocs?: Record<string, string>
|
||||
query?: Record<string, FieldSpec>
|
||||
body?: Record<string, FieldSpec>
|
||||
opaqueBody?: boolean
|
||||
|
||||
@@ -0,0 +1,670 @@
|
||||
#!/usr/bin/env bun
|
||||
|
||||
/**
|
||||
* Generates the CLI command reference into `apps/docs/content/docs/en/cli`,
|
||||
* alongside that section's hand-written guides, and owns the section's
|
||||
* `meta.json` because the sidebar lists one entry per command group.
|
||||
*
|
||||
* The source of truth is the command tree the terminal itself parses —
|
||||
* `buildProgram()` from `packages/sim-cli` — not the CLI contract and not the
|
||||
* generated operation table. Both of those are upstream of the tree, so reading
|
||||
* them instead would mean reimplementing `buildGeneratedCommands`, and the docs
|
||||
* would be free to describe a surface no user can invoke.
|
||||
*
|
||||
* Run `bun run generate:cli-docs` after changing a command; `bun run
|
||||
* check:cli-docs` fails when the checked-in pages are stale, which is how CI
|
||||
* keeps them honest.
|
||||
*/
|
||||
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import type { Command } from 'commander'
|
||||
import { V2_OPERATIONS } from '../packages/sim-cli/src/generated/v2-api'
|
||||
import { buildProgram } from '../packages/sim-cli/src/program'
|
||||
|
||||
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
|
||||
const OUTPUT_DIR = path.join(ROOT, 'apps/docs/content/docs/en/cli')
|
||||
|
||||
/** Commander's synthetic help command is not part of the documented surface. */
|
||||
const HELP_COMMAND = 'help'
|
||||
|
||||
/**
|
||||
* Hand-written pages in `OUTPUT_DIR`, in sidebar order.
|
||||
*
|
||||
* Generated pages sit beside them rather than in a subfolder so each command
|
||||
* group is a root-level entry under the Commands heading instead of a nested
|
||||
* folder the reader has to open. That means the stale-file sweep would delete
|
||||
* these, so they are listed — the same guard `scripts/generate-docs.ts` uses for
|
||||
* its hand-authored integration pages.
|
||||
*/
|
||||
const GUIDE_PAGES = [
|
||||
'index',
|
||||
'authentication',
|
||||
'configuration',
|
||||
'output',
|
||||
'scripting',
|
||||
'troubleshooting',
|
||||
] as const
|
||||
|
||||
/** Generated page holding the global options and the commands that take no resource. */
|
||||
const OVERVIEW_PAGE = 'commands'
|
||||
|
||||
/** Generated page carrying every command at once, for search and for agents. */
|
||||
const REFERENCE_PAGE = 'reference'
|
||||
|
||||
/**
|
||||
* Sidebar titles for groups whose command name does not title-case cleanly.
|
||||
* Everything else gets its hyphens split and each word capitalized.
|
||||
*/
|
||||
const GROUP_TITLES: Record<string, string> = {
|
||||
'audit-logs': 'Audit Logs',
|
||||
'custom-tools': 'Custom Tools',
|
||||
'mcp-servers': 'MCP Servers',
|
||||
cli: 'CLI',
|
||||
}
|
||||
|
||||
interface DocumentedCommand {
|
||||
/** Full invocation path, e.g. `workflows runs get`. */
|
||||
path: string[]
|
||||
command: Command
|
||||
}
|
||||
|
||||
function titleFor(name: string): string {
|
||||
const override = GROUP_TITLES[name]
|
||||
if (override) return override
|
||||
return name
|
||||
.split('-')
|
||||
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
||||
.join(' ')
|
||||
}
|
||||
|
||||
function subcommands(command: Command): Command[] {
|
||||
return command.commands.filter((child) => child.name() !== HELP_COMMAND)
|
||||
}
|
||||
|
||||
/** Depth-first walk yielding every leaf command, in the order commander lists them. */
|
||||
function collectLeaves(command: Command, prefix: string[]): DocumentedCommand[] {
|
||||
const children = subcommands(command)
|
||||
if (children.length === 0) return [{ path: prefix, command }]
|
||||
return children.flatMap((child) => collectLeaves(child, [...prefix, child.name()]))
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps a value in a code span for a Markdown table cell.
|
||||
*
|
||||
* A code span already shields `<` and `{` from MDX, and character references
|
||||
* are NOT decoded inside one — escaping `<` to `<` here would render the
|
||||
* entity itself, so `|` is the only character that still has to be escaped. It
|
||||
* has to be: a literal pipe ends the cell, and flags like `--mode <a|b>` and
|
||||
* the row filter help both contain one.
|
||||
*/
|
||||
function code(value: string): string {
|
||||
return `\`${escapeTablePipes(value)}\``
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes a value so a Markdown table row cannot be split by its content.
|
||||
*
|
||||
* A backslash has to be doubled before pipes are escaped, or an input already
|
||||
* ending in one turns `a\` + `|` into `a\\|`: the table parser reads `\\` as an
|
||||
* escaped backslash, leaving the pipe unescaped, and the cell splits early.
|
||||
*
|
||||
* The doubling is correct inside a code span too, even though code spans do not
|
||||
* process backslash escapes — the table layer consumes one level of escaping
|
||||
* before inline parsing runs, so `a\\\|` arrives at the code span as `a\|`.
|
||||
*/
|
||||
function escapeTablePipes(value: string): string {
|
||||
return value.replace(/\\/g, '\\\\').replace(/\|/g, '\\|')
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes prose — text NOT inside a code span — for a Markdown table cell.
|
||||
*
|
||||
* Here the entities are the right answer: MDX reads `{` as the start of a JS
|
||||
* expression and `<` as the start of a JSX tag, and both appear in help text
|
||||
* that embeds JSON examples.
|
||||
*/
|
||||
function escapeCell(value: string): string {
|
||||
return escapeTablePipes(escapeProse(value))
|
||||
}
|
||||
|
||||
/** Escapes MDX-significant characters in body prose. */
|
||||
function escapeProse(value: string): string {
|
||||
return value
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/\{/g, '{')
|
||||
.replace(/\}/g, '}')
|
||||
}
|
||||
|
||||
function usageLine(entry: DocumentedCommand): string {
|
||||
const parts = ['sim', ...entry.path]
|
||||
for (const argument of entry.command.registeredArguments) {
|
||||
const name = argument.variadic ? `${argument.name()}...` : argument.name()
|
||||
parts.push(argument.required ? `<${name}>` : `[${name}]`)
|
||||
}
|
||||
if (entry.command.options.length > 0) parts.push('[options]')
|
||||
return parts.join(' ')
|
||||
}
|
||||
|
||||
/**
|
||||
* Commander help already spells required-ness inside the description of a
|
||||
* derived flag. The table states it in its own column, so the trailing marker
|
||||
* would read as "Yes | Workflow ID (required)".
|
||||
*/
|
||||
function stripRequiredSuffix(description: string): string {
|
||||
return description.replace(/\s*\(required\)\s*$/i, '')
|
||||
}
|
||||
|
||||
/** Help text is written without terminal punctuation; appended clauses need it. */
|
||||
function asSentence(value: string): string {
|
||||
if (!value) return ''
|
||||
return /[.!?]$/.test(value) ? value : `${value}.`
|
||||
}
|
||||
|
||||
/** Returns a table-ready cell: escaped prose, with code spans left intact. */
|
||||
function describeOption(option: Command['options'][number]): string {
|
||||
const parts = [asSentence(escapeCell(stripRequiredSuffix(option.description || '')))]
|
||||
if (option.argChoices && option.argChoices.length > 0) {
|
||||
parts.push(`Accepted values: ${option.argChoices.map(code).join(', ')}.`)
|
||||
}
|
||||
if (option.defaultValue !== undefined) {
|
||||
parts.push(`Defaults to ${code(String(option.defaultValue))}.`)
|
||||
}
|
||||
const description = parts.filter(Boolean).join(' ')
|
||||
return description || '—'
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps a Markdown table so `CommandTable` can size its columns.
|
||||
*
|
||||
* The blank lines are load-bearing: without them MDX treats the table as raw
|
||||
* JSX children and stops parsing it as Markdown.
|
||||
*/
|
||||
function sizedTable(rows: string[]): string[] {
|
||||
return ['', '<CommandTable>', '', ...rows, '', '</CommandTable>']
|
||||
}
|
||||
|
||||
function renderArguments(entry: DocumentedCommand): string[] {
|
||||
const args = entry.command.registeredArguments
|
||||
if (args.length === 0) return []
|
||||
|
||||
// Positional descriptions come from the route contract and are usually absent;
|
||||
// a column of em-dashes is worse than no column.
|
||||
const described = args.some((argument) => Boolean(argument.description))
|
||||
|
||||
const rows = args.map((argument) => {
|
||||
const name = argument.variadic ? `${argument.name()}...` : argument.name()
|
||||
const required = argument.required ? 'Yes' : 'No'
|
||||
const cells = [code(name), required]
|
||||
if (described) cells.push(escapeCell(argument.description) || '—')
|
||||
return `| ${cells.join(' | ')} |`
|
||||
})
|
||||
|
||||
const header = described ? '| Argument | Required | Description |' : '| Argument | Required |'
|
||||
const divider = described ? '| --- | --- | --- |' : '| --- | --- |'
|
||||
return ['', '**Arguments**', ...sizedTable([header, divider, ...rows])]
|
||||
}
|
||||
|
||||
function renderOptions(entry: DocumentedCommand): string[] {
|
||||
const options = entry.command.options
|
||||
if (options.length === 0) return []
|
||||
|
||||
const rows = options.map(
|
||||
(option) =>
|
||||
`| ${code(option.flags)} | ${option.mandatory ? 'Yes' : 'No'} | ${describeOption(option)} |`
|
||||
)
|
||||
|
||||
return [
|
||||
'',
|
||||
'**Options**',
|
||||
...sizedTable(['| Option | Required | Description |', '| --- | --- | --- |', ...rows]),
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* Words that must keep their casing when a heading is sentence-cased.
|
||||
*
|
||||
* The test for "this word was capitalized only because the summary is Title
|
||||
* Case" cannot tell `Documents` from `JSON`, and lowercasing the latter is the
|
||||
* more visible mistake.
|
||||
*/
|
||||
const ACRONYMS = new Set([
|
||||
'API',
|
||||
'APIs',
|
||||
'CSV',
|
||||
'ID',
|
||||
'IDs',
|
||||
'JSON',
|
||||
'MCP',
|
||||
'OAuth',
|
||||
'SSE',
|
||||
'SSO',
|
||||
'URL',
|
||||
'YAML',
|
||||
])
|
||||
|
||||
/**
|
||||
* The command's one-line description, as a heading.
|
||||
*
|
||||
* The command itself is the obvious heading and is the wrong one: every entry
|
||||
* on a page shares the same `sim <group>` prefix, so the table of contents
|
||||
* became a column of "sim knowledge documents …" that has to be read to the
|
||||
* last word to tell two entries apart. The description distinguishes them at
|
||||
* the first word instead, and the exact invocation is still directly below in
|
||||
* the code block.
|
||||
*
|
||||
* A trailing parenthetical is dropped — those qualify behavior ("(requested
|
||||
* outputs are included in JSON or YAML output)") and belong in the body, not in
|
||||
* a sidebar entry.
|
||||
*/
|
||||
function headingFor(entry: DocumentedCommand): string {
|
||||
const description = entry.command.description()
|
||||
if (!description) return `sim ${entry.path.join(' ')}`
|
||||
|
||||
return description
|
||||
.replace(/\s*\([^)]*\)\s*$/, '')
|
||||
.trim()
|
||||
.split(' ')
|
||||
.map((word, index) => {
|
||||
if (ACRONYMS.has(word)) return word
|
||||
if (index === 0) return word.charAt(0).toUpperCase() + word.slice(1)
|
||||
// Only fold words that look Title-Cased; `--tag` or `blockName` stay put.
|
||||
return /^[A-Z][a-z]+$/.test(word) ? word.toLowerCase() : word
|
||||
})
|
||||
.join(' ')
|
||||
}
|
||||
|
||||
interface RenderOptions {
|
||||
/** Markdown heading depth, so the master page can nest commands under groups. */
|
||||
level?: number
|
||||
/**
|
||||
* Use the invocation as the heading instead of the description.
|
||||
*
|
||||
* Correct on the master page for two reasons: descriptions are only unique
|
||||
* within a group, so "Delete folder" would collide four ways across one page;
|
||||
* and a reader — human or agent — arriving at a page of every command is
|
||||
* looking one up by name, not browsing by task.
|
||||
*/
|
||||
commandHeadings?: boolean
|
||||
}
|
||||
|
||||
function renderCommand(entry: DocumentedCommand, options: RenderOptions = {}): string[] {
|
||||
const { level = 2, commandHeadings = false } = options
|
||||
const aliases = entry.command.aliases()
|
||||
const heading = commandHeadings ? `sim ${entry.path.join(' ')}` : headingFor(entry)
|
||||
const description = entry.command.description()
|
||||
|
||||
const lines = [`${'#'.repeat(level)} ${escapeProse(heading)}`, '']
|
||||
if (commandHeadings && description) lines.push(escapeProse(description), '')
|
||||
lines.push('```bash', usageLine(entry), '```')
|
||||
// Only when the heading dropped something — otherwise this restates it.
|
||||
if (
|
||||
!commandHeadings &&
|
||||
description &&
|
||||
description !== heading &&
|
||||
description.trimEnd().endsWith(')')
|
||||
) {
|
||||
lines.push('', escapeProse(description))
|
||||
}
|
||||
if (aliases.length > 0) {
|
||||
const spelled = aliases.map(
|
||||
(alias) => `\`sim ${[...entry.path.slice(0, -1), alias].join(' ')}\``
|
||||
)
|
||||
lines.push('', `Also available as ${spelled.join(', ')}.`)
|
||||
}
|
||||
lines.push(...renderArguments(entry), ...renderOptions(entry), '')
|
||||
return lines
|
||||
}
|
||||
|
||||
function frontmatter(title: string, description: string, imports: string[] = []): string[] {
|
||||
return [
|
||||
'---',
|
||||
`title: ${title}`,
|
||||
`description: ${description}`,
|
||||
'---',
|
||||
'',
|
||||
"import { CommandTable } from '@/components/ui/command-table'",
|
||||
...imports,
|
||||
'',
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* Fails when two commands on one page reduce to the same heading.
|
||||
*
|
||||
* Headings are descriptions now, and descriptions are not guaranteed unique the
|
||||
* way command paths are. Two identical `## ` headings would collide on the same
|
||||
* anchor, so one table-of-contents entry would scroll to the other command. The
|
||||
* fix is a distinguishing `describe` in the CLI contract.
|
||||
*/
|
||||
function assertDistinctHeadings(page: string, entries: DocumentedCommand[]): void {
|
||||
const byHeading = new Map<string, string[]>()
|
||||
for (const entry of entries) {
|
||||
const heading = headingFor(entry)
|
||||
byHeading.set(heading, [...(byHeading.get(heading) ?? []), `sim ${entry.path.join(' ')}`])
|
||||
}
|
||||
|
||||
const collisions = [...byHeading].filter(([, commands]) => commands.length > 1)
|
||||
if (collisions.length === 0) return
|
||||
|
||||
for (const [heading, commands] of collisions) {
|
||||
console.error(`${page}: "${heading}" is the heading for ${commands.join(' and ')}`)
|
||||
}
|
||||
console.error(
|
||||
'\nTwo commands on a page share a heading, so they share an anchor.\n' +
|
||||
'Give one a distinct `describe` in packages/sim-cli/src/contract/commands.ts.'
|
||||
)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Every command on one page.
|
||||
*
|
||||
* A reference split across fourteen pages is fine to browse and bad to consult:
|
||||
* an agent, or anyone using in-page search, has to guess which page holds a
|
||||
* command before it can read it. This is the single fetch that answers any
|
||||
* question about the surface — the same shape as Claude Code's own
|
||||
* `cli-reference`, and the page `/cli/reference.mdx` serves as raw Markdown.
|
||||
*/
|
||||
function renderReferencePage(
|
||||
program: Command,
|
||||
groups: Command[],
|
||||
globals: DocumentedCommand[]
|
||||
): string {
|
||||
const lines = [
|
||||
...frontmatter('Complete reference', 'Every sim command, argument, and flag on a single page', [
|
||||
"import { Callout } from 'fumadocs-ui/components/callout'",
|
||||
]),
|
||||
'Every command the CLI exposes, on one page, generated from the CLI itself.',
|
||||
'For a guided tour start at the [overview](/cli/commands); this page exists to',
|
||||
'be searched, bookmarked, and fed to tools.',
|
||||
'',
|
||||
'<Callout type="info">',
|
||||
'Append `.mdx` to any page in these docs to get its raw Markdown —',
|
||||
'[`/cli/reference.mdx`](/cli/reference.mdx) is this page as plain text. The whole',
|
||||
'documentation set is also published as [`/llms.txt`](/llms.txt) and',
|
||||
'[`/llms-full.txt`](/llms-full.txt) for coding agents.',
|
||||
'</Callout>',
|
||||
'',
|
||||
'## Global options',
|
||||
'',
|
||||
'These apply to every command, and may be written before or after it.',
|
||||
// Deliberately unwrapped, like the same table on the overview page:
|
||||
// `CommandTable` sizes its second column for the `Required` cell of the
|
||||
// three-column tables, which on this two-column one would crush the
|
||||
// description into 5.5rem.
|
||||
'',
|
||||
'| Option | Description |',
|
||||
'| --- | --- |',
|
||||
...program.options.map((option) => `| ${code(option.flags)} | ${describeOption(option)} |`),
|
||||
'',
|
||||
]
|
||||
|
||||
for (const leaf of globals) lines.push(...renderCommand(leaf, { commandHeadings: true }))
|
||||
|
||||
for (const group of groups) {
|
||||
lines.push(`## sim ${group.name()}`, '')
|
||||
const aliases = group.aliases()
|
||||
if (aliases.length > 0) {
|
||||
lines.push(`Also spelled ${aliases.map((alias) => `\`sim ${alias}\``).join(', ')}.`, '')
|
||||
}
|
||||
for (const leaf of collectLeaves(group, [group.name()])) {
|
||||
lines.push(...renderCommand(leaf, { level: 3, commandHeadings: true }))
|
||||
}
|
||||
}
|
||||
|
||||
return `${lines.join('\n').trimEnd()}\n`
|
||||
}
|
||||
|
||||
function renderGroupPage(group: Command): string {
|
||||
const name = group.name()
|
||||
const leaves = collectLeaves(group, [name])
|
||||
const aliases = group.aliases()
|
||||
|
||||
assertDistinctHeadings(`${name}.mdx`, leaves)
|
||||
|
||||
const lines = [
|
||||
...frontmatter(
|
||||
titleFor(name),
|
||||
`${group.description() || `The sim ${name} commands`} — every subcommand, argument, and flag`
|
||||
),
|
||||
]
|
||||
|
||||
if (aliases.length > 0) {
|
||||
lines.push(
|
||||
`\`sim ${name}\` is also spelled ${aliases.map((alias) => `\`sim ${alias}\``).join(', ')}.`,
|
||||
''
|
||||
)
|
||||
}
|
||||
|
||||
lines.push(
|
||||
'Every command below also accepts the [global options](/cli/commands#global-options).',
|
||||
''
|
||||
)
|
||||
|
||||
for (const leaf of leaves) lines.push(...renderCommand(leaf))
|
||||
|
||||
return `${lines.join('\n').trimEnd()}\n`
|
||||
}
|
||||
|
||||
function renderIndexPage(
|
||||
program: Command,
|
||||
groups: Command[],
|
||||
globals: DocumentedCommand[]
|
||||
): string {
|
||||
const lines = [
|
||||
...frontmatter('Overview', 'Global options, and every sim command group'),
|
||||
'Every `sim` command follows the same shape:',
|
||||
'',
|
||||
'```bash',
|
||||
'sim <resource> [sub-resource] <verb> [arguments] [options]',
|
||||
'```',
|
||||
'',
|
||||
'Resource groups are plural, and each one also accepts its singular spelling —',
|
||||
'`sim workflow get` and `sim workflows get` are the same command. `knowledge`',
|
||||
'additionally answers to `kb`.',
|
||||
'',
|
||||
'## Global options',
|
||||
'',
|
||||
'These apply to every command, and may be written before or after it.',
|
||||
'',
|
||||
'| Option | Description |',
|
||||
'| --- | --- |',
|
||||
...program.options.map((option) => `| ${code(option.flags)} | ${describeOption(option)} |`),
|
||||
'',
|
||||
'## Command groups',
|
||||
'',
|
||||
'| Group | Description |',
|
||||
'| --- | --- |',
|
||||
...groups.map(
|
||||
(group) =>
|
||||
`| [${code(`sim ${group.name()}`)}](/cli/${group.name()}) | ${escapeCell(group.description()) || '—'} |`
|
||||
),
|
||||
'',
|
||||
]
|
||||
|
||||
for (const leaf of globals) lines.push(...renderCommand(leaf))
|
||||
|
||||
return `${lines.join('\n').trimEnd()}\n`
|
||||
}
|
||||
|
||||
/**
|
||||
* The section's sidebar.
|
||||
*
|
||||
* Generated because the command-group entries are, and a hand-maintained copy
|
||||
* would drift the moment a group is added. The hand-written guides stay in
|
||||
* `GUIDE_PAGES` so adding one is an edit here rather than a change to how the
|
||||
* pages are produced.
|
||||
*/
|
||||
function renderMeta(groups: Command[]): string {
|
||||
return `${JSON.stringify(
|
||||
{
|
||||
title: 'CLI',
|
||||
root: true,
|
||||
pages: [
|
||||
'---Sim CLI---',
|
||||
...GUIDE_PAGES,
|
||||
'---Commands---',
|
||||
OVERVIEW_PAGE,
|
||||
...groups.map((group) => group.name()),
|
||||
REFERENCE_PAGE,
|
||||
],
|
||||
},
|
||||
null,
|
||||
2
|
||||
)}\n`
|
||||
}
|
||||
|
||||
/**
|
||||
* Fails on two commands sharing one invocation path.
|
||||
*
|
||||
* Commander resolves a duplicate name to the first registered match, so the
|
||||
* loser is unreachable from the terminal while still appearing in `--help` —
|
||||
* which is how `knowledge documents update` shadowed the single-document
|
||||
* update. Left alone the generator would emit two identical headings and
|
||||
* document a command nobody can run, so the collision fails the build here
|
||||
* instead: the fix is a `command` entry in the CLI contract.
|
||||
*/
|
||||
function assertNoDuplicatePaths(leaves: DocumentedCommand[]): void {
|
||||
const seen = new Set<string>()
|
||||
const duplicates = new Set<string>()
|
||||
for (const leaf of leaves) {
|
||||
const invocation = leaf.path.join(' ')
|
||||
if (seen.has(invocation)) duplicates.add(invocation)
|
||||
seen.add(invocation)
|
||||
}
|
||||
if (duplicates.size === 0) return
|
||||
for (const invocation of duplicates) {
|
||||
console.error(`duplicate command path: sim ${invocation}`)
|
||||
}
|
||||
console.error(
|
||||
'\nTwo operations derive to the same command, so one is unreachable.\n' +
|
||||
'Give one of them a `command` in packages/sim-cli/src/contract/commands.ts.'
|
||||
)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Fails on a request field that documents itself nowhere.
|
||||
*
|
||||
* Without a `.describe()` on the route contract, the CLI can only fall back to
|
||||
* restating the flag name — `--sort-by Set sort by` — and that fallback lands
|
||||
* verbatim in `--help` and in these pages. It reads like documentation while
|
||||
* telling the reader nothing, which is worse than an obvious hole.
|
||||
*
|
||||
* The contract is the right place to fix it because the same prose feeds the
|
||||
* OpenAPI specs and the API reference, so one `.describe()` documents the
|
||||
* field everywhere it appears.
|
||||
*
|
||||
* `workspaceId` and `cursor` are exempt: neither is ever a flag — the profile
|
||||
* supplies one and pagination consumes the other.
|
||||
*/
|
||||
function assertEveryFieldDocumented(): void {
|
||||
const undocumented: string[] = []
|
||||
|
||||
for (const [operation, spec] of Object.entries(V2_OPERATIONS)) {
|
||||
for (const slot of ['query', 'body'] as const) {
|
||||
const fields = (spec as Record<string, unknown>)[slot]
|
||||
if (!fields || typeof fields !== 'object') continue
|
||||
for (const [field, descriptor] of Object.entries(fields)) {
|
||||
if (field === 'workspaceId' || field === 'cursor') continue
|
||||
const described = (descriptor as { describe?: unknown }).describe
|
||||
if (typeof described !== 'string' || !described.trim()) {
|
||||
undocumented.push(`${operation}.${slot}.${field}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (undocumented.length === 0) return
|
||||
for (const field of undocumented) console.error(`undocumented request field: ${field}`)
|
||||
console.error(
|
||||
`\n${undocumented.length} field(s) would render as "Set <name>" in --help and in the docs.\n` +
|
||||
'Add a `.describe()` in apps/sim/lib/api/contracts/v2, then run:\n' +
|
||||
' bun run generate:cli-api && bun run generate:cli-docs'
|
||||
)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
/** The sidebar names every guide, so a renamed or deleted one must not fail silently. */
|
||||
function assertGuidesExist(): void {
|
||||
const missing = GUIDE_PAGES.filter((page) => !fs.existsSync(path.join(OUTPUT_DIR, `${page}.mdx`)))
|
||||
if (missing.length === 0) return
|
||||
for (const page of missing) console.error(`missing hand-written guide: cli/${page}.mdx`)
|
||||
console.error('\nCreate it, or drop it from GUIDE_PAGES in scripts/generate-cli-docs.ts.')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
function build(): Map<string, string> {
|
||||
assertEveryFieldDocumented()
|
||||
assertGuidesExist()
|
||||
|
||||
const program = buildProgram({ version: false })
|
||||
const top = subcommands(program)
|
||||
const groups = top.filter((command) => subcommands(command).length > 0)
|
||||
const globals = top
|
||||
.filter((command) => subcommands(command).length === 0)
|
||||
.map((command) => ({ path: [command.name()], command }))
|
||||
|
||||
assertNoDuplicatePaths([
|
||||
...groups.flatMap((group) => collectLeaves(group, [group.name()])),
|
||||
...globals,
|
||||
])
|
||||
|
||||
const files = new Map<string, string>()
|
||||
files.set('meta.json', renderMeta(groups))
|
||||
files.set(`${OVERVIEW_PAGE}.mdx`, renderIndexPage(program, groups, globals))
|
||||
files.set(`${REFERENCE_PAGE}.mdx`, renderReferencePage(program, groups, globals))
|
||||
for (const group of groups) files.set(`${group.name()}.mdx`, renderGroupPage(group))
|
||||
return files
|
||||
}
|
||||
|
||||
/** Generated pages already on disk. Hand-written guides are not the sweep's to remove. */
|
||||
function currentFiles(): Map<string, string> {
|
||||
if (!fs.existsSync(OUTPUT_DIR)) return new Map()
|
||||
const guides = new Set<string>(GUIDE_PAGES.map((page) => `${page}.mdx`))
|
||||
return new Map(
|
||||
fs
|
||||
.readdirSync(OUTPUT_DIR, { withFileTypes: true })
|
||||
.filter((entry) => entry.isFile() && !guides.has(entry.name))
|
||||
.map(
|
||||
(entry) => [entry.name, fs.readFileSync(path.join(OUTPUT_DIR, entry.name), 'utf8')] as const
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
function main(): void {
|
||||
const check = process.argv.includes('--check')
|
||||
const expected = build()
|
||||
const actual = currentFiles()
|
||||
|
||||
const stale = [...actual.keys()].filter((name) => !expected.has(name))
|
||||
const changed = [...expected.entries()].filter(([name, content]) => actual.get(name) !== content)
|
||||
|
||||
if (check) {
|
||||
if (stale.length === 0 && changed.length === 0) {
|
||||
console.log(`CLI docs are up to date (${expected.size} files).`)
|
||||
return
|
||||
}
|
||||
for (const name of changed)
|
||||
console.error(`stale: ${path.relative(ROOT, path.join(OUTPUT_DIR, name[0]))}`)
|
||||
for (const name of stale)
|
||||
console.error(`orphaned: ${path.relative(ROOT, path.join(OUTPUT_DIR, name))}`)
|
||||
console.error('\nRun `bun run generate:cli-docs` and commit the result.')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
fs.mkdirSync(OUTPUT_DIR, { recursive: true })
|
||||
for (const name of stale) fs.rmSync(path.join(OUTPUT_DIR, name))
|
||||
for (const [name, content] of expected) {
|
||||
fs.writeFileSync(path.join(OUTPUT_DIR, name), content)
|
||||
}
|
||||
console.log(
|
||||
`Wrote ${expected.size} files to ${path.relative(ROOT, OUTPUT_DIR)}` +
|
||||
(stale.length > 0 ? `, removed ${stale.length} orphaned` : '')
|
||||
)
|
||||
}
|
||||
|
||||
main()
|
||||
@@ -291,6 +291,28 @@ function pathParams(routePath: string): string[] {
|
||||
return [...routePath.matchAll(/\[([^\]]+)\]/g)].map((m) => m[1])
|
||||
}
|
||||
|
||||
/**
|
||||
* `.describe()` for each path parameter, so a positional argument can explain
|
||||
* itself the way a flag does.
|
||||
*
|
||||
* The params schema is otherwise read only for its field names, which the route
|
||||
* path already supplies — the prose attached to them was being discarded, and
|
||||
* `sim tables rows get <tableId> <rowId>` had nothing to say about either.
|
||||
*/
|
||||
function pathParamDocs(schema: z.ZodType | undefined): Record<string, string> {
|
||||
if (!schema) return {}
|
||||
|
||||
const json = z.toJSONSchema(schema, { io: 'input', unrepresentable: 'any' }) as JsonSchema
|
||||
const docs: Record<string, string> = {}
|
||||
|
||||
for (const [key, property] of Object.entries(json.properties ?? {})) {
|
||||
const description = (property as JsonSchema).description
|
||||
if (typeof description === 'string' && description.trim()) docs[key] = description.trim()
|
||||
}
|
||||
|
||||
return docs
|
||||
}
|
||||
|
||||
/**
|
||||
* The kind a request field reduces to for the CLI's purposes.
|
||||
*
|
||||
@@ -411,6 +433,15 @@ function renderSlotMap(schema: z.ZodType | undefined, indent: string): string |
|
||||
)
|
||||
}
|
||||
if (property.default !== undefined) parts.push(`default: ${JSON.stringify(property.default)}`)
|
||||
// The contract's own `.describe()` is the field's documentation, and it is
|
||||
// already what the OpenAPI specs publish. Carrying it here is what lets
|
||||
// `--help` say what a flag means instead of restating its name back at the
|
||||
// reader as "Set sort by". Read from the reference site first: a field that
|
||||
// narrows a shared `$defs` schema describes its own use of it.
|
||||
const description = properties[key].description ?? property.description
|
||||
if (typeof description === 'string' && description.trim()) {
|
||||
parts.push(`describe: ${JSON.stringify(description.trim())}`)
|
||||
}
|
||||
return `${indent} ${JSON.stringify(key)}: { ${parts.join(', ')} },`
|
||||
})
|
||||
|
||||
@@ -480,6 +511,14 @@ function render(operations: Operation[]): string {
|
||||
out.push(` method: '${op.contract.method}',`)
|
||||
out.push(` path: '${op.contract.path}',`)
|
||||
out.push(` pathParams: [${params.map((p) => `'${p}'`).join(', ')}] as const,`)
|
||||
const paramDocs = pathParamDocs(op.contract.params)
|
||||
const documentedParams = params.filter((p) => paramDocs[p])
|
||||
if (documentedParams.length > 0) {
|
||||
const entries = documentedParams.map(
|
||||
(p) => `${JSON.stringify(p)}: ${JSON.stringify(paramDocs[p])}`
|
||||
)
|
||||
out.push(` pathParamDocs: { ${entries.join(', ')} },`)
|
||||
}
|
||||
out.push(` responseMode: '${op.contract.response.mode}',`)
|
||||
// OpenAPI writes `{id}` where the contract writes `[id]`.
|
||||
const summary = summaries.get(
|
||||
|
||||
Reference in New Issue
Block a user