refactor(cli)!: complete app-owned profile startup

This commit is contained in:
Turtle
2026-08-09 18:23:25 +08:00
parent 7e3a82eacc
commit d4ccfbd80f
97 changed files with 260 additions and 224 deletions

View File

@@ -1,6 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write packages/ui/cmdline/README.md
README.md: cd3350678d38802c18ff26dd47214b5019b8c404
README.zh.md: ad726cd0726cbbd22736321a8c52b04e23d557fa
# pnpm run verify-translation-pairing --write packages/boot/cmdline/README.md
README.md: 5a7e267691cd19548a19e812390865f140e3a620
README.zh.md: 6d5892e97708a6bc464dafee3dcba2521129ea93

View File

@@ -60,7 +60,7 @@ Loader defers a row's `!!js` interpolation until that row's declared injections
### One command line, one owner
A composition has exactly one command-line owner. An app that layers over another one disables the underlying startup row and names both services, so the rows it absorbed start on the values their own fallbacks name — [`dsh-headless`](../../bundle/headless/README.md) does this over [`dsh-web-app`](../../bundle/web-app/README.md).
A composition has exactly one command-line owner. An app that layers over another one disables the underlying startup row and provides every startup service its retained rows inject.
An out-of-tree plugin brings its own commander copy, so commander's control-flow errors are detected structurally rather than by class identity; an identity check would rethrow a printed help as a fatal load failure.

View File

@@ -60,7 +60,7 @@ Loader 会把一行的 `!!js` 插值推迟到该行声明的注入全部激活
### 一条命令行,一个所有者
一套组合有且只有一个命令行所有者。叠加在另一应用之上的应用会禁用下层的启动行,并同时点名两个服务,使它吸收过来的行按各自回退值启动:[`dsh-headless`](../../bundle/headless/README.md) 相对 [`dsh-web-app`](../../bundle/web-app/README.md) 就是这么做的。
一套组合有且只有一个命令行所有者。叠加在另一应用之上的应用会禁用下层的启动行,并提供保留下来的各行所注入的全部启动服务。
树外插件会带来自己的一份 commander 副本,因此 commander 的控制流错误按结构识别,而不是按类身份识别;按身份判断会把已经打印出来的 help 重新抛成致命的加载失败。

View File

@@ -1,6 +1,6 @@
{
"name": "@deepseek-ai/dsh-cmdline",
"description": "Command-line seam between a dsh launcher and app bundles: cmdlineArgs exposes inner arguments, while injected startup rows parse them into app-owned runtime services",
"description": "Command-line handoff between a dsh launcher and app bundles: cmdlineArgs exposes inner arguments, while injected startup rows parse them into app-owned runtime services",
"version": "0.0.1",
"private": true,
"type": "module",

View File

@@ -140,10 +140,9 @@ export type StartupPlan<T = unknown> = (program: Command, rows: readonly EntryOp
* is written, the service is never provided, dependent rows stay pending, and
* `ctx.appExit` is requested.
*
* An app that layers over another one (the one-shot bundle rides over the web
* bundle) disables the underlying startup row and names both services, because
* a composition has exactly one command-line owner: the rows of the app it
* absorbed then start on the values their own fallbacks name.
* A custom app that layers over another one disables the underlying startup
* row and names every startup service its retained rows inject, because a
* composition has exactly one command-line owner.
* @param ctx - plugin context carrying `cmdlineArgs`, `appExit`, and the Loader.
* @param services - the service name, or names, this startup row provides.
* @param program - the app's commander program, with its flags and description already declared.
@@ -186,8 +185,8 @@ export function runStartup<T>(
} catch (error) {
// exitOverride turns help, version, a parse error, and a plan's own
// program.error() into a CommanderError; commander has already written the
// text through the output configured above. The app's rows ship disabled,
// so leaving them alone is what keeps the app unstarted.
// text through the output configured above. With no startup service,
// dependent rows remain pending and the app stays unstarted.
if (!isCommanderError(error)) throw error
exit(error.exitCode)
return undefined