description: "App-owned command lines for dsh app bins: your app parses its own flags, --help, and exit behavior from the launcher's remaining arguments."
English | 中文
dsh-cmdline lets an app parse its own flags, --help, and errors from the arguments left unchanged after launcher flags. Parsed values can override configuration defaults without rewriting configuration. The app can also request process exit through the launcher's shutdown path. Use this package for app bins with their own command-line interface. It adds no prompt, schema, or model-visible content.
Your app reads the invocation's inner arguments at startup, and any number of its plugins can use them. The common path: a startup plugin reads the arguments, parses them, and publishes the parsed values; other rows configure themselves from those values.
The launcher makes three things available to your app:
ctx.cmdlineArgs — the inner arguments of your invocation. Reading them returns an immutable snapshot and never consumes or changes them: dsh --profile tui --resume abc gives your app ['--resume', 'abc'].ctx.appExit — a way to ask the process to exit once the tree has shut down, wired to the launcher's shutdown controller.ctx.appReady — the successful-startup signal, committed only after the Loader tree and launcher-owned setup succeed.An app launched with no arguments sees an empty list — that is the honest answer, not a missing value.
exitOnStdinEnd(ctx, label) binds a successfully started stdio application's EOF to ctx.appExit(0). It never reads or resumes stdin, so a protocol transport receives bytes buffered before it mounts; startup rejection wins over a racing EOF, and the owning fiber removes both pending listeners.
You bring your own commander program: declare your flags and your actions, and the package runs it against the inner arguments. Your action is the only place validation happens, and it publishes whatever your rows need. The plugin's Loader row carries no special marker:
- id: web-startup
name: '@deepseek-ai/dsh-web-app/startup'
Rows configured from the parsed values inject the published service and read it directly in their config:
- id: webserver
name: '@deepseek-ai/dsh-host-webserver'
inject: [webStartup]
config:
host: !!js ctx.webStartup.host ?? '127.0.0.1'
port: !!js ctx.webStartup.port ?? 3080
The outcomes: dsh --profile web --port 8080 starts the server on port 8080 even when the config says 3080, because the flag wins. --help prints your app's help and exits 0 without starting anything; a rejected value (for example a non-numeric port) prints your error and exits nonzero, and no row that depends on the parsed values ever starts.
The value written beside a !!js expression is the fallback: the flag wins when present, the written value is used otherwise. Resolution happens once at startup, after your parser ran, so a flag is never silently reset by a later config reload.
Any number of plugins can read the same arguments — reading never consumes them — and each can parse what it needs and publish its own values. The launcher does not decide who owns the command line: an app with no reader ignores its arguments.
Apps built outside this repository behave the same way: their --help prints and exits instead of crashing, even though they carry their own commander copy.
Read these pages when the package-level contract is not enough. They move from the handoff mechanism to the apps that consume it.
None, as this package resolves the process command line before any session exists; configured rows own every model-visible consequence.
None; this package neither assembles nor sends a provider request.
These limits describe where app-owned command lines are a poor fit or need special care. They are current package constraints, not a task backlog.
--patch placed after an app flag belongs to the app. The launcher's parser consumes one --, so an app argument that must survive as a literal -- needs -- --.config drops its expressions — a flag beats the value written beside it, not a literal a user wrote in place of the expression; keeping the expression is what keeps the flag winning.