@deepseek-ai/dsh-cmdline 0.0.1-rc.2 → 0.0.1-rc.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write packages/boot/cmdline/README.md
5
- README.md: 2e8e58b23785fa78bd2663a459817669309a81be
6
- README.zh.md: c04d76905edb4afa6b18b36b8284b14990be6bdd
5
+ README.md: 33125014539e801dbd2952a3b4513cafc80bdcee
6
+ README.zh.md: 7ef49a1027d3c17817c9171e1166ed6feecd8559
package/README.md CHANGED
@@ -15,15 +15,16 @@ An embedding host with no command line provides an empty list; that is the hones
15
15
 
16
16
  ## Ordinary providers and injected config
17
17
 
18
- Any app plugin may inject `cmdlineArgs`, parse it, and publish an ordinary app-owned service. `parseCmdline(ctx, program, plan)` is only a commander adapter; the caller owns the returned value and service:
18
+ Any app plugin may inject `cmdlineArgs`, parse it, and publish an ordinary app-owned service. `parseCmdline(ctx, program)` is only a commander adapter; the program's own action owns validation and the published service:
19
19
 
20
20
  ```ts ignore
21
21
  export const name = 'web-startup'
22
22
  export const inject = ['cmdlineArgs']
23
23
 
24
24
  export function apply(ctx: Context): void {
25
- const values = parseCmdline(ctx, webCommand(), planWebStartup)
26
- if (values !== undefined) ctx.provide('webStartup', values)
25
+ const program = webCommand()
26
+ program.action(() => ctx.provide('webStartup', webValuesFrom(program)))
27
+ parseCmdline(ctx, program)
27
28
  }
28
29
  ```
29
30
 
@@ -45,7 +46,7 @@ Every row configured from those values uses ordinary service injection and direc
45
46
  port: !!js ctx.webStartup.port ?? 3080
46
47
  ```
47
48
 
48
- `parseCmdline` parses the immutable arguments and asks `plan` for the app-owned value. On `--help`, `--version`, a parse error, or a `program.error(...)` from the plan, it writes commander's text, requests exit, and returns `undefined`; the provider publishes nothing, so dependent rows never activate.
49
+ `parseCmdline` refuses at load a program in which no command declares an action, routes every command's exit and output through the launcher (commander copies those settings into subcommands only at registration), and parses the immutable arguments; commander runs the invoked command's synchronous action on success. An action rejects an invalid invocation with `program.error(...)` — before publishing, since statements ahead of the rejection have already run. On `--help`, `--version`, a parse error, or that rejection, the helper writes commander's text and requests exit; the provider publishes nothing, so dependent rows never activate.
49
50
 
50
51
  ### How injection orders config
51
52
 
package/README.zh.md CHANGED
@@ -15,15 +15,16 @@ dsh 启动器交给它所引导应用的那条命令行。启动器只解析属
15
15
 
16
16
  ## 普通提供方与注入配置
17
17
 
18
- 任何应用插件都可以注入 `cmdlineArgs`、解析它,再发布一个普通的应用自有服务。`parseCmdline(ctx, program, plan)` 只适配 commander;返回值与服务都归调用方持有:
18
+ 任何应用插件都可以注入 `cmdlineArgs`、解析它,再发布一个普通的应用自有服务。`parseCmdline(ctx, program)` 只适配 commander;校验与发布的服务都归 program 自己的 action 持有:
19
19
 
20
20
  ```ts ignore
21
21
  export const name = 'web-startup'
22
22
  export const inject = ['cmdlineArgs']
23
23
 
24
24
  export function apply(ctx: Context): void {
25
- const values = parseCmdline(ctx, webCommand(), planWebStartup)
26
- if (values !== undefined) ctx.provide('webStartup', values)
25
+ const program = webCommand()
26
+ program.action(() => ctx.provide('webStartup', webValuesFrom(program)))
27
+ parseCmdline(ctx, program)
27
28
  }
28
29
  ```
29
30
 
@@ -45,7 +46,7 @@ export function apply(ctx: Context): void {
45
46
  port: !!js ctx.webStartup.port ?? 3080
46
47
  ```
47
48
 
48
- `parseCmdline` 解析不可变参数,再向 `plan` 索取应用自有取值。遇到 `--help`、`--version`、解析错误,或 `plan` 发出的 `program.error(...)` 时,它输出 commander 文本、请求退出并返回 `undefined`;提供方什么也不发布,因此依赖行不会激活。
49
+ `parseCmdline` 在加载时拒绝整棵命令树中没有任何命令声明 action 的 program,把每个命令的退出与输出都接到启动器上(commander 只在注册时把这些设置复制进子命令),再解析不可变参数;解析成功时 commander 运行被调用命令的同步 action。action 用 `program.error(...)` 拒绝无效调用——必须先拒绝后发布,因为写在拒绝之前的语句已经执行。遇到 `--help`、`--version`、解析错误或这种拒绝时,该适配器输出 commander 文本并请求退出;提供方什么也不发布,因此依赖行不会激活。
49
50
 
