@actiondock/cli 2.10.0 → 2.11.0

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.
Files changed (64) hide show
  1. package/README.md +2 -2
  2. package/bin/ad.js +225 -5
  3. package/dist/commands/action/create.d.ts +1 -1
  4. package/dist/commands/action/create.js +6 -14
  5. package/dist/commands/add.js +1 -1
  6. package/dist/commands/config/delete.js +3 -1
  7. package/dist/commands/config/env.js +2 -1
  8. package/dist/commands/config/get.js +3 -1
  9. package/dist/commands/config/list.js +6 -5
  10. package/dist/commands/config/set.js +3 -1
  11. package/dist/commands/describe.js +2 -3
  12. package/dist/commands/export.js +34 -16
  13. package/dist/commands/index.d.ts +37 -24
  14. package/dist/commands/index.js +326 -48
  15. package/dist/commands/mcp.js +0 -1
  16. package/dist/commands/playbook.js +15 -35
  17. package/dist/commands/profile.js +2 -1
  18. package/dist/commands/remove.js +1 -1
  19. package/dist/commands/run.d.ts +2 -1
  20. package/dist/commands/run.js +4 -56
  21. package/dist/commands/runs.d.ts +6 -1
  22. package/dist/commands/runs.js +274 -11
  23. package/dist/commands/serve.js +5 -2
  24. package/dist/commands/state.js +84 -15
  25. package/dist/index.d.ts +10 -2
  26. package/dist/index.js +15 -12
  27. package/dist/prompt.d.ts +3 -0
  28. package/dist/prompt.js +6 -14
  29. package/dist/renderer.d.ts +7 -137
  30. package/dist/renderer.js +6 -438
  31. package/dist/renderers/action.d.ts +51 -0
  32. package/dist/renderers/action.js +112 -0
  33. package/dist/renderers/config.d.ts +31 -0
  34. package/dist/renderers/config.js +93 -0
  35. package/dist/renderers/playbook.d.ts +19 -0
  36. package/dist/renderers/playbook.js +42 -0
  37. package/dist/renderers/project.d.ts +45 -0
  38. package/dist/renderers/project.js +196 -0
  39. package/dist/renderers/runs.d.ts +15 -0
  40. package/dist/renderers/runs.js +47 -0
  41. package/dist/renderers/state.d.ts +4 -0
  42. package/dist/renderers/state.js +19 -0
  43. package/dist/services/config-merge.js +1 -2
  44. package/dist/services/index.d.ts +1 -0
  45. package/dist/services/index.js +1 -0
  46. package/dist/services/run-watch.d.ts +156 -0
  47. package/dist/services/run-watch.js +395 -0
  48. package/dist/utils/cert.js +1 -1
  49. package/dist/utils/index.d.ts +1 -1
  50. package/dist/utils/index.js +1 -1
  51. package/dist/utils/input.d.ts +2 -1
  52. package/dist/utils/input.js +2 -1
  53. package/dist/utils/target.d.ts +6 -2
  54. package/dist/utils/target.js +4 -4
  55. package/dist/utils/utils.js +3 -11
  56. package/dist/utils/warning.d.ts +4 -0
  57. package/dist/utils/warning.js +4 -0
  58. package/package.json +5 -5
  59. package/dist/commands/state-list.d.ts +0 -24
  60. package/dist/commands/state-list.js +0 -81
  61. package/dist/standalone/index.d.ts +0 -12
  62. package/dist/standalone/index.js +0 -13
  63. package/dist/utils/duration.d.ts +0 -1
  64. package/dist/utils/duration.js +0 -1
