2026-08-06 20:52:26 +08:00
/**
2026-08-10 21:49:11 +08:00
* The web app's command-line provider: it parses the `dsh --profile web` flag
2026-08-11 15:39:03 +08:00
* family (`--host`, `--port`, `--trusted-host`) and its `--help`
2026-08-10 21:49:11 +08:00
* text, then provides the immutable values as {@link WEB_STARTUP_SERVICE}.
* Ordinary rows inject that service before reading it from lazy config.
2026-08-06 20:52:26 +08:00
* @module @deepseek-ai/dsh-web-app/startup
*/
import { Command } from 'commander'
2026-08-10 22:34:07 +08:00
import type { Context } from '@deepseek-ai/cordis'
2026-08-10 21:49:11 +08:00
import { parseCmdline } from '@deepseek-ai/dsh-cmdline'
2026-08-06 20:52:26 +08:00
/** Stable Cordis plugin name. */
export const name = 'web-startup'
/** Services required before the flags can be resolved. */
export const inject = [ 'cmdlineArgs' ]
2026-08-10 21:49:11 +08:00
/** Service provided by this ordinary plugin and injected by flag-configured rows. */
2026-08-06 20:52:26 +08:00
export const WEB_STARTUP_SERVICE = 'webStartup'
2026-08-07 11:58:03 +08:00
/** What the web rows read from {@link WEB_STARTUP_SERVICE}. */
export interface WebStartupValues {
/** `--host`, absent when the invocation did not name one. */
host? : string
/** `--port`, absent when the invocation did not name one. */
port? : number
2026-08-10 21:49:11 +08:00
/** Explicit `--trusted-host` authorities, in argument order. */
2026-08-07 11:58:03 +08:00
trustedHosts : string [ ]
2026-08-06 20:52:26 +08:00
}
/** The web flag family, as commander parsed it. */
interface WebOptions {
host? : string
port? : string
trustedHost? : string [ ]
}
/**
* This app's command: its flags, its description, and its help text.
* @returns a fresh program, so one process can parse more than once (tests).
*/
function webCommand ( ) : Command {
return new Command ( )
. name ( 'dsh --profile web' )
. description ( 'Serve the DeepSeek Harness browser UI.' )
. helpOption ( '-h, --help' , 'show this help' )
2026-08-13 14:54:15 +08:00
. option ( '--host <host>' , 'bind host' )
2026-08-06 20:52:26 +08:00
. option ( '--port <port>' , 'listen port; pass 0 to let the OS pick a free one' )
. option ( '--trusted-host <authority...>' , 'extra authority the /api browser-trust fence accepts (host or host:port; repeatable)' )
. addHelpText ( 'after' , `
Examples:
2026-08-09 18:23:25 +08:00
dsh --profile web serve on the composed host and port
dsh --profile web --port 8080 serve on another port
2026-08-06 20:52:26 +08:00
` )
}
/**
2026-08-11 18:41:21 +08:00
* Parse and provide the Web invocation as an ordinary Cordis service. The
2026-08-13 16:37:49 +08:00
* command's action publishes the flags this invocation named; `--host 0.0.0.0`
* or a non-numeric `--port` is a usage error, so on rejection (and on `--help`)
* nothing is provided.
2026-08-10 21:49:11 +08:00
* @param ctx - plugin context carrying the command line.
2026-08-06 20:52:26 +08:00
*/
2026-08-07 11:58:03 +08:00
export function apply ( ctx : Context ) : void {
2026-08-11 18:41:21 +08:00
const program = webCommand ( )
program . action ( ( ) = > {
const options = program . opts < WebOptions > ( )
2026-08-13 14:54:15 +08:00
if ( options . host === '0.0.0.0' ) {
2026-08-13 15:10:12 +08:00
program . error ( 'error: --host 0.0.0.0 is intentionally not supported yet for safety: it would expose remote code execution to the network; use 127.0.0.1 instead' )
2026-08-13 14:54:15 +08:00
}
2026-08-11 18:41:21 +08:00
if ( options . port !== undefined && ! /^\d+$/ . test ( options . port ) ) {
program . error ( ` error: --port must be a number, got ${ JSON . stringify ( options . port ) } ` )
}
ctx . provide ( WEB_STARTUP_SERVICE , {
. . . options . host !== undefined && { host : options.host } ,
. . . options . port !== undefined && { port : Number ( options . port ) } ,
trustedHosts : options.trustedHost ? ? [ ] ,
} satisfies WebStartupValues )
} )
parseCmdline ( ctx , program )
2026-08-06 20:52:26 +08:00
}