50
51
  ### 注入如何排列配置求值
51
52
 
package/lib/index.js CHANGED
@@ -9,7 +9,8 @@
9
9
  * text, and its parse errors instead of the launcher knowing them.
10
10
  *
11
11
  * Any app plugin can inject `cmdlineArgs` and call {@link parseCmdline}. A
12
- * provider may publish the parsed values as its own service, and ordinary rows
12
+ * provider may publish the parsed values as its own service from its program's
13
+ * commander action, and ordinary rows
13
14
  * can inject that service and read it from lazily resolved config —
14
15
  * `port: !!js ctx.webStartup.port ?? 3080` — so a flag beats the value written
15
16
  * beside it. No row has launcher-level command-line status.
@@ -34,36 +35,67 @@ const internals = {
34
35
  };
35
36
  /**
36
37
  * Parse the launcher's immutable argument snapshot with an app's commander
37
- * program. The caller decides whether and how to publish the returned value;
38
- * this helper has no Loader-row or service ownership semantics.
38
+ * program. Commander runs the program's own synchronous action handler on a
39
+ * successful parse; app code there publishes its service and rejects an
40
+ * invalid invocation with `program.error(...)`. This helper has no Loader-row
41
+ * or service ownership semantics.
39
42
  *
40
- * Help, version, and rejected arguments are terminal for the process: commander
41
- * writes the text, the helper requests `ctx.appExit`, and it returns
42
- * `undefined` so the caller publishes nothing.
43
+ * Help, version, and rejected arguments — from the grammar or from an action
44
+ * — are terminal for the process: commander writes the text and the helper
45
+ * requests `ctx.appExit`. The action never runs on help, version, or a
46
+ * grammar rejection; an action must reject before it publishes, because
47
+ * statements before its `program.error(...)` have already run.
43
48
  * @param ctx - plugin context carrying `cmdlineArgs` and `appExit`.
44
- * @param program - the app's commander program, with its flags and description already declared.
45
- * @param plan - this invocation's resolved value; omitted returns an empty object.
46
- * @returns the resolved value, or `undefined` when the app asked to exit.
47
- * @throws when the launcher did not provide the command line and exit request.
49
+ * @param program - the app's commander program, with its flags, description,
50
+ * actions, and any subcommands already declared.
51
+ * @throws when the launcher did not provide the command line and exit request,
52
+ * or when no command in the program declares an action.
48
53
  */
