dsh-web-app owns --host/--port/--dev/--workspace-root/--trusted-host and its --help in a web-startup row; the rows it configures wait for the webStartup service, and the client-plugin HMR receiver now ships disabled so --dev is a row toggle rather than a runtime insert (the Loader cannot resolve a row inserted from inside a mounting plugin). dsh-headless owns the task positional and rejects a missing task as its own usage error. Its runner ships disabled, not merely waiting: the schema requires the task, and a row's config is validated when its fiber is created, before the startup row can supply one. A composition has exactly one command-line owner, so the patch disables the web startup row and this one provides webStartup too, leaving the web rows on their composed one-shot values. The keyless web scaffold provides the same three values with no arguments, which is what an embedding host with no command line does.
71 lines
3.0 KiB
TypeScript
71 lines
3.0 KiB
TypeScript
/**
|
|
* The one-shot app's startup row: it owns the `dsh --profile headless` command
|
|
* line — the task text is this command's positional argument — and its
|
|
* `--help` text, then provides {@link HEADLESS_STARTUP_SERVICE} with the task
|
|
* the user asked for. The runner waits for it, so a missing task is a usage
|
|
* error printed by this command instead of a schema failure inside the runner.
|
|
*
|
|
* This app layers over the web app, and a composition has exactly one
|
|
* command-line owner: the bundle patch disables the web startup row, and this
|
|
* one also provides {@link WEB_STARTUP_SERVICE} so the web rows start on their
|
|
* composed (one-shot) values.
|
|
* @module @deepseek-ai/dsh-headless/startup
|
|
*/
|
|
|
|
import { Command } from 'commander'
|
|
import type { Context } from 'cordis'
|
|
import type { EntryOptions } from '@cordisjs/plugin-loader'
|
|
import { overrideConfig, runStartup, type RowChange } from '@deepseek-ai/dsh-cmdline'
|
|
import { WEB_STARTUP_SERVICE } from '@deepseek-ai/dsh-web-app/startup'
|
|
|
|
/** Stable Cordis plugin name. */
|
|
export const name = 'headless-startup'
|
|
|
|
/** Services required before the task can be resolved. */
|
|
export const inject = ['cmdlineArgs']
|
|
|
|
/** The startup service the one-shot runner row injects. */
|
|
export const HEADLESS_STARTUP_SERVICE = 'headlessStartup'
|
|
|
|
/** The runner row this app configures. */
|
|
const RUNNER_ROW_ID = 'headless-runner'
|
|
|
|
/**
|
|
* This app's command: the task positional, its description, and its help text.
|
|
* @returns a fresh program, so one process can parse more than once (tests).
|
|
*/
|
|
function headlessCommand(): Command {
|
|
return new Command()
|
|
.name('dsh --profile headless')
|
|
.description('Answer one task, print the final assistant message, and exit.')
|
|
.helpOption('-h, --help', 'show this help')
|
|
.argument('[task...]', 'the task text; multiple words are joined by spaces')
|
|
.addHelpText('after', `
|
|
Examples:
|
|
dsh --profile headless "run the tests" answer one task and exit
|
|
`)
|
|
}
|
|
|
|
/**
|
|
* Turn the parsed command line into the runner row's task.
|
|
* @param program - the parsed headless command.
|
|
* @param rows - the waiting rows' composed options, in tree order.
|
|
* @returns row id → changes.
|
|
*/
|
|
function planHeadlessStartup(program: Command, rows: readonly EntryOptions[]): Map<string, RowChange> {
|
|
const task = program.args.join(' ')
|
|
if (task === '') program.error('error: a task is required, for example: dsh --profile headless "run the tests"')
|
|
const runner = rows.find(row => row.id === RUNNER_ROW_ID)
|
|
if (runner === undefined) throw new Error(`headless-startup: the composition has no waiting "${RUNNER_ROW_ID}" row to run the task`)
|
|
return new Map([[RUNNER_ROW_ID, overrideConfig(runner, { task })]])
|
|
}
|
|
|
|
/**
|
|
* Resolve the task and start the rows waiting for it.
|
|
* @param ctx - plugin context carrying the command line and the Loader.
|
|
* @returns nothing once the runner is released, or once `--help` or a missing task requested exit.
|
|
*/
|
|
export function apply(ctx: Context): Promise<void> {
|
|
return runStartup(ctx, [HEADLESS_STARTUP_SERVICE, WEB_STARTUP_SERVICE], headlessCommand(), planHeadlessStartup)
|
|
}
|