The web runtime creates its dev-mode client-hmr row in the root tree after Loader settlement with plain loader.create, deleting the vendored Entry.enableRuntime state machine and dsh-cmdline's enableRow export. Include declares the existing EntryGroup.key tree-carrier marker instead of the EntryConfigResolver protocol (its own path stays literal; nothing used a dynamic path). The launcher recognizes no app row: SIGTERM exits 0 on every surface, every boot watches its user patch layers, and the headless runner exits through ctx.appExit, deleting ctx.headlessIo. Also restores the vendor README rescope entry to the position the rescope-vendor exact-edit anchor requires, fixing the master hygiene regression.
4.0 KiB
@deepseek-ai/dsh-cmdline
English | 中文
dsh 启动器交给它所引导应用的那条命令行。启动器只解析属于自己的 flag(--profile、--patch、配置 dump),并把其后的一切原样交给配置树,因此 flag 家族、--help 文本和解析错误都由应用自己持有,启动器不必知道它们。
启动器提供的值
启动器在任何配置树条目挂载之前调用 provideCmdline(ctx, host),它提供:
ctx.cmdlineArgs:本次调用的内层参数。get()就是它的全部接口,返回一份快照:dsh --profile tui --resume abc得到['--resume', 'abc']。ctx.appExit:一个有边界的进程退出请求,接到启动器的关停控制器上。
没有命令行的嵌入宿主提供空列表;这是诚实的答案,而不是缺失的值。
普通提供方与注入配置
任何应用插件都可以注入 cmdlineArgs、解析它,再发布一个普通的应用自有服务。parseCmdline(ctx, program, plan) 只适配 commander;返回值与服务都归调用方持有:
export const name = 'web-startup'
export const inject = ['cmdlineArgs']
export function apply(ctx: Context): void {
const values = parseCmdline(ctx, webCommand(), planWebStartup)
if (values !== undefined) ctx.provide('webStartup', values)
}
它的 Loader 行不携带启动器标记,也没有特殊类型:
- id: web-startup
name: '@deepseek-ai/dsh-web-app/startup'
所有由这些取值配置的行都使用普通服务注入,并在惰性配置中直接访问该服务:
- 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
parseCmdline 解析不可变参数,再向 plan 索取应用自有取值。遇到 --help、--version、解析错误,或 plan 发出的 program.error(...) 时,它输出 commander 文本、请求退出并返回 undefined;提供方什么也不发布,因此依赖行不会激活。
注入如何排列配置求值
Loader 会把一行的 !!js 插值推迟到该行声明的注入全部激活之后,再基于该行的插件上下文求值。所以上例可以直接读取 ctx.webStartup:Loader 索取 webserver 的配置之前,Cordis 已经填入了这个注入服务。Include 树会保留嵌套表达式节点,直到各个目标行到达这一时点。提供方替换与活动 patch 重载都会针对当前注入服务重新插值,因此启动 flag 不会被悄悄重置。
共享不可变参数
get() 不会消费或修改 argv。多个插件可以解析同一份快照,并分别提供服务。启动器不会检查组合中的命令行所有者;没有读取方的 profile 只会忽略自己的应用参数。
树外插件会带来自己的一份 commander 副本,因此 commander 的控制流错误按结构识别,而不是按类身份识别;按身份判断会把已经打印出来的 help 重新抛成致命的加载失败。
模型体验
无。本包在任何会话存在之前解析进程自身的命令行。
KV Cache 影响
无;本包既不组装也不发送提供方请求。
已知限制与延期工作
- 启动器的 flag 必须写在应用参数之前:切分按位置进行,启动器不认识的第一个 token 就是内层参数的起点,因此写在某个应用 flag 之后的
--patch属于应用。启动器的解析器会消耗掉一个--,因此必须以字面量--存活到应用的参数需要写成-- --。 - 应用自有服务没有静态声明的提供方:消费行通过普通注入点名它;缺少提供方的组合包会在结算时失败,由待处理条目点名该服务,而不是在加载时失败。
- 用户 patch 若整体替换某行的
config,会连同其中的表达式一起丢掉:flag 胜过的是表达式旁写着的那个值,而不是用户用字面量替换掉表达式之后的结果;保留表达式才能保留 flag 的优先级。