49
- function parseCmdline(ctx, program, plan = (() => ({}))) {
54
+ function parseCmdline(ctx, program) {
50
55
  const args = ctx.get("cmdlineArgs");
51
56
  const exit = ctx.get("appExit");
52
57
  if (args === void 0 || exit === void 0) throw new Error(`${program.name()}: the launcher must provide ctx.cmdlineArgs and ctx.appExit before the tree mounts`);
53
- program.exitOverride().configureOutput({
54
- writeOut: (text) => void internals.stdout.write(text),
55
- writeErr: (text) => void internals.stderr.write(text)
56
- });
58
+ if (!hasAction(program)) throw new Error(`${program.name()}: no command in the program declares an action; parseCmdline runs the invoked command's action on a successful parse, and app code there publishes its service`);
59
+ configureExitAndOutput(program);
57
60
  try {
58
61
  program.parse(args.get(), { from: "user" });
59
- return plan(program, ctx);
60
62
  } catch (error) {
61
63
  if (!isCommanderError(error)) throw error;
62
64
  exit(error.exitCode);
63
- return;
64
65
  }
65
66
  }
66
67
  /**
68
+ * Whether any command in the tree declares an action handler.
69
+ *
70
+ * The `Command` type cannot express the action precondition, so the handler is
71
+ * read structurally (as {@link isCommanderError} reads commander's control-flow
72
+ * errors): without this guard, a program that forgot its action would parse
73
+ * successfully, publish nothing, and surface only as dependent rows pending on
74
+ * the absent service.
75
+ * @param command - the command whose tree is inspected.
76
+ * @returns true when the command or any registered subcommand has an action.
77
+ */
78
+ function hasAction(command) {
79
+ if (typeof command._actionHandler === "function") return true;
80
+ return command.commands.some(hasAction);
81
+ }
82
+ /**
83
+ * Route every command's exit and output through the launcher adapter.
84
+ *
85
+ * Commander copies `exitOverride` and output configuration into a subcommand
86
+ * only at registration, so a root-only override would let an
87
+ * already-registered subcommand's rejection write to the process streams and
88
+ * call `process.exit` directly, bypassing `ctx.appExit`.
89
+ * @param command - the root of the command tree to configure.
90
+ */
91
+ function configureExitAndOutput(command) {
92
+ command.exitOverride().configureOutput({
93
+ writeOut: (text) => void internals.stdout.write(text),
94
+ writeErr: (text) => void internals.stderr.write(text)
95
+ });
96
+ for (const child of command.commands) configureExitAndOutput(child);
97
+ }
98
+ /**
67
99
  * Whether a thrown value is commander's own control-flow error (help, version,
68
100
  * a parse error, or `program.error`).
69
101
  *
@@ -8,7 +8,8 @@
8
8
  * text, and its parse errors instead of the launcher knowing them.
9
9
  *
10
10
  * Any app plugin can inject `cmdlineArgs` and call {@link parseCmdline}. A
11
- * provider may publish the parsed values as its own service, and ordinary rows
11
+ * provider may publish the parsed values as its own service from its program's
12
+ * commander action, and ordinary rows
12
13
  * can inject that service and read it from lazily resolved config —
13
14
  * `port: !!js ctx.webStartup.port ?? 3080` — so a flag beats the value written
14
15
  * beside it. No row has launcher-level command-line status.
@@ -68,28 +69,23 @@ export declare const internals: {
68
69
  write(chunk: string): unknown;
69
70
  };
70
71
  };
71
- /**
72
- * Resolve parsed arguments into an app-owned value. Call
73
- * `program.error(...)` to reject the invocation with a usage message instead
74
- * of throwing.
75
- * @param program - the parsed commander program.
76
- * @param ctx - the plugin context that received the command line.
77
- * @returns the value an ordinary provider plugin may publish.
78
- */
79
- export type CmdlinePlan<T = unknown> = (program: Command, ctx: Context) => T;
80
72
  /**
81
73
  * Parse the launcher's immutable argument snapshot with an app's commander
82
- * program. The caller decides whether and how to publish the returned value;
83
- * this helper has no Loader-row or service ownership semantics.
74
+ * program. Commander runs the program's own synchronous action handler on a
75
+ * successful parse; app code there publishes its service and rejects an
76
+ * invalid invocation with `program.error(...)`. This helper has no Loader-row
77
+ * or service ownership semantics.
84
78
  *
85
- * Help, version, and rejected arguments are terminal for the process: commander
86
- * writes the text, the helper requests `ctx.appExit`, and it returns
87
- * `undefined` so the caller publishes nothing.
79
+ * Help, version, and rejected arguments — from the grammar or from an action
80
+ * — are terminal for the process: commander writes the text and the helper
81
+ * requests `ctx.appExit`. The action never runs on help, version, or a
82
+ * grammar rejection; an action must reject before it publishes, because
83
+ * statements before its `program.error(...)` have already run.
88
84
  * @param ctx - plugin context carrying `cmdlineArgs` and `appExit`.
89
- * @param program - the app's commander program, with its flags and description already declared.
90
- * @param plan - this invocation's resolved value; omitted returns an empty object.
91
- * @returns the resolved value, or `undefined` when the app asked to exit.
92
- * @throws when the launcher did not provide the command line and exit request.
85
+ * @param program - the app's commander program, with its flags, description,
86
+ * actions, and any subcommands already declared.
87
+ * @throws when the launcher did not provide the command line and exit request,
88
+ * or when no command in the program declares an action.
93
89
  */
94
- export declare function parseCmdline<T>(ctx: Context, program: Command, plan?: CmdlinePlan<T>): T | undefined;
90
+ export declare function parseCmdline(ctx: Context, program: Command): void;
95
91
  //# sourceMappingURL=index.d.ts.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-cmdline",
3
3
  "description": "Immutable command-line handoff from a dsh launcher to any app plugin that injects cmdlineArgs",
4
- "version": "0.0.1-rc.2",
4
+ "version": "0.0.1-rc.3",
5
5
  "publishConfig": {
6
6
  "access": "restricted"
7
7
  },
@@ -33,14 +33,14 @@
33
33
  "license": "BSD-3-Clause",
34
34
  "peerDependencies": {
35
35
  "@deepseek-ai/cordis-plugin-loader": "^1.0.1-rc.1",
36
- "@deepseek-ai/dsh-invariants": "^0.0.1-rc.2",
37
- "@deepseek-ai/cordis": "^4.0.1-rc.1"
36
+ "@deepseek-ai/cordis": "^4.0.1-rc.1",
37
+ "@deepseek-ai/dsh-invariants": "^0.0.1-rc.3"
38
38
  },
39
39
  "devDependencies": {
40
40
  "commander": "^15.0.0",
41
+ "@deepseek-ai/cordis-plugin-include": "^1.0.5-rc.1",
41
42
  "@deepseek-ai/cordis-plugin-loader": "^1.0.1-rc.1",
42
- "@deepseek-ai/dsh-invariants": "^0.0.1-rc.2",
43
- "@deepseek-ai/cordis": "^4.0.1-rc.1",
44
- "@deepseek-ai/cordis-plugin-include": "^1.0.5-rc.1"
43
+ "@deepseek-ai/dsh-invariants": "^0.0.1-rc.3",
44
+ "@deepseek-ai/cordis": "^4.0.1-rc.1"
45
45
  }
46
46
  }