diff --git a/apps/docs/app/[lang]/[[...slug]]/page.tsx b/apps/docs/app/[lang]/[[...slug]]/page.tsx index a4ffafdad87..91e4c60e1c2 100644 --- a/apps/docs/app/[lang]/[[...slug]]/page.tsx +++ b/apps/docs/app/[lang]/[[...slug]]/page.tsx @@ -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 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 (`//...`) and its index (`/`). - 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 (`//...`) and its index (`/`). + 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 diff --git a/apps/docs/components/navbar/navbar.tsx b/apps/docs/components/navbar/navbar.tsx index d6d23260800..15652ed652a 100644 --- a/apps/docs/components/navbar/navbar.tsx +++ b/apps/docs/components/navbar/navbar.tsx @@ -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'), + match: (p: string) => !SECTION_TABS.some((section) => isInSection(p, section)), external: false, }, { - label: 'Academy', - href: '/academy', - match: (p: string) => p.includes('/academy'), + label: 'API Reference', + href: '/api-reference/getting-started', + match: (p: string) => isInSection(p, 'api-reference'), external: false, }, { - label: 'API Reference', - href: '/api-reference/getting-started', - match: (p: string) => p.includes('/api-reference'), + 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 diff --git a/apps/docs/components/ui/command-table.tsx b/apps/docs/components/ui/command-table.tsx new file mode 100644 index 00000000000..79ffc82ef17 --- /dev/null +++ b/apps/docs/components/ui/command-table.tsx @@ -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 ` 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 ( +
+ {children} +
+ ) +} diff --git a/apps/docs/content/docs/en/cli/audit-logs.mdx b/apps/docs/content/docs/en/cli/audit-logs.mdx new file mode 100644 index 00000000000..8428737e164 --- /dev/null +++ b/apps/docs/content/docs/en/cli/audit-logs.mdx @@ -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 [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Audit-log entry identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--organization ` | Yes | Organization ID (personal API key required). | + + + +## List audit logs + +```bash +sim audit-logs list [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--action ` | No | Filter by exact action name. | +| `--resource-type ` | 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 ` | No | Filter by exact resource identifier. | +| `--start-date ` | 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 ` | 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 ` | No | Maximum items to return (0 for everything). Defaults to `100`. | +| `--organization ` | Yes | Organization ID (personal API key required). | +| `--actor-email ` | No | Filter by actor email address. | +| `--all-workspaces` | No | Do not filter to the configured workspace (personal API key required for account-wide access). | + + diff --git a/apps/docs/content/docs/en/cli/authentication.mdx b/apps/docs/content/docs/en/cli/authentication.mdx new file mode 100644 index 00000000000..80b6567b71b --- /dev/null +++ b/apps/docs/content/docs/en/cli/authentication.mdx @@ -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. + + +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. + + +| Option | What it does | +| --- | --- | +| `--no-browser` | Print the URL instead of opening a browser | +| `--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 ` 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 +``` + + +`sim logout` removes the key from disk but does **not** revoke it. Revoke keys in +Sim under **Settings → API keys**. + + +## 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. + + +`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. + + +### 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. diff --git a/apps/docs/content/docs/en/cli/billing.mdx b/apps/docs/content/docs/en/cli/billing.mdx new file mode 100644 index 00000000000..aeef1624dfc --- /dev/null +++ b/apps/docs/content/docs/en/cli/billing.mdx @@ -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** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--all-workspaces` | No | Do not filter to the configured workspace (personal API key required for account-wide access). | + + + +## List credit usage events + +```bash +sim billing logs [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--source ` | 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 ` | No | Billing period. Accepted values: `1d`, `7d`, `30d`, `all`, `custom`. | +| `--start-date ` | No | Custom period start (ISO 8601). | +| `--end-date ` | No | Custom period end (ISO 8601). | +| `--limit ` | 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). | + + diff --git a/apps/docs/content/docs/en/cli/commands.mdx b/apps/docs/content/docs/en/cli/commands.mdx new file mode 100644 index 00000000000..dab4a4295f2 --- /dev/null +++ b/apps/docs/content/docs/en/cli/commands.mdx @@ -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 [sub-resource] [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 ` | Profile to use (env: SIM_PROFILE). | +| `--endpoint ` | Sim deployment to talk to (env: SIM_ENDPOINT). | +| `-w, --workspace ` | Workspace to target (env: SIM_WORKSPACE). | +| `--output ` | 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** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--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. | + + + +## Remove the profile's stored API key + +```bash +sim logout [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--all` | No | Remove the profile entirely, including its settings. | + + + +## 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** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--set-endpoint ` | No | Sim deployment to talk to. | +| `--set-workspace ` | No | Default workspace for workspace-scoped commands. | +| `--set-output ` | No | Default output format (table \| json \| yaml \| text). | +| `--unset ` | No | Remove settings (endpoint, workspace, output). | + + diff --git a/apps/docs/content/docs/en/cli/configuration.mdx b/apps/docs/content/docs/en/cli/configuration.mdx new file mode 100644 index 00000000000..b24adfb8ca0 --- /dev/null +++ b/apps/docs/content/docs/en/cli/configuration.mdx @@ -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 ` | The Sim deployment to talk to | +| `--set-workspace ` | Default workspace for workspace-scoped commands | +| `--set-output ` | Default output format: `table`, `json`, `yaml`, or `text` | +| `--unset ` | Remove settings — `endpoint`, `workspace`, or `output` | + +Run `sim configure` with no flags to print the profile's stored settings. + + +API keys are deliberately **not** settable through `sim configure`. Use +[`sim login`](/cli/authentication), or `SIM_API_KEY` for CI. + + +## 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_… +``` + + +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. + + +## 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 +``` diff --git a/apps/docs/content/docs/en/cli/credentials.mdx b/apps/docs/content/docs/en/cli/credentials.mdx new file mode 100644 index 00000000000..534d284bf88 --- /dev/null +++ b/apps/docs/content/docs/en/cli/credentials.mdx @@ -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 [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `credentialId` | Yes | Credential to disconnect. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `-y, --yes` | No | Skip the confirmation. | + + + +## List credential providers + +```bash +sim credentials providers list [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--search ` | No | Case-insensitive substring match against the credential provider name. | + + + +## List credentials + +```bash +sim credentials list [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--type ` | No | Restrict results to this credential type. Accepted values: `oauth`, `service_account`. | +| `--provider-id ` | No | Restrict results to credentials for this integration provider. | +| `--search ` | No | Case-insensitive substring match against the credential display name. | +| `--sort-by ` | No | Field used to sort the result. Accepted values: `displayName`, `createdAt`, `updatedAt`. | +| `--sort-order ` | No | Sort direction. Accepted values: `asc`, `desc`. | +| `--limit ` | No | Maximum items to return (0 for everything). Defaults to `100`. | + + + +## Create a service-account credential using its discovered provider schema + +```bash +sim credentials create [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `providerId` | Yes | Service-account provider to create a credential for | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--name ` | Yes | Name shown for the credential in Sim. | +| `--credentials ` | Yes | Provider credentials as JSON (or @path / @- to read a file or stdin). | +| `--description ` | No | Optional credential description. | +| `--id ` | No | Client-generated credential ID when provider discovery requires it. | + + + +## Create a short-lived link for connecting an OAuth provider + +```bash +sim credentials connect [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `providerId` | Yes | OAuth provider to connect | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--name ` | Yes | Name shown for the new credential in Sim. | + + + +## Create a short-lived link for reconnecting an OAuth credential + +```bash +sim credentials reconnect +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `credentialId` | Yes | Existing OAuth credential to re-authorize | + + diff --git a/apps/docs/content/docs/en/cli/custom-tools.mdx b/apps/docs/content/docs/en/cli/custom-tools.mdx new file mode 100644 index 00000000000..4830c239cc6 --- /dev/null +++ b/apps/docs/content/docs/en/cli/custom-tools.mdx @@ -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** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--title ` | Yes | Display title, unique within the workspace. | +| `--schema ` | Yes | OpenAI function schema: {"type":"function","function":{"name":"...","parameters":{"type":"object","properties":{}}}} (JSON, or @path / @- to read a file or stdin). | +| `--code ` | Yes | Tool implementation executed in the sandboxed function runtime. | + + + +## Delete custom tool + +```bash +sim custom-tools delete [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique custom tool identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `-y, --yes` | No | Skip the confirmation. | + + + +## Get custom tool + +```bash +sim custom-tools get +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique custom tool identifier. | + + + +## List custom tools + +```bash +sim custom-tools list [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--search ` | No | Case-insensitive substring match against the tool title. | +| `--sort-by ` | No | Field used to sort the result. Accepted values: `title`, `createdAt`, `updatedAt`. | +| `--sort-order ` | No | Sort direction. Accepted values: `asc`, `desc`. | +| `--limit ` | No | Maximum items to return (0 for everything). Defaults to `100`. | + + + +## Update custom tool + +```bash +sim custom-tools update [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique custom tool identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--title ` | No | New display title for the tool. | +| `--schema ` | No | OpenAI function schema: {"type":"function","function":{"name":"...","parameters":{"type":"object","properties":{}}}} (JSON, or @path / @- to read a file or stdin). | +| `--code ` | No | Replacement tool implementation. | + + diff --git a/apps/docs/content/docs/en/cli/files.mdx b/apps/docs/content/docs/en/cli/files.mdx new file mode 100644 index 00000000000..43e106fac57 --- /dev/null +++ b/apps/docs/content/docs/en/cli/files.mdx @@ -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** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--file-ids ` | Yes | File identifiers to update. (space-separated, or @path / @- with one value per line). | +| `-y, --yes` | No | Skip the confirmation. | + + + +## Create file + +```bash +sim files create [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--name ` | Yes | File name, including its extension. Path separators and dot segments are rejected. | +| `--content-type ` | No | MIME type. When omitted, it is inferred from the file extension. | +| `--folder ` | No | Folder path; the leading / is optional. | +| `--content ` | 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 ` | No | Encoding of the content field. Accepted values: `utf-8`, `base64`. | + + + +## Create a file folder at a path + +```bash +sim files folders create +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | Yes | Folder path; the leading / is optional | + + + +## Delete folder + +```bash +sim files folders delete [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | Yes | Folder path; the leading / is optional | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--recursive` | No | Delete the folder and its descendants. | +| `-y, --yes` | No | Skip the confirmation. | + + + +## List folders + +```bash +sim files folders list [options] +``` + +Also available as `sim files folders ls`. + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--parent ` | No | Direct parent folder path. | +| `--search ` | No | Case-insensitive substring match against the folder name. | +| `--sort-by ` | 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 ` | No | Sort direction. Accepted values: `asc`, `desc`. | + + + +## Rename or move a file folder + +```bash +sim files folders move +``` + +Also available as `sim files folders mv`. + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | Yes | Folder path; the leading / is optional | +| `destination` | Yes | Folder path; the leading / is optional | + + + +## Delete file + +```bash +sim files delete [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `fileId` | Yes | File identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `-y, --yes` | No | Skip the confirmation. | + + + +## Show file metadata and sharing status + +```bash +sim files describe [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `fileId` | Yes | File identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--scope ` | 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`. | + + + +## Show a file’s share settings + +```bash +sim files share get +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `fileId` | Yes | File identifier. | + + + +## Enable or disable sharing for a file + +```bash +sim files share set [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `fileId` | Yes | File identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--is-active ` | 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 ` | 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 ` | 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 ` | 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). | + + + +## List files + +```bash +sim files list [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--folder ` | No | Folder path; the leading / is optional. | +| `--scope ` | 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 ` | No | Case-insensitive substring match against the file name. | +| `--sort-by ` | 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 ` | No | Sort direction. Accepted values: `asc`, `desc`. | +| `--limit ` | No | Maximum items to return (0 for everything). Defaults to `100`. | + + + +## Move files into another folder + +```bash +sim files move [options] +``` + +Also available as `sim files mv`. + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--file-ids ` | Yes | File identifiers to update. (space-separated, or @path / @- with one value per line). | +| `--to ` | No | Destination folder path; omit for root. | + + + +## Rename a file + +```bash +sim files rename [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `fileId` | Yes | File identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--name ` | Yes | New file name, including its extension. | + + + +## Restore file + +```bash +sim files restore create +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `fileId` | Yes | File identifier. | + + + +## Replace a file’s contents + +```bash +sim files set-content [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `fileId` | Yes | File identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--content ` | 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 ` | No | Content encoding. Accepted values: `utf-8`, `base64`. | + + + +## Upload a file to the workspace + +```bash +sim files upload [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | Yes | Local file to upload | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--folder ` | No | Destination folder path (defaults to /). | +| `--name ` | No | Store it under a different name. | + + + +## Get a file’s content + +```bash +sim files get [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `fileId` | Yes | File whose content to read | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `-o, --output-file ` | No | Write content to a file instead of stdout. | +| `--force` | No | Overwrite --output-file if it already exists. | + + + +## List file resources and child folders together + +```bash +sim files ls [path] [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | No | Folder path to list; defaults to the root folder | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--search ` | No | Filter folders and resources by name. | +| `--limit ` | No | Maximum combined items to return (0 for everything). Defaults to `100`. | + + + +## Create a file directory at a path + +```bash +sim files mkdir +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | Yes | Folder path to create; the leading / is optional | + + diff --git a/apps/docs/content/docs/en/cli/index.mdx b/apps/docs/content/docs/en/cli/index.mdx new file mode 100644 index 00000000000..50449ec925d --- /dev/null +++ b/apps/docs/content/docs/en/cli/index.mdx @@ -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 + + + + ```bash + npm install --global sim + ``` + + + ```bash + pnpm add --global sim + ``` + + + ```bash + bun add --global sim + ``` + + + ```bash + yarn global add sim + ``` + + + +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 + + + + +### 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. + + + +### 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. + + + +### 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 +``` + + + +### 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 `. + + + + +## How commands are shaped + +Every command reads the same way: + +```bash +sim [sub-resource] [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 diff --git a/apps/docs/content/docs/en/cli/knowledge.mdx b/apps/docs/content/docs/en/cli/knowledge.mdx new file mode 100644 index 00000000000..2333dc80edb --- /dev/null +++ b/apps/docs/content/docs/en/cli/knowledge.mdx @@ -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 [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `knowledgeBaseId` | Yes | Unique knowledge base identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--operation ` | Yes | Whether the selected documents become enabled or disabled for search. Accepted values: `enable`, `disable`. | +| `--document ` | 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 ` | No | With `selectAll`, restrict the update to documents in this state. Accepted values: `all`, `enabled`, `disabled`. | + + + +## Delete document + +```bash +sim knowledge documents delete [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `knowledgeBaseId` | Yes | Unique knowledge base identifier. | +| `documentId` | Yes | Unique knowledge document identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `-y, --yes` | No | Skip the confirmation. | + + + +## Get document + +```bash +sim knowledge documents get +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `knowledgeBaseId` | Yes | Unique knowledge base identifier. | +| `documentId` | Yes | Unique knowledge document identifier. | + + + +## List documents + +```bash +sim knowledge documents list [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `knowledgeBaseId` | Yes | Unique knowledge base identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--limit ` | No | Maximum items to return (0 for everything). Defaults to `100`. | +| `--search ` | No | Case-insensitive substring match against the document filename. | +| `--enabled-filter ` | No | Filter by whether documents are enabled for search. Accepted values: `all`, `enabled`, `disabled`. | +| `--sort-by ` | 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 ` | No | Sort direction. Accepted values: `asc`, `desc`. | +| `--tag-filters ` | 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. | + + + +## Update document + +```bash +sim knowledge documents update [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique knowledge base identifier. | +| `documentId` | Yes | Unique knowledge document identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--filename ` | 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 ` | No | New value for tag slot 1. | +| `--tag2 ` | No | New value for tag slot 2. | +| `--tag3 ` | No | New value for tag slot 3. | +| `--tag4 ` | No | New value for tag slot 4. | +| `--tag5 ` | No | New value for tag slot 5. | +| `--tag6 ` | No | New value for tag slot 6. | +| `--tag7 ` | No | New value for tag slot 7. | +| `--number1 ` | No | New value for number tag slot 1. | +| `--number2 ` | No | New value for number tag slot 2. | +| `--number3 ` | No | New value for number tag slot 3. | +| `--number4 ` | No | New value for number tag slot 4. | +| `--number5 ` | No | New value for number tag slot 5. | +| `--date1 ` | No | New value for date tag slot 1, formatted YYYY-MM-DD. | +| `--date2 ` | 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. | + + + +## Upload a document to a knowledge base + +```bash +sim knowledge documents upload [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `knowledgeBaseId` | Yes | Knowledge base to upload into | +| `path` | Yes | Local file to upload | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--name ` | No | Store it under a different name. | +| `--tag ` | No | Document tags, in tag1 through tag7 order. | +| `--recipe ` | No | Document processing recipe. | +| `--lang ` | No | Document language code. | + + + +## Create knowledge base + +```bash +sim knowledge create [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--name ` | Yes | Human-readable knowledge base name. | +| `--description ` | No | Optional knowledge base description. | +| `--chunking-config ` | No | Chunking configuration; defaults are applied when omitted. (JSON, or @path / @- to read a file or stdin). | +| `--folder ` | No | Folder path; the leading / is optional. | + + + +## Create a knowledge folder at a path + +```bash +sim knowledge folders create +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | Yes | Folder path; the leading / is optional | + + + +## Delete folder + +```bash +sim knowledge folders delete [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | Yes | Folder path; the leading / is optional | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--recursive` | No | Delete the folder and its descendants. | +| `-y, --yes` | No | Skip the confirmation. | + + + +## List folders + +```bash +sim knowledge folders list [options] +``` + +Also available as `sim knowledge folders ls`. + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--parent ` | No | Direct parent folder path. | +| `--search ` | No | Case-insensitive substring match against the folder name. | +| `--sort-by ` | 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 ` | No | Sort direction. Accepted values: `asc`, `desc`. | + + + +## Rename or move a knowledge folder + +```bash +sim knowledge folders move +``` + +Also available as `sim knowledge folders mv`. + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | Yes | Folder path; the leading / is optional | +| `destination` | Yes | Folder path; the leading / is optional | + + + +## Delete knowledge base + +```bash +sim knowledge delete [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique knowledge base identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `-y, --yes` | No | Skip the confirmation. | + + + +## Get knowledge base + +```bash +sim knowledge get +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique knowledge base identifier. | + + + +## List knowledge bases + +```bash +sim knowledge list [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--folder ` | No | Folder path; the leading / is optional. | +| `--search ` | No | Case-insensitive substring match against the resource name. | +| `--sort-by ` | 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 ` | No | Sort direction. Accepted values: `asc`, `desc`. | +| `--limit ` | No | Maximum items to return (0 for everything). Defaults to `100`. | + + + +## List tags + +```bash +sim knowledge tags list +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique knowledge base identifier. | + + + +## Search knowledge + +```bash +sim knowledge search [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--kb ` | Yes | Knowledge base ID (repeatable) (space-separated, or @path / @- with one value per line). | +| `--query ` | No | Text to search for. | +| `--top-k ` | 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 ` | No | Tag filters as [{"tagName":"...","operator":"...","value":"..."}] (JSON, or @path / @- to read a file or stdin). | +| `--search-mode ` | 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 ` | 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 ` | 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. | + + + +## Update knowledge base + +```bash +sim knowledge update [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique knowledge base identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--name ` | No | New knowledge base name. | +| `--description ` | No | New knowledge base description. | +| `--chunking-config ` | No | New document chunking configuration. (JSON, or @path / @- to read a file or stdin). | +| `--folder ` | No | Folder path; the leading / is optional. | + + + +## Move a knowledge base to a folder + +```bash +sim knowledge mv +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique knowledge base identifier. | +| `folder` | Yes | Folder path; the leading / is optional | + + + +## List knowledge resources and child folders together + +```bash +sim knowledge ls [path] [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | No | Folder path to list; defaults to the root folder | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--search ` | No | Filter folders and resources by name. | +| `--limit ` | No | Maximum combined items to return (0 for everything). Defaults to `100`. | + + + +## Create a knowledge directory at a path + +```bash +sim knowledge mkdir +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | Yes | Folder path to create; the leading / is optional | + + diff --git a/apps/docs/content/docs/en/cli/logs.mdx b/apps/docs/content/docs/en/cli/logs.mdx new file mode 100644 index 00000000000..1418a0226b0 --- /dev/null +++ b/apps/docs/content/docs/en/cli/logs.mdx @@ -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 [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `runId` | Yes | Unique workflow run identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--trace` | No | Show expanded trace spans with inputs, outputs, errors, timing, and cost. | + + + +## List logs + +```bash +sim logs list [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--workflow ` | No | Comma-separated workflow identifiers to include. An empty entry is rejected. (space-separated, or @path / @- with one value per line). | +| `--trigger ` | 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 ` | No | Severity level to include. Accepted values: `info`, `error`. | +| `--start-date ` | 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 ` | 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 ` | 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 ` | 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 ` | 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 ` | 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 ` | No | AI model used during execution. | +| `--details ` | 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 ` | No | Maximum items to return (0 for everything). Defaults to `100`. | +| `--order ` | 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 ` | No | Exact run identifier to match. | +| `--folder ` | No | Folder path; the leading / is optional (space-separated, or @path / @- with one value per line). | + + diff --git a/apps/docs/content/docs/en/cli/mcp-servers.mdx b/apps/docs/content/docs/en/cli/mcp-servers.mdx new file mode 100644 index 00000000000..ea6c97506f0 --- /dev/null +++ b/apps/docs/content/docs/en/cli/mcp-servers.mdx @@ -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** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--name ` | Yes | Server display name. | +| `--description ` | No | Optional server description. | +| `--transport ` | No | Transport used to communicate with the server. Applied server-side as `streamable-http` when omitted on create. Accepted values: `streamable-http`. | +| `--url ` | 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 ` | 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 ` | 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 ` | No | Per-request timeout in milliseconds. Applied server-side as 30000 when omitted on create. | +| `--retries ` | 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 ` | No | Pre-registered OAuth client identifier. Changing it on update revokes the stored OAuth grant and forces reauthorization. | +| `--oauth-client-secret ` | 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. | + + + +## Delete MCP server + +```bash +sim mcp-servers delete [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique MCP server identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `-y, --yes` | No | Skip the confirmation. | + + + +## Get MCP server + +```bash +sim mcp-servers get +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique MCP server identifier. | + + + +## List MCP servers + +```bash +sim mcp-servers list [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--search ` | No | Case-insensitive substring match against the server name. | +| `--sort-by ` | 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 ` | No | Sort direction. Accepted values: `asc`, `desc`. | +| `--limit ` | No | Maximum items to return (0 for everything). Defaults to `100`. | + + + +## List MCP server tools + +```bash +sim mcp-servers tools list [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique MCP server identifier. | + + + +**Options** + + + +| 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. | + + + +## Update MCP server + +```bash +sim mcp-servers update [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique MCP server identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--name ` | No | Server display name. | +| `--description ` | No | Optional server description. | +| `--transport ` | No | Transport used to communicate with the server. Applied server-side as `streamable-http` when omitted on create. Accepted values: `streamable-http`. | +| `--url ` | No | Immutable server URL. When provided, it must equal the current URL; use delete and create to change endpoints. | +| `--auth-type ` | 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 ` | 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 ` | No | Per-request timeout in milliseconds. Applied server-side as 30000 when omitted on create. | +| `--retries ` | 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 ` | No | Pre-registered OAuth client identifier. Changing it on update revokes the stored OAuth grant and forces reauthorization. | +| `--oauth-client-secret ` | 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. | + + diff --git a/apps/docs/content/docs/en/cli/meta.json b/apps/docs/content/docs/en/cli/meta.json new file mode 100644 index 00000000000..a18d504f907 --- /dev/null +++ b/apps/docs/content/docs/en/cli/meta.json @@ -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" + ] +} diff --git a/apps/docs/content/docs/en/cli/output.mdx b/apps/docs/content/docs/en/cli/output.mdx new file mode 100644 index 00000000000..340b73e25ef --- /dev/null +++ b/apps/docs/content/docs/en/cli/output.mdx @@ -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. + + +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. + + +## 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 +``` diff --git a/apps/docs/content/docs/en/cli/reference.mdx b/apps/docs/content/docs/en/cli/reference.mdx new file mode 100644 index 00000000000..ba55b5d3677 --- /dev/null +++ b/apps/docs/content/docs/en/cli/reference.mdx @@ -0,0 +1,3704 @@ +--- +title: Complete reference +description: Every sim command, argument, and flag on a single page +--- + +import { CommandTable } from '@/components/ui/command-table' +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. + + +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. + + +## Global options + +These apply to every command, and may be written before or after it. + +| Option | Description | +| --- | --- | +| `-P, --profile ` | Profile to use (env: SIM_PROFILE). | +| `--endpoint ` | Sim deployment to talk to (env: SIM_ENDPOINT). | +| `-w, --workspace ` | Workspace to target (env: SIM_WORKSPACE). | +| `--output ` | Output format for this command. Accepted values: `table`, `json`, `yaml`, `text`. | + +## sim login + +Authorize this terminal and store an API key for the profile + +```bash +sim login [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--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. | + + + +## sim logout + +Remove the profile's stored API key + +```bash +sim logout [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--all` | No | Remove the profile entirely, including its settings. | + + + +## sim whoami + +Show the resolved profile and where each setting came from + +```bash +sim whoami +``` + +## sim profiles + +List the profiles defined in the config and credentials files + +```bash +sim profiles +``` + +Also available as `sim profile`. + +## sim configure + +Set a profile's endpoint, default workspace, or output format + +```bash +sim configure [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--set-endpoint ` | No | Sim deployment to talk to. | +| `--set-workspace ` | No | Default workspace for workspace-scoped commands. | +| `--set-output ` | No | Default output format (table \| json \| yaml \| text). | +| `--unset ` | No | Remove settings (endpoint, workspace, output). | + + + +## sim audit-logs + +Also spelled `sim audit-log`. + +### sim audit-logs get + +Get Audit Log + +```bash +sim audit-logs get [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Audit-log entry identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--organization ` | Yes | Organization ID (personal API key required). | + + + +### sim audit-logs list + +List Audit Logs + +```bash +sim audit-logs list [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--action ` | No | Filter by exact action name. | +| `--resource-type ` | 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 ` | No | Filter by exact resource identifier. | +| `--start-date ` | 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 ` | 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 ` | No | Maximum items to return (0 for everything). Defaults to `100`. | +| `--organization ` | Yes | Organization ID (personal API key required). | +| `--actor-email ` | No | Filter by actor email address. | +| `--all-workspaces` | No | Do not filter to the configured workspace (personal API key required for account-wide access). | + + + +## sim billing + +### sim billing status + +Show billing status and current-period credit usage + +```bash +sim billing status [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--all-workspaces` | No | Do not filter to the configured workspace (personal API key required for account-wide access). | + + + +### sim billing logs + +List credit usage events + +```bash +sim billing logs [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--source ` | 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 ` | No | Billing period. Accepted values: `1d`, `7d`, `30d`, `all`, `custom`. | +| `--start-date ` | No | Custom period start (ISO 8601). | +| `--end-date ` | No | Custom period end (ISO 8601). | +| `--limit ` | 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). | + + + +## sim credentials + +Also spelled `sim credential`. + +### sim credentials delete + +Disconnect Credential + +```bash +sim credentials delete [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `credentialId` | Yes | Credential to disconnect. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `-y, --yes` | No | Skip the confirmation. | + + + +### sim credentials providers list + +List Credential Providers + +```bash +sim credentials providers list [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--search ` | No | Case-insensitive substring match against the credential provider name. | + + + +### sim credentials list + +List Credentials + +```bash +sim credentials list [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--type ` | No | Restrict results to this credential type. Accepted values: `oauth`, `service_account`. | +| `--provider-id ` | No | Restrict results to credentials for this integration provider. | +| `--search ` | No | Case-insensitive substring match against the credential display name. | +| `--sort-by ` | No | Field used to sort the result. Accepted values: `displayName`, `createdAt`, `updatedAt`. | +| `--sort-order ` | No | Sort direction. Accepted values: `asc`, `desc`. | +| `--limit ` | No | Maximum items to return (0 for everything). Defaults to `100`. | + + + +### sim credentials create + +Create a service-account credential using its discovered provider schema + +```bash +sim credentials create [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `providerId` | Yes | Service-account provider to create a credential for | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--name ` | Yes | Name shown for the credential in Sim. | +| `--credentials ` | Yes | Provider credentials as JSON (or @path / @- to read a file or stdin). | +| `--description ` | No | Optional credential description. | +| `--id ` | No | Client-generated credential ID when provider discovery requires it. | + + + +### sim credentials connect + +Create a short-lived link for connecting an OAuth provider + +```bash +sim credentials connect [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `providerId` | Yes | OAuth provider to connect | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--name ` | Yes | Name shown for the new credential in Sim. | + + + +### sim credentials reconnect + +Create a short-lived link for reconnecting an OAuth credential + +```bash +sim credentials reconnect +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `credentialId` | Yes | Existing OAuth credential to re-authorize | + + + +## sim custom-tools + +Also spelled `sim custom-tool`. + +### sim custom-tools create + +Create Custom Tool + +```bash +sim custom-tools create [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--title ` | Yes | Display title, unique within the workspace. | +| `--schema ` | Yes | OpenAI function schema: {"type":"function","function":{"name":"...","parameters":{"type":"object","properties":{}}}} (JSON, or @path / @- to read a file or stdin). | +| `--code ` | Yes | Tool implementation executed in the sandboxed function runtime. | + + + +### sim custom-tools delete + +Delete Custom Tool + +```bash +sim custom-tools delete [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique custom tool identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `-y, --yes` | No | Skip the confirmation. | + + + +### sim custom-tools get + +Get Custom Tool + +```bash +sim custom-tools get +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique custom tool identifier. | + + + +### sim custom-tools list + +List Custom Tools + +```bash +sim custom-tools list [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--search ` | No | Case-insensitive substring match against the tool title. | +| `--sort-by ` | No | Field used to sort the result. Accepted values: `title`, `createdAt`, `updatedAt`. | +| `--sort-order ` | No | Sort direction. Accepted values: `asc`, `desc`. | +| `--limit ` | No | Maximum items to return (0 for everything). Defaults to `100`. | + + + +### sim custom-tools update + +Update Custom Tool + +```bash +sim custom-tools update [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique custom tool identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--title ` | No | New display title for the tool. | +| `--schema ` | No | OpenAI function schema: {"type":"function","function":{"name":"...","parameters":{"type":"object","properties":{}}}} (JSON, or @path / @- to read a file or stdin). | +| `--code ` | No | Replacement tool implementation. | + + + +## sim files + +Also spelled `sim file`. + +### sim files batch-delete + +Delete several files at once + +```bash +sim files batch-delete [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--file-ids ` | Yes | File identifiers to update. (space-separated, or @path / @- with one value per line). | +| `-y, --yes` | No | Skip the confirmation. | + + + +### sim files create + +Create File + +```bash +sim files create [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--name ` | Yes | File name, including its extension. Path separators and dot segments are rejected. | +| `--content-type ` | No | MIME type. When omitted, it is inferred from the file extension. | +| `--folder ` | No | Folder path; the leading / is optional. | +| `--content ` | 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 ` | No | Encoding of the content field. Accepted values: `utf-8`, `base64`. | + + + +### sim files folders create + +Create a file folder at a path + +```bash +sim files folders create +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | Yes | Folder path; the leading / is optional | + + + +### sim files folders delete + +Delete Folder + +```bash +sim files folders delete [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | Yes | Folder path; the leading / is optional | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--recursive` | No | Delete the folder and its descendants. | +| `-y, --yes` | No | Skip the confirmation. | + + + +### sim files folders list + +List Folders + +```bash +sim files folders list [options] +``` + +Also available as `sim files folders ls`. + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--parent ` | No | Direct parent folder path. | +| `--search ` | No | Case-insensitive substring match against the folder name. | +| `--sort-by ` | 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 ` | No | Sort direction. Accepted values: `asc`, `desc`. | + + + +### sim files folders move + +Rename or move a file folder + +```bash +sim files folders move +``` + +Also available as `sim files folders mv`. + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | Yes | Folder path; the leading / is optional | +| `destination` | Yes | Folder path; the leading / is optional | + + + +### sim files delete + +Delete File + +```bash +sim files delete [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `fileId` | Yes | File identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `-y, --yes` | No | Skip the confirmation. | + + + +### sim files describe + +Show file metadata and sharing status + +```bash +sim files describe [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `fileId` | Yes | File identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--scope ` | 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`. | + + + +### sim files share get + +Show a file’s share settings + +```bash +sim files share get +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `fileId` | Yes | File identifier. | + + + +### sim files share set + +Enable or disable sharing for a file + +```bash +sim files share set [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `fileId` | Yes | File identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--is-active ` | 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 ` | 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 ` | 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 ` | 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). | + + + +### sim files list + +List Files + +```bash +sim files list [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--folder ` | No | Folder path; the leading / is optional. | +| `--scope ` | 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 ` | No | Case-insensitive substring match against the file name. | +| `--sort-by ` | 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 ` | No | Sort direction. Accepted values: `asc`, `desc`. | +| `--limit ` | No | Maximum items to return (0 for everything). Defaults to `100`. | + + + +### sim files move + +Move files into another folder + +```bash +sim files move [options] +``` + +Also available as `sim files mv`. + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--file-ids ` | Yes | File identifiers to update. (space-separated, or @path / @- with one value per line). | +| `--to ` | No | Destination folder path; omit for root. | + + + +### sim files rename + +Rename a file + +```bash +sim files rename [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `fileId` | Yes | File identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--name ` | Yes | New file name, including its extension. | + + + +### sim files restore create + +Restore File + +```bash +sim files restore create +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `fileId` | Yes | File identifier. | + + + +### sim files set-content + +Replace a file’s contents + +```bash +sim files set-content [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `fileId` | Yes | File identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--content ` | 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 ` | No | Content encoding. Accepted values: `utf-8`, `base64`. | + + + +### sim files upload + +Upload a file to the workspace + +```bash +sim files upload [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | Yes | Local file to upload | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--folder ` | No | Destination folder path (defaults to /). | +| `--name ` | No | Store it under a different name. | + + + +### sim files get + +Get a file’s content + +```bash +sim files get [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `fileId` | Yes | File whose content to read | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `-o, --output-file ` | No | Write content to a file instead of stdout. | +| `--force` | No | Overwrite --output-file if it already exists. | + + + +### sim files ls + +List file resources and child folders together + +```bash +sim files ls [path] [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | No | Folder path to list; defaults to the root folder | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--search ` | No | Filter folders and resources by name. | +| `--limit ` | No | Maximum combined items to return (0 for everything). Defaults to `100`. | + + + +### sim files mkdir + +Create a file directory at a path + +```bash +sim files mkdir +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | Yes | Folder path to create; the leading / is optional | + + + +## sim knowledge + +Also spelled `sim kb`. + +### sim knowledge documents batch-update + +Enable or disable every matching document + +```bash +sim knowledge documents batch-update [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `knowledgeBaseId` | Yes | Unique knowledge base identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--operation ` | Yes | Whether the selected documents become enabled or disabled for search. Accepted values: `enable`, `disable`. | +| `--document ` | 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 ` | No | With `selectAll`, restrict the update to documents in this state. Accepted values: `all`, `enabled`, `disabled`. | + + + +### sim knowledge documents delete + +Delete Document + +```bash +sim knowledge documents delete [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `knowledgeBaseId` | Yes | Unique knowledge base identifier. | +| `documentId` | Yes | Unique knowledge document identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `-y, --yes` | No | Skip the confirmation. | + + + +### sim knowledge documents get + +Get Document + +```bash +sim knowledge documents get +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `knowledgeBaseId` | Yes | Unique knowledge base identifier. | +| `documentId` | Yes | Unique knowledge document identifier. | + + + +### sim knowledge documents list + +List Documents + +```bash +sim knowledge documents list [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `knowledgeBaseId` | Yes | Unique knowledge base identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--limit ` | No | Maximum items to return (0 for everything). Defaults to `100`. | +| `--search ` | No | Case-insensitive substring match against the document filename. | +| `--enabled-filter ` | No | Filter by whether documents are enabled for search. Accepted values: `all`, `enabled`, `disabled`. | +| `--sort-by ` | 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 ` | No | Sort direction. Accepted values: `asc`, `desc`. | +| `--tag-filters ` | 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. | + + + +### sim knowledge documents update + +Update Document + +```bash +sim knowledge documents update [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique knowledge base identifier. | +| `documentId` | Yes | Unique knowledge document identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--filename ` | 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 ` | No | New value for tag slot 1. | +| `--tag2 ` | No | New value for tag slot 2. | +| `--tag3 ` | No | New value for tag slot 3. | +| `--tag4 ` | No | New value for tag slot 4. | +| `--tag5 ` | No | New value for tag slot 5. | +| `--tag6 ` | No | New value for tag slot 6. | +| `--tag7 ` | No | New value for tag slot 7. | +| `--number1 ` | No | New value for number tag slot 1. | +| `--number2 ` | No | New value for number tag slot 2. | +| `--number3 ` | No | New value for number tag slot 3. | +| `--number4 ` | No | New value for number tag slot 4. | +| `--number5 ` | No | New value for number tag slot 5. | +| `--date1 ` | No | New value for date tag slot 1, formatted YYYY-MM-DD. | +| `--date2 ` | 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. | + + + +### sim knowledge documents upload + +Upload a document to a knowledge base + +```bash +sim knowledge documents upload [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `knowledgeBaseId` | Yes | Knowledge base to upload into | +| `path` | Yes | Local file to upload | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--name ` | No | Store it under a different name. | +| `--tag ` | No | Document tags, in tag1 through tag7 order. | +| `--recipe ` | No | Document processing recipe. | +| `--lang ` | No | Document language code. | + + + +### sim knowledge create + +Create Knowledge Base + +```bash +sim knowledge create [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--name ` | Yes | Human-readable knowledge base name. | +| `--description ` | No | Optional knowledge base description. | +| `--chunking-config ` | No | Chunking configuration; defaults are applied when omitted. (JSON, or @path / @- to read a file or stdin). | +| `--folder ` | No | Folder path; the leading / is optional. | + + + +### sim knowledge folders create + +Create a knowledge folder at a path + +```bash +sim knowledge folders create +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | Yes | Folder path; the leading / is optional | + + + +### sim knowledge folders delete + +Delete Folder + +```bash +sim knowledge folders delete [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | Yes | Folder path; the leading / is optional | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--recursive` | No | Delete the folder and its descendants. | +| `-y, --yes` | No | Skip the confirmation. | + + + +### sim knowledge folders list + +List Folders + +```bash +sim knowledge folders list [options] +``` + +Also available as `sim knowledge folders ls`. + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--parent ` | No | Direct parent folder path. | +| `--search ` | No | Case-insensitive substring match against the folder name. | +| `--sort-by ` | 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 ` | No | Sort direction. Accepted values: `asc`, `desc`. | + + + +### sim knowledge folders move + +Rename or move a knowledge folder + +```bash +sim knowledge folders move +``` + +Also available as `sim knowledge folders mv`. + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | Yes | Folder path; the leading / is optional | +| `destination` | Yes | Folder path; the leading / is optional | + + + +### sim knowledge delete + +Delete Knowledge Base + +```bash +sim knowledge delete [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique knowledge base identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `-y, --yes` | No | Skip the confirmation. | + + + +### sim knowledge get + +Get Knowledge Base + +```bash +sim knowledge get +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique knowledge base identifier. | + + + +### sim knowledge list + +List Knowledge Bases + +```bash +sim knowledge list [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--folder ` | No | Folder path; the leading / is optional. | +| `--search ` | No | Case-insensitive substring match against the resource name. | +| `--sort-by ` | 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 ` | No | Sort direction. Accepted values: `asc`, `desc`. | +| `--limit ` | No | Maximum items to return (0 for everything). Defaults to `100`. | + + + +### sim knowledge tags list + +List Tags + +```bash +sim knowledge tags list +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique knowledge base identifier. | + + + +### sim knowledge search + +Search Knowledge + +```bash +sim knowledge search [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--kb ` | Yes | Knowledge base ID (repeatable) (space-separated, or @path / @- with one value per line). | +| `--query ` | No | Text to search for. | +| `--top-k ` | 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 ` | No | Tag filters as [{"tagName":"...","operator":"...","value":"..."}] (JSON, or @path / @- to read a file or stdin). | +| `--search-mode ` | 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 ` | 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 ` | 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. | + + + +### sim knowledge update + +Update Knowledge Base + +```bash +sim knowledge update [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique knowledge base identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--name ` | No | New knowledge base name. | +| `--description ` | No | New knowledge base description. | +| `--chunking-config ` | No | New document chunking configuration. (JSON, or @path / @- to read a file or stdin). | +| `--folder ` | No | Folder path; the leading / is optional. | + + + +### sim knowledge mv + +Move a knowledge base to a folder + +```bash +sim knowledge mv +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique knowledge base identifier. | +| `folder` | Yes | Folder path; the leading / is optional | + + + +### sim knowledge ls + +List knowledge resources and child folders together + +```bash +sim knowledge ls [path] [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | No | Folder path to list; defaults to the root folder | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--search ` | No | Filter folders and resources by name. | +| `--limit ` | No | Maximum combined items to return (0 for everything). Defaults to `100`. | + + + +### sim knowledge mkdir + +Create a knowledge directory at a path + +```bash +sim knowledge mkdir +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | Yes | Folder path to create; the leading / is optional | + + + +## sim logs + +Also spelled `sim log`. + +### sim logs get + +Show run diagnostics + +```bash +sim logs get [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `runId` | Yes | Unique workflow run identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--trace` | No | Show expanded trace spans with inputs, outputs, errors, timing, and cost. | + + + +### sim logs list + +List Logs + +```bash +sim logs list [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--workflow ` | No | Comma-separated workflow identifiers to include. An empty entry is rejected. (space-separated, or @path / @- with one value per line). | +| `--trigger ` | 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 ` | No | Severity level to include. Accepted values: `info`, `error`. | +| `--start-date ` | 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 ` | 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 ` | 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 ` | 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 ` | 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 ` | 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 ` | No | AI model used during execution. | +| `--details ` | 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 ` | No | Maximum items to return (0 for everything). Defaults to `100`. | +| `--order ` | 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 ` | No | Exact run identifier to match. | +| `--folder ` | No | Folder path; the leading / is optional (space-separated, or @path / @- with one value per line). | + + + +## sim mcp-servers + +Also spelled `sim mcp-server`. + +### sim mcp-servers create + +Create MCP Server + +```bash +sim mcp-servers create [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--name ` | Yes | Server display name. | +| `--description ` | No | Optional server description. | +| `--transport ` | No | Transport used to communicate with the server. Applied server-side as `streamable-http` when omitted on create. Accepted values: `streamable-http`. | +| `--url ` | 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 ` | 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 ` | 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 ` | No | Per-request timeout in milliseconds. Applied server-side as 30000 when omitted on create. | +| `--retries ` | 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 ` | No | Pre-registered OAuth client identifier. Changing it on update revokes the stored OAuth grant and forces reauthorization. | +| `--oauth-client-secret ` | 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. | + + + +### sim mcp-servers delete + +Delete MCP Server + +```bash +sim mcp-servers delete [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique MCP server identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `-y, --yes` | No | Skip the confirmation. | + + + +### sim mcp-servers get + +Get MCP Server + +```bash +sim mcp-servers get +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique MCP server identifier. | + + + +### sim mcp-servers list + +List MCP Servers + +```bash +sim mcp-servers list [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--search ` | No | Case-insensitive substring match against the server name. | +| `--sort-by ` | 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 ` | No | Sort direction. Accepted values: `asc`, `desc`. | +| `--limit ` | No | Maximum items to return (0 for everything). Defaults to `100`. | + + + +### sim mcp-servers tools list + +List MCP Server Tools + +```bash +sim mcp-servers tools list [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique MCP server identifier. | + + + +**Options** + + + +| 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. | + + + +### sim mcp-servers update + +Update MCP Server + +```bash +sim mcp-servers update [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique MCP server identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--name ` | No | Server display name. | +| `--description ` | No | Optional server description. | +| `--transport ` | No | Transport used to communicate with the server. Applied server-side as `streamable-http` when omitted on create. Accepted values: `streamable-http`. | +| `--url ` | No | Immutable server URL. When provided, it must equal the current URL; use delete and create to change endpoints. | +| `--auth-type ` | 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 ` | 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 ` | No | Per-request timeout in milliseconds. Applied server-side as 30000 when omitted on create. | +| `--retries ` | 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 ` | No | Pre-registered OAuth client identifier. Changing it on update revokes the stored OAuth grant and forces reauthorization. | +| `--oauth-client-secret ` | 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. | + + + +## sim secrets + +Also spelled `sim secret`. + +### sim secrets delete + +Delete Secret + +```bash +sim secrets delete [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `name` | Yes | Secret to create, replace, or delete. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--scope ` | 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. | + + + +### sim secrets list + +List Secrets + +```bash +sim secrets list [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--scope ` | No | Restrict results to one ownership scope. Accepted values: `workspace`, `personal`. | +| `--search ` | No | Case-insensitive substring match against the secret name. | +| `--sort-by ` | 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 ` | No | Sort direction. Accepted values: `asc`, `desc`. | +| `--limit ` | No | Maximum items to return (0 for everything). Defaults to `100`. | + + + +### sim secrets set + +Create or replace a named secret + +```bash +sim secrets set [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `name` | Yes | Secret name, as referenced in workflows | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--scope ` | Yes | Secret ownership scope. Accepted values: `workspace`, `personal`. | +| `--value ` | No | Secret value; visible to shell history when supplied directly. | + + + +## sim skills + +Also spelled `sim skill`. + +### sim skills create + +Create Skill + +```bash +sim skills create [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--name ` | Yes | Kebab-case name, unique within the workspace and not reserved by a built-in skill. | +| `--description ` | Yes | One-line summary of when the skill applies. | +| `--content ` | Yes | Skill body containing the instructions given to the agent. | + + + +### sim skills delete + +Delete Skill + +```bash +sim skills delete [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique skill identifier. A built-in skill is `builtin-` followed by its name, for example `builtin-research`. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `-y, --yes` | No | Skip the confirmation. | + + + +### sim skills get + +Get Skill + +```bash +sim skills get +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique skill identifier. A built-in skill is `builtin-` followed by its name, for example `builtin-research`. | + + + +### sim skills list + +List Skills + +```bash +sim skills list [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--search ` | No | Case-insensitive substring match against the skill name. | +| `--sort-by ` | 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 ` | No | Sort direction. Accepted values: `asc`, `desc`. | +| `--limit ` | No | Maximum items to return (0 for everything). Defaults to `100`. | + + + +### sim skills update + +Update Skill + +```bash +sim skills update [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique skill identifier. A built-in skill is `builtin-` followed by its name, for example `builtin-research`. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--name ` | No | New kebab-case skill name. | +| `--description ` | No | New one-line summary of when the skill applies. | +| `--content ` | No | Replacement skill body. | + + + +## sim tables + +Also spelled `sim table`. + +### sim tables columns create + +Add Column + +```bash +sim tables columns create [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--column ` | Yes | Column definition to add. (JSON, or @path / @- to read a file or stdin). | + + + +### sim tables columns delete + +Delete Column + +```bash +sim tables columns delete [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--column-name ` | Yes | Name of the column to delete. | +| `-y, --yes` | No | Skip the confirmation. | + + + +### sim tables columns run + +Run a column’s workflow + +```bash +sim tables columns run [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--group-ids ` | Yes | Workflow or enrichment groups to run. (space-separated, or @path / @- with one value per line). | +| `--run-mode ` | No | Whether to run all or only incomplete cells. Accepted values: `all`, `incomplete`. | +| `--row-ids ` | No | Explicit row subset to run. (space-separated, or @path / @- with one value per line). | +| `--filter ` | No | Predicate: {"all":[{"field":"status","op":"eq","value":"active"}]}; groups use all/any. Operators: eq, ne, gt, gte, lt, lte, in, nin, contains, ncontains, startsWith, endsWith, like, ilike, nlike, nilike, isEmpty, isNotEmpty, isNull, isNotNull (JSON, or @path / @- to read a file or stdin). | +| `--exclude-row-ids ` | No | Rows excluded from a select-all run scope. (space-separated, or @path / @- with one value per line). | +| `--limit ` | No | Optional cap on eligible rows to run. (JSON, or @path / @- to read a file or stdin). | + + + +### sim tables columns update + +Update Column + +```bash +sim tables columns update [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--column-name ` | Yes | Current name of the column to update. | +| `--updates ` | Yes | Mutable column fields. (JSON, or @path / @- to read a file or stdin). | + + + +### sim tables groups create + +Add Workflow Group + +```bash +sim tables groups create [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--group ` | Yes | Workflow or enrichment producer definition. (JSON, or @path / @- to read a file or stdin). | +| `--output-columns ` | Yes | Columns created for producer outputs. (JSON, or @path / @- to read a file or stdin). | +| `--auto-run` | No | Whether to schedule existing rows after group creation. | +| `--no-auto-run` | No | Send --auto-run as false. | + + + +### sim tables groups delete + +Delete Workflow Group + +```bash +sim tables groups delete [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--group-id ` | Yes | Workflow group to delete. | +| `-y, --yes` | No | Skip the confirmation. | + + + +### sim tables groups list + +List Workflow Groups + +```bash +sim tables groups list +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +### sim tables groups update + +Update Workflow Group + +```bash +sim tables groups update [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--group-id ` | Yes | Workflow group to update. | +| `--workflow-id ` | No | Replacement backing workflow identifier. | +| `--name ` | No | Replacement workflow-group display name. | +| `--dependencies ` | No | Replacement input dependencies. (JSON, or @path / @- to read a file or stdin). | +| `--outputs ` | No | Replacement producer outputs. (JSON, or @path / @- to read a file or stdin). | +| `--new-output-columns ` | No | Columns to add for new outputs. (JSON, or @path / @- to read a file or stdin). | +| `--mapping-updates ` | No | Existing output-column mapping changes. (JSON, or @path / @- to read a file or stdin). | +| `--input-mappings ` | No | Replacement workflow input mappings. (JSON, or @path / @- to read a file or stdin). | +| `--deployment-mode ` | No | Replacement workflow execution mode. Accepted values: `live`, `deployed`. | +| `--type ` | No | Workflow-group producer type. Must match the group's stored type — a group's producer cannot be changed after creation. Accepted values: `manual`, `enrichment`. | +| `--auto-run` | No | Replacement automatic-run setting. | +| `--no-auto-run` | No | Send --auto-run as false. | + + + +### sim tables exports cancel + +Cancel Table Export + +```bash +sim tables exports cancel +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `exportId` | Yes | Unique table-export identifier. | + + + +### sim tables exports create + +Create Table Export + +```bash +sim tables exports create [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--format ` | No | Export file format. Accepted values: `csv`, `json`. | + + + +### sim tables exports get + +Get Table Export + +```bash +sim tables exports get +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `exportId` | Yes | Unique table-export identifier. | + + + +### sim tables exports download + +Get the download URL for a finished export + +```bash +sim tables exports download +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `exportId` | Yes | Unique table-export identifier. | + + + +### sim tables imports cancel + +Cancel Table Import + +```bash +sim tables imports cancel +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `importId` | Yes | Unique table-import identifier. | + + + +### sim tables imports get + +Get Table Import + +```bash +sim tables imports get +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `importId` | Yes | Unique table-import identifier. | + + + +### sim tables cancel-runs + +Stop every running column job + +```bash +sim tables cancel-runs [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--scope ` | Yes | Whether to cancel across the table or one row. Accepted values: `all`, `row`. | +| `--row-id ` | No | Row whose runs should be canceled for row scope. | +| `--filter ` | No | Predicate: {"all":[{"field":"status","op":"eq","value":"active"}]}; groups use all/any. Operators: eq, ne, gt, gte, lt, lte, in, nin, contains, ncontains, startsWith, endsWith, like, ilike, nlike, nilike, isEmpty, isNotEmpty, isNull, isNotNull (JSON, or @path / @- to read a file or stdin). | +| `--exclude-row-ids ` | No | Rows excluded from an all-scope cancellation. (space-separated, or @path / @- with one value per line). | + + + +### sim tables create + +Create Table + +```bash +sim tables create [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--name ` | Yes | Identifier: letters, numbers, and underscores; cannot start with a number. | +| `--description ` | No | Optional table description. | +| `--schema ` | Yes | Table schema: {"columns":[{"name":"email","type":"string"}]} (JSON, or @path / @- to read a file or stdin). | +| `--folder ` | No | Folder path; the leading / is optional. | + + + +### sim tables folders create + +Create a table folder at a path + +```bash +sim tables folders create +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | Yes | Folder path; the leading / is optional | + + + +### sim tables folders delete + +Delete Folder + +```bash +sim tables folders delete [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | Yes | Folder path; the leading / is optional | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--recursive` | No | Delete the folder and its descendants. | +| `-y, --yes` | No | Skip the confirmation. | + + + +### sim tables folders list + +List Folders + +```bash +sim tables folders list [options] +``` + +Also available as `sim tables folders ls`. + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--parent ` | No | Direct parent folder path. | +| `--search ` | No | Case-insensitive substring match against the folder name. | +| `--sort-by ` | 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 ` | No | Sort direction. Accepted values: `asc`, `desc`. | + + + +### sim tables folders move + +Rename or move a table folder + +```bash +sim tables folders move +``` + +Also available as `sim tables folders mv`. + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | Yes | Folder path; the leading / is optional | +| `destination` | Yes | Folder path; the leading / is optional | + + + +### sim tables rows create + +Create Rows + +```bash +sim tables rows create [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--data ` | No | One row keyed by column name (JSON, or @path / @-; choose exactly one body flag). | +| `--rows ` | No | Several rows keyed by column name (JSON, or @path / @-; choose exactly one body flag). | + + + +### sim tables rows delete + +Delete Row + +```bash +sim tables rows delete [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | +| `rowId` | Yes | Unique table row identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `-y, --yes` | No | Skip the confirmation. | + + + +### sim tables rows batch-delete + +Delete rows matching a filter, or an explicit list of ids + +```bash +sim tables rows batch-delete [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--filter ` | No | Predicate: {"all":[{"field":"status","op":"eq","value":"active"}]}; groups use all/any. Operators: eq, ne, gt, gte, lt, lte, in, nin, contains, ncontains, startsWith, endsWith, like, ilike, nlike, nilike, isEmpty, isNotEmpty, isNull, isNotNull (JSON, or @path / @- to read a file or stdin). | +| `--limit ` | No | Maximum items to return (0 for everything). Defaults to `100`. | +| `--row ` | No | Explicit row identifiers to delete. (space-separated, or @path / @- with one value per line). | +| `-y, --yes` | No | Skip the confirmation. | + + + +### sim tables rows find + +Find rows matching a predicate + +```bash +sim tables rows find [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--q ` | Yes | Value to find. | +| `--filter ` | No | Predicate: {"all":[{"field":"status","op":"eq","value":"active"}]}; groups use all/any. Operators: eq, ne, gt, gte, lt, lte, in, nin, contains, ncontains, startsWith, endsWith, like, ilike, nlike, nilike, isEmpty, isNotEmpty, isNull, isNotNull (JSON, or @path / @- to read a file or stdin). | +| `--sort ` | No | Ordered sort keys: [{"field":"createdAt","direction":"desc"}] (direction: asc or desc) (JSON, or @path / @- to read a file or stdin). | + + + +### sim tables rows get + +Get Row + +```bash +sim tables rows get +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | +| `rowId` | Yes | Unique table row identifier. | + + + +### sim tables rows list + +List Rows + +```bash +sim tables rows list [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--limit ` | No | Maximum items to return (0 for everything). Defaults to `100`. | + + + +### sim tables rows query + +Query Rows + +```bash +sim tables rows query [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--filter ` | No | Predicate: {"all":[{"field":"status","op":"eq","value":"active"}]}; groups use all/any. Operators: eq, ne, gt, gte, lt, lte, in, nin, contains, ncontains, startsWith, endsWith, like, ilike, nlike, nilike, isEmpty, isNotEmpty, isNull, isNotNull (JSON, or @path / @- to read a file or stdin). | +| `--sort ` | No | Ordered sort keys: [{"field":"createdAt","direction":"desc"}] (direction: asc or desc) (JSON, or @path / @- to read a file or stdin). | +| `--limit ` | No | Maximum items to return (0 for everything). Defaults to `100`. | + + + +### sim tables rows enrich + +Run one row’s enrichment group + +```bash +sim tables rows enrich +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | +| `rowId` | Yes | Unique table row identifier. | +| `groupId` | Yes | Workflow or enrichment group to run. | + + + +### sim tables rows batch-update + +Update every row matching a filter + +```bash +sim tables rows batch-update [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--filter ` | Yes | Predicate: {"all":[{"field":"status","op":"eq","value":"active"}]}; groups use all/any. Operators: eq, ne, gt, gte, lt, lte, in, nin, contains, ncontains, startsWith, endsWith, like, ilike, nlike, nilike, isEmpty, isNotEmpty, isNull, isNotNull (JSON, or @path / @- to read a file or stdin). | +| `--data ` | Yes | Row-data patch applied to every matching row. (JSON, or @path / @- to read a file or stdin). | +| `--limit ` | No | Maximum items to return (0 for everything). Defaults to `100`. | +| `-y, --yes` | No | Skip the confirmation. | + + + +### sim tables rows update + +Update Row + +```bash +sim tables rows update [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | +| `rowId` | Yes | Unique table row identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--data ` | Yes | Partial row-data patch keyed by column name. (JSON, or @path / @- to read a file or stdin). | + + + +### sim tables views create + +Create View + +```bash +sim tables views create [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--name ` | Yes | Saved-view display name. | +| `--config ` | Yes | Saved filter, sort, and column-layout configuration. (JSON, or @path / @- to read a file or stdin). | + + + +### sim tables views delete + +Delete View + +```bash +sim tables views delete [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | +| `viewId` | Yes | Unique saved-view identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `-y, --yes` | No | Skip the confirmation. | + + + +### sim tables views get + +Get View + +```bash +sim tables views get +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | +| `viewId` | Yes | Unique saved-view identifier. | + + + +### sim tables views list + +List Views + +```bash +sim tables views list +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +### sim tables views update + +Update View + +```bash +sim tables views update [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | +| `viewId` | Yes | Unique saved-view identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--name ` | No | Replacement saved-view display name. | +| `--config ` | No | Complete replacement saved-view configuration. (JSON, or @path / @- to read a file or stdin). | +| `--config-patch ` | No | Saved-view configuration fields to shallow-merge. (JSON, or @path / @- to read a file or stdin). | +| `--is-default` | No | Whether to promote this view to the table default. | +| `--no-is-default` | No | Send --is-default as false. | + + + +### sim tables delete + +Delete Table + +```bash +sim tables delete [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `-y, --yes` | No | Skip the confirmation. | + + + +### sim tables get + +Get Table + +```bash +sim tables get +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +### sim tables list + +List Tables + +```bash +sim tables list [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--folder ` | No | Folder path; the leading / is optional. | +| `--search ` | No | Case-insensitive substring match against the resource name. | +| `--sort-by ` | 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 ` | No | Sort direction. Accepted values: `asc`, `desc`. | +| `--limit ` | No | Maximum items to return (0 for everything). Defaults to `100`. | + + + +### sim tables count create + +Count Rows + +```bash +sim tables count create [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--predicate ` | No | Recursive predicate tree. Each group node is exactly one non-empty `all` or `any` array whose members are further groups or `{ field, op, value }` conditions; the root must be a group, not a bare condition. At most 100 members per group, 10 levels of nesting, and 500 nodes in total. The negating operators include nulls: `ne`, `nin`, `ncontains`, `nlike`, and `nilike` match rows whose column is null or absent, so "not X" is not the complement of "X" over a nullable column. That holds for every column type, multi-select included. To exclude nulls, `all`-combine the negation with `isNotEmpty` (multi-select) or `isNotNull`. Comparison: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`. Membership: `in`, `nin` (array operand). Emptiness: `isEmpty`, `isNotEmpty`, `isNull`, `isNotNull` (no operand). Substring, always case-insensitive, operand matched literally: `contains`, `ncontains`, `startsWith`, `endsWith`. Pattern: `like`/`nlike` (case-sensitive), `ilike`/`nilike` (case-insensitive). **`*` is the only wildcard** and stands for any run of characters; `%`, `_`, and backslash match themselves. Use `like: "Hi*"`, not `like: "Hi%"`. A `select` column compares by option id and restricts its operators: single-select accepts `eq`, `ne`, `in`, `nin`; multi-select accepts `contains`, `ncontains`. Option names are accepted as operands and resolved to ids. (JSON, or @path / @- to read a file or stdin). | + + + +### sim tables update + +Update Table + +```bash +sim tables update [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--name ` | No | Identifier: letters, numbers, and underscores; cannot start with a number. | +| `--description ` | No | Replacement table description, or null to clear it. | +| `--folder ` | No | Folder path; the leading / is optional. | + + + +### sim tables mv + +Move a table to a folder + +```bash +sim tables mv +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | +| `folder` | Yes | Folder path; the leading / is optional | + + + +### sim tables upsert + +Insert a row, or update the one that conflicts on a unique column + +```bash +sim tables upsert [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--data ` | Yes | Complete set of row cells keyed by column name. On the update branch this REPLACES the matched row: any column not present here is cleared, unlike the merging `PATCH /api/v2/tables/{tableId}/rows/{rowId}`. (JSON, or @path / @- to read a file or stdin). | +| `--on ` | No | Unique column to resolve the conflict against. | + + + +### sim tables import + +Import a CSV, into a new table by default + +```bash +sim tables import [path] [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | No | Local CSV file to import; omit when using --file-id | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--name ` | No | Identifier for the new table: letters, numbers, and underscores; defaults to the sanitized file name. | +| `--table-id ` | No | Import into this existing table instead of creating one. | +| `--mode ` | No | How to write into --table-id (default: append). Accepted values: `append`, `replace`. | +| `--folder ` | No | Folder path for the new table. | +| `--file-id ` | No | Import a file already in the workspace instead of a local path. | +| `--mapping ` | No | Column mapping (--table-id only). | +| `--create-columns ` | No | Columns to create (--table-id only). | +| `--timezone ` | No | Timezone for date parsing, e.g. America/New_York. | +| `--no-wait` | No | Return once the import is queued instead of watching it. | + + + +### sim tables ls + +List table resources and child folders together + +```bash +sim tables ls [path] [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | No | Folder path to list; defaults to the root folder | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--search ` | No | Filter folders and resources by name. | +| `--limit ` | No | Maximum combined items to return (0 for everything). Defaults to `100`. | + + + +### sim tables mkdir + +Create a table directory at a path + +```bash +sim tables mkdir +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | Yes | Folder path to create; the leading / is optional | + + + +## sim workflows + +Also spelled `sim workflow`. + +### sim workflows runs cancel + +Cancel a running workflow run + +```bash +sim workflows runs cancel [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `runId` | Yes | Unique workflow run identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--workflow ` | Yes | Workflow ID. | + + + +### sim workflows runs get + +Show run status (requested outputs are included in JSON or YAML output) + +```bash +sim workflows runs get [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `runId` | Yes | Unique workflow run identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--workflow ` | Yes | Workflow ID. | +| `--include-output` | No | Include the final output in JSON or YAML output. | +| `--select-output ` | No | Include blockName.field values in JSON or YAML output (e.g. agent_1.content) (space-separated, or @path / @- with one value per line). | + + + +### sim workflows runs list + +List runs for a workflow + +```bash +sim workflows runs list [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--workflow ` | Yes | Workflow ID. | +| `--status ` | No | Filter by run status. Accepted values: `pending`, `running`, `completed`, `failed`, `cancelled`, `paused`. | +| `--trigger ` | No | Filter by trigger type. | +| `--start-date ` | 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 ` | 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 ` | No | Maximum items to return (0 for everything). Defaults to `100`. | +| `--order ` | 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`. | + + + +### sim workflows runs resume + +Resume a paused run (output is included in JSON or YAML output) + +```bash +sim workflows runs resume [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `runId` | Yes | Unique workflow run identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--workflow ` | Yes | Workflow ID. | +| `--context ` | Yes | Pause context ID returned by run status. | +| `--input ` | No | Resume input as JSON (JSON, or @path / @- to read a file or stdin). | + + + +### sim workflows create + +Create Workflow + +```bash +sim workflows create [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--name ` | Yes | Workflow name. | +| `--description ` | No | Optional workflow description. | +| `--folder ` | No | Folder path; the leading / is optional. | + + + +### sim workflows folders create + +Create a workflow folder at a path + +```bash +sim workflows folders create +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | Yes | Folder path; the leading / is optional | + + + +### sim workflows folders delete + +Delete Workflow Folder + +```bash +sim workflows folders delete [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | Yes | Folder path; the leading / is optional | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--recursive` | No | Delete the folder and its descendants. | +| `-y, --yes` | No | Skip the confirmation. | + + + +### sim workflows folders list + +List Workflow Folders + +```bash +sim workflows folders list [options] +``` + +Also available as `sim workflows folders ls`. + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--parent ` | No | Direct parent folder path. | +| `--search ` | No | Case-insensitive substring match against the folder name. | +| `--sort-by ` | 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 ` | No | Sort direction. Accepted values: `asc`, `desc`. | + + + +### sim workflows folders move + +Rename or move a workflow folder + +```bash +sim workflows folders move +``` + +Also available as `sim workflows folders mv`. + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | Yes | Folder path; the leading / is optional | +| `destination` | Yes | Folder path; the leading / is optional | + + + +### sim workflows delete + +Delete Workflow + +```bash +sim workflows delete [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique workflow identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `-y, --yes` | No | Skip the confirmation. | + + + +### sim workflows deploy + +Deploy Workflow + +```bash +sim workflows deploy [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique workflow identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--name ` | No | Optional label for the deployment version. | +| `--description ` | No | Optional release note for the deployment version. | + + + +### sim workflows run + +Run a deployed workflow + +```bash +sim workflows run [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique workflow identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--input ` | 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 ` | 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 ` | 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 ` | No | Maximum total bytes of file content to inline as base64. Rejected when `async` is true. | + + + +### sim workflows export + +Print a workflow as a portable JSON document + +```bash +sim workflows export +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique workflow identifier. | + + + +### sim workflows get + +Get Workflow + +```bash +sim workflows get +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique workflow identifier. | + + + +### sim workflows deployment list + +Get Workflow Deployment + +```bash +sim workflows deployment list +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique workflow identifier. | + + + +### sim workflows versions get + +Get Workflow Version + +```bash +sim workflows versions get +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique workflow identifier. | +| `version` | Yes | Numeric deployment version. | + + + +### sim workflows versions list + +List Workflow Versions + +```bash +sim workflows versions list [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique workflow identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--limit ` | No | Maximum items to return (0 for everything). Defaults to `100`. | + + + +### sim workflows import + +Import Workflow + +```bash +sim workflows import [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--workflow ` | Yes | Workflow export object, bare workflow state, or JSON string containing either form. (JSON, or @path / @- to read a file or stdin). | +| `--folder ` | No | Folder path; the leading / is optional. | +| `--name ` | No | Override for the imported workflow name. | +| `--description ` | No | Override for the imported workflow description. | + + + +### sim workflows list + +List Workflows + +```bash +sim workflows list [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--folder ` | 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 ` | No | Maximum items to return (0 for everything). Defaults to `100`. | +| `--search ` | No | Case-insensitive substring match against the resource name. | +| `--sort-by ` | 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 ` | No | Sort direction. Accepted values: `asc`, `desc`. | + + + +### sim workflows rollback + +Rollback Workflow + +```bash +sim workflows rollback [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique workflow identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--version ` | No | Deployment version to reactivate. Omit to select the previous active version. | + + + +### sim workflows undeploy + +Take a workflow out of deployment + +```bash +sim workflows undeploy +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique workflow identifier. | + + + +### sim workflows update + +Update Workflow + +```bash +sim workflows update [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique workflow identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--name ` | No | Replacement workflow name. | +| `--description ` | No | Replacement workflow description; null clears it. | +| `--folder ` | No | Folder path; the leading / is optional. | + + + +### sim workflows mv + +Move a workflow to a folder + +```bash +sim workflows mv +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique workflow identifier. | +| `folder` | Yes | Folder path; the leading / is optional | + + + +### sim workflows ls + +List workflow resources and child folders together + +```bash +sim workflows ls [path] [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | No | Folder path to list; defaults to the root folder | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--search ` | No | Filter folders and resources by name. | +| `--limit ` | No | Maximum combined items to return (0 for everything). Defaults to `100`. | + + + +### sim workflows mkdir + +Create a workflow directory at a path + +```bash +sim workflows mkdir +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | Yes | Folder path to create; the leading / is optional | + + + +## sim workspaces + +Also spelled `sim workspace`. + +### sim workspaces get + +Get Workspace + +```bash +sim workspaces get +``` + +### sim workspaces members + +List workspace members + +```bash +sim workspaces members [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--limit ` | No | Maximum items to return (0 for everything). Defaults to `100`. | + + diff --git a/apps/docs/content/docs/en/cli/scripting.mdx b/apps/docs/content/docs/en/cli/scripting.mdx new file mode 100644 index 00000000000..ace39d804e5 --- /dev/null +++ b/apps/docs/content/docs/en/cli/scripting.mdx @@ -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. + + +`batch-delete` and `batch-update` also carry the standard `--limit` default of +`100`. Set `--limit 0` when you intend to affect every matching row. + + +## 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 +``` + + +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. + + +## 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 +``` diff --git a/apps/docs/content/docs/en/cli/secrets.mdx b/apps/docs/content/docs/en/cli/secrets.mdx new file mode 100644 index 00000000000..231a493adcd --- /dev/null +++ b/apps/docs/content/docs/en/cli/secrets.mdx @@ -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 [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `name` | Yes | Secret to create, replace, or delete. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--scope ` | 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. | + + + +## List secrets + +```bash +sim secrets list [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--scope ` | No | Restrict results to one ownership scope. Accepted values: `workspace`, `personal`. | +| `--search ` | No | Case-insensitive substring match against the secret name. | +| `--sort-by ` | 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 ` | No | Sort direction. Accepted values: `asc`, `desc`. | +| `--limit ` | No | Maximum items to return (0 for everything). Defaults to `100`. | + + + +## Create or replace a named secret + +```bash +sim secrets set [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `name` | Yes | Secret name, as referenced in workflows | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--scope ` | Yes | Secret ownership scope. Accepted values: `workspace`, `personal`. | +| `--value ` | No | Secret value; visible to shell history when supplied directly. | + + diff --git a/apps/docs/content/docs/en/cli/skills.mdx b/apps/docs/content/docs/en/cli/skills.mdx new file mode 100644 index 00000000000..e5560dd276f --- /dev/null +++ b/apps/docs/content/docs/en/cli/skills.mdx @@ -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** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--name ` | Yes | Kebab-case name, unique within the workspace and not reserved by a built-in skill. | +| `--description ` | Yes | One-line summary of when the skill applies. | +| `--content ` | Yes | Skill body containing the instructions given to the agent. | + + + +## Delete skill + +```bash +sim skills delete [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique skill identifier. A built-in skill is `builtin-` followed by its name, for example `builtin-research`. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `-y, --yes` | No | Skip the confirmation. | + + + +## Get skill + +```bash +sim skills get +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique skill identifier. A built-in skill is `builtin-` followed by its name, for example `builtin-research`. | + + + +## List skills + +```bash +sim skills list [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--search ` | No | Case-insensitive substring match against the skill name. | +| `--sort-by ` | 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 ` | No | Sort direction. Accepted values: `asc`, `desc`. | +| `--limit ` | No | Maximum items to return (0 for everything). Defaults to `100`. | + + + +## Update skill + +```bash +sim skills update [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique skill identifier. A built-in skill is `builtin-` followed by its name, for example `builtin-research`. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--name ` | No | New kebab-case skill name. | +| `--description ` | No | New one-line summary of when the skill applies. | +| `--content ` | No | Replacement skill body. | + + diff --git a/apps/docs/content/docs/en/cli/tables.mdx b/apps/docs/content/docs/en/cli/tables.mdx new file mode 100644 index 00000000000..281a03b44df --- /dev/null +++ b/apps/docs/content/docs/en/cli/tables.mdx @@ -0,0 +1,1078 @@ +--- +title: Tables +description: Manage tables — every subcommand, argument, and flag +--- + +import { CommandTable } from '@/components/ui/command-table' + +`sim tables` is also spelled `sim table`. + +Every command below also accepts the [global options](/cli/commands#global-options). + +## Add column + +```bash +sim tables columns create [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--column ` | Yes | Column definition to add. (JSON, or @path / @- to read a file or stdin). | + + + +## Delete column + +```bash +sim tables columns delete [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--column-name ` | Yes | Name of the column to delete. | +| `-y, --yes` | No | Skip the confirmation. | + + + +## Run a column’s workflow + +```bash +sim tables columns run [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--group-ids ` | Yes | Workflow or enrichment groups to run. (space-separated, or @path / @- with one value per line). | +| `--run-mode ` | No | Whether to run all or only incomplete cells. Accepted values: `all`, `incomplete`. | +| `--row-ids ` | No | Explicit row subset to run. (space-separated, or @path / @- with one value per line). | +| `--filter ` | No | Predicate: {"all":[{"field":"status","op":"eq","value":"active"}]}; groups use all/any. Operators: eq, ne, gt, gte, lt, lte, in, nin, contains, ncontains, startsWith, endsWith, like, ilike, nlike, nilike, isEmpty, isNotEmpty, isNull, isNotNull (JSON, or @path / @- to read a file or stdin). | +| `--exclude-row-ids ` | No | Rows excluded from a select-all run scope. (space-separated, or @path / @- with one value per line). | +| `--limit ` | No | Optional cap on eligible rows to run. (JSON, or @path / @- to read a file or stdin). | + + + +## Update column + +```bash +sim tables columns update [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--column-name ` | Yes | Current name of the column to update. | +| `--updates ` | Yes | Mutable column fields. (JSON, or @path / @- to read a file or stdin). | + + + +## Add workflow group + +```bash +sim tables groups create [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--group ` | Yes | Workflow or enrichment producer definition. (JSON, or @path / @- to read a file or stdin). | +| `--output-columns ` | Yes | Columns created for producer outputs. (JSON, or @path / @- to read a file or stdin). | +| `--auto-run` | No | Whether to schedule existing rows after group creation. | +| `--no-auto-run` | No | Send --auto-run as false. | + + + +## Delete workflow group + +```bash +sim tables groups delete [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--group-id ` | Yes | Workflow group to delete. | +| `-y, --yes` | No | Skip the confirmation. | + + + +## List workflow groups + +```bash +sim tables groups list +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +## Update workflow group + +```bash +sim tables groups update [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--group-id ` | Yes | Workflow group to update. | +| `--workflow-id ` | No | Replacement backing workflow identifier. | +| `--name ` | No | Replacement workflow-group display name. | +| `--dependencies ` | No | Replacement input dependencies. (JSON, or @path / @- to read a file or stdin). | +| `--outputs ` | No | Replacement producer outputs. (JSON, or @path / @- to read a file or stdin). | +| `--new-output-columns ` | No | Columns to add for new outputs. (JSON, or @path / @- to read a file or stdin). | +| `--mapping-updates ` | No | Existing output-column mapping changes. (JSON, or @path / @- to read a file or stdin). | +| `--input-mappings ` | No | Replacement workflow input mappings. (JSON, or @path / @- to read a file or stdin). | +| `--deployment-mode ` | No | Replacement workflow execution mode. Accepted values: `live`, `deployed`. | +| `--type ` | No | Workflow-group producer type. Must match the group's stored type — a group's producer cannot be changed after creation. Accepted values: `manual`, `enrichment`. | +| `--auto-run` | No | Replacement automatic-run setting. | +| `--no-auto-run` | No | Send --auto-run as false. | + + + +## Cancel table export + +```bash +sim tables exports cancel +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `exportId` | Yes | Unique table-export identifier. | + + + +## Create table export + +```bash +sim tables exports create [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--format ` | No | Export file format. Accepted values: `csv`, `json`. | + + + +## Get table export + +```bash +sim tables exports get +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `exportId` | Yes | Unique table-export identifier. | + + + +## Get the download URL for a finished export + +```bash +sim tables exports download +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `exportId` | Yes | Unique table-export identifier. | + + + +## Cancel table import + +```bash +sim tables imports cancel +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `importId` | Yes | Unique table-import identifier. | + + + +## Get table import + +```bash +sim tables imports get +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `importId` | Yes | Unique table-import identifier. | + + + +## Stop every running column job + +```bash +sim tables cancel-runs [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--scope ` | Yes | Whether to cancel across the table or one row. Accepted values: `all`, `row`. | +| `--row-id ` | No | Row whose runs should be canceled for row scope. | +| `--filter ` | No | Predicate: {"all":[{"field":"status","op":"eq","value":"active"}]}; groups use all/any. Operators: eq, ne, gt, gte, lt, lte, in, nin, contains, ncontains, startsWith, endsWith, like, ilike, nlike, nilike, isEmpty, isNotEmpty, isNull, isNotNull (JSON, or @path / @- to read a file or stdin). | +| `--exclude-row-ids ` | No | Rows excluded from an all-scope cancellation. (space-separated, or @path / @- with one value per line). | + + + +## Create table + +```bash +sim tables create [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--name ` | Yes | Identifier: letters, numbers, and underscores; cannot start with a number. | +| `--description ` | No | Optional table description. | +| `--schema ` | Yes | Table schema: {"columns":[{"name":"email","type":"string"}]} (JSON, or @path / @- to read a file or stdin). | +| `--folder ` | No | Folder path; the leading / is optional. | + + + +## Create a table folder at a path + +```bash +sim tables folders create +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | Yes | Folder path; the leading / is optional | + + + +## Delete folder + +```bash +sim tables folders delete [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | Yes | Folder path; the leading / is optional | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--recursive` | No | Delete the folder and its descendants. | +| `-y, --yes` | No | Skip the confirmation. | + + + +## List folders + +```bash +sim tables folders list [options] +``` + +Also available as `sim tables folders ls`. + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--parent ` | No | Direct parent folder path. | +| `--search ` | No | Case-insensitive substring match against the folder name. | +| `--sort-by ` | 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 ` | No | Sort direction. Accepted values: `asc`, `desc`. | + + + +## Rename or move a table folder + +```bash +sim tables folders move +``` + +Also available as `sim tables folders mv`. + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | Yes | Folder path; the leading / is optional | +| `destination` | Yes | Folder path; the leading / is optional | + + + +## Create rows + +```bash +sim tables rows create [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--data ` | No | One row keyed by column name (JSON, or @path / @-; choose exactly one body flag). | +| `--rows ` | No | Several rows keyed by column name (JSON, or @path / @-; choose exactly one body flag). | + + + +## Delete row + +```bash +sim tables rows delete [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | +| `rowId` | Yes | Unique table row identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `-y, --yes` | No | Skip the confirmation. | + + + +## Delete rows matching a filter, or an explicit list of ids + +```bash +sim tables rows batch-delete [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--filter ` | No | Predicate: {"all":[{"field":"status","op":"eq","value":"active"}]}; groups use all/any. Operators: eq, ne, gt, gte, lt, lte, in, nin, contains, ncontains, startsWith, endsWith, like, ilike, nlike, nilike, isEmpty, isNotEmpty, isNull, isNotNull (JSON, or @path / @- to read a file or stdin). | +| `--limit ` | No | Maximum items to return (0 for everything). Defaults to `100`. | +| `--row ` | No | Explicit row identifiers to delete. (space-separated, or @path / @- with one value per line). | +| `-y, --yes` | No | Skip the confirmation. | + + + +## Find rows matching a predicate + +```bash +sim tables rows find [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--q ` | Yes | Value to find. | +| `--filter ` | No | Predicate: {"all":[{"field":"status","op":"eq","value":"active"}]}; groups use all/any. Operators: eq, ne, gt, gte, lt, lte, in, nin, contains, ncontains, startsWith, endsWith, like, ilike, nlike, nilike, isEmpty, isNotEmpty, isNull, isNotNull (JSON, or @path / @- to read a file or stdin). | +| `--sort ` | No | Ordered sort keys: [{"field":"createdAt","direction":"desc"}] (direction: asc or desc) (JSON, or @path / @- to read a file or stdin). | + + + +## Get row + +```bash +sim tables rows get +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | +| `rowId` | Yes | Unique table row identifier. | + + + +## List rows + +```bash +sim tables rows list [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--limit ` | No | Maximum items to return (0 for everything). Defaults to `100`. | + + + +## Query rows + +```bash +sim tables rows query [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--filter ` | No | Predicate: {"all":[{"field":"status","op":"eq","value":"active"}]}; groups use all/any. Operators: eq, ne, gt, gte, lt, lte, in, nin, contains, ncontains, startsWith, endsWith, like, ilike, nlike, nilike, isEmpty, isNotEmpty, isNull, isNotNull (JSON, or @path / @- to read a file or stdin). | +| `--sort ` | No | Ordered sort keys: [{"field":"createdAt","direction":"desc"}] (direction: asc or desc) (JSON, or @path / @- to read a file or stdin). | +| `--limit ` | No | Maximum items to return (0 for everything). Defaults to `100`. | + + + +## Run one row’s enrichment group + +```bash +sim tables rows enrich +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | +| `rowId` | Yes | Unique table row identifier. | +| `groupId` | Yes | Workflow or enrichment group to run. | + + + +## Update every row matching a filter + +```bash +sim tables rows batch-update [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--filter ` | Yes | Predicate: {"all":[{"field":"status","op":"eq","value":"active"}]}; groups use all/any. Operators: eq, ne, gt, gte, lt, lte, in, nin, contains, ncontains, startsWith, endsWith, like, ilike, nlike, nilike, isEmpty, isNotEmpty, isNull, isNotNull (JSON, or @path / @- to read a file or stdin). | +| `--data ` | Yes | Row-data patch applied to every matching row. (JSON, or @path / @- to read a file or stdin). | +| `--limit ` | No | Maximum items to return (0 for everything). Defaults to `100`. | +| `-y, --yes` | No | Skip the confirmation. | + + + +## Update row + +```bash +sim tables rows update [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | +| `rowId` | Yes | Unique table row identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--data ` | Yes | Partial row-data patch keyed by column name. (JSON, or @path / @- to read a file or stdin). | + + + +## Create view + +```bash +sim tables views create [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--name ` | Yes | Saved-view display name. | +| `--config ` | Yes | Saved filter, sort, and column-layout configuration. (JSON, or @path / @- to read a file or stdin). | + + + +## Delete view + +```bash +sim tables views delete [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | +| `viewId` | Yes | Unique saved-view identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `-y, --yes` | No | Skip the confirmation. | + + + +## Get view + +```bash +sim tables views get +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | +| `viewId` | Yes | Unique saved-view identifier. | + + + +## List views + +```bash +sim tables views list +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +## Update view + +```bash +sim tables views update [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | +| `viewId` | Yes | Unique saved-view identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--name ` | No | Replacement saved-view display name. | +| `--config ` | No | Complete replacement saved-view configuration. (JSON, or @path / @- to read a file or stdin). | +| `--config-patch ` | No | Saved-view configuration fields to shallow-merge. (JSON, or @path / @- to read a file or stdin). | +| `--is-default` | No | Whether to promote this view to the table default. | +| `--no-is-default` | No | Send --is-default as false. | + + + +## Delete table + +```bash +sim tables delete [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `-y, --yes` | No | Skip the confirmation. | + + + +## Get table + +```bash +sim tables get +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +## List tables + +```bash +sim tables list [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--folder ` | No | Folder path; the leading / is optional. | +| `--search ` | No | Case-insensitive substring match against the resource name. | +| `--sort-by ` | 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 ` | No | Sort direction. Accepted values: `asc`, `desc`. | +| `--limit ` | No | Maximum items to return (0 for everything). Defaults to `100`. | + + + +## Count rows + +```bash +sim tables count create [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--predicate ` | No | Recursive predicate tree. Each group node is exactly one non-empty `all` or `any` array whose members are further groups or `{ field, op, value }` conditions; the root must be a group, not a bare condition. At most 100 members per group, 10 levels of nesting, and 500 nodes in total. The negating operators include nulls: `ne`, `nin`, `ncontains`, `nlike`, and `nilike` match rows whose column is null or absent, so "not X" is not the complement of "X" over a nullable column. That holds for every column type, multi-select included. To exclude nulls, `all`-combine the negation with `isNotEmpty` (multi-select) or `isNotNull`. Comparison: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`. Membership: `in`, `nin` (array operand). Emptiness: `isEmpty`, `isNotEmpty`, `isNull`, `isNotNull` (no operand). Substring, always case-insensitive, operand matched literally: `contains`, `ncontains`, `startsWith`, `endsWith`. Pattern: `like`/`nlike` (case-sensitive), `ilike`/`nilike` (case-insensitive). **`*` is the only wildcard** and stands for any run of characters; `%`, `_`, and backslash match themselves. Use `like: "Hi*"`, not `like: "Hi%"`. A `select` column compares by option id and restricts its operators: single-select accepts `eq`, `ne`, `in`, `nin`; multi-select accepts `contains`, `ncontains`. Option names are accepted as operands and resolved to ids. (JSON, or @path / @- to read a file or stdin). | + + + +## Update table + +```bash +sim tables update [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--name ` | No | Identifier: letters, numbers, and underscores; cannot start with a number. | +| `--description ` | No | Replacement table description, or null to clear it. | +| `--folder ` | No | Folder path; the leading / is optional. | + + + +## Move a table to a folder + +```bash +sim tables mv +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | +| `folder` | Yes | Folder path; the leading / is optional | + + + +## Insert a row, or update the one that conflicts on a unique column + +```bash +sim tables upsert [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `tableId` | Yes | Unique table identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--data ` | Yes | Complete set of row cells keyed by column name. On the update branch this REPLACES the matched row: any column not present here is cleared, unlike the merging `PATCH /api/v2/tables/{tableId}/rows/{rowId}`. (JSON, or @path / @- to read a file or stdin). | +| `--on ` | No | Unique column to resolve the conflict against. | + + + +## Import a CSV, into a new table by default + +```bash +sim tables import [path] [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | No | Local CSV file to import; omit when using --file-id | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--name ` | No | Identifier for the new table: letters, numbers, and underscores; defaults to the sanitized file name. | +| `--table-id ` | No | Import into this existing table instead of creating one. | +| `--mode ` | No | How to write into --table-id (default: append). Accepted values: `append`, `replace`. | +| `--folder ` | No | Folder path for the new table. | +| `--file-id ` | No | Import a file already in the workspace instead of a local path. | +| `--mapping ` | No | Column mapping (--table-id only). | +| `--create-columns ` | No | Columns to create (--table-id only). | +| `--timezone ` | No | Timezone for date parsing, e.g. America/New_York. | +| `--no-wait` | No | Return once the import is queued instead of watching it. | + + + +## List table resources and child folders together + +```bash +sim tables ls [path] [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | No | Folder path to list; defaults to the root folder | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--search ` | No | Filter folders and resources by name. | +| `--limit ` | No | Maximum combined items to return (0 for everything). Defaults to `100`. | + + + +## Create a table directory at a path + +```bash +sim tables mkdir +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | Yes | Folder path to create; the leading / is optional | + + diff --git a/apps/docs/content/docs/en/cli/troubleshooting.mdx b/apps/docs/content/docs/en/cli/troubleshooting.mdx new file mode 100644 index 00000000000..4a7e10ef796 --- /dev/null +++ b/apps/docs/content/docs/en/cli/troubleshooting.mdx @@ -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. + + +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**. + + +## 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 ` + +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 +``` + +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 +``` + +### `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 ` + +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}/…`. + + +`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. + + +### 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: ` + +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 ''` + +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 ' 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). + +### ` 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 + +### ` 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 +``` diff --git a/apps/docs/content/docs/en/cli/workflows.mdx b/apps/docs/content/docs/en/cli/workflows.mdx new file mode 100644 index 00000000000..23b387b8202 --- /dev/null +++ b/apps/docs/content/docs/en/cli/workflows.mdx @@ -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 [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `runId` | Yes | Unique workflow run identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--workflow ` | Yes | Workflow ID. | + + + +## Show run status + +```bash +sim workflows runs get [options] +``` + +Show run status (requested outputs are included in JSON or YAML output) + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `runId` | Yes | Unique workflow run identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--workflow ` | Yes | Workflow ID. | +| `--include-output` | No | Include the final output in JSON or YAML output. | +| `--select-output ` | No | Include blockName.field values in JSON or YAML output (e.g. agent_1.content) (space-separated, or @path / @- with one value per line). | + + + +## List runs for a workflow + +```bash +sim workflows runs list [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--workflow ` | Yes | Workflow ID. | +| `--status ` | No | Filter by run status. Accepted values: `pending`, `running`, `completed`, `failed`, `cancelled`, `paused`. | +| `--trigger ` | No | Filter by trigger type. | +| `--start-date ` | 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 ` | 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 ` | No | Maximum items to return (0 for everything). Defaults to `100`. | +| `--order ` | 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`. | + + + +## Resume a paused run + +```bash +sim workflows runs resume [options] +``` + +Resume a paused run (output is included in JSON or YAML output) + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `runId` | Yes | Unique workflow run identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--workflow ` | Yes | Workflow ID. | +| `--context ` | Yes | Pause context ID returned by run status. | +| `--input ` | No | Resume input as JSON (JSON, or @path / @- to read a file or stdin). | + + + +## Create workflow + +```bash +sim workflows create [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--name ` | Yes | Workflow name. | +| `--description ` | No | Optional workflow description. | +| `--folder ` | No | Folder path; the leading / is optional. | + + + +## Create a workflow folder at a path + +```bash +sim workflows folders create +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | Yes | Folder path; the leading / is optional | + + + +## Delete workflow folder + +```bash +sim workflows folders delete [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | Yes | Folder path; the leading / is optional | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--recursive` | No | Delete the folder and its descendants. | +| `-y, --yes` | No | Skip the confirmation. | + + + +## List workflow folders + +```bash +sim workflows folders list [options] +``` + +Also available as `sim workflows folders ls`. + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--parent ` | No | Direct parent folder path. | +| `--search ` | No | Case-insensitive substring match against the folder name. | +| `--sort-by ` | 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 ` | No | Sort direction. Accepted values: `asc`, `desc`. | + + + +## Rename or move a workflow folder + +```bash +sim workflows folders move +``` + +Also available as `sim workflows folders mv`. + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | Yes | Folder path; the leading / is optional | +| `destination` | Yes | Folder path; the leading / is optional | + + + +## Delete workflow + +```bash +sim workflows delete [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique workflow identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `-y, --yes` | No | Skip the confirmation. | + + + +## Deploy workflow + +```bash +sim workflows deploy [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique workflow identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--name ` | No | Optional label for the deployment version. | +| `--description ` | No | Optional release note for the deployment version. | + + + +## Run a deployed workflow + +```bash +sim workflows run [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique workflow identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--input ` | 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 ` | 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 ` | 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 ` | No | Maximum total bytes of file content to inline as base64. Rejected when `async` is true. | + + + +## Print a workflow as a portable JSON document + +```bash +sim workflows export +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique workflow identifier. | + + + +## Get workflow + +```bash +sim workflows get +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique workflow identifier. | + + + +## Get workflow deployment + +```bash +sim workflows deployment list +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique workflow identifier. | + + + +## Get workflow version + +```bash +sim workflows versions get +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique workflow identifier. | +| `version` | Yes | Numeric deployment version. | + + + +## List workflow versions + +```bash +sim workflows versions list [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique workflow identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--limit ` | No | Maximum items to return (0 for everything). Defaults to `100`. | + + + +## Import workflow + +```bash +sim workflows import [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--workflow ` | Yes | Workflow export object, bare workflow state, or JSON string containing either form. (JSON, or @path / @- to read a file or stdin). | +| `--folder ` | No | Folder path; the leading / is optional. | +| `--name ` | No | Override for the imported workflow name. | +| `--description ` | No | Override for the imported workflow description. | + + + +## List workflows + +```bash +sim workflows list [options] +``` + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--folder ` | 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 ` | No | Maximum items to return (0 for everything). Defaults to `100`. | +| `--search ` | No | Case-insensitive substring match against the resource name. | +| `--sort-by ` | 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 ` | No | Sort direction. Accepted values: `asc`, `desc`. | + + + +## Rollback workflow + +```bash +sim workflows rollback [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique workflow identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--version ` | No | Deployment version to reactivate. Omit to select the previous active version. | + + + +## Take a workflow out of deployment + +```bash +sim workflows undeploy +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique workflow identifier. | + + + +## Update workflow + +```bash +sim workflows update [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique workflow identifier. | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--name ` | No | Replacement workflow name. | +| `--description ` | No | Replacement workflow description; null clears it. | +| `--folder ` | No | Folder path; the leading / is optional. | + + + +## Move a workflow to a folder + +```bash +sim workflows mv +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `id` | Yes | Unique workflow identifier. | +| `folder` | Yes | Folder path; the leading / is optional | + + + +## List workflow resources and child folders together + +```bash +sim workflows ls [path] [options] +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | No | Folder path to list; defaults to the root folder | + + + +**Options** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--search ` | No | Filter folders and resources by name. | +| `--limit ` | No | Maximum combined items to return (0 for everything). Defaults to `100`. | + + + +## Create a workflow directory at a path + +```bash +sim workflows mkdir +``` + +**Arguments** + + + +| Argument | Required | Description | +| --- | --- | --- | +| `path` | Yes | Folder path to create; the leading / is optional | + + diff --git a/apps/docs/content/docs/en/cli/workspaces.mdx b/apps/docs/content/docs/en/cli/workspaces.mdx new file mode 100644 index 00000000000..82a36c1300b --- /dev/null +++ b/apps/docs/content/docs/en/cli/workspaces.mdx @@ -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** + + + +| Option | Required | Description | +| --- | --- | --- | +| `--limit ` | No | Maximum items to return (0 for everything). Defaults to `100`. | + + diff --git a/package.json b/package.json index 9681964645f..666b0cb93df 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/packages/sim-cli/README.md b/packages/sim-cli/README.md index 979c2b77142..6f389db41f8 100644 --- a/packages/sim-cli/README.md +++ b/packages/sim-cli/README.md @@ -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 ] sim tables get sim tables update [--name ] [--description ] [--folder ] sim tables mv -sim tables columns +sim tables columns create|update|delete|run sim tables rows list [--limit ] sim tables rows create --data sim tables rows create --rows @@ -173,6 +172,8 @@ sim knowledge search --query --kb … [--search-mode vector|hybrid] sim knowledge documents list [--search ] sim knowledge documents get sim knowledge documents upload [--tag ...] +sim knowledge documents update [--filename ] [--enabled] +sim knowledge documents batch-update --operation enable|disable sim knowledge documents delete --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. diff --git a/packages/sim-cli/src/commands/credentials.ts b/packages/sim-cli/src/commands/credentials.ts index 39b7684f978..a00d1c81b69 100644 --- a/packages/sim-cli/src/commands/credentials.ts +++ b/packages/sim-cli/src/commands/credentials.ts @@ -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 ') + .command('create') + .argument('', 'Service-account provider to create a credential for') .description('Create a service-account credential using its discovered provider schema') .requiredOption('--name ', 'Name shown for the credential in Sim') .requiredOption( @@ -179,7 +180,8 @@ export function attachCredentialCommands(program: Command): void { ) credentials - .command('connect ') + .command('connect') + .argument('', 'OAuth provider to connect') .description('Create a short-lived link for connecting an OAuth provider') .requiredOption('--name ', '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 ') + .command('reconnect') + .argument('', '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 }) diff --git a/packages/sim-cli/src/commands/protocol/files-get.ts b/packages/sim-cli/src/commands/protocol/files-get.ts index ae325c09816..494a30908a1 100644 --- a/packages/sim-cli/src/commands/protocol/files-get.ts +++ b/packages/sim-cli/src/commands/protocol/files-get.ts @@ -174,7 +174,8 @@ export function isTerminalSafeContentType(contentType: string | null): boolean { export function attachFileGet(files: Command): void { files - .command('get ') + .command('get') + .argument('', 'File whose content to read') .description('Get a file’s content') .option('-o, --output-file ', 'Write content to a file instead of stdout') .option('--force', 'Overwrite --output-file if it already exists') diff --git a/packages/sim-cli/src/commands/protocol/files-upload.ts b/packages/sim-cli/src/commands/protocol/files-upload.ts index 99dfe3c9418..c276b0b77d9 100644 --- a/packages/sim-cli/src/commands/protocol/files-upload.ts +++ b/packages/sim-cli/src/commands/protocol/files-upload.ts @@ -8,7 +8,8 @@ import { printProtocolResult } from './result' export function attachFileUpload(files: Command): void { files - .command('upload ') + .command('upload') + .argument('', 'Local file to upload') .description('Upload a file to the workspace') .option('--folder ', 'Destination folder path (defaults to /)') .option('--name ', 'Store it under a different name') diff --git a/packages/sim-cli/src/commands/protocol/knowledge-document-upload.ts b/packages/sim-cli/src/commands/protocol/knowledge-document-upload.ts index f268a0e5a25..212cedb6842 100644 --- a/packages/sim-cli/src/commands/protocol/knowledge-document-upload.ts +++ b/packages/sim-cli/src/commands/protocol/knowledge-document-upload.ts @@ -37,7 +37,9 @@ function uploadMetadata(options: KnowledgeDocumentUploadOptions): Record ') + .command('upload') + .argument('', 'Knowledge base to upload into') + .argument('', 'Local file to upload') .description('Upload a document to a knowledge base') .option('--name ', 'Store it under a different name') .option('--tag ', 'Document tags, in tag1 through tag7 order') diff --git a/packages/sim-cli/src/commands/protocol/resource-directory.ts b/packages/sim-cli/src/commands/protocol/resource-directory.ts index fa0ca8eb07a..daf20cc6c88 100644 --- a/packages/sim-cli/src/commands/protocol/resource-directory.ts +++ b/packages/sim-cli/src/commands/protocol/resource-directory.ts @@ -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 ', 'Filter folders and resources by name') @@ -181,7 +182,8 @@ export function attachResourceDirectoryCommands( }) group - .command('mkdir ') + .command('mkdir') + .argument('', '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, command: Command) => { diff --git a/packages/sim-cli/src/commands/protocol/tables-import.ts b/packages/sim-cli/src/commands/protocol/tables-import.ts index d60bbcc6288..4d64f5b3ed2 100644 --- a/packages/sim-cli/src/commands/protocol/tables-import.ts +++ b/packages/sim-cli/src/commands/protocol/tables-import.ts @@ -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 ', diff --git a/packages/sim-cli/src/commands/secrets.ts b/packages/sim-cli/src/commands/secrets.ts index 922e348c132..5c24291a3ee 100644 --- a/packages/sim-cli/src/commands/secrets.ts +++ b/packages/sim-cli/src/commands/secrets.ts @@ -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 ') + .command('set') + .argument('', 'Secret name, as referenced in workflows') .description('Create or replace a named secret') .addOption( new Option('--scope ', 'Secret ownership scope') diff --git a/packages/sim-cli/src/contract/commands.ts b/packages/sim-cli/src/contract/commands.ts index 46c7c65e108..4c7119c800f 100644 --- a/packages/sim-cli/src/contract/commands.ts +++ b/packages/sim-cli/src/contract/commands.ts @@ -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', diff --git a/packages/sim-cli/src/generated/v2-api.ts b/packages/sim-cli/src/generated/v2-api.ts index 9acec0d2016..cac9ffe9f72 100644 --- a/packages/sim-cli/src/generated/v2-api.ts +++ b/packages/sim-cli/src/generated/v2-api.ts @@ -5744,44 +5744,71 @@ export const V2_OPERATIONS = { method: 'DELETE', path: '/api/v2/files/uploads/[uploadId]', pathParams: ['uploadId'] as const, + pathParamDocs: { uploadId: 'Upload session identifier.' }, responseMode: 'json', summary: 'Abort File Upload', query: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace that owns the upload session.', + }, }, }, abortKnowledgeDocumentUpload: { method: 'DELETE', path: '/api/v2/knowledge/[id]/documents/uploads/[uploadId]', pathParams: ['id', 'uploadId'] as const, + pathParamDocs: { + id: 'Unique knowledge base identifier.', + uploadId: 'Upload session identifier returned when the upload was created.', + }, responseMode: 'json', summary: 'Abort Document Upload', query: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace that owns the knowledge base.', + }, }, }, addTableColumn: { method: 'POST', path: '/api/v2/tables/[tableId]/columns', pathParams: ['tableId'] as const, + pathParamDocs: { tableId: 'Unique table identifier.' }, responseMode: 'json', summary: 'Add Column', body: { - workspaceId: { kind: 'string', required: true }, - column: { kind: 'object', required: true }, + workspaceId: { kind: 'string', required: true, describe: 'Workspace that owns the table.' }, + column: { kind: 'object', required: true, describe: 'Column definition to add.' }, }, }, addWorkflowGroup: { method: 'POST', path: '/api/v2/tables/[tableId]/groups', pathParams: ['tableId'] as const, + pathParamDocs: { tableId: 'Unique table identifier.' }, responseMode: 'json', summary: 'Add Workflow Group', body: { - workspaceId: { kind: 'string', required: true }, - group: { kind: 'object', required: true }, - outputColumns: { kind: 'array', required: true }, - autoRun: { kind: 'boolean', default: false }, + workspaceId: { kind: 'string', required: true, describe: 'Unique workspace identifier.' }, + group: { + kind: 'object', + required: true, + describe: 'Workflow or enrichment producer definition.', + }, + outputColumns: { + kind: 'array', + required: true, + describe: 'Columns created for producer outputs.', + }, + autoRun: { + kind: 'boolean', + default: false, + describe: 'Whether to schedule existing rows after group creation.', + }, }, }, bulkDeleteFiles: { @@ -5791,62 +5818,101 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'Delete Files', body: { - workspaceId: { kind: 'string', required: true }, - fileIds: { kind: 'array', required: true }, + workspaceId: { kind: 'string', required: true, describe: 'Workspace containing the files.' }, + fileIds: { kind: 'array', required: true, describe: 'File identifiers to update.' }, }, }, bulkUpdateKnowledgeDocuments: { method: 'PATCH', path: '/api/v2/knowledge/[id]/documents', pathParams: ['id'] as const, + pathParamDocs: { id: 'Unique knowledge base identifier.' }, responseMode: 'json', summary: 'Bulk Enable or Disable Documents', body: { - workspaceId: { kind: 'string', required: true }, - operation: { kind: 'enum', required: true, values: ['enable', 'disable'] as const }, - documentIds: { kind: 'array' }, - selectAll: { kind: 'boolean' }, - enabledFilter: { kind: 'enum', values: ['all', 'enabled', 'disabled'] as const }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace that owns the knowledge base.', + }, + operation: { + kind: 'enum', + required: true, + values: ['enable', 'disable'] as const, + describe: 'Whether the selected documents become enabled or disabled for search.', + }, + documentIds: { kind: 'array', describe: 'Documents to update, by identifier.' }, + selectAll: { + kind: 'boolean', + describe: + 'Update every document in the knowledge base instead of an explicit list, narrowed by `enabledFilter`.', + }, + enabledFilter: { + kind: 'enum', + values: ['all', 'enabled', 'disabled'] as const, + describe: 'With `selectAll`, restrict the update to documents in this state.', + }, }, }, cancelTableExport: { method: 'DELETE', path: '/api/v2/tables/exports/[exportId]', pathParams: ['exportId'] as const, + pathParamDocs: { exportId: 'Unique table-export identifier.' }, responseMode: 'json', summary: 'Cancel Table Export', query: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace that owns the transfer resource.', + }, }, }, cancelTableImport: { method: 'DELETE', path: '/api/v2/tables/imports/[importId]', pathParams: ['importId'] as const, + pathParamDocs: { importId: 'Unique table-import identifier.' }, responseMode: 'json', summary: 'Cancel Table Import', query: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace that owns the transfer resource.', + }, }, }, cancelTableRuns: { method: 'POST', path: '/api/v2/tables/[tableId]/cancel-runs', pathParams: ['tableId'] as const, + pathParamDocs: { tableId: 'Unique table identifier.' }, responseMode: 'json', summary: 'Cancel Column Runs', body: { - workspaceId: { kind: 'string', required: true }, - scope: { kind: 'enum', required: true, values: ['all', 'row'] as const }, - rowId: { kind: 'string' }, - filter: { kind: 'unknown' }, - excludeRowIds: { kind: 'array' }, + workspaceId: { kind: 'string', required: true, describe: 'Unique workspace identifier.' }, + scope: { + kind: 'enum', + required: true, + values: ['all', 'row'] as const, + describe: 'Whether to cancel across the table or one row.', + }, + rowId: { kind: 'string', describe: 'Row whose runs should be canceled for row scope.' }, + filter: { + kind: 'unknown', + describe: + 'Recursive predicate tree. Each group node is exactly one non-empty `all` or `any` array whose members are further groups or `{ field, op, value }` conditions; the root must be a group, not a bare condition. At most 100 members per group, 10 levels of nesting, and 500 nodes in total. The negating operators include nulls: `ne`, `nin`, `ncontains`, `nlike`, and `nilike` match rows whose column is null or absent, so "not X" is not the complement of "X" over a nullable column. That holds for every column type, multi-select included. To exclude nulls, `all`-combine the negation with `isNotEmpty` (multi-select) or `isNotNull`. Comparison: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`. Membership: `in`, `nin` (array operand). Emptiness: `isEmpty`, `isNotEmpty`, `isNull`, `isNotNull` (no operand). Substring, always case-insensitive, operand matched literally: `contains`, `ncontains`, `startsWith`, `endsWith`. Pattern: `like`/`nlike` (case-sensitive), `ilike`/`nilike` (case-insensitive). **`*` is the only wildcard** and stands for any run of characters; `%`, `_`, and backslash match themselves. Use `like: "Hi*"`, not `like: "Hi%"`. A `select` column compares by option id and restricts its operators: single-select accepts `eq`, `ne`, `in`, `nin`; multi-select accepts `contains`, `ncontains`. Option names are accepted as operands and resolved to ids.', + }, + excludeRowIds: { kind: 'array', describe: 'Rows excluded from an all-scope cancellation.' }, }, }, cancelWorkflowRun: { method: 'POST', path: '/api/v2/workflows/[id]/runs/[runId]/cancel', pathParams: ['id', 'runId'] as const, + pathParamDocs: { id: 'Unique workflow identifier.', runId: 'Unique workflow run identifier.' }, responseMode: 'json', summary: 'Cancel Workflow Run', }, @@ -5854,30 +5920,48 @@ export const V2_OPERATIONS = { method: 'POST', path: '/api/v2/files/uploads/[uploadId]/complete', pathParams: ['uploadId'] as const, + pathParamDocs: { uploadId: 'Upload session identifier.' }, responseMode: 'json', summary: 'Complete File Upload', query: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace that owns the upload session.', + }, }, }, completeKnowledgeDocumentUpload: { method: 'POST', path: '/api/v2/knowledge/[id]/documents/uploads/[uploadId]/complete', pathParams: ['id', 'uploadId'] as const, + pathParamDocs: { + id: 'Unique knowledge base identifier.', + uploadId: 'Upload session identifier returned when the upload was created.', + }, responseMode: 'json', summary: 'Complete Document Upload', query: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace that owns the knowledge base.', + }, }, }, completeTableImport: { method: 'POST', path: '/api/v2/tables/imports/[importId]/complete', pathParams: ['importId'] as const, + pathParamDocs: { importId: 'Unique table-import identifier.' }, responseMode: 'json', summary: 'Complete Table Import Upload', query: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace that owns the transfer resource.', + }, }, }, createCredentialConnection: { @@ -5887,7 +5971,11 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'Create Credential Connection', body: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace that will own the credential.', + }, }, opaqueBody: true, }, @@ -5898,10 +5986,26 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'Create Custom Tool', body: { - workspaceId: { kind: 'string', required: true }, - title: { kind: 'string', required: true }, - schema: { kind: 'object', required: true }, - code: { kind: 'string', required: true }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace in which to create the custom tool.', + }, + title: { + kind: 'string', + required: true, + describe: 'Display title, unique within the workspace.', + }, + schema: { + kind: 'object', + required: true, + describe: 'OpenAI-style function declaration describing the callable tool surface.', + }, + code: { + kind: 'string', + required: true, + describe: 'Tool implementation executed in the sandboxed function runtime.', + }, }, }, createFile: { @@ -5911,12 +6015,37 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'Create File', body: { - workspaceId: { kind: 'string', required: true }, - name: { kind: 'string', required: true }, - contentType: { kind: 'string' }, - folderPath: { kind: 'string' }, - content: { kind: 'string', default: '' }, - encoding: { kind: 'enum', values: ['utf-8', 'base64'] as const, default: 'utf-8' }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace in which to create the file.', + }, + name: { + kind: 'string', + required: true, + describe: + 'File name, including its extension. Path separators and dot segments are rejected.', + }, + contentType: { + kind: 'string', + describe: 'MIME type. When omitted, it is inferred from the file extension.', + }, + folderPath: { + kind: 'string', + describe: 'Canonical containing-folder path. Omit for the workspace root.', + }, + content: { + kind: 'string', + default: '', + describe: + '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: { + kind: 'enum', + values: ['utf-8', 'base64'] as const, + default: 'utf-8', + describe: 'Encoding of the content field.', + }, }, }, createFileFolder: { @@ -5926,8 +6055,12 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'Create Folder', body: { - workspaceId: { kind: 'string', required: true }, - path: { kind: 'string', required: true }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace in which to create the folder.', + }, + path: { kind: 'string', required: true, describe: 'Path of the folder to create.' }, }, }, createFileUpload: { @@ -5937,24 +6070,40 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'Create File Upload', body: { - workspaceId: { kind: 'string', required: true }, - name: { kind: 'string', required: true }, - contentType: { kind: 'string', required: true }, - size: { kind: 'integer', required: true }, - folderPath: { kind: 'string' }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace in which the file will be registered.', + }, + name: { kind: 'string', required: true, describe: 'File name, including its extension.' }, + contentType: { kind: 'string', required: true, describe: 'MIME type of the uploaded file.' }, + size: { kind: 'integer', required: true, describe: 'Exact file size in bytes.' }, + folderPath: { + kind: 'string', + describe: 'Canonical destination folder path. Omit for the workspace root.', + }, }, }, createFileUploadPartUrls: { method: 'POST', path: '/api/v2/files/uploads/[uploadId]/parts', pathParams: ['uploadId'] as const, + pathParamDocs: { uploadId: 'Upload session identifier.' }, responseMode: 'json', summary: 'Create File Upload Part URLs', query: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace that owns the upload session.', + }, }, body: { - partNumbers: { kind: 'array', required: true }, + partNumbers: { + kind: 'array', + required: true, + describe: 'Multipart part numbers for which signed URLs should be created.', + }, }, }, createKnowledgeBase: { @@ -5964,45 +6113,80 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'Create Knowledge Base', body: { - workspaceId: { kind: 'string', required: true }, - name: { kind: 'string', required: true }, - description: { kind: 'string' }, - chunkingConfig: { kind: 'object' }, - folderPath: { kind: 'string' }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace in which to create the knowledge base.', + }, + name: { kind: 'string', required: true, describe: 'Human-readable knowledge base name.' }, + description: { kind: 'string', describe: 'Optional knowledge base description.' }, + chunkingConfig: { + kind: 'object', + describe: 'Chunking configuration; defaults are applied when omitted.', + }, + folderPath: { + kind: 'string', + describe: 'Containing folder path; omission creates the knowledge base at the root.', + }, }, }, createKnowledgeDocumentUpload: { method: 'POST', path: '/api/v2/knowledge/[id]/documents/uploads', pathParams: ['id'] as const, + pathParamDocs: { id: 'Unique knowledge base identifier.' }, responseMode: 'json', summary: 'Create Document Upload', body: { - workspaceId: { kind: 'string', required: true }, - name: { kind: 'string', required: true }, - contentType: { kind: 'string', required: true }, - size: { kind: 'integer', required: true }, - tag1: { kind: 'string' }, - tag2: { kind: 'string' }, - tag3: { kind: 'string' }, - tag4: { kind: 'string' }, - tag5: { kind: 'string' }, - tag6: { kind: 'string' }, - tag7: { kind: 'string' }, - processingOptions: { kind: 'object' }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace that owns the knowledge base.', + }, + name: { + kind: 'string', + required: true, + describe: 'Filename recorded on the knowledge document.', + }, + contentType: { + kind: 'string', + required: true, + describe: 'Supported MIME type for the document.', + }, + size: { kind: 'integer', required: true, describe: 'Exact file size in bytes.' }, + tag1: { kind: 'string', describe: 'Value for tag slot 1.' }, + tag2: { kind: 'string', describe: 'Value for tag slot 2.' }, + tag3: { kind: 'string', describe: 'Value for tag slot 3.' }, + tag4: { kind: 'string', describe: 'Value for tag slot 4.' }, + tag5: { kind: 'string', describe: 'Value for tag slot 5.' }, + tag6: { kind: 'string', describe: 'Value for tag slot 6.' }, + tag7: { kind: 'string', describe: 'Value for tag slot 7.' }, + processingOptions: { kind: 'object', describe: 'Optional processing recipe and language.' }, }, }, createKnowledgeDocumentUploadPartUrls: { method: 'POST', path: '/api/v2/knowledge/[id]/documents/uploads/[uploadId]/parts', pathParams: ['id', 'uploadId'] as const, + pathParamDocs: { + id: 'Unique knowledge base identifier.', + uploadId: 'Upload session identifier returned when the upload was created.', + }, responseMode: 'json', summary: 'Create Document Upload Part URLs', query: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace that owns the knowledge base.', + }, }, body: { - partNumbers: { kind: 'array', required: true }, + partNumbers: { + kind: 'array', + required: true, + describe: 'Multipart part numbers for which signed URLs should be created.', + }, }, }, createKnowledgeFolder: { @@ -6012,8 +6196,12 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'Create Folder', body: { - workspaceId: { kind: 'string', required: true }, - path: { kind: 'string', required: true }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace in which to create the folder.', + }, + path: { kind: 'string', required: true, describe: 'Path of the folder to create.' }, }, }, createMcpServer: { @@ -6023,18 +6211,64 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'Create MCP Server', body: { - workspaceId: { kind: 'string', required: true }, - name: { kind: 'string', required: true }, - description: { kind: 'string' }, - transport: { kind: 'enum', values: ['streamable-http'] as const, default: 'streamable-http' }, - url: { kind: 'string', required: true }, - authType: { kind: 'enum', values: ['none', 'headers', 'oauth'] as const }, - headers: { kind: 'object' }, - timeout: { kind: 'integer', default: 30000 }, - retries: { kind: 'integer', default: 3 }, - enabled: { kind: 'boolean', default: true }, - oauthClientId: { kind: 'string' }, - oauthClientSecret: { kind: 'string' }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace in which to register the server.', + }, + name: { kind: 'string', required: true, describe: 'Server display name.' }, + description: { kind: 'string', describe: 'Optional server description.' }, + transport: { + kind: 'enum', + values: ['streamable-http'] as const, + default: 'streamable-http', + describe: + 'Transport used to communicate with the server. Applied server-side as `streamable-http` when omitted on create.', + }, + url: { + kind: 'string', + required: true, + describe: + '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.', + }, + authType: { + kind: 'enum', + values: ['none', 'headers', 'oauth'] as const, + describe: + '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.', + }, + headers: { + kind: 'object', + describe: + '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.', + }, + timeout: { + kind: 'integer', + default: 30000, + describe: + 'Per-request timeout in milliseconds. Applied server-side as 30000 when omitted on create.', + }, + retries: { + kind: 'integer', + default: 3, + describe: 'Number of retries per request. Applied server-side as 3 when omitted on create.', + }, + enabled: { + kind: 'boolean', + default: true, + describe: + 'Whether the server tools are available to workflows. Applied server-side as true when omitted on create.', + }, + oauthClientId: { + kind: 'string', + describe: + 'Pre-registered OAuth client identifier. Changing it on update revokes the stored OAuth grant and forces reauthorization.', + }, + oauthClientSecret: { + kind: 'string', + describe: + '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.', + }, }, }, createServiceAccountCredential: { @@ -6044,25 +6278,46 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'Create Service-Account Credential', body: { - workspaceId: { kind: 'string', required: true }, - type: { kind: 'string', required: true }, - providerId: { kind: 'string', required: true }, - displayName: { kind: 'string' }, - description: { kind: 'string' }, - id: { kind: 'string' }, - serviceAccountJson: { kind: 'string' }, - apiToken: { kind: 'string' }, - domain: { kind: 'string' }, - signingSecret: { kind: 'string' }, - botToken: { kind: 'string' }, - clientId: { kind: 'string' }, - clientSecret: { kind: 'string' }, - certificateId: { kind: 'string' }, - orgId: { kind: 'string' }, - dataCenter: { kind: 'string' }, - authMethod: { kind: 'string' }, - privateKey: { kind: 'string' }, - username: { kind: 'string' }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace that will own the credential.', + }, + type: { + kind: 'string', + required: true, + describe: 'Service-account credential discriminator.', + }, + providerId: { + kind: 'string', + required: true, + describe: 'Exact service-account provider ID returned by provider discovery.', + }, + displayName: { + kind: 'string', + describe: 'Optional name; providers may derive one from the verified account identity.', + }, + description: { kind: 'string', describe: 'Optional credential description.' }, + id: { + kind: 'string', + describe: 'Required only when provider discovery requests a client-generated ID.', + }, + serviceAccountJson: { + kind: 'string', + describe: 'Write-only Google service-account JSON key.', + }, + apiToken: { kind: 'string', describe: 'Write-only provider API token.' }, + domain: { kind: 'string', describe: 'Provider account domain.' }, + signingSecret: { kind: 'string', describe: 'Write-only webhook signing secret.' }, + botToken: { kind: 'string', describe: 'Write-only bot token.' }, + clientId: { kind: 'string', describe: 'OAuth client identifier.' }, + clientSecret: { kind: 'string', describe: 'Write-only OAuth client secret.' }, + certificateId: { kind: 'string', describe: 'Provider certificate mapping identifier.' }, + orgId: { kind: 'string', describe: 'Provider organization ID.' }, + dataCenter: { kind: 'string', describe: 'Provider data center.' }, + authMethod: { kind: 'string', describe: 'Provider authentication method.' }, + privateKey: { kind: 'string', describe: 'Write-only PEM private key.' }, + username: { kind: 'string', describe: 'Provider run-as username.' }, }, }, createSkill: { @@ -6072,10 +6327,27 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'Create Skill', body: { - workspaceId: { kind: 'string', required: true }, - name: { kind: 'string', required: true }, - description: { kind: 'string', required: true }, - content: { kind: 'string', required: true }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace in which to create the skill.', + }, + name: { + kind: 'string', + required: true, + describe: + 'Kebab-case name, unique within the workspace and not reserved by a built-in skill.', + }, + description: { + kind: 'string', + required: true, + describe: 'One-line summary of when the skill applies.', + }, + content: { + kind: 'string', + required: true, + describe: 'Skill body containing the instructions given to the agent.', + }, }, }, createTable: { @@ -6085,22 +6357,28 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'Create Table', body: { - name: { kind: 'string', required: true }, - description: { kind: 'string' }, - workspaceId: { kind: 'string', required: true }, - schema: { kind: 'object', required: true }, - folderPath: { kind: 'string' }, + name: { kind: 'string', required: true, describe: 'Table name.' }, + description: { kind: 'string', describe: 'Optional table description.' }, + workspaceId: { kind: 'string', required: true, describe: 'Unique workspace identifier.' }, + schema: { kind: 'object', required: true, describe: 'Initial table column definitions.' }, + folderPath: { kind: 'string', describe: 'Folder in which to create the table.' }, }, }, createTableExport: { method: 'POST', path: '/api/v2/tables/[tableId]/exports', pathParams: ['tableId'] as const, + pathParamDocs: { tableId: 'Unique table identifier.' }, responseMode: 'json', summary: 'Create Table Export', body: { - workspaceId: { kind: 'string', required: true }, - format: { kind: 'enum', values: ['csv', 'json'] as const, default: 'csv' }, + workspaceId: { kind: 'string', required: true, describe: 'Unique workspace identifier.' }, + format: { + kind: 'enum', + values: ['csv', 'json'] as const, + default: 'csv', + describe: 'Export file format.', + }, }, }, createTableFolder: { @@ -6110,8 +6388,12 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'Create Folder', body: { - workspaceId: { kind: 'string', required: true }, - path: { kind: 'string', required: true }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace in which to create the folder.', + }, + path: { kind: 'string', required: true, describe: 'Path of the folder to create.' }, }, }, createTableImport: { @@ -6121,35 +6403,48 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'Create Table Import', body: { - workspaceId: { kind: 'string', required: true }, - source: { kind: 'unknown', required: true }, - target: { kind: 'unknown', required: true }, - mapping: { kind: 'object' }, - createColumns: { kind: 'array' }, - timezone: { kind: 'string' }, + workspaceId: { kind: 'string', required: true, describe: 'Unique workspace identifier.' }, + source: { kind: 'unknown', required: true, describe: 'CSV source for the import.' }, + target: { kind: 'unknown', required: true, describe: 'New or existing table import target.' }, + mapping: { kind: 'object', describe: 'CSV headers mapped to existing table columns.' }, + createColumns: { + kind: 'array', + describe: 'CSV headers for which new columns should be created.', + }, + timezone: { kind: 'string', describe: 'IANA timezone used to interpret local date values.' }, }, }, createTableImportPartUrls: { method: 'POST', path: '/api/v2/tables/imports/[importId]/parts', pathParams: ['importId'] as const, + pathParamDocs: { importId: 'Unique table-import identifier.' }, responseMode: 'json', summary: 'Create Table Import Part URLs', query: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace that owns the transfer resource.', + }, }, body: { - partNumbers: { kind: 'array', required: true }, + partNumbers: { + kind: 'array', + required: true, + describe: 'Multipart part numbers for which signed URLs should be created.', + }, }, }, createTableRows: { method: 'POST', path: '/api/v2/tables/[tableId]/rows', pathParams: ['tableId'] as const, + pathParamDocs: { tableId: 'Unique table identifier.' }, responseMode: 'json', summary: 'Create Rows', body: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { kind: 'string', required: true, describe: 'Unique workspace identifier.' }, }, opaqueBody: true, }, @@ -6157,12 +6452,17 @@ export const V2_OPERATIONS = { method: 'POST', path: '/api/v2/tables/[tableId]/views', pathParams: ['tableId'] as const, + pathParamDocs: { tableId: 'Unique table identifier.' }, responseMode: 'json', summary: 'Create View', body: { - workspaceId: { kind: 'string', required: true }, - name: { kind: 'string', required: true }, - config: { kind: 'object', required: true }, + workspaceId: { kind: 'string', required: true, describe: 'Workspace that owns the table.' }, + name: { kind: 'string', required: true, describe: 'Saved-view display name.' }, + config: { + kind: 'object', + required: true, + describe: 'Saved filter, sort, and column-layout configuration.', + }, }, }, createWorkflow: { @@ -6172,10 +6472,18 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'Create Workflow', body: { - workspaceId: { kind: 'string', required: true }, - name: { kind: 'string', required: true }, - description: { kind: 'string' }, - folderPath: { kind: 'string' }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace in which to create the workflow.', + }, + name: { kind: 'string', required: true, describe: 'Workflow name.' }, + description: { kind: 'string', describe: 'Optional workflow description.' }, + folderPath: { + kind: 'string', + describe: + 'Folder path. A missing leading slash is normalized before validation. Segments are percent-encoded, so a folder shown as "New folder" is `/New%20folder`: everything outside `A-Z a-z 0-9 - _ . ~` is escaped as uppercase hex, and only that exact encoding is accepted. A trailing slash, an empty segment, and a literal `.` or `..` segment are rejected. At most 64 segments and 4096 encoded bytes.', + }, }, }, createWorkflowFolder: { @@ -6185,38 +6493,53 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'Create Workflow Folder', body: { - workspaceId: { kind: 'string', required: true }, - path: { kind: 'string', required: true }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace in which to create the folder.', + }, + path: { kind: 'string', required: true, describe: 'Path of the folder to create.' }, }, }, deleteCredential: { method: 'DELETE', path: '/api/v2/credentials/[credentialId]', pathParams: ['credentialId'] as const, + pathParamDocs: { credentialId: 'Credential to disconnect.' }, responseMode: 'json', summary: 'Disconnect Credential', query: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace expected to own the credential.', + }, }, }, deleteCustomTool: { method: 'DELETE', path: '/api/v2/custom-tools/[id]', pathParams: ['id'] as const, + pathParamDocs: { id: 'Unique custom tool identifier.' }, responseMode: 'json', summary: 'Delete Custom Tool', query: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace that owns the custom tool.', + }, }, }, deleteFile: { method: 'DELETE', path: '/api/v2/files/[fileId]', pathParams: ['fileId'] as const, + pathParamDocs: { fileId: 'File identifier.' }, responseMode: 'json', summary: 'Delete File', query: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { kind: 'string', required: true, describe: 'Workspace that owns the file.' }, }, }, deleteFileFolder: { @@ -6226,8 +6549,8 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'Delete Folder', query: { - workspaceId: { kind: 'string', required: true }, - path: { kind: 'string', required: true }, + workspaceId: { kind: 'string', required: true, describe: 'Workspace containing the folder.' }, + path: { kind: 'string', required: true, describe: 'Path of the folder to delete.' }, recursive: { kind: 'enum', values: [ @@ -6245,6 +6568,8 @@ export const V2_OPERATIONS = { 'disabled', ] as const, default: 'false', + describe: + "Delete the folder's nested files and folders too. An empty folder deletes either way; a non-empty one needs this. The listed spellings are the whole accepted vocabulary and are case-sensitive; any other value is rejected.", }, }, }, @@ -6252,20 +6577,33 @@ export const V2_OPERATIONS = { method: 'DELETE', path: '/api/v2/knowledge/[id]', pathParams: ['id'] as const, + pathParamDocs: { id: 'Unique knowledge base identifier.' }, responseMode: 'json', summary: 'Delete Knowledge Base', query: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace that owns the knowledge base.', + }, }, }, deleteKnowledgeDocument: { method: 'DELETE', path: '/api/v2/knowledge/[id]/documents/[documentId]', pathParams: ['id', 'documentId'] as const, + pathParamDocs: { + id: 'Unique knowledge base identifier.', + documentId: 'Unique knowledge document identifier.', + }, responseMode: 'json', summary: 'Delete Document', query: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace that owns the knowledge base.', + }, }, }, deleteKnowledgeFolder: { @@ -6275,8 +6613,8 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'Delete Folder', query: { - workspaceId: { kind: 'string', required: true }, - path: { kind: 'string', required: true }, + workspaceId: { kind: 'string', required: true, describe: 'Workspace containing the folder.' }, + path: { kind: 'string', required: true, describe: 'Path of the folder to delete.' }, recursive: { kind: 'enum', values: [ @@ -6294,6 +6632,8 @@ export const V2_OPERATIONS = { 'disabled', ] as const, default: 'false', + describe: + "Delete the folder's nested files and folders too. An empty folder deletes either way; a non-empty one needs this. The listed spellings are the whole accepted vocabulary and are case-sensitive; any other value is rejected.", }, }, }, @@ -6301,52 +6641,74 @@ export const V2_OPERATIONS = { method: 'DELETE', path: '/api/v2/mcp-servers/[id]', pathParams: ['id'] as const, + pathParamDocs: { id: 'Unique MCP server identifier.' }, responseMode: 'json', summary: 'Delete MCP Server', query: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace that owns the MCP server.', + }, }, }, deleteSecret: { method: 'DELETE', path: '/api/v2/secrets/[name]', pathParams: ['name'] as const, + pathParamDocs: { name: 'Secret to create, replace, or delete.' }, responseMode: 'json', summary: 'Delete Secret', query: { - workspaceId: { kind: 'string', required: true }, - scope: { kind: 'enum', required: true, values: ['workspace', 'personal'] as const }, + workspaceId: { + kind: 'string', + required: true, + describe: + 'Workspace the request is authorized against. A workspace secret is deleted from it; a personal secret is deleted for the caller in all of their workspaces.', + }, + scope: { + kind: 'enum', + required: true, + values: ['workspace', 'personal'] as const, + describe: + '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.', + }, }, }, deleteSkill: { method: 'DELETE', path: '/api/v2/skills/[id]', pathParams: ['id'] as const, + pathParamDocs: { + id: 'Unique skill identifier. A built-in skill is `builtin-` followed by its name, for example `builtin-research`.', + }, responseMode: 'json', summary: 'Delete Skill', query: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { kind: 'string', required: true, describe: 'Workspace that owns the skill.' }, }, }, deleteTable: { method: 'DELETE', path: '/api/v2/tables/[tableId]', pathParams: ['tableId'] as const, + pathParamDocs: { tableId: 'Unique table identifier.' }, responseMode: 'json', summary: 'Delete Table', query: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { kind: 'string', required: true, describe: 'Workspace that owns the table.' }, }, }, deleteTableColumn: { method: 'DELETE', path: '/api/v2/tables/[tableId]/columns', pathParams: ['tableId'] as const, + pathParamDocs: { tableId: 'Unique table identifier.' }, responseMode: 'json', summary: 'Delete Column', body: { - workspaceId: { kind: 'string', required: true }, - columnName: { kind: 'string', required: true }, + workspaceId: { kind: 'string', required: true, describe: 'Unique workspace identifier.' }, + columnName: { kind: 'string', required: true, describe: 'Name of the column to delete.' }, }, }, deleteTableFolder: { @@ -6356,8 +6718,8 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'Delete Folder', query: { - workspaceId: { kind: 'string', required: true }, - path: { kind: 'string', required: true }, + workspaceId: { kind: 'string', required: true, describe: 'Workspace containing the folder.' }, + path: { kind: 'string', required: true, describe: 'Path of the folder to delete.' }, recursive: { kind: 'enum', values: [ @@ -6375,6 +6737,8 @@ export const V2_OPERATIONS = { 'disabled', ] as const, default: 'false', + describe: + "Delete the folder's nested files and folders too. An empty folder deletes either way; a non-empty one needs this. The listed spellings are the whole accepted vocabulary and are case-sensitive; any other value is rejected.", }, }, }, @@ -6382,39 +6746,47 @@ export const V2_OPERATIONS = { method: 'DELETE', path: '/api/v2/tables/[tableId]/rows/[rowId]', pathParams: ['tableId', 'rowId'] as const, + pathParamDocs: { tableId: 'Unique table identifier.', rowId: 'Unique table row identifier.' }, responseMode: 'json', summary: 'Delete Row', query: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { kind: 'string', required: true, describe: 'Workspace that owns the table.' }, }, }, deleteTableRows: { method: 'DELETE', path: '/api/v2/tables/[tableId]/rows', pathParams: ['tableId'] as const, + pathParamDocs: { tableId: 'Unique table identifier.' }, responseMode: 'json', summary: 'Delete Rows', body: { - workspaceId: { kind: 'string', required: true }, - filter: { kind: 'unknown' }, - limit: { kind: 'integer' }, - rowIds: { kind: 'array' }, + workspaceId: { kind: 'string', required: true, describe: 'Unique workspace identifier.' }, + filter: { + kind: 'unknown', + describe: + 'Recursive predicate tree. Each group node is exactly one non-empty `all` or `any` array whose members are further groups or `{ field, op, value }` conditions; the root must be a group, not a bare condition. At most 100 members per group, 10 levels of nesting, and 500 nodes in total. The negating operators include nulls: `ne`, `nin`, `ncontains`, `nlike`, and `nilike` match rows whose column is null or absent, so "not X" is not the complement of "X" over a nullable column. That holds for every column type, multi-select included. To exclude nulls, `all`-combine the negation with `isNotEmpty` (multi-select) or `isNotNull`. Comparison: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`. Membership: `in`, `nin` (array operand). Emptiness: `isEmpty`, `isNotEmpty`, `isNull`, `isNotNull` (no operand). Substring, always case-insensitive, operand matched literally: `contains`, `ncontains`, `startsWith`, `endsWith`. Pattern: `like`/`nlike` (case-sensitive), `ilike`/`nilike` (case-insensitive). **`*` is the only wildcard** and stands for any run of characters; `%`, `_`, and backslash match themselves. Use `like: "Hi*"`, not `like: "Hi%"`. A `select` column compares by option id and restricts its operators: single-select accepts `eq`, `ne`, `in`, `nin`; multi-select accepts `contains`, `ncontains`. Option names are accepted as operands and resolved to ids.', + }, + limit: { kind: 'integer', describe: 'Maximum matching rows to delete.' }, + rowIds: { kind: 'array', describe: 'Explicit row identifiers to delete.' }, }, }, deleteTableView: { method: 'DELETE', path: '/api/v2/tables/[tableId]/views/[viewId]', pathParams: ['tableId', 'viewId'] as const, + pathParamDocs: { tableId: 'Unique table identifier.', viewId: 'Unique saved-view identifier.' }, responseMode: 'json', summary: 'Delete View', query: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { kind: 'string', required: true, describe: 'Workspace that owns the table.' }, }, }, deleteWorkflow: { method: 'DELETE', path: '/api/v2/workflows/[id]', pathParams: ['id'] as const, + pathParamDocs: { id: 'Unique workflow identifier.' }, responseMode: 'json', summary: 'Delete Workflow', }, @@ -6425,8 +6797,8 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'Delete Workflow Folder', query: { - workspaceId: { kind: 'string', required: true }, - path: { kind: 'string', required: true }, + workspaceId: { kind: 'string', required: true, describe: 'Workspace containing the folder.' }, + path: { kind: 'string', required: true, describe: 'Path of the folder to delete.' }, recursive: { kind: 'enum', values: [ @@ -6444,6 +6816,8 @@ export const V2_OPERATIONS = { 'disabled', ] as const, default: 'false', + describe: + "Delete the folder's nested files and folders too. An empty folder deletes either way; a non-empty one needs this. The listed spellings are the whole accepted vocabulary and are case-sensitive; any other value is rejected.", }, }, }, @@ -6451,56 +6825,102 @@ export const V2_OPERATIONS = { method: 'DELETE', path: '/api/v2/tables/[tableId]/groups', pathParams: ['tableId'] as const, + pathParamDocs: { tableId: 'Unique table identifier.' }, responseMode: 'json', summary: 'Delete Workflow Group', body: { - workspaceId: { kind: 'string', required: true }, - groupId: { kind: 'string', required: true }, + workspaceId: { kind: 'string', required: true, describe: 'Unique workspace identifier.' }, + groupId: { kind: 'string', required: true, describe: 'Workflow group to delete.' }, }, }, deployWorkflow: { method: 'POST', path: '/api/v2/workflows/[id]/deploy', pathParams: ['id'] as const, + pathParamDocs: { id: 'Unique workflow identifier.' }, responseMode: 'json', summary: 'Deploy Workflow', body: { - name: { kind: 'string' }, - description: { kind: 'string' }, + name: { kind: 'string', describe: 'Optional label for the deployment version.' }, + description: { + kind: 'string', + describe: 'Optional release note for the deployment version.', + }, }, }, downloadFile: { method: 'GET', path: '/api/v2/files/[fileId]', pathParams: ['fileId'] as const, + pathParamDocs: { fileId: 'File identifier.' }, responseMode: 'binary', summary: 'Download File', query: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { kind: 'string', required: true, describe: 'Workspace that owns the file.' }, }, }, executeWorkflow: { method: 'POST', path: '/api/v2/workflows/[id]/execute', pathParams: ['id'] as const, + pathParamDocs: { id: 'Unique workflow identifier.' }, responseMode: 'json', summary: 'Execute Workflow', body: { - input: { kind: 'object' }, - async: { kind: 'boolean', default: false }, - executionTimeoutSeconds: { kind: 'integer' }, - stream: { kind: 'boolean', default: false }, - selectedOutputs: { kind: 'array' }, - includeThinking: { kind: 'boolean', default: false }, - includeToolCalls: { kind: 'boolean', default: false }, - includeFileBase64: { kind: 'boolean' }, - base64MaxBytes: { kind: 'integer' }, + input: { + kind: 'object', + describe: 'Workflow input keyed by deployed trigger input-field name.', + }, + async: { + kind: 'boolean', + default: false, + describe: + 'Queue the run and return a 202 receipt when true. Requires an API key, cannot be combined with `stream`, and rejects all streaming and output-shaping options (`selectedOutputs`, `includeThinking`, `includeToolCalls`, `includeFileBase64`, `base64MaxBytes`).', + }, + executionTimeoutSeconds: { + kind: 'integer', + describe: + "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.", + }, + stream: { + kind: 'boolean', + default: false, + describe: + 'Return Server-Sent Events instead of JSON when true. Cannot be combined with `async`.', + }, + selectedOutputs: { + kind: 'array', + describe: + 'Block output references to include in a streamed response. Rejected when `async` is true.', + }, + includeThinking: { + kind: 'boolean', + default: false, + describe: + 'Include model reasoning events in an agent-event stream. Requires `stream: true` and the `X-Sim-Stream-Protocol: agent-events-v1` request header, and is rejected when `async` is true.', + }, + includeToolCalls: { + kind: 'boolean', + default: false, + describe: + 'Include tool-call events in an agent-event stream. Requires `stream: true` and the `X-Sim-Stream-Protocol: agent-events-v1` request header, and is rejected when `async` is true.', + }, + includeFileBase64: { + kind: 'boolean', + describe: 'Inline eligible output files as base64 content. Rejected when `async` is true.', + }, + base64MaxBytes: { + kind: 'integer', + describe: + 'Maximum total bytes of file content to inline as base64. Rejected when `async` is true.', + }, }, }, exportWorkflow: { method: 'GET', path: '/api/v2/workflows/[id]/export', pathParams: ['id'] as const, + pathParamDocs: { id: 'Unique workflow identifier.' }, responseMode: 'json', summary: 'Export Workflow', }, @@ -6508,23 +6928,33 @@ export const V2_OPERATIONS = { method: 'POST', path: '/api/v2/tables/[tableId]/rows/find', pathParams: ['tableId'] as const, + pathParamDocs: { tableId: 'Unique table identifier.' }, responseMode: 'json', summary: 'Find Rows', body: { - workspaceId: { kind: 'string', required: true }, - q: { kind: 'string', required: true }, - predicate: { kind: 'unknown' }, - sort: { kind: 'array' }, + workspaceId: { kind: 'string', required: true, describe: 'Unique workspace identifier.' }, + q: { kind: 'string', required: true, describe: 'Case-insensitive cell substring to find.' }, + predicate: { + kind: 'unknown', + describe: + 'Recursive predicate tree. Each group node is exactly one non-empty `all` or `any` array whose members are further groups or `{ field, op, value }` conditions; the root must be a group, not a bare condition. At most 100 members per group, 10 levels of nesting, and 500 nodes in total. The negating operators include nulls: `ne`, `nin`, `ncontains`, `nlike`, and `nilike` match rows whose column is null or absent, so "not X" is not the complement of "X" over a nullable column. That holds for every column type, multi-select included. To exclude nulls, `all`-combine the negation with `isNotEmpty` (multi-select) or `isNotNull`. Comparison: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`. Membership: `in`, `nin` (array operand). Emptiness: `isEmpty`, `isNotEmpty`, `isNull`, `isNotNull` (no operand). Substring, always case-insensitive, operand matched literally: `contains`, `ncontains`, `startsWith`, `endsWith`. Pattern: `like`/`nlike` (case-sensitive), `ilike`/`nilike` (case-insensitive). **`*` is the only wildcard** and stands for any run of characters; `%`, `_`, and backslash match themselves. Use `like: "Hi*"`, not `like: "Hi%"`. A `select` column compares by option id and restricts its operators: single-select accepts `eq`, `ne`, `in`, `nin`; multi-select accepts `contains`, `ncontains`. Option names are accepted as operands and resolved to ids.', + }, + sort: { kind: 'array', describe: 'Ordered table-row sort specification.' }, }, }, getAuditLog: { method: 'GET', path: '/api/v2/audit-logs/[id]', pathParams: ['id'] as const, + pathParamDocs: { id: 'Audit-log entry identifier.' }, responseMode: 'json', summary: 'Get Audit Log', query: { - organizationId: { kind: 'string', required: true }, + organizationId: { + kind: 'string', + required: true, + describe: 'Organization whose audit-log entry should be returned.', + }, }, }, getBillingStatus: { @@ -6534,64 +6964,95 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'Get Billing Status', query: { - workspaceId: { kind: 'string' }, + workspaceId: { + kind: 'string', + describe: + 'Workspace whose payer should be resolved. Workspace API keys are pinned to their own workspace.', + }, }, }, getCustomTool: { method: 'GET', path: '/api/v2/custom-tools/[id]', pathParams: ['id'] as const, + pathParamDocs: { id: 'Unique custom tool identifier.' }, responseMode: 'json', summary: 'Get Custom Tool', query: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace that owns the custom tool.', + }, }, }, getFile: { method: 'GET', path: '/api/v2/files/[fileId]/metadata', pathParams: ['fileId'] as const, + pathParamDocs: { fileId: 'File identifier.' }, responseMode: 'json', summary: 'Get File Metadata', query: { - workspaceId: { kind: 'string', required: true }, - scope: { kind: 'enum', values: ['active', 'archived'] as const, default: 'active' }, + workspaceId: { kind: 'string', required: true, describe: 'Workspace that owns the file.' }, + scope: { + kind: 'enum', + values: ['active', 'archived'] as const, + default: 'active', + describe: + '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.', + }, }, }, getFileShare: { method: 'GET', path: '/api/v2/files/[fileId]/share', pathParams: ['fileId'] as const, + pathParamDocs: { fileId: 'File identifier.' }, responseMode: 'json', summary: 'Get File Share', query: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { kind: 'string', required: true, describe: 'Workspace that owns the file.' }, }, }, getKnowledgeBase: { method: 'GET', path: '/api/v2/knowledge/[id]', pathParams: ['id'] as const, + pathParamDocs: { id: 'Unique knowledge base identifier.' }, responseMode: 'json', summary: 'Get Knowledge Base', query: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace that owns the knowledge base.', + }, }, }, getKnowledgeDocument: { method: 'GET', path: '/api/v2/knowledge/[id]/documents/[documentId]', pathParams: ['id', 'documentId'] as const, + pathParamDocs: { + id: 'Unique knowledge base identifier.', + documentId: 'Unique knowledge document identifier.', + }, responseMode: 'json', summary: 'Get Document', query: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace that owns the knowledge base.', + }, }, }, getLog: { method: 'GET', path: '/api/v2/logs/[runId]', pathParams: ['runId'] as const, + pathParamDocs: { runId: 'Unique workflow run identifier.' }, responseMode: 'json', summary: 'Get Log', }, @@ -6599,76 +7060,98 @@ export const V2_OPERATIONS = { method: 'GET', path: '/api/v2/mcp-servers/[id]', pathParams: ['id'] as const, + pathParamDocs: { id: 'Unique MCP server identifier.' }, responseMode: 'json', summary: 'Get MCP Server', query: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace that owns the MCP server.', + }, }, }, getSkill: { method: 'GET', path: '/api/v2/skills/[id]', pathParams: ['id'] as const, + pathParamDocs: { + id: 'Unique skill identifier. A built-in skill is `builtin-` followed by its name, for example `builtin-research`.', + }, responseMode: 'json', summary: 'Get Skill', query: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { kind: 'string', required: true, describe: 'Workspace that owns the skill.' }, }, }, getTable: { method: 'GET', path: '/api/v2/tables/[tableId]', pathParams: ['tableId'] as const, + pathParamDocs: { tableId: 'Unique table identifier.' }, responseMode: 'json', summary: 'Get Table', query: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { kind: 'string', required: true, describe: 'Workspace that owns the table.' }, }, }, getTableExport: { method: 'GET', path: '/api/v2/tables/exports/[exportId]', pathParams: ['exportId'] as const, + pathParamDocs: { exportId: 'Unique table-export identifier.' }, responseMode: 'json', summary: 'Get Table Export', query: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace that owns the transfer resource.', + }, }, }, getTableImport: { method: 'GET', path: '/api/v2/tables/imports/[importId]', pathParams: ['importId'] as const, + pathParamDocs: { importId: 'Unique table-import identifier.' }, responseMode: 'json', summary: 'Get Table Import', query: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace that owns the transfer resource.', + }, }, }, getTableRow: { method: 'GET', path: '/api/v2/tables/[tableId]/rows/[rowId]', pathParams: ['tableId', 'rowId'] as const, + pathParamDocs: { tableId: 'Unique table identifier.', rowId: 'Unique table row identifier.' }, responseMode: 'json', summary: 'Get Row', query: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { kind: 'string', required: true, describe: 'Workspace that owns the table.' }, }, }, getTableView: { method: 'GET', path: '/api/v2/tables/[tableId]/views/[viewId]', pathParams: ['tableId', 'viewId'] as const, + pathParamDocs: { tableId: 'Unique table identifier.', viewId: 'Unique saved-view identifier.' }, responseMode: 'json', summary: 'Get View', query: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { kind: 'string', required: true, describe: 'Workspace that owns the table.' }, }, }, getWorkflow: { method: 'GET', path: '/api/v2/workflows/[id]', pathParams: ['id'] as const, + pathParamDocs: { id: 'Unique workflow identifier.' }, responseMode: 'json', summary: 'Get Workflow', }, @@ -6676,6 +7159,7 @@ export const V2_OPERATIONS = { method: 'GET', path: '/api/v2/workflows/[id]/deployment', pathParams: ['id'] as const, + pathParamDocs: { id: 'Unique workflow identifier.' }, responseMode: 'json', summary: 'Get Workflow Deployment', }, @@ -6683,17 +7167,27 @@ export const V2_OPERATIONS = { method: 'GET', path: '/api/v2/workflows/[id]/runs/[runId]', pathParams: ['id', 'runId'] as const, + pathParamDocs: { id: 'Unique workflow identifier.', runId: 'Unique workflow run identifier.' }, responseMode: 'json', summary: 'Get Workflow Run', query: { - includeOutput: { kind: 'boolean' }, - selectedOutputs: { kind: 'string' }, + includeOutput: { + kind: 'boolean', + describe: + 'Include the final workflow output when true. It does not gate `blockOutputs`, which `selectedOutputs` selects on its own.', + }, + selectedOutputs: { + kind: 'string', + describe: + 'Comma-separated block output references to include, as `blockId` or `blockId.path`. Block *names* are not resolved here — unlike the execute request, this resource reads a recorded run and matches ids only, so a name selects nothing and yields an empty `blockOutputs`.', + }, }, }, getWorkflowVersion: { method: 'GET', path: '/api/v2/workflows/[id]/versions/[version]', pathParams: ['id', 'version'] as const, + pathParamDocs: { id: 'Unique workflow identifier.', version: 'Numeric deployment version.' }, responseMode: 'json', summary: 'Get Workflow Version', }, @@ -6701,6 +7195,7 @@ export const V2_OPERATIONS = { method: 'GET', path: '/api/v2/workspaces/[workspaceId]', pathParams: ['workspaceId'] as const, + pathParamDocs: { workspaceId: 'Workspace to retrieve.' }, responseMode: 'json', summary: 'Get Workspace', }, @@ -6711,11 +7206,23 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'Import Workflow', body: { - workspaceId: { kind: 'string', required: true }, - workflow: { kind: 'unknown', required: true }, - folderPath: { kind: 'string' }, - name: { kind: 'string' }, - description: { kind: 'string' }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace in which to import the workflow.', + }, + workflow: { + kind: 'unknown', + required: true, + describe: + 'Workflow export object, bare workflow state, or JSON string containing either form.', + }, + folderPath: { + kind: 'string', + describe: 'Destination folder path; omit for the workspace root.', + }, + name: { kind: 'string', describe: 'Override for the imported workflow name.' }, + description: { kind: 'string', describe: 'Override for the imported workflow description.' }, }, }, listAuditLogs: { @@ -6725,17 +7232,45 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'List Audit Logs', query: { - action: { kind: 'string' }, - resourceType: { kind: 'string' }, - resourceId: { kind: 'string' }, - workspaceId: { kind: 'string' }, - startDate: { kind: 'string' }, - endDate: { kind: 'string' }, - includeDeparted: { kind: 'boolean' }, - limit: { kind: 'integer', default: 50 }, - cursor: { kind: 'string' }, - organizationId: { kind: 'string', required: true }, - actorEmail: { kind: 'string' }, + action: { kind: 'string', describe: 'Filter by exact action name.' }, + resourceType: { + kind: 'string', + describe: + 'Filter by resource type. Accepts a comma-separated set; members are trimmed and deduplicated, and member order affects neither the result nor the cursor.', + }, + resourceId: { kind: 'string', describe: 'Filter by exact resource identifier.' }, + workspaceId: { kind: 'string', describe: 'Filter to actions in one workspace.' }, + startDate: { + kind: 'string', + describe: + '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.', + }, + endDate: { + kind: 'string', + describe: + '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.', + }, + includeDeparted: { + kind: 'boolean', + describe: 'Include actions by users who have left the organization.', + }, + limit: { + kind: 'integer', + default: 50, + describe: + 'Maximum audit entries to return per page. Must be a whole number from 1 to 100. Defaults to 50.', + }, + cursor: { + kind: 'string', + describe: + 'Opaque cursor from the previous page. Send it back with the same sort and filters; only `limit` may change. Change anything else and pagination must restart without a cursor.', + }, + organizationId: { + kind: 'string', + required: true, + describe: 'Organization whose audit trail should be queried.', + }, + actorEmail: { kind: 'string', describe: 'Filter by actor email address.' }, }, }, listBillingLogs: { @@ -6758,17 +7293,40 @@ export const V2_OPERATIONS = { 'enrichment', 'voice-output', ] as const, + describe: 'Restrict results to one usage source.', + }, + workspaceId: { + kind: 'string', + describe: 'Restrict results to one workspace whose payer the caller can inspect.', }, - workspaceId: { kind: 'string' }, period: { kind: 'enum', values: ['1d', '7d', '30d', 'all', 'custom'] as const, default: '30d', + describe: + 'Relative window, all history, or a custom date range. `startDate` and `endDate` are accepted only with `custom`; every other value computes its own window.', + }, + startDate: { + kind: 'string', + describe: + 'Only include usage events recorded at or after this UTC ISO 8601 timestamp, e.g. `2026-08-06T00:00:00Z`. Requires `period=custom`. 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.', + }, + endDate: { + kind: 'string', + describe: + 'Only include usage events recorded at or before this UTC ISO 8601 timestamp, e.g. `2026-08-06T00:00:00Z`. Requires `period=custom`, and defaults to now when omitted. 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: { + kind: 'integer', + default: 50, + describe: + 'Maximum usage events per page. Must be a whole number from 1 to 100. Defaults to 50.', + }, + cursor: { + kind: 'string', + describe: + 'Opaque cursor from the previous page. Send it back with the same sort and filters; only `limit` may change. Change anything else and pagination must restart without a cursor.', }, - startDate: { kind: 'string' }, - endDate: { kind: 'string' }, - limit: { kind: 'integer', default: 50 }, - cursor: { kind: 'string' }, }, }, listCredentialProviders: { @@ -6778,8 +7336,16 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'List Credential Providers', query: { - workspaceId: { kind: 'string', required: true }, - search: { kind: 'string' }, + workspaceId: { + kind: 'string', + required: true, + describe: + 'Workspace used to evaluate credential-provider availability and integration policy.', + }, + search: { + kind: 'string', + describe: 'Case-insensitive substring match against the credential provider name.', + }, }, }, listCredentials: { @@ -6789,18 +7355,47 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'List Credentials', query: { - workspaceId: { kind: 'string', required: true }, - type: { kind: 'enum', values: ['oauth', 'service_account'] as const }, - providerId: { kind: 'string' }, - search: { kind: 'string' }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace whose credentials should be listed.', + }, + type: { + kind: 'enum', + values: ['oauth', 'service_account'] as const, + describe: 'Restrict results to this credential type.', + }, + providerId: { + kind: 'string', + describe: 'Restrict results to credentials for this integration provider.', + }, + search: { + kind: 'string', + describe: 'Case-insensitive substring match against the credential display name.', + }, sortBy: { kind: 'enum', values: ['displayName', 'createdAt', 'updatedAt'] as const, default: 'createdAt', + describe: 'Field used to sort the result.', + }, + sortOrder: { + kind: 'enum', + values: ['asc', 'desc'] as const, + default: 'desc', + describe: 'Sort direction.', + }, + limit: { + kind: 'integer', + default: 50, + describe: + 'Maximum credentials to return per page. Must be a whole number from 1 to 100. Defaults to 50.', + }, + cursor: { + kind: 'string', + describe: + 'Opaque cursor from the previous page. Send it back with the same sort and filters; only `limit` may change. Change anything else and pagination must restart without a cursor.', }, - sortOrder: { kind: 'enum', values: ['asc', 'desc'] as const, default: 'desc' }, - limit: { kind: 'integer', default: 50 }, - cursor: { kind: 'string' }, }, }, listCustomTools: { @@ -6810,16 +7405,38 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'List Custom Tools', query: { - workspaceId: { kind: 'string', required: true }, - search: { kind: 'string' }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace that owns the custom tool.', + }, + search: { + kind: 'string', + describe: 'Case-insensitive substring match against the tool title.', + }, sortBy: { kind: 'enum', values: ['title', 'createdAt', 'updatedAt'] as const, default: 'createdAt', + describe: 'Field used to sort the result.', + }, + sortOrder: { + kind: 'enum', + values: ['asc', 'desc'] as const, + default: 'desc', + describe: 'Sort direction.', + }, + limit: { + kind: 'integer', + default: 50, + describe: + 'Maximum custom tools to return per page. Must be a whole number from 1 to 100. Defaults to 50.', + }, + cursor: { + kind: 'string', + describe: + 'Opaque cursor from the previous page. Send it back with the same sort and filters; only `limit` may change. Change anything else and pagination must restart without a cursor.', }, - sortOrder: { kind: 'enum', values: ['asc', 'desc'] as const, default: 'desc' }, - limit: { kind: 'integer', default: 50 }, - cursor: { kind: 'string' }, }, }, listFileFolders: { @@ -6829,15 +7446,32 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'List Folders', query: { - workspaceId: { kind: 'string', required: true }, - parentPath: { kind: 'string' }, - search: { kind: 'string' }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace whose folders should be listed.', + }, + parentPath: { + kind: 'string', + describe: 'Restrict results to direct children of this parent path.', + }, + search: { + kind: 'string', + describe: 'Case-insensitive substring match against the folder name.', + }, sortBy: { kind: 'enum', values: ['name', 'createdAt', 'updatedAt'] as const, default: 'name', + describe: + '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.', + }, + sortOrder: { + kind: 'enum', + values: ['asc', 'desc'] as const, + default: 'asc', + describe: 'Sort direction.', }, - sortOrder: { kind: 'enum', values: ['asc', 'desc'] as const, default: 'asc' }, }, }, listFiles: { @@ -6847,18 +7481,51 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'List Files', query: { - workspaceId: { kind: 'string', required: true }, - folderPath: { kind: 'string' }, - scope: { kind: 'enum', values: ['active', 'archived'] as const, default: 'active' }, - search: { kind: 'string' }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace whose files should be listed.', + }, + folderPath: { + kind: 'string', + describe: + 'Restrict results to files directly inside this folder. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error.', + }, + scope: { + kind: 'enum', + values: ['active', 'archived'] as const, + default: 'active', + describe: + '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.', + }, + search: { + kind: 'string', + describe: 'Case-insensitive substring match against the file name.', + }, sortBy: { kind: 'enum', values: ['name', 'size', 'uploadedAt', 'updatedAt'] as const, default: 'uploadedAt', + describe: + '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.', + }, + sortOrder: { + kind: 'enum', + values: ['asc', 'desc'] as const, + default: 'asc', + describe: 'Sort direction.', + }, + limit: { + kind: 'integer', + default: 100, + describe: + 'Maximum files per page. Values outside 1–1000 are truncated and clamped into that range rather than rejected. Defaults to 100.', + }, + cursor: { + kind: 'string', + describe: + 'Opaque cursor from the previous page. Send it back with the same sort and filters; only `limit` may change. Change anything else and pagination must restart without a cursor.', }, - sortOrder: { kind: 'enum', values: ['asc', 'desc'] as const, default: 'asc' }, - limit: { kind: 'integer', default: 100 }, - cursor: { kind: 'string' }, }, }, listKnowledgeBases: { @@ -6868,33 +7535,74 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'List Knowledge Bases', query: { - workspaceId: { kind: 'string', required: true }, - folderPath: { kind: 'string' }, - search: { kind: 'string' }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace whose knowledge bases should be listed.', + }, + folderPath: { + kind: 'string', + describe: + 'Restrict results to knowledge bases in this folder. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error.', + }, + search: { + kind: 'string', + describe: 'Case-insensitive substring match against the resource name.', + }, sortBy: { kind: 'enum', values: ['name', 'createdAt', 'updatedAt'] as const, default: 'createdAt', + describe: + '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.', + }, + sortOrder: { + kind: 'enum', + values: ['asc', 'desc'] as const, + default: 'asc', + describe: 'Sort direction.', + }, + limit: { + kind: 'integer', + default: 50, + describe: + 'Maximum knowledge bases to return per page. Must be a whole number from 1 to 100. Defaults to 50.', + }, + cursor: { + kind: 'string', + describe: + 'Opaque cursor from the previous page. Send it back with the same sort and filters; only `limit` may change. Change anything else and pagination must restart without a cursor.', }, - sortOrder: { kind: 'enum', values: ['asc', 'desc'] as const, default: 'asc' }, - limit: { kind: 'integer', default: 50 }, - cursor: { kind: 'string' }, }, }, listKnowledgeDocuments: { method: 'GET', path: '/api/v2/knowledge/[id]/documents', pathParams: ['id'] as const, + pathParamDocs: { id: 'Unique knowledge base identifier.' }, responseMode: 'json', summary: 'List Documents', query: { - workspaceId: { kind: 'string', required: true }, - limit: { kind: 'integer', default: 50 }, - search: { kind: 'string' }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace that owns the knowledge base.', + }, + limit: { + kind: 'integer', + default: 50, + describe: + 'Maximum documents to return per page. Must be a whole number from 1 to 100. Defaults to 50.', + }, + search: { + kind: 'string', + describe: 'Case-insensitive substring match against the document filename.', + }, enabledFilter: { kind: 'enum', values: ['all', 'enabled', 'disabled'] as const, default: 'all', + describe: 'Filter by whether documents are enabled for search.', }, sortBy: { kind: 'enum', @@ -6908,10 +7616,25 @@ export const V2_OPERATIONS = { 'enabled', ] as const, default: 'uploadedAt', + describe: + '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.', + }, + sortOrder: { + kind: 'enum', + values: ['asc', 'desc'] as const, + default: 'desc', + describe: 'Sort direction.', + }, + cursor: { + kind: 'string', + describe: + 'Opaque cursor from the previous page. Send it back with the same sort and filters; only `limit` may change. Change anything else and pagination must restart without a cursor.', + }, + tagFilters: { + kind: 'string', + describe: + '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.', }, - sortOrder: { kind: 'enum', values: ['asc', 'desc'] as const, default: 'desc' }, - cursor: { kind: 'string' }, - tagFilters: { kind: 'string' }, }, }, listKnowledgeFolders: { @@ -6921,25 +7644,47 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'List Folders', query: { - workspaceId: { kind: 'string', required: true }, - parentPath: { kind: 'string' }, - search: { kind: 'string' }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace whose folders should be listed.', + }, + parentPath: { + kind: 'string', + describe: 'Restrict results to direct children of this parent path.', + }, + search: { + kind: 'string', + describe: 'Case-insensitive substring match against the folder name.', + }, sortBy: { kind: 'enum', values: ['name', 'createdAt', 'updatedAt'] as const, default: 'name', + describe: + '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.', + }, + sortOrder: { + kind: 'enum', + values: ['asc', 'desc'] as const, + default: 'asc', + describe: 'Sort direction.', }, - sortOrder: { kind: 'enum', values: ['asc', 'desc'] as const, default: 'asc' }, }, }, listKnowledgeTags: { method: 'GET', path: '/api/v2/knowledge/[id]/tags', pathParams: ['id'] as const, + pathParamDocs: { id: 'Unique knowledge base identifier.' }, responseMode: 'json', summary: 'List Tags', query: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace that owns the knowledge base.', + }, }, }, listLogs: { @@ -6949,25 +7694,97 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'List Logs', query: { - workspaceId: { kind: 'string', required: true }, - workflowIds: { kind: 'string' }, - triggers: { kind: 'string' }, - level: { kind: 'enum', values: ['info', 'error'] as const }, - startDate: { kind: 'string' }, - endDate: { kind: 'string' }, - minDurationMs: { kind: 'integer' }, - maxDurationMs: { kind: 'integer' }, - minCost: { kind: 'number' }, - maxCost: { kind: 'number' }, - model: { kind: 'string' }, - details: { kind: 'enum', values: ['basic', 'full'] as const, default: 'basic' }, - includeTraceSpans: { kind: 'boolean' }, - includeFinalOutput: { kind: 'boolean' }, - limit: { kind: 'integer', default: 100 }, - cursor: { kind: 'string' }, - order: { kind: 'enum', values: ['asc', 'desc'] as const, default: 'desc' }, - runId: { kind: 'string' }, - folderPaths: { kind: 'string' }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace whose execution logs should be returned.', + }, + workflowIds: { + kind: 'string', + describe: 'Comma-separated workflow identifiers to include. An empty entry is rejected.', + }, + triggers: { + kind: 'string', + describe: + '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`.', + }, + level: { + kind: 'enum', + values: ['info', 'error'] as const, + describe: 'Severity level to include.', + }, + startDate: { + kind: 'string', + describe: + '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.', + }, + endDate: { + kind: 'string', + describe: + '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.', + }, + minDurationMs: { + kind: 'integer', + describe: + '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.', + }, + maxDurationMs: { + kind: 'integer', + describe: + '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.', + }, + minCost: { + kind: 'number', + describe: + '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.', + }, + maxCost: { + kind: 'number', + describe: + '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: { kind: 'string', describe: 'AI model used during execution.' }, + details: { + kind: 'enum', + values: ['basic', 'full'] as const, + default: 'basic', + describe: + 'Response detail level. `full` adds the `workflow` summary to every item. `includeTraceSpans=true` and `includeFinalOutput=true` each imply `full`, so either one adds `workflow` even when `details=basic` is sent explicitly.', + }, + includeTraceSpans: { + kind: 'boolean', + describe: + 'Whether to include block-level trace spans. Implies `details=full`. Spans are pruned on their own retention schedule, so a run whose spans have aged out returns `traceSpans: []` rather than an error.', + }, + includeFinalOutput: { + kind: 'boolean', + describe: + 'Whether to include the final workflow output. Implies `details=full`, so the `workflow` summary is present regardless of what `details` is set to.', + }, + limit: { + kind: 'integer', + default: 100, + describe: + 'Maximum log entries per page. Values outside 1–1000 are truncated and clamped into that range rather than rejected. Defaults to 100.', + }, + cursor: { + kind: 'string', + describe: + 'Opaque cursor from the previous page. Send it back with the same sort and filters; only `limit` may change. Change anything else and pagination must restart without a cursor.', + }, + order: { + kind: 'enum', + values: ['asc', 'desc'] as const, + default: 'desc', + describe: + '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.', + }, + runId: { kind: 'string', describe: 'Exact run identifier to match.' }, + folderPaths: { + kind: 'string', + describe: + 'Comma-separated workflow folder paths to include. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error.', + }, }, }, listMcpServers: { @@ -6977,27 +7794,59 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'List MCP Servers', query: { - workspaceId: { kind: 'string', required: true }, - search: { kind: 'string' }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace that owns the MCP server.', + }, + search: { + kind: 'string', + describe: 'Case-insensitive substring match against the server name.', + }, sortBy: { kind: 'enum', values: ['name', 'createdAt', 'updatedAt'] as const, default: 'createdAt', + describe: + '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.', + }, + sortOrder: { + kind: 'enum', + values: ['asc', 'desc'] as const, + default: 'desc', + describe: 'Sort direction.', + }, + limit: { + kind: 'integer', + default: 50, + describe: + 'Maximum MCP servers to return per page. Must be a whole number from 1 to 100. Defaults to 50.', + }, + cursor: { + kind: 'string', + describe: + 'Opaque cursor from the previous page. Send it back with the same sort and filters; only `limit` may change. Change anything else and pagination must restart without a cursor.', }, - sortOrder: { kind: 'enum', values: ['asc', 'desc'] as const, default: 'desc' }, - limit: { kind: 'integer', default: 50 }, - cursor: { kind: 'string' }, }, }, listMcpServerTools: { method: 'GET', path: '/api/v2/mcp-servers/[id]/tools', pathParams: ['id'] as const, + pathParamDocs: { id: 'Unique MCP server identifier.' }, responseMode: 'json', summary: 'List MCP Server Tools', query: { - workspaceId: { kind: 'string', required: true }, - refresh: { kind: 'boolean' }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace that owns the MCP server.', + }, + refresh: { + kind: 'boolean', + describe: + '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.', + }, }, }, listSecrets: { @@ -7007,17 +7856,44 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'List Secrets', query: { - workspaceId: { kind: 'string', required: true }, - scope: { kind: 'enum', values: ['workspace', 'personal'] as const }, - search: { kind: 'string' }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace whose secret metadata should be listed.', + }, + scope: { + kind: 'enum', + values: ['workspace', 'personal'] as const, + describe: 'Restrict results to one ownership scope.', + }, + search: { + kind: 'string', + describe: 'Case-insensitive substring match against the secret name.', + }, sortBy: { kind: 'enum', values: ['name', 'createdAt', 'updatedAt'] as const, default: 'name', + describe: + '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.', + }, + sortOrder: { + kind: 'enum', + values: ['asc', 'desc'] as const, + default: 'asc', + describe: 'Sort direction.', + }, + limit: { + kind: 'integer', + default: 50, + describe: + 'Maximum secrets to return per page. Must be a whole number from 1 to 100. Defaults to 50.', + }, + cursor: { + kind: 'string', + describe: + 'Opaque cursor from the previous page. Send it back with the same sort and filters; only `limit` may change. Change anything else and pagination must restart without a cursor.', }, - sortOrder: { kind: 'enum', values: ['asc', 'desc'] as const, default: 'asc' }, - limit: { kind: 'integer', default: 50 }, - cursor: { kind: 'string' }, }, }, listSkills: { @@ -7027,16 +7903,35 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'List Skills', query: { - workspaceId: { kind: 'string', required: true }, - search: { kind: 'string' }, + workspaceId: { kind: 'string', required: true, describe: 'Workspace that owns the skill.' }, + search: { + kind: 'string', + describe: 'Case-insensitive substring match against the skill name.', + }, sortBy: { kind: 'enum', values: ['name', 'createdAt', 'updatedAt'] as const, default: 'createdAt', + describe: + '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.', + }, + sortOrder: { + kind: 'enum', + values: ['asc', 'desc'] as const, + default: 'desc', + describe: 'Sort direction.', + }, + limit: { + kind: 'integer', + default: 50, + describe: + 'Maximum skills to return per page. Must be a whole number from 1 to 100. Defaults to 50.', + }, + cursor: { + kind: 'string', + describe: + 'Opaque cursor from the previous page. Send it back with the same sort and filters; only `limit` may change. Change anything else and pagination must restart without a cursor.', }, - sortOrder: { kind: 'enum', values: ['asc', 'desc'] as const, default: 'desc' }, - limit: { kind: 'integer', default: 50 }, - cursor: { kind: 'string' }, }, }, listTableFolders: { @@ -7046,27 +7941,54 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'List Folders', query: { - workspaceId: { kind: 'string', required: true }, - parentPath: { kind: 'string' }, - search: { kind: 'string' }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace whose folders should be listed.', + }, + parentPath: { + kind: 'string', + describe: 'Restrict results to direct children of this parent path.', + }, + search: { + kind: 'string', + describe: 'Case-insensitive substring match against the folder name.', + }, sortBy: { kind: 'enum', values: ['name', 'createdAt', 'updatedAt'] as const, default: 'name', + describe: + '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.', + }, + sortOrder: { + kind: 'enum', + values: ['asc', 'desc'] as const, + default: 'asc', + describe: 'Sort direction.', }, - sortOrder: { kind: 'enum', values: ['asc', 'desc'] as const, default: 'asc' }, }, }, listTableRows: { method: 'GET', path: '/api/v2/tables/[tableId]/rows', pathParams: ['tableId'] as const, + pathParamDocs: { tableId: 'Unique table identifier.' }, responseMode: 'json', summary: 'List Rows', query: { - workspaceId: { kind: 'string', required: true }, - limit: { kind: 'integer', default: 100 }, - cursor: { kind: 'string' }, + workspaceId: { kind: 'string', required: true, describe: 'Workspace that owns the table.' }, + limit: { + kind: 'integer', + default: 100, + describe: + 'Maximum rows to return per page. Must be a whole number from 1 to 1000. Defaults to 100.', + }, + cursor: { + kind: 'string', + describe: + 'Opaque cursor from the previous page. Send it back with the same sort and filters; only `limit` may change. Change anything else and pagination must restart without a cursor.', + }, }, }, listTables: { @@ -7076,27 +7998,55 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'List Tables', query: { - workspaceId: { kind: 'string', required: true }, - folderPath: { kind: 'string' }, - search: { kind: 'string' }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace whose tables should be listed.', + }, + folderPath: { + kind: 'string', + describe: + 'Restrict results to tables in this folder. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error.', + }, + search: { + kind: 'string', + describe: 'Case-insensitive substring match against the resource name.', + }, sortBy: { kind: 'enum', values: ['name', 'createdAt', 'updatedAt'] as const, default: 'createdAt', + describe: + '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.', + }, + sortOrder: { + kind: 'enum', + values: ['asc', 'desc'] as const, + default: 'asc', + describe: 'Sort direction.', + }, + limit: { + kind: 'integer', + default: 100, + describe: + 'Maximum tables to return per page. Values outside 1–1000 are truncated and clamped into that range rather than rejected. Defaults to 100.', + }, + cursor: { + kind: 'string', + describe: + 'Opaque cursor from the previous page. Send it back with the same sort and filters; only `limit` may change. Change anything else and pagination must restart without a cursor.', }, - sortOrder: { kind: 'enum', values: ['asc', 'desc'] as const, default: 'asc' }, - limit: { kind: 'integer', default: 100 }, - cursor: { kind: 'string' }, }, }, listTableViews: { method: 'GET', path: '/api/v2/tables/[tableId]/views', pathParams: ['tableId'] as const, + pathParamDocs: { tableId: 'Unique table identifier.' }, responseMode: 'json', summary: 'List Views', query: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { kind: 'string', required: true, describe: 'Workspace that owns the table.' }, }, }, listWorkflowFolders: { @@ -7106,44 +8056,87 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'List Workflow Folders', query: { - workspaceId: { kind: 'string', required: true }, - parentPath: { kind: 'string' }, - search: { kind: 'string' }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace whose folders should be listed.', + }, + parentPath: { + kind: 'string', + describe: 'Restrict results to direct children of this parent path.', + }, + search: { + kind: 'string', + describe: 'Case-insensitive substring match against the folder name.', + }, sortBy: { kind: 'enum', values: ['name', 'createdAt', 'updatedAt'] as const, default: 'name', + describe: + '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.', + }, + sortOrder: { + kind: 'enum', + values: ['asc', 'desc'] as const, + default: 'asc', + describe: 'Sort direction.', }, - sortOrder: { kind: 'enum', values: ['asc', 'desc'] as const, default: 'asc' }, }, }, listWorkflowGroups: { method: 'GET', path: '/api/v2/tables/[tableId]/groups', pathParams: ['tableId'] as const, + pathParamDocs: { tableId: 'Unique table identifier.' }, responseMode: 'json', summary: 'List Workflow Groups', query: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { kind: 'string', required: true, describe: 'Workspace that owns the table.' }, }, }, listWorkflowRuns: { method: 'GET', path: '/api/v2/workflows/[id]/runs', pathParams: ['id'] as const, + pathParamDocs: { id: 'Unique workflow identifier.' }, responseMode: 'json', summary: 'List Workflow Runs', query: { status: { kind: 'enum', values: ['pending', 'running', 'completed', 'failed', 'cancelled', 'paused'] as const, + describe: 'Filter by run status.', + }, + trigger: { kind: 'string', describe: 'Filter by trigger type.' }, + startDate: { + kind: 'string', + describe: + '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.', + }, + endDate: { + kind: 'string', + describe: + '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: { + kind: 'integer', + default: 50, + describe: + 'Maximum workflow runs to return per page. Must be a whole number from 1 to 100. Defaults to 50.', + }, + cursor: { + kind: 'string', + describe: + 'Opaque cursor from the previous page. Send it back with the same sort and filters; only `limit` may change. Change anything else and pagination must restart without a cursor.', + }, + order: { + kind: 'enum', + values: ['asc', 'desc'] as const, + default: 'desc', + describe: + '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.', }, - trigger: { kind: 'string' }, - startDate: { kind: 'string' }, - endDate: { kind: 'string' }, - limit: { kind: 'integer', default: 50 }, - cursor: { kind: 'string' }, - order: { kind: 'enum', values: ['asc', 'desc'] as const, default: 'desc' }, }, }, listWorkflows: { @@ -7153,40 +8146,90 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'List Workflows', query: { - workspaceId: { kind: 'string', required: true }, - folderPath: { kind: 'string' }, - deployedOnly: { kind: 'boolean' }, - limit: { kind: 'integer', default: 50 }, - cursor: { kind: 'string' }, - search: { kind: 'string' }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace whose workflows should be listed.', + }, + folderPath: { + kind: 'string', + describe: + 'Restrict results to workflows in this folder path. A path that names no folder narrows the result to nothing, so the response is an empty page rather than an error.', + }, + deployedOnly: { + kind: 'boolean', + describe: 'Return only workflows with an active deployment when true.', + }, + limit: { + kind: 'integer', + default: 50, + describe: + 'Maximum workflows to return per page. Must be a whole number from 1 to 100. Defaults to 50.', + }, + cursor: { + kind: 'string', + describe: + 'Opaque cursor from the previous page. Send it back with the same sort and filters; only `limit` may change. Change anything else and pagination must restart without a cursor.', + }, + search: { + kind: 'string', + describe: 'Case-insensitive substring match against the resource name.', + }, sortBy: { kind: 'enum', values: ['position', 'name', 'createdAt', 'updatedAt', 'runCount'] as const, default: 'position', + describe: + '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.', + }, + sortOrder: { + kind: 'enum', + values: ['asc', 'desc'] as const, + default: 'asc', + describe: 'Sort direction.', }, - sortOrder: { kind: 'enum', values: ['asc', 'desc'] as const, default: 'asc' }, }, }, listWorkflowVersions: { method: 'GET', path: '/api/v2/workflows/[id]/versions', pathParams: ['id'] as const, + pathParamDocs: { id: 'Unique workflow identifier.' }, responseMode: 'json', summary: 'List Workflow Versions', query: { - limit: { kind: 'integer', default: 50 }, - cursor: { kind: 'string' }, + limit: { + kind: 'integer', + default: 50, + describe: + 'Maximum deployment versions to return per page. Must be a whole number from 1 to 100. Defaults to 50.', + }, + cursor: { + kind: 'string', + describe: + 'Opaque cursor from the previous page. Send it back with the same sort and filters; only `limit` may change. Change anything else and pagination must restart without a cursor.', + }, }, }, listWorkspaceMembers: { method: 'GET', path: '/api/v2/workspaces/[workspaceId]/members', pathParams: ['workspaceId'] as const, + pathParamDocs: { workspaceId: 'Workspace to retrieve.' }, responseMode: 'json', summary: 'List Workspace Members', query: { - limit: { kind: 'integer', default: 50 }, - cursor: { kind: 'string' }, + limit: { + kind: 'integer', + default: 50, + describe: + 'Maximum members to return per page. Must be a whole number from 1 to 100. Defaults to 50.', + }, + cursor: { + kind: 'string', + describe: + 'Opaque cursor from the previous page. Send it back with the same sort and filters; only `limit` may change. Change anything else and pagination must restart without a cursor.', + }, }, }, moveFileItems: { @@ -7196,34 +8239,50 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'Move Files', body: { - workspaceId: { kind: 'string', required: true }, - fileIds: { kind: 'array', required: true }, - targetFolderPath: { kind: 'string' }, + workspaceId: { kind: 'string', required: true, describe: 'Workspace containing the files.' }, + fileIds: { kind: 'array', required: true, describe: 'File identifiers to update.' }, + targetFolderPath: { + kind: 'string', + describe: 'Destination folder path. Omit to move files to the workspace root.', + }, }, }, queryRows: { method: 'POST', path: '/api/v2/tables/[tableId]/query', pathParams: ['tableId'] as const, + pathParamDocs: { tableId: 'Unique table identifier.' }, responseMode: 'json', summary: 'Query Rows', body: { - workspaceId: { kind: 'string', required: true }, - predicate: { kind: 'unknown' }, - sort: { kind: 'array' }, - limit: { kind: 'integer' }, - cursor: { kind: 'string' }, + workspaceId: { kind: 'string', required: true, describe: 'Unique workspace identifier.' }, + predicate: { + kind: 'unknown', + describe: + 'Recursive predicate tree. Each group node is exactly one non-empty `all` or `any` array whose members are further groups or `{ field, op, value }` conditions; the root must be a group, not a bare condition. At most 100 members per group, 10 levels of nesting, and 500 nodes in total. The negating operators include nulls: `ne`, `nin`, `ncontains`, `nlike`, and `nilike` match rows whose column is null or absent, so "not X" is not the complement of "X" over a nullable column. That holds for every column type, multi-select included. To exclude nulls, `all`-combine the negation with `isNotEmpty` (multi-select) or `isNotNull`. Comparison: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`. Membership: `in`, `nin` (array operand). Emptiness: `isEmpty`, `isNotEmpty`, `isNull`, `isNotNull` (no operand). Substring, always case-insensitive, operand matched literally: `contains`, `ncontains`, `startsWith`, `endsWith`. Pattern: `like`/`nlike` (case-sensitive), `ilike`/`nilike` (case-insensitive). **`*` is the only wildcard** and stands for any run of characters; `%`, `_`, and backslash match themselves. Use `like: "Hi*"`, not `like: "Hi%"`. A `select` column compares by option id and restricts its operators: single-select accepts `eq`, `ne`, `in`, `nin`; multi-select accepts `contains`, `ncontains`. Option names are accepted as operands and resolved to ids.', + }, + sort: { kind: 'array', describe: 'Ordered table-row sort specification.' }, + limit: { + kind: 'integer', + describe: 'Maximum rows to return; zero requests an unbounded result.', + }, + cursor: { kind: 'string', describe: 'Opaque cursor returned by the previous query page.' }, }, }, queryRowsCount: { method: 'POST', path: '/api/v2/tables/[tableId]/query/count', pathParams: ['tableId'] as const, + pathParamDocs: { tableId: 'Unique table identifier.' }, responseMode: 'json', summary: 'Count Rows', body: { - workspaceId: { kind: 'string', required: true }, - predicate: { kind: 'unknown' }, + workspaceId: { kind: 'string', required: true, describe: 'Unique workspace identifier.' }, + predicate: { + kind: 'unknown', + describe: + 'Recursive predicate tree. Each group node is exactly one non-empty `all` or `any` array whose members are further groups or `{ field, op, value }` conditions; the root must be a group, not a bare condition. At most 100 members per group, 10 levels of nesting, and 500 nodes in total. The negating operators include nulls: `ne`, `nin`, `ncontains`, `nlike`, and `nilike` match rows whose column is null or absent, so "not X" is not the complement of "X" over a nullable column. That holds for every column type, multi-select included. To exclude nulls, `all`-combine the negation with `isNotEmpty` (multi-select) or `isNotNull`. Comparison: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`. Membership: `in`, `nin` (array operand). Emptiness: `isEmpty`, `isNotEmpty`, `isNull`, `isNotNull` (no operand). Substring, always case-insensitive, operand matched literally: `contains`, `ncontains`, `startsWith`, `endsWith`. Pattern: `like`/`nlike` (case-sensitive), `ilike`/`nilike` (case-insensitive). **`*` is the only wildcard** and stands for any run of characters; `%`, `_`, and backslash match themselves. Use `like: "Hi*"`, not `like: "Hi%"`. A `select` column compares by option id and restricts its operators: single-select accepts `eq`, `ne`, `in`, `nin`; multi-select accepts `contains`, `ncontains`. Option names are accepted as operands and resolved to ids.', + }, }, }, relocateFileFolder: { @@ -7233,9 +8292,13 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'Rename or Move Folder', body: { - workspaceId: { kind: 'string', required: true }, - path: { kind: 'string', required: true }, - destinationPath: { kind: 'string', required: true }, + workspaceId: { kind: 'string', required: true, describe: 'Workspace containing the folder.' }, + path: { kind: 'string', required: true, describe: 'Current folder path.' }, + destinationPath: { + kind: 'string', + required: true, + describe: 'New full path for the folder and its descendants.', + }, }, }, relocateKnowledgeFolder: { @@ -7245,9 +8308,13 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'Rename or Move Folder', body: { - workspaceId: { kind: 'string', required: true }, - path: { kind: 'string', required: true }, - destinationPath: { kind: 'string', required: true }, + workspaceId: { kind: 'string', required: true, describe: 'Workspace containing the folder.' }, + path: { kind: 'string', required: true, describe: 'Current folder path.' }, + destinationPath: { + kind: 'string', + required: true, + describe: 'New full path for the folder and its descendants.', + }, }, }, relocateTableFolder: { @@ -7257,9 +8324,13 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'Rename or Move Folder', body: { - workspaceId: { kind: 'string', required: true }, - path: { kind: 'string', required: true }, - destinationPath: { kind: 'string', required: true }, + workspaceId: { kind: 'string', required: true, describe: 'Workspace containing the folder.' }, + path: { kind: 'string', required: true, describe: 'Current folder path.' }, + destinationPath: { + kind: 'string', + required: true, + describe: 'New full path for the folder and its descendants.', + }, }, }, relocateWorkflowFolder: { @@ -7269,77 +8340,115 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'Rename or Move Workflow Folder', body: { - workspaceId: { kind: 'string', required: true }, - path: { kind: 'string', required: true }, - destinationPath: { kind: 'string', required: true }, + workspaceId: { kind: 'string', required: true, describe: 'Workspace containing the folder.' }, + path: { kind: 'string', required: true, describe: 'Current folder path.' }, + destinationPath: { + kind: 'string', + required: true, + describe: 'New full path for the folder and its descendants.', + }, }, }, renameFile: { method: 'PATCH', path: '/api/v2/files/[fileId]', pathParams: ['fileId'] as const, + pathParamDocs: { fileId: 'File identifier.' }, responseMode: 'json', summary: 'Rename File', body: { - workspaceId: { kind: 'string', required: true }, - name: { kind: 'string', required: true }, + workspaceId: { kind: 'string', required: true, describe: 'Workspace that owns the file.' }, + name: { kind: 'string', required: true, describe: 'New file name, including its extension.' }, }, }, restoreFile: { method: 'POST', path: '/api/v2/files/[fileId]/restore', pathParams: ['fileId'] as const, + pathParamDocs: { fileId: 'File identifier.' }, responseMode: 'json', summary: 'Restore File', body: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace that owns the archived file.', + }, }, }, resumeWorkflow: { method: 'POST', path: '/api/v2/workflows/[id]/runs/[runId]/resume', pathParams: ['id', 'runId'] as const, + pathParamDocs: { id: 'Unique workflow identifier.', runId: 'Unique workflow run identifier.' }, responseMode: 'json', summary: 'Resume Workflow Run', body: { - contextId: { kind: 'string', required: true }, - input: { kind: 'unknown' }, + contextId: { + kind: 'string', + required: true, + describe: 'Human-in-the-loop pause-context identifier.', + }, + input: { kind: 'unknown', describe: 'Input supplied to the paused workflow block.' }, }, }, rollbackWorkflow: { method: 'POST', path: '/api/v2/workflows/[id]/rollback', pathParams: ['id'] as const, + pathParamDocs: { id: 'Unique workflow identifier.' }, responseMode: 'json', summary: 'Rollback Workflow', body: { - version: { kind: 'integer' }, + version: { + kind: 'integer', + describe: 'Deployment version to reactivate. Omit to select the previous active version.', + }, }, }, runRowEnrichment: { method: 'POST', path: '/api/v2/tables/[tableId]/rows/[rowId]/enrichment/[groupId]', pathParams: ['tableId', 'rowId', 'groupId'] as const, + pathParamDocs: { + tableId: 'Unique table identifier.', + rowId: 'Unique table row identifier.', + groupId: 'Workflow or enrichment group to run.', + }, responseMode: 'json', summary: 'Run Enrichment For One Row', body: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { kind: 'string', required: true, describe: 'Unique workspace identifier.' }, }, }, runTableColumn: { method: 'POST', path: '/api/v2/tables/[tableId]/columns/run', pathParams: ['tableId'] as const, + pathParamDocs: { tableId: 'Unique table identifier.' }, responseMode: 'json', summary: 'Run Column Groups', body: { - workspaceId: { kind: 'string', required: true }, - groupIds: { kind: 'array', required: true }, - runMode: { kind: 'enum', values: ['all', 'incomplete'] as const, default: 'all' }, - rowIds: { kind: 'array' }, - filter: { kind: 'unknown' }, - excludeRowIds: { kind: 'array' }, - limit: { kind: 'object' }, + workspaceId: { kind: 'string', required: true, describe: 'Unique workspace identifier.' }, + groupIds: { + kind: 'array', + required: true, + describe: 'Workflow or enrichment groups to run.', + }, + runMode: { + kind: 'enum', + values: ['all', 'incomplete'] as const, + default: 'all', + describe: 'Whether to run all or only incomplete cells.', + }, + rowIds: { kind: 'array', describe: 'Explicit row subset to run.' }, + filter: { + kind: 'unknown', + describe: + 'Recursive predicate tree. Each group node is exactly one non-empty `all` or `any` array whose members are further groups or `{ field, op, value }` conditions; the root must be a group, not a bare condition. At most 100 members per group, 10 levels of nesting, and 500 nodes in total. The negating operators include nulls: `ne`, `nin`, `ncontains`, `nlike`, and `nilike` match rows whose column is null or absent, so "not X" is not the complement of "X" over a nullable column. That holds for every column type, multi-select included. To exclude nulls, `all`-combine the negation with `isNotEmpty` (multi-select) or `isNotNull`. Comparison: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`. Membership: `in`, `nin` (array operand). Emptiness: `isEmpty`, `isNotEmpty`, `isNull`, `isNotNull` (no operand). Substring, always case-insensitive, operand matched literally: `contains`, `ncontains`, `startsWith`, `endsWith`. Pattern: `like`/`nlike` (case-sensitive), `ilike`/`nilike` (case-insensitive). **`*` is the only wildcard** and stands for any run of characters; `%`, `_`, and backslash match themselves. Use `like: "Hi*"`, not `like: "Hi%"`. A `select` column compares by option id and restricts its operators: single-select accepts `eq`, `ne`, `in`, `nin`; multi-select accepts `contains`, `ncontains`. Option names are accepted as operands and resolved to ids.', + }, + excludeRowIds: { kind: 'array', describe: 'Rows excluded from a select-all run scope.' }, + limit: { kind: 'object', describe: 'Optional cap on eligible rows to run.' }, }, }, searchKnowledge: { @@ -7349,47 +8458,105 @@ export const V2_OPERATIONS = { responseMode: 'json', summary: 'Search Knowledge', body: { - workspaceId: { kind: 'string', required: true }, - knowledgeBaseIds: { kind: 'unknown', required: true }, - query: { kind: 'string' }, - topK: { kind: 'number', default: 10 }, - tagFilters: { kind: 'array' }, - searchMode: { kind: 'enum', default: 'vector' }, - rerankerEnabled: { kind: 'boolean' }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace that owns the knowledge bases.', + }, + knowledgeBaseIds: { + kind: 'unknown', + required: true, + describe: 'One knowledge base identifier or an array of up to 20 identifiers.', + }, + query: { + kind: 'string', + describe: + "Natural-language query; required when tag filters are omitted. At most 32768 characters — longer text exceeds the embedding model's per-input token ceiling and would be truncated before the billed search ran.", + }, + topK: { + kind: 'number', + default: 10, + describe: + '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.', + }, + tagFilters: { + kind: 'array', + describe: + 'Structured tag filters, at most 10 of them. Every filter must hold, including two that name the same tag: repeating one tag narrows the result rather than widening it, matching `GET /api/v2/knowledge/{id}/documents`. To match either of two values for one tag, issue a search per value. Each filtered tag must resolve to the same slot and field type in every knowledge base selected; one missing from any of them, or defined inconsistently across them, is rejected rather than ignored, and those knowledge bases must be searched separately. List the available names with `GET /api/v2/knowledge/{id}/tags`.', + }, + searchMode: { + kind: 'enum', + default: 'vector', + describe: + 'Retrieval strategy: vector is semantic-only, while hybrid also runs full-text search.', + }, + rerankerEnabled: { + kind: 'boolean', + describe: + '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.', + }, rerankerModel: { kind: 'enum', values: ['rerank-v4.0-pro', 'rerank-v4.0-fast', 'rerank-v3.5'] as const, default: 'rerank-v4.0-fast', + describe: + 'Reranking model to use when `rerankerEnabled` is true. Defaults to `rerank-v4.0-fast`.', + }, + rerankerInputCount: { + kind: 'integer', + describe: + '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.', }, - rerankerInputCount: { kind: 'integer' }, }, }, setSecret: { method: 'PUT', path: '/api/v2/secrets/[name]', pathParams: ['name'] as const, + pathParamDocs: { name: 'Secret to create, replace, or delete.' }, responseMode: 'json', summary: 'Set Secret', body: { - workspaceId: { kind: 'string', required: true }, - scope: { kind: 'enum', required: true, values: ['workspace', 'personal'] as const }, - value: { kind: 'string', required: true }, + workspaceId: { + kind: 'string', + required: true, + describe: + 'Workspace the request is authorized against. A workspace secret is written to it; a personal secret is written to the caller and is available in all of their workspaces.', + }, + scope: { + kind: 'enum', + required: true, + values: ['workspace', 'personal'] as const, + describe: + '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.', + }, + value: { + kind: 'string', + required: true, + describe: 'Write-only secret value. It is never returned.', + }, }, }, tableExportDownload: { method: 'GET', path: '/api/v2/tables/exports/[exportId]/download', pathParams: ['exportId'] as const, + pathParamDocs: { exportId: 'Unique table-export identifier.' }, responseMode: 'json', summary: 'Download Table Export', query: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace that owns the transfer resource.', + }, }, }, undeployWorkflow: { method: 'DELETE', path: '/api/v2/workflows/[id]/deploy', pathParams: ['id'] as const, + pathParamDocs: { id: 'Unique workflow identifier.' }, responseMode: 'json', summary: 'Undeploy Workflow', }, @@ -7397,235 +8564,402 @@ export const V2_OPERATIONS = { method: 'PATCH', path: '/api/v2/custom-tools/[id]', pathParams: ['id'] as const, + pathParamDocs: { id: 'Unique custom tool identifier.' }, responseMode: 'json', summary: 'Update Custom Tool', body: { - workspaceId: { kind: 'string', required: true }, - title: { kind: 'string' }, - schema: { kind: 'object' }, - code: { kind: 'string' }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace that owns the custom tool.', + }, + title: { kind: 'string', describe: 'New display title for the tool.' }, + schema: { kind: 'object', describe: 'Replacement function declaration.' }, + code: { kind: 'string', describe: 'Replacement tool implementation.' }, }, }, updateFileContent: { method: 'PUT', path: '/api/v2/files/[fileId]/content', pathParams: ['fileId'] as const, + pathParamDocs: { fileId: 'File identifier.' }, responseMode: 'json', summary: 'Replace File Content', body: { - workspaceId: { kind: 'string', required: true }, - content: { kind: 'string', required: true }, - encoding: { kind: 'enum', values: ['utf-8', 'base64'] as const, default: 'utf-8' }, + workspaceId: { kind: 'string', required: true, describe: 'Workspace that owns the file.' }, + content: { + kind: 'string', + required: true, + describe: + '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: { + kind: 'enum', + values: ['utf-8', 'base64'] as const, + default: 'utf-8', + describe: 'Encoding of the content field.', + }, }, }, updateKnowledgeBase: { method: 'PATCH', path: '/api/v2/knowledge/[id]', pathParams: ['id'] as const, + pathParamDocs: { id: 'Unique knowledge base identifier.' }, responseMode: 'json', summary: 'Update Knowledge Base', body: { - workspaceId: { kind: 'string', required: true }, - name: { kind: 'string' }, - description: { kind: 'string' }, - chunkingConfig: { kind: 'object' }, - folderPath: { kind: 'string' }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace that owns the knowledge base.', + }, + name: { kind: 'string', describe: 'New knowledge base name.' }, + description: { kind: 'string', describe: 'New knowledge base description.' }, + chunkingConfig: { kind: 'object', describe: 'New document chunking configuration.' }, + folderPath: { kind: 'string', describe: 'New containing-folder path.' }, }, }, updateKnowledgeDocument: { method: 'PATCH', path: '/api/v2/knowledge/[id]/documents/[documentId]', pathParams: ['id', 'documentId'] as const, + pathParamDocs: { + id: 'Unique knowledge base identifier.', + documentId: 'Unique knowledge document identifier.', + }, responseMode: 'json', summary: 'Update Document', body: { - workspaceId: { kind: 'string', required: true }, - filename: { kind: 'string' }, - enabled: { kind: 'boolean' }, - tag1: { kind: 'string' }, - tag2: { kind: 'string' }, - tag3: { kind: 'string' }, - tag4: { kind: 'string' }, - tag5: { kind: 'string' }, - tag6: { kind: 'string' }, - tag7: { kind: 'string' }, - number1: { kind: 'number' }, - number2: { kind: 'number' }, - number3: { kind: 'number' }, - number4: { kind: 'number' }, - number5: { kind: 'number' }, - date1: { kind: 'string' }, - date2: { kind: 'string' }, - boolean1: { kind: 'boolean' }, - boolean2: { kind: 'boolean' }, - boolean3: { kind: 'boolean' }, - retryProcessing: { kind: 'boolean' }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace that owns the knowledge base.', + }, + filename: { kind: 'string', describe: 'New filename for the document.' }, + enabled: { + kind: 'boolean', + describe: 'Whether the document participates in search. Disabling keeps it indexed.', + }, + tag1: { kind: 'string', describe: 'New value for tag slot 1.' }, + tag2: { kind: 'string', describe: 'New value for tag slot 2.' }, + tag3: { kind: 'string', describe: 'New value for tag slot 3.' }, + tag4: { kind: 'string', describe: 'New value for tag slot 4.' }, + tag5: { kind: 'string', describe: 'New value for tag slot 5.' }, + tag6: { kind: 'string', describe: 'New value for tag slot 6.' }, + tag7: { kind: 'string', describe: 'New value for tag slot 7.' }, + number1: { kind: 'number', describe: 'New value for number tag slot 1.' }, + number2: { kind: 'number', describe: 'New value for number tag slot 2.' }, + number3: { kind: 'number', describe: 'New value for number tag slot 3.' }, + number4: { kind: 'number', describe: 'New value for number tag slot 4.' }, + number5: { kind: 'number', describe: 'New value for number tag slot 5.' }, + date1: { kind: 'string', describe: 'New value for date tag slot 1, formatted YYYY-MM-DD.' }, + date2: { kind: 'string', describe: 'New value for date tag slot 2, formatted YYYY-MM-DD.' }, + boolean1: { kind: 'boolean', describe: 'New value for boolean tag slot 1.' }, + boolean2: { kind: 'boolean', describe: 'New value for boolean tag slot 2.' }, + boolean3: { kind: 'boolean', describe: 'New value for boolean tag slot 3.' }, + retryProcessing: { + kind: 'boolean', + describe: + '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.', + }, }, }, updateMcpServer: { method: 'PATCH', path: '/api/v2/mcp-servers/[id]', pathParams: ['id'] as const, + pathParamDocs: { id: 'Unique MCP server identifier.' }, responseMode: 'json', summary: 'Update MCP Server', body: { - workspaceId: { kind: 'string', required: true }, - name: { kind: 'string' }, - description: { kind: 'string' }, - transport: { kind: 'enum', values: ['streamable-http'] as const, default: 'streamable-http' }, - url: { kind: 'string' }, - authType: { kind: 'enum', values: ['none', 'headers', 'oauth'] as const }, - headers: { kind: 'object' }, - timeout: { kind: 'integer', default: 30000 }, - retries: { kind: 'integer', default: 3 }, - enabled: { kind: 'boolean', default: true }, - oauthClientId: { kind: 'string' }, - oauthClientSecret: { kind: 'string' }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace that owns the MCP server.', + }, + name: { kind: 'string', describe: 'Server display name.' }, + description: { kind: 'string', describe: 'Optional server description.' }, + transport: { + kind: 'enum', + values: ['streamable-http'] as const, + default: 'streamable-http', + describe: + 'Transport used to communicate with the server. Applied server-side as `streamable-http` when omitted on create.', + }, + url: { + kind: 'string', + describe: + 'Immutable server URL. When provided, it must equal the current URL; use delete and create to change endpoints.', + }, + authType: { + kind: 'enum', + values: ['none', 'headers', 'oauth'] as const, + describe: + '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.', + }, + headers: { + kind: 'object', + describe: + '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.', + }, + timeout: { + kind: 'integer', + default: 30000, + describe: + 'Per-request timeout in milliseconds. Applied server-side as 30000 when omitted on create.', + }, + retries: { + kind: 'integer', + default: 3, + describe: 'Number of retries per request. Applied server-side as 3 when omitted on create.', + }, + enabled: { + kind: 'boolean', + default: true, + describe: + 'Whether the server tools are available to workflows. Applied server-side as true when omitted on create.', + }, + oauthClientId: { + kind: 'string', + describe: + 'Pre-registered OAuth client identifier. Changing it on update revokes the stored OAuth grant and forces reauthorization.', + }, + oauthClientSecret: { + kind: 'string', + describe: + '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.', + }, }, }, updateRowsByFilter: { method: 'PATCH', path: '/api/v2/tables/[tableId]/rows', pathParams: ['tableId'] as const, + pathParamDocs: { tableId: 'Unique table identifier.' }, responseMode: 'json', summary: 'Update Rows by Filter', body: { - workspaceId: { kind: 'string', required: true }, - filter: { kind: 'unknown', required: true }, - data: { kind: 'object', required: true }, - limit: { kind: 'integer' }, + workspaceId: { kind: 'string', required: true, describe: 'Unique workspace identifier.' }, + filter: { + kind: 'unknown', + required: true, + describe: + 'Recursive predicate tree. Each group node is exactly one non-empty `all` or `any` array whose members are further groups or `{ field, op, value }` conditions; the root must be a group, not a bare condition. At most 100 members per group, 10 levels of nesting, and 500 nodes in total. The negating operators include nulls: `ne`, `nin`, `ncontains`, `nlike`, and `nilike` match rows whose column is null or absent, so "not X" is not the complement of "X" over a nullable column. That holds for every column type, multi-select included. To exclude nulls, `all`-combine the negation with `isNotEmpty` (multi-select) or `isNotNull`. Comparison: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`. Membership: `in`, `nin` (array operand). Emptiness: `isEmpty`, `isNotEmpty`, `isNull`, `isNotNull` (no operand). Substring, always case-insensitive, operand matched literally: `contains`, `ncontains`, `startsWith`, `endsWith`. Pattern: `like`/`nlike` (case-sensitive), `ilike`/`nilike` (case-insensitive). **`*` is the only wildcard** and stands for any run of characters; `%`, `_`, and backslash match themselves. Use `like: "Hi*"`, not `like: "Hi%"`. A `select` column compares by option id and restricts its operators: single-select accepts `eq`, `ne`, `in`, `nin`; multi-select accepts `contains`, `ncontains`. Option names are accepted as operands and resolved to ids.', + }, + data: { + kind: 'object', + required: true, + describe: 'Row-data patch applied to every matching row.', + }, + limit: { kind: 'integer', describe: 'Maximum matching rows to update.' }, }, }, updateSkill: { method: 'PATCH', path: '/api/v2/skills/[id]', pathParams: ['id'] as const, + pathParamDocs: { + id: 'Unique skill identifier. A built-in skill is `builtin-` followed by its name, for example `builtin-research`.', + }, responseMode: 'json', summary: 'Update Skill', body: { - workspaceId: { kind: 'string', required: true }, - name: { kind: 'string' }, - description: { kind: 'string' }, - content: { kind: 'string' }, + workspaceId: { kind: 'string', required: true, describe: 'Workspace that owns the skill.' }, + name: { kind: 'string', describe: 'New kebab-case skill name.' }, + description: { kind: 'string', describe: 'New one-line summary of when the skill applies.' }, + content: { kind: 'string', describe: 'Replacement skill body.' }, }, }, updateTable: { method: 'PATCH', path: '/api/v2/tables/[tableId]', pathParams: ['tableId'] as const, + pathParamDocs: { tableId: 'Unique table identifier.' }, responseMode: 'json', summary: 'Update Table', body: { - workspaceId: { kind: 'string', required: true }, - name: { kind: 'string' }, - description: { kind: 'string' }, - folderPath: { kind: 'string' }, + workspaceId: { kind: 'string', required: true, describe: 'Unique workspace identifier.' }, + name: { kind: 'string', describe: 'Replacement table name.' }, + description: { + kind: 'string', + describe: 'Replacement table description, or null to clear it.', + }, + folderPath: { + kind: 'string', + describe: + 'Folder path. A missing leading slash is normalized before validation. Segments are percent-encoded, so a folder shown as "New folder" is `/New%20folder`: everything outside `A-Z a-z 0-9 - _ . ~` is escaped as uppercase hex, and only that exact encoding is accepted. A trailing slash, an empty segment, and a literal `.` or `..` segment are rejected. At most 64 segments and 4096 encoded bytes.', + }, }, }, updateTableColumn: { method: 'PATCH', path: '/api/v2/tables/[tableId]/columns', pathParams: ['tableId'] as const, + pathParamDocs: { tableId: 'Unique table identifier.' }, responseMode: 'json', summary: 'Update Column', body: { - workspaceId: { kind: 'string', required: true }, - columnName: { kind: 'string', required: true }, - updates: { kind: 'object', required: true }, + workspaceId: { kind: 'string', required: true, describe: 'Workspace that owns the table.' }, + columnName: { + kind: 'string', + required: true, + describe: 'Current name of the column to update.', + }, + updates: { kind: 'object', required: true, describe: 'Mutable column fields.' }, }, }, updateTableRow: { method: 'PATCH', path: '/api/v2/tables/[tableId]/rows/[rowId]', pathParams: ['tableId', 'rowId'] as const, + pathParamDocs: { tableId: 'Unique table identifier.', rowId: 'Unique table row identifier.' }, responseMode: 'json', summary: 'Update Row', body: { - workspaceId: { kind: 'string', required: true }, - data: { kind: 'object', required: true }, + workspaceId: { kind: 'string', required: true, describe: 'Unique workspace identifier.' }, + data: { + kind: 'object', + required: true, + describe: 'Partial row-data patch keyed by column name.', + }, }, }, updateTableView: { method: 'PATCH', path: '/api/v2/tables/[tableId]/views/[viewId]', pathParams: ['tableId', 'viewId'] as const, + pathParamDocs: { tableId: 'Unique table identifier.', viewId: 'Unique saved-view identifier.' }, responseMode: 'json', summary: 'Update View', body: { - workspaceId: { kind: 'string', required: true }, - name: { kind: 'string' }, - config: { kind: 'object' }, - configPatch: { kind: 'object' }, - isDefault: { kind: 'boolean' }, + workspaceId: { kind: 'string', required: true, describe: 'Workspace that owns the table.' }, + name: { kind: 'string', describe: 'Replacement saved-view display name.' }, + config: { kind: 'object', describe: 'Complete replacement saved-view configuration.' }, + configPatch: { + kind: 'object', + describe: 'Saved-view configuration fields to shallow-merge.', + }, + isDefault: { + kind: 'boolean', + describe: 'Whether to promote this view to the table default.', + }, }, }, updateWorkflow: { method: 'PATCH', path: '/api/v2/workflows/[id]', pathParams: ['id'] as const, + pathParamDocs: { id: 'Unique workflow identifier.' }, responseMode: 'json', summary: 'Update Workflow', body: { - name: { kind: 'string' }, - description: { kind: 'string' }, - folderPath: { kind: 'string' }, + name: { kind: 'string', describe: 'Replacement workflow name.' }, + description: { + kind: 'string', + describe: 'Replacement workflow description; null clears it.', + }, + folderPath: { + kind: 'string', + describe: 'Destination folder path; `/` moves the workflow to the workspace root.', + }, }, }, updateWorkflowGroup: { method: 'PATCH', path: '/api/v2/tables/[tableId]/groups', pathParams: ['tableId'] as const, + pathParamDocs: { tableId: 'Unique table identifier.' }, responseMode: 'json', summary: 'Update Workflow Group', body: { - workspaceId: { kind: 'string', required: true }, - groupId: { kind: 'string', required: true }, - workflowId: { kind: 'string' }, - name: { kind: 'string' }, - dependencies: { kind: 'object' }, - outputs: { kind: 'array' }, - newOutputColumns: { kind: 'array' }, - mappingUpdates: { kind: 'array' }, - inputMappings: { kind: 'array' }, - deploymentMode: { kind: 'enum', values: ['live', 'deployed'] as const }, - type: { kind: 'enum', values: ['manual', 'enrichment'] as const }, - autoRun: { kind: 'boolean' }, + workspaceId: { kind: 'string', required: true, describe: 'Unique workspace identifier.' }, + groupId: { kind: 'string', required: true, describe: 'Workflow group to update.' }, + workflowId: { kind: 'string', describe: 'Replacement backing workflow identifier.' }, + name: { kind: 'string', describe: 'Replacement workflow-group display name.' }, + dependencies: { kind: 'object', describe: 'Replacement input dependencies.' }, + outputs: { kind: 'array', describe: 'Replacement producer outputs.' }, + newOutputColumns: { kind: 'array', describe: 'Columns to add for new outputs.' }, + mappingUpdates: { kind: 'array', describe: 'Existing output-column mapping changes.' }, + inputMappings: { kind: 'array', describe: 'Replacement workflow input mappings.' }, + deploymentMode: { + kind: 'enum', + values: ['live', 'deployed'] as const, + describe: 'Replacement workflow execution mode.', + }, + type: { + kind: 'enum', + values: ['manual', 'enrichment'] as const, + describe: + "Workflow-group producer type. Must match the group's stored type — a group's producer cannot be changed after creation.", + }, + autoRun: { kind: 'boolean', describe: 'Replacement automatic-run setting.' }, }, }, uploadKnowledgeDocument: { method: 'POST', path: '/api/v2/knowledge/[id]/documents', pathParams: ['id'] as const, + pathParamDocs: { id: 'Unique knowledge base identifier.' }, responseMode: 'json', summary: 'Upload Document', query: { - workspaceId: { kind: 'string', required: true }, + workspaceId: { + kind: 'string', + required: true, + describe: 'Workspace that owns the knowledge base.', + }, }, }, upsertFileShare: { method: 'PATCH', path: '/api/v2/files/[fileId]/share', pathParams: ['fileId'] as const, + pathParamDocs: { fileId: 'File identifier.' }, responseMode: 'json', summary: 'Enable or Disable File Share', body: { - workspaceId: { kind: 'string', required: true }, - isActive: { kind: 'boolean', required: true }, - authType: { kind: 'enum', values: ['public', 'password', 'email', 'sso'] as const }, - password: { kind: 'string' }, - allowedEmails: { kind: 'array' }, + workspaceId: { kind: 'string', required: true, describe: 'Workspace that owns the file.' }, + isActive: { + kind: 'boolean', + required: true, + describe: + '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.', + }, + authType: { + kind: 'enum', + values: ['public', 'password', 'email', 'sso'] as const, + describe: + '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.', + }, + password: { + kind: 'string', + describe: + 'Password for a password-gated share. Kept when omitted; enabling `password` with neither a supplied nor a stored password is a 400.', + }, + allowedEmails: { + kind: 'array', + describe: + '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.', + }, }, }, upsertTableRow: { method: 'POST', path: '/api/v2/tables/[tableId]/rows/upsert', pathParams: ['tableId'] as const, + pathParamDocs: { tableId: 'Unique table identifier.' }, responseMode: 'json', summary: 'Upsert Row', body: { - workspaceId: { kind: 'string', required: true }, - data: { kind: 'object', required: true }, - conflictTarget: { kind: 'string' }, + workspaceId: { kind: 'string', required: true, describe: 'Unique workspace identifier.' }, + data: { + kind: 'object', + required: true, + describe: + 'Complete set of row cells keyed by column name. On the update branch this REPLACES the matched row: any column not present here is cleared, unlike the merging `PATCH /api/v2/tables/{tableId}/rows/{rowId}`.', + }, + conflictTarget: { kind: 'string', describe: 'Unique column used to detect a conflict.' }, }, }, } as const diff --git a/packages/sim-cli/src/index.ts b/packages/sim-cli/src/index.ts index 4e26c52ed1c..04198f56d8c 100644 --- a/packages/sim-cli/src/index.ts +++ b/packages/sim-cli/src/index.ts @@ -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 ', 'Profile to use (env: SIM_PROFILE)') - .option('--endpoint ', 'Sim deployment to talk to (env: SIM_ENDPOINT)') - .option('-w, --workspace ', 'Workspace to target (env: SIM_WORKSPACE)') - .addOption( - new Option('--output ', '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)}`)) diff --git a/packages/sim-cli/src/program.ts b/packages/sim-cli/src/program.ts new file mode 100644 index 00000000000..c4de9012f2b --- /dev/null +++ b/packages/sim-cli/src/program.ts @@ -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 ', 'Profile to use (env: SIM_PROFILE)') + .option('--endpoint ', 'Sim deployment to talk to (env: SIM_ENDPOINT)') + .option('-w, --workspace ', 'Workspace to target (env: SIM_WORKSPACE)') + .addOption( + new Option('--output ', '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 +} diff --git a/packages/sim-cli/src/runtime/build.ts b/packages/sim-cli/src/runtime/build.ts index 5753fb09cd7..0ffb597f79c 100644 --- a/packages/sim-cli/src/runtime/build.ts +++ b/packages/sim-cli/src/runtime/build.ts @@ -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> = { @@ -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) { diff --git a/packages/sim-cli/src/runtime/options.ts b/packages/sim-cli/src/runtime/options.ts index 9c2a1576ffc..6fa18ea6ae7 100644 --- a/packages/sim-cli/src/runtime/options.ts +++ b/packages/sim-cli/src/runtime/options.ts @@ -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 ` 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} `, - `${flag.describe ?? `Set ${field}`} (required)` - ) + new Option(`${short}--${name} `, `${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 ? '' : wantsJson ? '' : '' 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() ) } diff --git a/packages/sim-cli/src/runtime/request.ts b/packages/sim-cli/src/runtime/request.ts index 848acdadb1e..f30ff7e3f35 100644 --- a/packages/sim-cli/src/runtime/request.ts +++ b/packages/sim-cli/src/runtime/request.ts @@ -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 } /** diff --git a/packages/sim-cli/src/runtime/types.ts b/packages/sim-cli/src/runtime/types.ts index ab9892aec8c..2e9ce942d3f 100644 --- a/packages/sim-cli/src/runtime/types.ts +++ b/packages/sim-cli/src/runtime/types.ts @@ -5,6 +5,8 @@ export interface OperationSpec { method: NonNullable path: string pathParams: readonly string[] + /** `.describe()` per path parameter, used as positional-argument help. */ + pathParamDocs?: Record query?: Record body?: Record opaqueBody?: boolean diff --git a/scripts/generate-cli-docs.ts b/scripts/generate-cli-docs.ts new file mode 100644 index 00000000000..6b7bfcb972d --- /dev/null +++ b/scripts/generate-cli-docs.ts @@ -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 = { + '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 ` 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, '}') +} + +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 ['', '', '', ...rows, '', ''] +} + +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 ` 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() + 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.', + '', + '', + '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.', + '', + '', + '## 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 [sub-resource] [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() + const duplicates = new Set() + 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)[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 " 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 { + 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() + 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 { + if (!fs.existsSync(OUTPUT_DIR)) return new Map() + const guides = new Set(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() diff --git a/scripts/generate-v2-cli-api.ts b/scripts/generate-v2-cli-api.ts index a5ee67f9cd8..6c00fb1f48b 100644 --- a/scripts/generate-v2-cli-api.ts +++ b/scripts/generate-v2-cli-api.ts @@ -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 ` had nothing to say about either. + */ +function pathParamDocs(schema: z.ZodType | undefined): Record { + if (!schema) return {} + + const json = z.toJSONSchema(schema, { io: 'input', unrepresentable: 'any' }) as JsonSchema + const docs: Record = {} + + 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(