@@ -1,29 +1,6 @@
1
- import { readFileSync } from "node:fs";
1
+ import { existsSync, readFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { Command } from "commander";
4
- import { registerActionCommands } from "./action/index.js";
5
- import { registerAddCommand } from "./add.js";
6
- import { registerBuildCommand } from "./build.js";
7
- import { registerConfigCommands } from "./config/index.js";
8
- import { registerDescribeCommand } from "./describe.js";
9
- import { registerDoctorCommand } from "./doctor.js";
10
- import { registerExportCommand } from "./export.js";
11
- import { registerGenerateCommands } from "./generate.js";
12
- import { registerInfoCommand } from "./info.js";
13
- import { registerInitCommand } from "./init.js";
14
- import { registerLinkCommands } from "./link.js";
15
- import { registerListCommand } from "./list.js";
16
- import { registerMcpCommands } from "./mcp.js";
17
- import { registerPackCommand } from "./pack.js";
18
- import { registerPlaybookCommands } from "./playbook.js";
19
- import { registerProfileCommands } from "./profile.js";
20
- import { registerRemoveCommand } from "./remove.js";
21
- import { registerRunCommand } from "./run.js";
22
- import { registerRunsCommands } from "./runs.js";
23
- import { registerServeCommand } from "./serve.js";
24
- import { registerStateCommands } from "./state.js";
25
- import { registerTestCommand } from "./test.js";
26
- import { registerValidateCommand } from "./validate.js";
27
4
  /**
28
5
  * 读取 CLI 自身 package.json 的版本号(单一事实源)。
29
6
  * 兼容源码运行(src/commands)与构建产物运行(dist/commands)两种目录层级。
@@ -62,6 +39,288 @@ function applyCommonOptions(cmd) {
62
39
  applyCommonOptions(sub);
63
40
  }
64
41
  }
42
+ /**
43
+ * 动态加载子命令实现模块(兼容构建产物 .js 与开发环境 .ts)。
44
+ */
45
+ function importCommandModule(relativePath) {
46
+ const ext = existsSync(join(import.meta.dirname, "run.js")) ? ".js" : ".ts";
47
+ return import(`${relativePath}${ext}`);
48
+ }
49
+ const COMMAND_DECLARATIONS = [
50
+ {
51
+ name: "init",
52
+ spec: "init [directory]",
53
+ description: "Initialize a new ActionDock project",
54
+ loader: "./init",
55
+ registerFn: "registerInitCommand",
56
+ },
57
+ {
58
+ name: "add",
59
+ spec: "add <package>",
60
+ description: "Install and lock an Action package dependency into current project",
61
+ loader: "./add",
62
+ registerFn: "registerAddCommand",
63
+ },
64
+ {
65
+ name: "remove",
66
+ spec: "remove <package>",
67
+ description: "Remove an Action package dependency and update actiondock.lock.json",
68
+ loader: "./remove",
69
+ registerFn: "registerRemoveCommand",
70
+ },
71
+ {
72
+ name: "action",
73
+ spec: "action",
74
+ description: "Manage Action definitions and scaffolding (create)",
75
+ loader: "./action/create",
76
+ registerFn: "registerActionCommands",
77
+ },
78
+ {
79
+ name: "info",
80
+ spec: "info [patterns...]",
81
+ description: "Display information about current project, linked package, or remote target",
82
+ loader: "./info",
83
+ registerFn: "registerInfoCommand",
84
+ },
85
+ {
86
+ name: "doctor",
87
+ spec: "doctor",
88
+ description: "Check ActionDock environment, registry health, and project diagnostics",
89
+ loader: "./doctor",
90
+ registerFn: "registerDoctorCommand",
91
+ },
92
+ {
93
+ name: "list",
94
+ spec: "list [patterns...]",
95
+ description: "List actions in current project, linked packages, or remote profile",
96
+ loader: "./list",
97
+ registerFn: "registerListCommand",
98
+ },
99
+ {
100
+ name: "describe",
101
+ spec: "describe <id>",
102
+ description: "Show action definition, schema, and description",
103
+ loader: "./describe",
104
+ registerFn: "registerDescribeCommand",
105
+ },
106
+ {
107
+ name: "run",
108
+ spec: "run <id> [params...]",
109
+ description: "Execute an action (from current project, linked packages, or remote profile)",
110
+ loader: "./run",
111
+ registerFn: "registerRunCommand",
112
+ },
113
+ {
114
+ name: "validate",
115
+ spec: "validate [id]",
116
+ description: "Validate action schemas and definitions",
117
+ loader: "./validate",
118
+ registerFn: "registerValidateCommand",
119
+ },
120
+ {
121
+ name: "generate",
122
+ spec: "generate",
123
+ description: "Generate project artifacts such as TypeScript declarations",
124
+ loader: "./generate",
125
+ registerFn: "registerGenerateCommands",
126
+ },
127
+ {
128
+ name: "playbook",
129
+ spec: "playbook",
130
+ description: "Manage task Playbooks (Task SOPs for AI Agents)",
131
+ loader: "./playbook",
132
+ registerFn: "registerPlaybookCommands",
133
+ },
134
+ {
135
+ name: "config",
136
+ spec: "config",
137
+ description: "Manage runtime configuration store (Global & Project-level)",
138
+ loader: "./config/index",
139
+ registerFn: "registerConfigCommands",
140
+ },
141
+ {
142
+ name: "state",
143
+ spec: "state",
144
+ description: "Inspect and manage Shared State store",
145
+ loader: "./state",
146
+ registerFn: "registerStateCommands",
147
+ },
148
+ {
149
+ name: "runs",
150
+ spec: "runs",
151
+ description: "Inspect action execution history",
152
+ loader: "./runs",
153
+ registerFn: "registerRunsCommands",
154
+ },
155
+ {
156
+ name: "test",
157
+ spec: "test [pattern]",
158
+ description: "Run project tests using configured test runner (node:test or bun test)",
159
+ loader: "./test",
160
+ registerFn: "registerTestCommand",
161
+ },
162
+ {
163
+ name: "build",
164
+ spec: "build",
165
+ description: "Build project actions into a runnable Node.js delivery directory",
166
+ loader: "./build",
167
+ registerFn: "registerBuildCommand",
168
+ },
169
+ {
170
+ name: "pack",
171
+ spec: "pack",
172
+ description: "Pack Action package into a standard npm tarball (.tgz) for distribution",
173
+ loader: "./pack",
174
+ registerFn: "registerPackCommand",
175
+ },
176
+ {
177
+ name: "export",
178
+ spec: "export",
179
+ description: "Export project artifacts",
180
+ loader: "./export",
181
+ registerFn: "registerExportCommand",
182
+ },
183
+ {
184
+ name: "link",
185
+ spec: "link [path]",
186
+ description: "Link local Action package(s) or workspace directory into global registry for instant cross-directory execution",
187
+ loader: "./link",
188
+ registerFn: "registerLinkCommands",
189
+ linkedNames: ["link", "unlink"],
190
+ },
191
+ {
192
+ name: "unlink",
193
+ spec: "unlink [identifier]",
194
+ description: "Unlink a package, workspace, or prune stale entries from global developer registry",
195
+ loader: "./link",
196
+ registerFn: "registerLinkCommands",
197
+ linkedNames: ["link", "unlink"],
198
+ },
199
+ {
200
+ name: "profile",
201
+ spec: "profile",
202
+ description: "Manage multi-cloud and remote execution profiles",
203
+ loader: "./profile",
204
+ registerFn: "registerProfileCommands",
205
+ },
206
+ {
207
+ name: "serve",
208
+ spec: "serve",
209
+ description: "Start the ActionDock lightweight HTTP Runner server for remote execution",
210
+ loader: "./serve",
211
+ registerFn: "registerServeCommand",
212
+ },
213
+ {
214
+ name: "mcp",
215
+ spec: "mcp",
216
+ description: "Model Context Protocol (MCP) server for ActionDock Actions (STDIO default)",
217
+ loader: "./mcp",
218
+ registerFn: "registerMcpCommands",
219
+ },
220
+ ];
221
+ const RUNTIME_EXECUTABLES = new Set(["node", "bun", "tsx", "deno"]);
222
+ /**
223
+ * 判断命令行参数数组是否以宿主运行环境(node、bun、tsx 等)及入口脚本为前置元素。
224
+ */
225
+ export function isNodeArgv(argv) {
226
+ if (!Array.isArray(argv) || argv.length < 2)
227
+ return false;
228
+ const first = argv[0];
229
+ if (!first || first.startsWith("-"))
230
+ return false;
231
+ const base = first.split(/[/\\]/).pop()?.toLowerCase().replace(/\.exe$/i, "") ?? "";
232
+ if (RUNTIME_EXECUTABLES.has(base)) {
233
+ return true;
234
+ }
235
+ // 严格排除已知子命令与内置 help 命令,杜绝将纯用户子命令参数错误识别为运行时
236
+ if (COMMAND_DECLARATIONS.some((d) => d.name === first || d.alias === first) ||
237
+ first === "help") {
238
+ return false;
239
+ }
240
+ return false;
241
+ }
242
+ /**
243
+ * 从命令行参数数组中解析出目标子命令名称(跳过全局选项与启动包装参数)。
244
+ *
245
+ * @param argv 命令行原始参数数组
246
+ * @param parseOptions Commander 解析选项(如 { from: 'user' })
247
+ * @returns 目标子命令名称或 null
248
+ */
249
+ export function resolveTargetSubcommand(argv, parseOptions) {
250
+ let startIndex = 0;
251
+ if (parseOptions?.from === "user") {
252
+ startIndex = 0;
253
+ }
254
+ else if (parseOptions?.from === "eval") {
255
+ startIndex = 1;
256
+ }
257
+ else if (parseOptions?.from === "node") {
258
+ startIndex = 2;
259
+ }
260
+ else if (isNodeArgv(argv)) {
261
+ startIndex = 2;
262
+ }
263
+ else {
264
+ startIndex = 0;
265
+ }
266
+ let i = startIndex;
267
+ while (i < argv.length) {
268
+ const arg = argv[i];
269
+ if (arg === "--data-dir") {
270
+ i += 2;
271
+ continue;
272
+ }
273
+ if (arg.startsWith("-")) {
274
+ i++;
275
+ continue;
276
+ }
277
+ // 特殊处理 Commander 内置 help 命令(如 ad help run)
278
+ if (arg === "help" && i + 1 < argv.length && !argv[i + 1].startsWith("-")) {
279
+ return argv[i + 1];
280
+ }
281
+ return arg;
282
+ }
283
+ return null;
284
+ }
285
+ /**
286
+ * 按需加载并挂载目标子命令模块。
287
+ * 基于 Promise 缓存机制消除并发调用时的竞态条件。
288
+ */
289
+ async function loadAndAttachCommand(targetSubcommand, program, placeholders, loadingPromises, context) {
290
+ const decl = COMMAND_DECLARATIONS.find((d) => d.name === targetSubcommand || d.alias === targetSubcommand);
291
+ if (!decl)
292
+ return;
293
+ const existing = loadingPromises.get(decl.loader);
294
+ if (existing) {
295
+ return existing;
296
+ }
297
+ const loadPromise = (async () => {
298
+ // 移除对应的占位命令节点
299
+ const toRemove = decl.linkedNames || [decl.name];
300
+ for (const name of toRemove) {
301
+ const p = placeholders.get(name);
302
+ if (p) {
303
+ const idx = program.commands.indexOf(p);
304
+ if (idx !== -1) {
305
+ program.commands.splice(idx, 1);
306
+ }
307
+ placeholders.delete(name);
308
+ }
309
+ }
310
+ // 动态导入实际命令模块并挂载真实命令
311
+ const mod = await importCommandModule(decl.loader);
312
+ const fn = mod[decl.registerFn];
313
+ if (typeof fn === "function") {
314
+ fn(program, context);
315
+ applyCommonOptions(program);
316
+ }
317
+ })();
318
+ loadingPromises.set(decl.loader, loadPromise);
319
+ loadPromise.catch(() => {
320
+ loadingPromises.delete(decl.loader);
321
+ });
322
+ return loadPromise;
323
+ }
65
324
  export function createCliProgram(context) {
66
325
  const program = new Command();
67
326
  program
@@ -97,30 +356,49 @@ export function createCliProgram(context) {
97
356
  Workflow Guidance:
98
357
  For multi-step workflows, run 'ad playbook list' before invoking atomic actions.
99
358
  Run 'ad info [intent]' to discover packages and playbooks by keyword.`);
100
- registerInitCommand(program);
101
- registerAddCommand(program, context);
102
- registerRemoveCommand(program, context);
103
- registerActionCommands(program, context);
104
- registerInfoCommand(program, context);
105
- registerDoctorCommand(program, context);
106
- registerListCommand(program, context);
107
- registerDescribeCommand(program, context);
108
- registerRunCommand(program, context);
109
- registerValidateCommand(program, context);
110
- registerGenerateCommands(program, context);
111
- registerPlaybookCommands(program, context);
112
- registerConfigCommands(program, context);
113
- registerStateCommands(program, context);
114
- registerRunsCommands(program, context);
115
- registerTestCommand(program);
116
- registerBuildCommand(program, context);
117
- registerPackCommand(program, context);
118
- registerExportCommand(program, context);
119
- registerLinkCommands(program);
120
- registerProfileCommands(program);
121
- registerServeCommand(program, context);
122
- registerMcpCommands(program, context);
359
+ // 注册轻量级占位命令声明(零重型模块导入开销)
360
+ const placeholders = new Map();
361
+ for (const decl of COMMAND_DECLARATIONS) {
362
+ const cmd = program.command(decl.spec).description(decl.description);
363
+ if (decl.alias) {
364
+ cmd.alias(decl.alias);
365
+ }
366
+ placeholders.set(decl.name, cmd);
367
+ }
123
368
  applyCommonOptions(program);
369
+ // 拦截 parseAsync 以在实际执行或具体帮助展示时按需动态加载真实子命令
370
+ const originalParseAsync = program.parseAsync.bind(program);
371
+ const loadingPromises = new Map();
372
+ program.parseAsync = async function (argv, parseOptions) {
373
+ const targetArgv = argv || process.argv;
374
+ const targetSubcommand = resolveTargetSubcommand(targetArgv, parseOptions);
375
+ if (targetSubcommand) {
376
+ await loadAndAttachCommand(targetSubcommand, program, placeholders, loadingPromises, context);
377
+ }
378
+ return originalParseAsync(argv, parseOptions);
379
+ };
124
380
  return program;
125
381
  }
126
- export { registerActionCommands, registerAddCommand, registerBuildCommand, registerConfigCommands, registerDescribeCommand, registerDoctorCommand, registerExportCommand, registerGenerateCommands, registerInfoCommand, registerInitCommand, registerLinkCommands, registerListCommand, registerMcpCommands, registerPackCommand, registerPlaybookCommands, registerProfileCommands, registerRemoveCommand, registerRunCommand, registerRunsCommands, registerServeCommand, registerStateCommands, registerTestCommand, registerValidateCommand, };
382
+ export const registerActionCommands = (program, context) => importCommandModule("./action/create").then((m) => m.registerActionCommands(program, context));
383
+ export const registerAddCommand = (program, context) => importCommandModule("./add").then((m) => m.registerAddCommand(program, context));
384
+ export const registerBuildCommand = (program, context) => importCommandModule("./build").then((m) => m.registerBuildCommand(program, context));
385
+ export const registerConfigCommands = (program, context) => importCommandModule("./config/index").then((m) => m.registerConfigCommands(program, context));
386
+ export const registerDescribeCommand = (program, context) => importCommandModule("./describe").then((m) => m.registerDescribeCommand(program, context));
387
+ export const registerDoctorCommand = (program, context) => importCommandModule("./doctor").then((m) => m.registerDoctorCommand(program, context));
388
+ export const registerExportCommand = (program, context) => importCommandModule("./export").then((m) => m.registerExportCommand(program, context));
389
+ export const registerGenerateCommands = (program, context) => importCommandModule("./generate").then((m) => m.registerGenerateCommands(program, context));
390
+ export const registerInfoCommand = (program, context) => importCommandModule("./info").then((m) => m.registerInfoCommand(program, context));
391
+ export const registerInitCommand = (program) => importCommandModule("./init").then((m) => m.registerInitCommand(program));
392
+ export const registerLinkCommands = (program) => importCommandModule("./link").then((m) => m.registerLinkCommands(program));
393
+ export const registerListCommand = (program, context) => importCommandModule("./list").then((m) => m.registerListCommand(program, context));
394
+ export const registerMcpCommands = (program, context) => importCommandModule("./mcp").then((m) => m.registerMcpCommands(program, context));
395
+ export const registerPackCommand = (program, context) => importCommandModule("./pack").then((m) => m.registerPackCommand(program, context));
396
+ export const registerPlaybookCommands = (program, context) => importCommandModule("./playbook").then((m) => m.registerPlaybookCommands(program, context));
397
+ export const registerProfileCommands = (program) => importCommandModule("./profile").then((m) => m.registerProfileCommands(program));
398
+ export const registerRemoveCommand = (program, context) => importCommandModule("./remove").then((m) => m.registerRemoveCommand(program, context));
399
+ export const registerRunCommand = (program, context) => importCommandModule("./run").then((m) => m.registerRunCommand(program, context));
400
+ export const registerRunsCommands = (program, context) => importCommandModule("./runs").then((m) => m.registerRunsCommands(program, context));
401
+ export const registerServeCommand = (program, context) => importCommandModule("./serve").then((m) => m.registerServeCommand(program, context));
402
+ export const registerStateCommands = (program, context) => importCommandModule("./state").then((m) => m.registerStateCommands(program, context));
403
+ export const registerTestCommand = (program) => importCommandModule("./test").then((m) => m.registerTestCommand(program));
404
+ export const registerValidateCommand = (program, context) => importCommandModule("./validate").then((m) => m.registerValidateCommand(program, context));
@@ -116,7 +116,6 @@ export function registerMcpCommands(program, context) {
116
116
  const platform = createNodePlatform({
117
117
  customHome: context?.customHome,
118
118
  dataDir: options.dataDir || context?.dataDir,
119
- rootDir: projectRoots?.[0],
120
119
  });
121
120
  const { startMcpHttpServer } = await import("@actiondock/mcp");
122
121
  const server = await startMcpHttpServer({
@@ -3,11 +3,11 @@ import { dirname, isAbsolute, relative, resolve } from "node:path";
3
3
  import { assertPathWithinRoot, loadActions, loadManifest, loadPlaybooks, saveManifest, filterWithFallbackInfo, } from "@actiondock/core/project";
4
4
  import { DefaultActionCatalog, PackageGraphBuilder, resolveAction, resolvePlaybook, } from "@actiondock/core/graph";
5
5
  import { fetchRemotePlaybooks, fetchRemotePlaybookShow, } from "@actiondock/core/profile";
6
- import { findProjectRoot, loadProjectConfig, } from "@actiondock/core";
7
- import { listLinkedPackages, resolvePackageRoot, } from "@actiondock/core/registry";
8
- import { ArgumentError, CliError, ExecutionError, NO_PROJECT_NO_LINKED_MESSAGE, notInProjectError, packageNotFoundError, wrapAsExecutionError, } from "../errors.js";
6
+ import { loadProjectConfig, } from "@actiondock/core";
7
+ import { listLinkedPackages, } from "@actiondock/core/registry";
8
+ import { ArgumentError, CliError, ExecutionError, NO_PROJECT_NO_LINKED_MESSAGE, wrapAsExecutionError, } from "../errors.js";
9
9
  import { renderPlaybookDetail, renderPlaybookList, renderResult, writeStdout, } from "../renderer.js";
10
- import { applyTargetOptions, getEffectiveOptions, remoteTargetSuffix, resolveFallbackStrategy, resolveIntent, resolveTargetFromOptions, } from "../utils/index.js";
10
+ import { applyTargetOptions, getEffectiveOptions, remoteTargetSuffix, requirePackageRoot, resolveFallbackStrategy, resolveIntent, resolveLocalPackageRoot, resolveTargetFromOptions, } from "../utils/index.js";
11
11
  /**
12
12
  * 注册 Playbook 命令集合(list, show, validate, create)。
13
13
  *
@@ -55,16 +55,9 @@ export function registerPlaybookCommands(program, context) {
55
55
  return;
56
56
  }
57
57
  // 2. 本地工程模式
58
- let targetRoot = null;
59
- if (options.package) {
60
- targetRoot = resolvePackageRoot(options.package);
61
- if (!targetRoot) {
62
- throw packageNotFoundError(options.package);
63
- }
64
- }
65
- else {
66
- targetRoot = findProjectRoot();
67
- }
58
+ const targetRoot = options.package
59
+ ? requirePackageRoot(options.package).root
60
+ : resolveLocalPackageRoot();
68
61
  if (targetRoot) {
69
62
  const config = loadProjectConfig(targetRoot);
70
63
  const playbooks = loadPlaybooks(targetRoot, config.playbooksDir);
@@ -213,16 +206,13 @@ export function registerPlaybookCommands(program, context) {
213
206
  // 2. 本地项目模式
214
207
  let showTarget = id;
215
208
  if (options.package && !id.includes("/")) {
216
- const pkgRoot = resolvePackageRoot(options.package);
217
- if (!pkgRoot) {
218
- throw packageNotFoundError(options.package);
219
- }
209
+ requirePackageRoot(options.package);
220
210
  showTarget = `${options.package}/${id}`;
221
211
  }
222
212
  let resolved;
223
213
  try {
224
214
  const graph = new PackageGraphBuilder({
225
- root: findProjectRoot() || undefined,
215
+ root: resolveLocalPackageRoot() || undefined,
226
216
  customHome: context?.customHome,
227
217
  allowDevLinks: true,
228
218
  }).buildSync();
@@ -259,16 +249,9 @@ export function registerPlaybookCommands(program, context) {
259
249
  .option("--json", "Output as JSON")
260
250
  .action(async (id, rawOptions, cmd) => {
261
251
  const options = getEffectiveOptions(rawOptions, cmd);
262
- let root = null;
263
- if (options.package) {
264
- root = resolvePackageRoot(options.package);
265
- if (!root) {
266
- throw packageNotFoundError(options.package);
267
- }
268
- }
269
- else {
270
- root = findProjectRoot();
271
- }
252
+ const root = options.package
253
+ ? requirePackageRoot(options.package).root
254
+ : resolveLocalPackageRoot();
272
255
  const targets = [];
273
256
  if (root) {
274
257
  const config = loadProjectConfig(root);
@@ -288,7 +271,7 @@ export function registerPlaybookCommands(program, context) {
288
271
  let resolved;
289
272
  try {
290
273
  const graph = new PackageGraphBuilder({
291
- root: findProjectRoot() || undefined,
274
+ root: resolveLocalPackageRoot() || undefined,
292
275
  customHome: context?.customHome,
293
276
  allowDevLinks: true,
294
277
  }).buildSync();
@@ -327,7 +310,7 @@ export function registerPlaybookCommands(program, context) {
327
310
  }
328
311
  const results = [];
329
312
  const graph = new PackageGraphBuilder({
330
- root: findProjectRoot() || undefined,
313
+ root: resolveLocalPackageRoot() || undefined,
331
314
  customHome: context?.customHome,
332
315
  allowDevLinks: true,
333
316
  }).buildSync();
@@ -409,10 +392,7 @@ export function registerPlaybookCommands(program, context) {
409
392
  });
410
393
  }
411
394
  export function handlePlaybookCreate(id, options, context) {
412
- const root = findProjectRoot();
413
- if (!root) {
414
- throw notInProjectError();
415
- }
395
+ const { root } = requirePackageRoot(undefined);
416
396
  try {
417
397
  const config = loadProjectConfig(root);
418
398
  const pbDir = resolve(root, config.playbooksDir || "playbooks");
@@ -1,4 +1,5 @@
1
- import { addProfile, checkRemoteHealth, getProfile, listProfiles, loadProfiles, removeProfile, resolveProfileToken, resolveTarget, toSnakeUpperCase, updateProfile, useProfile, } from "@actiondock/core/profile";
1
+ import { addProfile, checkRemoteHealth, getProfile, listProfiles, loadProfiles, removeProfile, resolveProfileToken, resolveTarget, updateProfile, useProfile, } from "@actiondock/core/profile";
2
+ import { toSnakeUpperCase } from "@actiondock/core";
2
3
  import { filterWithFallbackInfo, maskSecretValue, } from "@actiondock/core/project";
3
4
  import { ArgumentError, ExecutionError, wrapAsExecutionError, } from "../errors.js";
4
5
  import { renderResult, writeStderr, writeStdout } from "../renderer.js";
@@ -79,7 +79,7 @@ export function registerRemoveCommand(program, context) {
79
79
  }
80
80
  // 调用包管理器执行卸载(说明符白名单校验,消除 win32 shell 拼接注入面)
81
81
  assertSafePackageSpec(npmPackageName);
82
- const installCmd = getInstallCommand(root);
82
+ const installCmd = await getInstallCommand(root);
83
83
  const pm = installCmd[0];
84
84
  const args = pm === "bun" ? ["remove", npmPackageName] : ["uninstall", npmPackageName, "--ignore-scripts"];
85
85
  const uninstallProc = await spawnAsync(pm, args, {
@@ -6,7 +6,8 @@ import type { CliContext } from "../types.js";
6
6
  * 以原始纯文本形式渲染 Action 执行终态结果(默认纯文本模式)。
7
7
  *
8
8
  * 设计契约:
9
- * 1. stdout: 仅承载业务有效载荷(payload)。保持原生排版、未转义多行与真实换行,供用户阅读或下游管道直接消费。
9
+ * - 通用纯文本透传:标量直接输出,结构化对象按标准规范呈现,杜绝私有业务字段嗅探。
10
+ * - 标准输出仅承载业务有效载荷,保持原生排版与真实换行,供用户调阅或下游管道消费。
10
11
  */
11
12
  export declare function renderRawExecutionResult(targetRef: string, result: ExecutionResult, context?: CliContext): void;
12
13
  /**
@@ -1,6 +1,6 @@
1
1
  import { validateActionInputValue, mapInputValidationFailure, } from "@actiondock/core/project";
2
2
  import { resolveTarget, } from "@actiondock/core/profile";
3
- import { resolveExecutionHint, } from "@actiondock/core";
3
+ import { resolveExecutionHint } from "@actiondock/core";
4
4
  import { ArgumentError, ExecutionError, SigintError, packageNotFoundError } from "../errors.js";
5
5
  import { writeStderr, writeStdout } from "../renderer.js";
6
6
  import { applyTargetOptions, getEffectiveOptions, resolveActionInput, resolveLocalPackageRoot, resolveTimeoutMs, withService, } from "../utils/index.js";
@@ -8,44 +8,18 @@ import { applyTargetOptions, getEffectiveOptions, resolveActionInput, resolveLoc
8
8
  * 以原始纯文本形式渲染 Action 执行终态结果(默认纯文本模式)。
9
9
  *
10
10
  * 设计契约:
11
- * 1. stdout: 仅承载业务有效载荷(payload)。保持原生排版、未转义多行与真实换行,供用户阅读或下游管道直接消费。
11
+ * - 通用纯文本透传:标量直接输出,结构化对象按标准规范呈现,杜绝私有业务字段嗅探。
12
+ * - 标准输出仅承载业务有效载荷,保持原生排版与真实换行,供用户调阅或下游管道消费。
12
13
  */
13
14
  export function renderRawExecutionResult(targetRef, result, context) {
14
15
  if (result.ok) {
15
16
  const data = result.data;
16
17
  let rawText;
17
- let metaInfo;
18
18
  if (typeof data === "string") {
19
19
  rawText = data;
20
20
  }
21
21
  else if (data !== null && typeof data === "object") {
22
- if ("content" in data && data.content !== undefined) {
23
- rawText =
24
- typeof data.content === "object" && data.content !== null
25
- ? JSON.stringify(data.content, null, 2)
26
- : String(data.content);
27
- const { content, ...rest } = data;
28
- if (Object.keys(rest).length > 0) {
29
- metaInfo = rest;
30
- }
31
- }
32
- else if ("text" in data && typeof data.text === "string") {
33
- rawText = data.text;
34
- const { text, ...rest } = data;
35
- if (Object.keys(rest).length > 0) {
36
- metaInfo = rest;
37
- }
38
- }
39
- else if ("message" in data && typeof data.message === "string") {
40
- rawText = data.message;
41
- const { message, ...rest } = data;
42
- if (Object.keys(rest).length > 0) {
43
- metaInfo = rest;
44
- }
45
- }
46
- else {
47
- rawText = JSON.stringify(data, null, 2);
48
- }
22
+ rawText = JSON.stringify(data, null, 2);
49
23
  }
50
24
  else if (data !== undefined) {
51
25
  rawText = String(data);
@@ -53,32 +27,6 @@ export function renderRawExecutionResult(targetRef, result, context) {
53
27
  else {
54
28
  rawText = "";
55
29
  }
56
- if (metaInfo) {
57
- const parts = [];
58
- const title = metaInfo.path ? String(metaInfo.path) : targetRef;
59
- parts.push(title);
60
- if (metaInfo.startLine !== undefined && metaInfo.endLine !== undefined) {
61
- parts.push(`lines ${metaInfo.startLine}-${metaInfo.endLine}`);
62
- }
63
- else if (metaInfo.line !== undefined) {
64
- parts.push(`line ${metaInfo.line}`);
65
- }
66
- if (metaInfo.hasMore !== undefined) {
67
- parts.push(`hasMore: ${metaInfo.hasMore}`);
68
- }
69
- if (metaInfo.truncated) {
70
- parts.push("truncated: true");
71
- }
72
- const handled = new Set(["path", "startLine", "endLine", "line", "hasMore", "truncated"]);
73
- for (const [k, v] of Object.entries(metaInfo)) {
74
- if (!handled.has(k) && v !== undefined) {
75
- parts.push(`${k}: ${typeof v === "object" ? JSON.stringify(v) : v}`);
76
- }
77
- }
78
- if (parts.length > 0) {
79
- writeStderr(`[${parts.join(" | ")}]`, context);
80
- }
81
- }
82
30
  writeStdout(rawText, context);
83
31
  }
84
32
  else {
@@ -1,7 +1,12 @@
1
1
  import { Command } from "commander";
2
2
  import type { CliContext } from "../types.js";
3
3
  /**
4
- * 注册 runs 动作执行历史管理命令(list、show、clear、cancel)。
4
+ * 归一化 requestId 选项值为去重后的非空原样字符串数组。
5
+ * 将 requestId 作为不作解释的原样字符串,不拆分逗号、不剔除首尾空白,只做非空有效字符串检查与去重。
6
+ */
7
+ export declare function normalizeRequestIds(raw: unknown): string[];
8
+ /**
9
+ * 注册 runs 动作执行历史管理命令(list、show、watch、clear、cancel)。
5
10
  *
6
11
  * @param program Commander 实例
7
12
  * @param context 命令行上下文