@actiondock/cli 2.7.0 → 2.8.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 (65) hide show
  1. package/README.md +4 -6
  2. package/dist/commands/action/create.js +4 -5
  3. package/dist/commands/add.js +9 -17
  4. package/dist/commands/build.js +3 -18
  5. package/dist/commands/config/delete.js +8 -7
  6. package/dist/commands/config/env.js +7 -11
  7. package/dist/commands/config/get.js +8 -5
  8. package/dist/commands/config/list.js +16 -22
  9. package/dist/commands/config/schema.d.ts +7 -1
  10. package/dist/commands/config/schema.js +44 -67
  11. package/dist/commands/config/set.js +9 -7
  12. package/dist/commands/describe.js +10 -9
  13. package/dist/commands/doctor.js +4 -3
  14. package/dist/commands/export.js +5 -13
  15. package/dist/commands/generate.js +4 -3
  16. package/dist/commands/info.js +3 -1
  17. package/dist/commands/init.js +3 -3
  18. package/dist/commands/link.js +4 -4
  19. package/dist/commands/list.js +19 -10
  20. package/dist/commands/mcp.js +27 -66
  21. package/dist/commands/pack.js +3 -12
  22. package/dist/commands/playbook.js +30 -10
  23. package/dist/commands/profile.js +10 -19
  24. package/dist/commands/remove.js +5 -14
  25. package/dist/commands/run.d.ts +5 -2
  26. package/dist/commands/run.js +19 -20
  27. package/dist/commands/runs.js +26 -35
  28. package/dist/commands/serve.js +66 -44
  29. package/dist/commands/state-list.d.ts +2 -2
  30. package/dist/commands/state-list.js +10 -8
  31. package/dist/commands/state.d.ts +2 -2
  32. package/dist/commands/state.js +110 -103
  33. package/dist/commands/test.js +1 -1
  34. package/dist/commands/validate.js +4 -17
  35. package/dist/errors.d.ts +14 -0
  36. package/dist/errors.js +46 -2
  37. package/dist/index.d.ts +2 -2
  38. package/dist/index.js +3 -3
  39. package/dist/renderer.d.ts +1 -1
  40. package/dist/renderer.js +1 -1
  41. package/dist/services/config-merge.d.ts +6 -5
  42. package/dist/services/config-merge.js +10 -8
  43. package/dist/services/inspect.js +3 -1
  44. package/dist/standalone/index.d.ts +4 -14
  45. package/dist/standalone/index.js +5 -44
  46. package/dist/types.d.ts +2 -1
  47. package/dist/types.js +1 -1
  48. package/dist/utils/cert.js +1 -1
  49. package/dist/utils/duration.d.ts +1 -1
  50. package/dist/utils/duration.js +1 -1
  51. package/dist/utils/index.d.ts +1 -2
  52. package/dist/utils/index.js +1 -2
  53. package/dist/utils/input.d.ts +9 -1
  54. package/dist/utils/input.js +12 -1
  55. package/dist/utils/server-launch.d.ts +61 -0
  56. package/dist/utils/server-launch.js +114 -0
  57. package/dist/utils/target.d.ts +14 -13
  58. package/dist/utils/target.js +20 -39
  59. package/dist/utils/utils.d.ts +101 -1
  60. package/dist/utils/utils.js +97 -7
  61. package/package.json +5 -6
  62. package/dist/utils/bytes.d.ts +0 -1
  63. package/dist/utils/bytes.js +0 -1
  64. package/dist/utils/filter.d.ts +0 -1
  65. package/dist/utils/filter.js +0 -1
@@ -1,7 +1,8 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { basename } from "node:path";
3
- import { discoverProjects, findProjectRoot, listLinkedPackages, resolvePackageRoot } from "@actiondock/core";
4
- import { ExecutionError, notInProjectError, packageNotFoundError } from "../errors.js";
3
+ import { discoverProjects, listLinkedPackages, resolvePackageRoot, } from "@actiondock/core/registry";
4
+ import { findProjectRoot, } from "@actiondock/core";
5
+ import { ExecutionError, notInProjectError, packageNotFoundError, wrapAsExecutionError } from "../errors.js";
5
6
  import { renderResult, writeStdout } from "../renderer.js";
6
7
  import { getEffectiveOptions, parseListOption } from "../utils/index.js";
7
8
  export function registerExportCommand(program, context) {
@@ -16,9 +17,6 @@ export function registerExportCommand(program, context) {
16
17
  .option("--all", "Export all linked packages from global registry")
17
18
  .option("--bundle [name]", "Export multiple packages as a unified composite Skill bundle (defaults to workspace directory name if omitted)")
18
19
  .option("-m, --mode <mode>", "Skill export mode ('source' or 'node')", "source")
19
- .option("-s, --standalone", "Export standalone binary skill (removed)")
20
- .option("-t, --target <target>", "Target compilation platform (removed)")
21
- .option("--bytecode", "Bytecode compilation (removed)")
22
20
  .option("-o, --out <path>", "Output skill directory")
23
21
  .option("-p, --playbook <playbooks...>", "Only export specific playbook(s) and their dependent actions", parseListOption)
24
22
  .option("-a, --actions <actions...>", "Only export specific action(s)", parseListOption)
@@ -31,10 +29,6 @@ export function registerExportCommand(program, context) {
31
29
  .option("--require-reproducible", "Require reproducible build and fail if install scripts must run")
32
30
  .action(async (rawOptions, cmd) => {
33
31
  const options = getEffectiveOptions(rawOptions, cmd);
34
- // 彻底删除 --standalone 单文件编译选项
35
- if (options.standalone || options.target !== undefined || options.bytecode !== undefined) {
36
- throw new ExecutionError("The '--standalone' option and binary target flags have been removed in ActionDock 2.0. Please use '--mode node' for self-contained Node.js directory skills, or '--mode source' for source-based skills.", undefined, "UNSUPPORTED_BUILD_MODE");
37
- }
38
32
  const mode = options.mode === "node" ? "node" : "source";
39
33
  if (options.bundle && mode === "node") {
40
34
  throw new ExecutionError("Composite Skill export (--bundle) currently only supports source mode. Please use '--mode source' to export as a composite workspace Skill.");
@@ -200,10 +194,8 @@ export function registerExportCommand(program, context) {
200
194
  });
201
195
  }
202
196
  catch (err) {
203
- if (err?.code === "UNSUPPORTED_BUILD_MODE") {
204
- throw new ExecutionError(err.message, undefined, "UNSUPPORTED_BUILD_MODE");
205
- }
206
- throw new ExecutionError(`Export failed: ${err.message}`);
197
+ // wrapAsExecutionError 保留原始错误码(含 UNSUPPORTED_BUILD_MODE)
198
+ throw wrapAsExecutionError(err, "Export failed: ");
207
199
  }
208
200
  });
209
201
  }
@@ -1,5 +1,6 @@
1
- import { findProjectRoot, writeActionTypes } from "@actiondock/core";
2
- import { ExecutionError, notInProjectError } from "../errors.js";
1
+ import { findProjectRoot, } from "@actiondock/core";
2
+ import { writeActionTypes, } from "@actiondock/core/project";
3
+ import { notInProjectError, wrapAsExecutionError } from "../errors.js";
3
4
  import { renderResult } from "../renderer.js";
4
5
  import { getEffectiveOptions } from "../utils/index.js";
5
6
  /**
@@ -36,7 +37,7 @@ export function registerGenerateCommands(program, context) {
36
37
  });
37
38
  }
38
39
  catch (err) {
39
- throw new ExecutionError(`Failed to generate types: ${err.message}`);
40
+ throw wrapAsExecutionError(err, "Failed to generate types: ");
40
41
  }
41
42
  });
42
43
  }
@@ -1,4 +1,6 @@
1
- import { fetchRemoteInfo, filterWithFallbackInfo, getRegistryStatus, resolvePackageRoot, } from "@actiondock/core";
1
+ import { fetchRemoteInfo, } from "@actiondock/core/profile";
2
+ import { filterWithFallbackInfo, } from "@actiondock/core/project";
3
+ import { getRegistryStatus, resolvePackageRoot, } from "@actiondock/core/registry";
2
4
  import { ExecutionError, NO_PROJECT_NO_LINKED_MESSAGE, packageNotFoundError } from "../errors.js";
3
5
  import { projectDetailToJson, renderAggregatedPackages, renderProjectDetail, renderRegistryTree, renderResult, } from "../renderer.js";
4
6
  import { getProjectDetailInfo, scanLocalAggregatedPackages } from "../services/index.js";
@@ -1,6 +1,6 @@
1
- import { initProject } from "@actiondock/core";
1
+ import { initProject, } from "@actiondock/core";
2
2
  import { writeStdout } from "../renderer.js";
3
- import { ExecutionError } from "../errors.js";
3
+ import { wrapAsExecutionError } from "../errors.js";
4
4
  export function registerInitCommand(program) {
5
5
  program
6
6
  .command("init [directory]")
@@ -18,7 +18,7 @@ export function registerInitCommand(program) {
18
18
  writeStdout(`Successfully initialized ActionDock project in ${dir}`);
19
19
  }
20
20
  catch (err) {
21
- throw new ExecutionError(err.message);
21
+ throw wrapAsExecutionError(err);
22
22
  }
23
23
  });
24
24
  }
@@ -1,5 +1,5 @@
1
- import { linkPackage, pruneRegistry, unlinkPackage, } from "@actiondock/core";
2
- import { ExecutionError } from "../errors.js";
1
+ import { linkPackage, pruneRegistry, unlinkPackage, } from "@actiondock/core/registry";
2
+ import { wrapAsExecutionError } from "../errors.js";
3
3
  import { writeStdout } from "../renderer.js";
4
4
  export function registerLinkCommands(program) {
5
5
  // ad link [path]
@@ -24,7 +24,7 @@ export function registerLinkCommands(program) {
24
24
  }
25
25
  }
26
26
  catch (err) {
27
- throw new ExecutionError(err.message);
27
+ throw wrapAsExecutionError(err);
28
28
  }
29
29
  });
30
30
  // ad unlink [id|path]
@@ -65,7 +65,7 @@ export function registerLinkCommands(program) {
65
65
  }
66
66
  }
67
67
  catch (err) {
68
- throw new ExecutionError(err.message);
68
+ throw wrapAsExecutionError(err);
69
69
  }
70
70
  });
71
71
  }
@@ -1,7 +1,7 @@
1
- import { filterWithFallbackInfo } from "@actiondock/core";
1
+ import { filterWithFallbackInfo, } from "@actiondock/core/project";
2
2
  import { packageNotFoundError } from "../errors.js";
3
3
  import { renderActionList, renderResult } from "../renderer.js";
4
- import { applyTargetOptions, getEffectiveOptions, resolveFallbackStrategy, resolveIntent, resolveLocalPackageRoot, withTarget, } from "../utils/index.js";
4
+ import { applyTargetOptions, getEffectiveOptions, remoteTargetSuffix, resolveFallbackStrategy, resolveIntent, resolveLocalPackageRoot, withService, } from "../utils/index.js";
5
5
  /**
6
6
  * 挂载 list 子命令至指定 Commander 节点。
7
7
  *
@@ -28,14 +28,23 @@ export function attachListCommand(parent, context) {
28
28
  if (options.package && !targetPackageRoot) {
29
29
  throw packageNotFoundError(options.package);
30
30
  }
31
- // 通过 Target 门面统一获取 Action 列表
32
- await withTarget(options, context, async (target, resolved) => {
33
- let rawSummaries = await target.listActions();
34
- // 若指定了目标包但本地未过滤,则精确匹配包标识
31
+ // 通过 Service 门面统一获取 Action 列表
32
+ await withService(options, context, async (service, resolved) => {
33
+ let rawSummaries = await service.discovery.listActions();
34
+ // 若指定了目标包但本地未过滤,则按包归属精确过滤:
35
+ // 仅保留全限定名命中目标包前缀、或 packageId 归属目标包的条目;
36
+ // 无法归属到目标包的无前缀短 id 条目不放行,避免他包短 id 混入
35
37
  if (options.package && !options.profile && !options.server) {
36
- rawSummaries = rawSummaries.filter((s) => s.id.startsWith(`${options.package}/`) ||
37
- s.packageId === options.package ||
38
- !s.id.includes("/"));
38
+ const targetPkg = options.package;
39
+ rawSummaries = rawSummaries.filter((s) => {
40
+ if (s.packageId) {
41
+ return (s.packageId === targetPkg ||
42
+ s.id === targetPkg ||
43
+ s.id.startsWith(`${targetPkg}/`));
44
+ }
45
+ // 无 packageId 归属信息的条目:仅全限定名前缀命中时放行
46
+ return s.id === targetPkg || s.id.startsWith(`${targetPkg}/`);
47
+ });
39
48
  }
40
49
  const rawList = rawSummaries.map((s) => {
41
50
  let id = s.id;
@@ -59,7 +68,7 @@ export function attachListCommand(parent, context) {
59
68
  return;
60
69
  }
61
70
  const title = resolved.type === "remote"
62
- ? `Actions on remote server ${resolved.serverUrl}${resolved.profileName ? ` (Profile: ${resolved.profileName})` : ""}`
71
+ ? `Actions ${remoteTargetSuffix(resolved)}`
63
72
  : targetPackageRoot
64
73
  ? `Actions in ${options.package || "current project"}`
65
74
  : "Available Actions";
@@ -1,9 +1,22 @@
1
1
  import { resolve } from "node:path";
2
- import { findProjectRoot, formatHostForUrl, loadProjectConfig, parseDuration } from "@actiondock/core";
3
- import { createNodePlatform } from "@actiondock/runtime-node";
4
- import { ArgumentError, ExecutionError } from "../errors.js";
5
- import { writeStderr, writeStdout } from "../renderer.js";
6
- import { getEffectiveOptions, parseByteSize, parseListOption } from "../utils/index.js";
2
+ import { createNodePlatform, findProjectRoot, loadProjectConfig, } from "@actiondock/core";
3
+ import { formatHostForUrl, } from "@actiondock/core/server";
4
+ import { ExecutionError } from "../errors.js";
5
+ import { writeStdout } from "../renderer.js";
6
+ import { getEffectiveOptions, normalizeCorsOrigins, parseListOption, printServerBanner, registerStopSignalHandler, resolveMaxBodyBytes, resolveServerHost, resolveServerPort, resolveServerToken, resolveTimeoutMs, } from "../utils/index.js";
7
+ /**
8
+ * 解析 mcp 命令双分支共享的目标选项(目录、包、超时)。
9
+ */
10
+ function resolveMcpServeOptions(options) {
11
+ return {
12
+ projectRoots: options.dir && options.dir.length > 0
13
+ ? options.dir.map((d) => resolve(d))
14
+ : undefined,
15
+ packageIds: options.package && options.package.length > 0 ? options.package : undefined,
16
+ all: Boolean(options.all),
17
+ timeoutMs: resolveTimeoutMs(options.timeout),
18
+ };
19
+ }
7
20
  /**
8
21
  * 注册 mcp 命令(STDIO 与 HTTP 传输服务模式)。
9
22
  *
@@ -21,20 +34,7 @@ export function registerMcpCommands(program, context) {
21
34
  .option("--data-dir <path>", "Custom database storage directory")
22
35
  .action(async (rawOptions, cmd) => {
23
36
  const options = getEffectiveOptions(rawOptions, cmd);
24
- let timeoutMs;
25
- if (options.timeout) {
26
- try {
27
- timeoutMs = parseDuration(options.timeout);
28
- }
29
- catch (err) {
30
- throw new ArgumentError(`Invalid timeout format: ${err.message}`);
31
- }
32
- }
33
- const projectRoots = options.dir && options.dir.length > 0
34
- ? options.dir.map((d) => resolve(d))
35
- : undefined;
36
- const packageIds = options.package && options.package.length > 0 ? options.package : undefined;
37
- const all = Boolean(options.all);
37
+ const { projectRoots, packageIds, all, timeoutMs } = resolveMcpServeOptions(options);
38
38
  try {
39
39
  const { startMcpStdio } = await import("@actiondock/mcp");
40
40
  await startMcpStdio({
@@ -69,38 +69,15 @@ export function registerMcpCommands(program, context) {
69
69
  .option("--data-dir <path>", "Custom database storage directory")
70
70
  .action(async (rawOptions, cmd) => {
71
71
  const options = getEffectiveOptions(rawOptions, cmd);
72
- const port = parseInt(options.port, 10) || 5178;
73
- const host = options.host || "127.0.0.1";
74
- const token = options.token ||
75
- (options.tokenEnv && typeof process !== "undefined" ? process.env?.[options.tokenEnv] : undefined) ||
76
- (typeof process !== "undefined" ? process.env?.ACTIONDOCK_TOKEN : undefined);
72
+ const port = resolveServerPort(options.port, 5178, true);
73
+ const host = resolveServerHost(options.host);
74
+ const token = resolveServerToken(options.token, options.tokenEnv);
77
75
  const allowInsecureNoAuth = Boolean(options.allowInsecureNoAuth);
78
76
  const allowInsecureHttp = Boolean(options.allowInsecureHttp);
79
77
  const allowQueryToken = Boolean(options.allowQueryToken);
80
- const corsOrigins = options.corsOrigin && options.corsOrigin.length > 0 ? options.corsOrigin : undefined;
81
- let maxBodyBytes;
82
- if (options.maxBody) {
83
- try {
84
- maxBodyBytes = parseByteSize(options.maxBody);
85
- }
86
- catch (err) {
87
- throw new ArgumentError(`Invalid max-body format: ${err.message}`);
88
- }
89
- }
90
- let timeoutMs;
91
- if (options.timeout) {
92
- try {
93
- timeoutMs = parseDuration(options.timeout);
94
- }
95
- catch (err) {
96
- throw new ArgumentError(`Invalid timeout format: ${err.message}`);
97
- }
98
- }
99
- const projectRoots = options.dir && options.dir.length > 0
100
- ? options.dir.map((d) => resolve(d))
101
- : undefined;
102
- const packageIds = options.package && options.package.length > 0 ? options.package : undefined;
103
- const all = Boolean(options.all);
78
+ const corsOrigins = normalizeCorsOrigins(options.corsOrigin);
79
+ const maxBodyBytes = resolveMaxBodyBytes(options.maxBody);
80
+ const { projectRoots, packageIds, all, timeoutMs } = resolveMcpServeOptions(options);
104
81
  let targetDescription = "ActionDock MCP Server";
105
82
  if (all) {
106
83
  targetDescription = "All Linked Packages (Global Registry Mode)";
@@ -161,30 +138,14 @@ export function registerMcpCommands(program, context) {
161
138
  });
162
139
  const displayHost = formatHostForUrl(host);
163
140
  const actualMcpHost = formatHostForUrl(host === "0.0.0.0" ? "127.0.0.1" : host);
164
- writeStdout(`\n======================================================`, context);
165
- writeStdout(` ActionDock 2.0 MCP HTTP Server`, context);
166
- writeStdout(`======================================================`, context);
167
- writeStdout(` * Listening on: http://${displayHost}:${server.port}`, context);
141
+ printServerBanner(`ActionDock 2.0 MCP HTTP Server`, "http", displayHost, server.port, context);
168
142
  writeStdout(` * MCP Endpoint: http://${actualMcpHost}:${server.port}/mcp`, context);
169
143
  writeStdout(` * Target: ${targetDescription}`, context);
170
144
  writeStdout(` * Authentication: ${token ? "Bearer Token Enabled" : "Disabled (Local)"}`, context);
171
145
  writeStdout(` * CORS Origins: ${corsOrigins ? corsOrigins.join(", ") : "Disabled (Default)"}`, context);
172
146
  writeStdout(`======================================================\n`, context);
173
147
  writeStdout(`Press Ctrl+C to terminate.\n`, context);
174
- const stopSignalHandler = () => {
175
- writeStdout("\nStopping MCP HTTP server...", context);
176
- Promise.resolve(server.stop())
177
- .catch((err) => {
178
- // 停止失败必须可见:写入 stderr 一行并标记失败退出码
179
- writeStderr(`[ERROR] Failed to stop MCP HTTP server gracefully: ${err instanceof Error ? err.message : String(err)}`, context);
180
- process.exitCode = 1;
181
- })
182
- .finally(() => {
183
- process.exit(typeof process.exitCode === "number" ? process.exitCode : 0);
184
- });
185
- };
186
- process.once("SIGINT", stopSignalHandler);
187
- process.once("SIGTERM", stopSignalHandler);
148
+ registerStopSignalHandler(server, `MCP HTTP server`, context);
188
149
  }
189
150
  catch (err) {
190
151
  throw new ExecutionError(`Failed to start ActionDock MCP HTTP server: ${err.message}`, err);
@@ -1,7 +1,6 @@
1
- import { findProjectRoot, resolvePackageRoot } from "@actiondock/core";
2
- import { ExecutionError, notInProjectError, packageNotFoundError } from "../errors.js";
1
+ import { ExecutionError } from "../errors.js";
3
2
  import { renderResult, writeStdout } from "../renderer.js";
4
- import { getEffectiveOptions } from "../utils/index.js";
3
+ import { getEffectiveOptions, requirePackageRoot } from "../utils/index.js";
5
4
  export function registerPackCommand(program, context) {
6
5
  program
7
6
  .command("pack")
@@ -12,15 +11,7 @@ export function registerPackCommand(program, context) {
12
11
  .option("--json", "Output as JSON")
13
12
  .action(async (rawOptions, cmd) => {
14
13
  const options = getEffectiveOptions(rawOptions, cmd);
15
- const root = options.package
16
- ? resolvePackageRoot(options.package)
17
- : findProjectRoot();
18
- if (!root) {
19
- if (options.package) {
20
- throw packageNotFoundError(options.package);
21
- }
22
- throw notInProjectError("Please specify -P, --package <id> or cd into a project directory.");
23
- }
14
+ const { root } = requirePackageRoot(options.package);
24
15
  const isMachine = Boolean(options.json);
25
16
  if (!isMachine && !options.dryRun) {
26
17
  writeStdout("Packing Action package...");
@@ -1,9 +1,13 @@
1
1
  import { existsSync, mkdirSync, writeFileSync } from "node:fs";
2
2
  import { dirname, isAbsolute, relative, resolve } from "node:path";
3
- import { assertPathWithinRoot, fetchRemotePlaybooks, fetchRemotePlaybookShow, filterWithFallbackInfo, findProjectRoot, listLinkedPackages, loadActions, loadManifest, loadPlaybooks, loadProjectConfig, resolveActionProject, resolvePackageRoot, resolvePlaybookProject, saveManifest, } from "@actiondock/core";
4
- import { ArgumentError, CliError, ExecutionError, NO_PROJECT_NO_LINKED_MESSAGE, notInProjectError, packageNotFoundError, } from "../errors.js";
3
+ import { assertPathWithinRoot, loadActions, loadManifest, loadPlaybooks, saveManifest, filterWithFallbackInfo, } from "@actiondock/core/project";
4
+ import { DefaultActionCatalog, PackageGraphBuilder, resolveAction, resolvePlaybook, } from "@actiondock/core/graph";
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";
5
9
  import { renderPlaybookDetail, renderPlaybookList, renderResult, writeStdout, } from "../renderer.js";
6
- import { applyTargetOptions, getEffectiveOptions, resolveIntent, resolveTargetFromOptions, } from "../utils/index.js";
10
+ import { applyTargetOptions, getEffectiveOptions, remoteTargetSuffix, resolveFallbackStrategy, resolveIntent, resolveTargetFromOptions, } from "../utils/index.js";
7
11
  /**
8
12
  * 注册 Playbook 命令集合(list, show, validate, create)。
9
13
  *
@@ -20,13 +24,13 @@ export function registerPlaybookCommands(program, context) {
20
24
  .description("List playbooks in current project or linked packages")
21
25
  .option("-i, --intent <pattern>", "Regex or fuzzy intent filter; falls back to full list when no match")
22
26
  .option("-P, --package <id>", "Target package ID or path"))
27
+ .option("--fallback", "Enable fallback to full list when no items match intent")
23
28
  .option("--no-fallback", "Disable fallback to full list when no items match intent")
24
29
  .option("--json", "Output as JSON")
25
30
  .action(async (patterns = [], rawOptions, cmd) => {
26
31
  const options = getEffectiveOptions(rawOptions, cmd);
27
32
  const effectiveIntent = resolveIntent(options.intent, patterns);
28
- const shouldFallback = options.fallback !== false;
29
- const isMachine = Boolean(options.json);
33
+ const { shouldFallback, isMachine } = resolveFallbackStrategy(options);
30
34
  // 1. 远端服务分支
31
35
  const target = resolveTargetFromOptions(options, context);
32
36
  if (target.type === "remote") {
@@ -38,7 +42,7 @@ export function registerPlaybookCommands(program, context) {
38
42
  });
39
43
  renderResult(remotePbs, {
40
44
  json: options.json,
41
- humanFormatter: () => renderPlaybookList(remotePbs, `Playbooks on remote server ${target.serverUrl}${target.profileName ? ` (Profile: ${target.profileName})` : ""}`, false, effectiveIntent),
45
+ humanFormatter: () => renderPlaybookList(remotePbs, `Playbooks ${remoteTargetSuffix(target)}`, false, effectiveIntent),
42
46
  context,
43
47
  });
44
48
  return;
@@ -187,7 +191,12 @@ export function registerPlaybookCommands(program, context) {
187
191
  }
188
192
  let resolved;
189
193
  try {
190
- resolved = resolvePlaybookProject(showTarget);
194
+ const graph = new PackageGraphBuilder({
195
+ root: findProjectRoot() || undefined,
196
+ customHome: context?.customHome,
197
+ allowDevLinks: true,
198
+ }).buildSync();
199
+ resolved = resolvePlaybook(showTarget, { graph });
191
200
  }
192
201
  catch (err) {
193
202
  throw new ArgumentError(err.message);
@@ -243,7 +252,12 @@ export function registerPlaybookCommands(program, context) {
243
252
  else if (id) {
244
253
  let resolved;
245
254
  try {
246
- resolved = resolvePlaybookProject(id);
255
+ const graph = new PackageGraphBuilder({
256
+ root: findProjectRoot() || undefined,
257
+ customHome: context?.customHome,
258
+ allowDevLinks: true,
259
+ }).buildSync();
260
+ resolved = resolvePlaybook(id, { graph });
247
261
  }
248
262
  catch (err) {
249
263
  throw new ArgumentError(err.message);
@@ -277,6 +291,12 @@ export function registerPlaybookCommands(program, context) {
277
291
  }
278
292
  }
279
293
  const results = [];
294
+ const graph = new PackageGraphBuilder({
295
+ root: findProjectRoot() || undefined,
296
+ customHome: context?.customHome,
297
+ allowDevLinks: true,
298
+ }).buildSync();
299
+ const catalog = new DefaultActionCatalog(graph);
280
300
  for (const target of targets) {
281
301
  const config = loadProjectConfig(target.root);
282
302
  const manifest = loadManifest(target.root);
@@ -294,7 +314,7 @@ export function registerPlaybookCommands(program, context) {
294
314
  for (const actRef of pb.actions) {
295
315
  if (actRef.includes("/")) {
296
316
  try {
297
- await resolveActionProject(actRef);
317
+ resolveAction(actRef, { graph, catalog, caller: target.packageId });
298
318
  }
299
319
  catch (e) {
300
320
  errors.push(`Referenced cross-package action '${actRef}' not resolvable: ${e.message}`);
@@ -408,6 +428,6 @@ This playbook provides task execution guidance for AI Agents.
408
428
  if (err instanceof CliError) {
409
429
  throw err;
410
430
  }
411
- throw new ExecutionError(err.message);
431
+ throw wrapAsExecutionError(err);
412
432
  }
413
433
  }
@@ -1,5 +1,6 @@
1
- import { addProfile, checkRemoteHealth, filterWithFallbackInfo, getProfile, listProfiles, loadProfiles, maskSecretValue, removeProfile, resolveProfileToken, resolveTarget, toSnakeUpperCase, updateProfile, useProfile, } from "@actiondock/core";
2
- import { ArgumentError, CliError, ExecutionError, } from "../errors.js";
1
+ import { addProfile, checkRemoteHealth, getProfile, listProfiles, loadProfiles, removeProfile, resolveProfileToken, resolveTarget, toSnakeUpperCase, updateProfile, useProfile, } from "@actiondock/core/profile";
2
+ import { filterWithFallbackInfo, maskSecretValue, } from "@actiondock/core/project";
3
+ import { ArgumentError, ExecutionError, wrapAsExecutionError, } from "../errors.js";
3
4
  import { renderResult, writeStderr, writeStdout } from "../renderer.js";
4
5
  import { getEffectiveOptions, resolveFallbackStrategy, resolveIntent } from "../utils/index.js";
5
6
  export function registerProfileCommands(program) {
@@ -76,9 +77,7 @@ export function registerProfileCommands(program) {
76
77
  });
77
78
  }
78
79
  catch (err) {
79
- if (err instanceof ArgumentError || err instanceof CliError)
80
- throw err;
81
- throw new ExecutionError(err.message);
80
+ throw wrapAsExecutionError(err);
82
81
  }
83
82
  });
84
83
  // ad profile add <name>
@@ -105,7 +104,7 @@ export function registerProfileCommands(program) {
105
104
  writeStdout(`[OK] Profile '${name}' configured for server: ${options.server}${options.insecure ? " (insecure: true)" : ""}`);
106
105
  }
107
106
  catch (err) {
108
- throw new ExecutionError(err.message);
107
+ throw wrapAsExecutionError(err);
109
108
  }
110
109
  });
111
110
  // ad profile update <name>
@@ -140,7 +139,7 @@ export function registerProfileCommands(program) {
140
139
  writeStdout(`[OK] Profile '${name}' updated`);
141
140
  }
142
141
  catch (err) {
143
- throw new ExecutionError(err.message);
142
+ throw wrapAsExecutionError(err);
144
143
  }
145
144
  });
146
145
  // ad profile use <name>
@@ -153,7 +152,7 @@ export function registerProfileCommands(program) {
153
152
  writeStdout(`[OK] Active profile switched to '${name}'`);
154
153
  }
155
154
  catch (err) {
156
- throw new ExecutionError(err.message);
155
+ throw wrapAsExecutionError(err);
157
156
  }
158
157
  });
159
158
  // ad profile show [name]
@@ -221,10 +220,7 @@ export function registerProfileCommands(program) {
221
220
  });
222
221
  }
223
222
  catch (err) {
224
- if (err instanceof ArgumentError || err instanceof CliError) {
225
- throw err;
226
- }
227
- throw new ExecutionError(err.message);
223
+ throw wrapAsExecutionError(err);
228
224
  }
229
225
  });
230
226
  // ad profile rm / remove <name>
@@ -244,10 +240,7 @@ export function registerProfileCommands(program) {
244
240
  writeStdout(`[OK] Profile '${name}' removed`);
245
241
  }
246
242
  catch (err) {
247
- if (err instanceof CliError) {
248
- throw err;
249
- }
250
- throw new ExecutionError(err.message);
243
+ throw wrapAsExecutionError(err);
251
244
  }
252
245
  });
253
246
  // ad profile test [name]
@@ -290,9 +283,7 @@ export function registerProfileCommands(program) {
290
283
  }
291
284
  }
292
285
  catch (err) {
293
- if (err instanceof ArgumentError || err instanceof CliError)
294
- throw err;
295
- throw new ExecutionError(err.message);
286
+ throw wrapAsExecutionError(err);
296
287
  }
297
288
  });
298
289
  }
@@ -1,8 +1,7 @@
1
- import { resolve } from "node:path";
2
- import { beginTransaction, findProjectRoot, getInstallCommand, loadLockfile, loadManifest, saveLockfile, saveManifest, } from "@actiondock/core";
3
- import { ArgumentError, ExecutionError, notInProjectError } from "../errors.js";
1
+ import { beginTransaction, getInstallCommand, loadLockfile, saveLockfile, saveManifest, } from "@actiondock/core/project";
2
+ import { ArgumentError, ExecutionError } from "../errors.js";
4
3
  import { renderResult, writeStderr } from "../renderer.js";
5
- import { assertSafePackageSpec, getEffectiveOptions, spawnAsync } from "../utils/index.js";
4
+ import { assertSafePackageSpec, getEffectiveOptions, requireProjectManifestRoot, rollbackAndRethrow, spawnAsync, } from "../utils/index.js";
6
5
  /**
7
6
  * 注册 ad remove 依赖移除命令。
8
7
  * 遵循规范:移除依赖并更新锁文件,保留该包的历史存储数据,检查反向引用防止误删。
@@ -15,14 +14,7 @@ export function registerRemoveCommand(program, context) {
15
14
  .option("--json", "Output as JSON")
16
15
  .action(async (packageIdentifier, rawOptions, cmd) => {
17
16
  const options = getEffectiveOptions(rawOptions, cmd);
18
- const root = options.package ? resolve(options.package) : findProjectRoot();
19
- if (!root) {
20
- throw notInProjectError("Please specify -P, --package <path> or cd into a project directory.");
21
- }
22
- const manifest = loadManifest(root);
23
- if (!manifest) {
24
- throw new ArgumentError(`actiondock.json not found in ${root}`);
25
- }
17
+ const { root, manifest } = requireProjectManifestRoot(options.package);
26
18
  // 寻址逻辑包标识
27
19
  let targetPackageId = null;
28
20
  let npmPackageName = packageIdentifier;
@@ -115,8 +107,7 @@ export function registerRemoveCommand(program, context) {
115
107
  });
116
108
  }
117
109
  catch (err) {
118
- await tx.rollback({ frozenInstall: false });
119
- throw new ExecutionError(err.message, err, err.code || "REMOVE_DEPENDENCY_FAILED");
110
+ await rollbackAndRethrow(tx, err, "REMOVE_DEPENDENCY_FAILED");
120
111
  }
121
112
  });
122
113
  }
@@ -1,4 +1,4 @@
1
- import { type InvocationControl } from "@actiondock/core";
1
+ import { type InvocationControl } from "@actiondock/core/server";
2
2
  import type { ExecutionResult } from "@actiondock/sdk";
3
3
  import { Command } from "commander";
4
4
  import type { CliContext } from "../types.js";
@@ -10,7 +10,10 @@ import type { CliContext } from "../types.js";
10
10
  */
11
11
  export declare function renderRawExecutionResult(targetRef: string, result: ExecutionResult, context?: CliContext): void;
12
12
  /**
13
- * 统一执行 Action 核心逻辑(使用 ActionDockTarget 门面)。
13
+ * 统一执行 Action 核心逻辑(使用 ActionDockService 服务端口)。
14
+ *
15
+ * @param id 目标 Action 标识
16
+ * @param options 执行选项(由 Command 统一构造)
14
17
  */
15
18
  export declare function executeAction(id: string, options: any, context?: CliContext, flatArgs?: string[], control?: InvocationControl): Promise<void>;
16
19
  /**
@@ -1,7 +1,8 @@
1
- import { parseDuration, resolveTarget, validateActionInputValue, mapInputValidationFailure, } from "@actiondock/core";
1
+ import { validateActionInputValue, mapInputValidationFailure, } from "@actiondock/core/project";
2
+ import { resolveTarget, } from "@actiondock/core/profile";
2
3
  import { ArgumentError, ExecutionError, SigintError, packageNotFoundError } from "../errors.js";
3
4
  import { writeStderr, writeStdout } from "../renderer.js";
4
- import { applyTargetOptions, getEffectiveOptions, resolveActionInput, resolveLocalPackageRoot, withTarget, } from "../utils/index.js";
5
+ import { applyTargetOptions, getEffectiveOptions, resolveActionInput, resolveLocalPackageRoot, resolveTimeoutMs, withService, } from "../utils/index.js";
5
6
  /**
6
7
  * 以原始纯文本形式渲染 Action 执行终态结果(默认纯文本模式)。
7
8
  *
@@ -95,7 +96,10 @@ export function renderRawExecutionResult(targetRef, result, context) {
95
96
  }
96
97
  }
97
98
  /**
98
- * 统一执行 Action 核心逻辑(使用 ActionDockTarget 门面)。
99
+ * 统一执行 Action 核心逻辑(使用 ActionDockService 服务端口)。
100
+ *
101
+ * @param id 目标 Action 标识
102
+ * @param options 执行选项(由 Command 统一构造)
99
103
  */
100
104
  export async function executeAction(id, options, context, flatArgs, control) {
101
105
  if (!id) {
@@ -114,15 +118,7 @@ export async function executeAction(id, options, context, flatArgs, control) {
114
118
  if (!check.valid) {
115
119
  throw mapInputValidationFailure("cli-pre-target", check);
116
120
  }
117
- let timeoutMs;
118
- if (options.timeout) {
119
- try {
120
- timeoutMs = parseDuration(options.timeout);
121
- }
122
- catch (err) {
123
- throw new ArgumentError(`Invalid timeout format: ${err.message}`);
124
- }
125
- }
121
+ const timeoutMs = resolveTimeoutMs(options.timeout);
126
122
  const configOverrides = {};
127
123
  if (options.config) {
128
124
  const list = Array.isArray(options.config) ? options.config : [options.config];
@@ -149,14 +145,14 @@ export async function executeAction(id, options, context, flatArgs, control) {
149
145
  throw packageNotFoundError(options.package);
150
146
  }
151
147
  let targetRef = id;
152
- if (options.package && !id.includes("/") && !id.includes(":")) {
148
+ if (options.package && !id.includes("/")) {
153
149
  targetRef = `${options.package}/${id}`;
154
150
  }
155
- // 通过 Target 统一执行
156
- await withTarget(options, context, async (target) => {
151
+ // 通过 Service 统一执行
152
+ await withService(options, context, async (service) => {
157
153
  const isMachine = Boolean(options.json);
158
154
  if (options.async) {
159
- const ticket = await target.startAction(targetRef, input, {
155
+ const ticket = await service.execution.start(targetRef, input, {
160
156
  signal: effectiveSignal,
161
157
  timeoutMs,
162
158
  config: configOverrides,
@@ -182,7 +178,7 @@ export async function executeAction(id, options, context, flatArgs, control) {
182
178
  }
183
179
  }
184
180
  else {
185
- const result = await target.runAction(targetRef, input, {
181
+ const result = await service.execution.run(targetRef, input, {
186
182
  signal: effectiveSignal,
187
183
  timeoutMs,
188
184
  config: configOverrides,
@@ -208,10 +204,13 @@ export async function executeAction(id, options, context, flatArgs, control) {
208
204
  }, { localRoot: targetPackageRoot || undefined, scanLinkedPackages: true, ownDataDir: true });
209
205
  }
210
206
  catch (err) {
207
+ // sigint 取消源、中断链路特征错误码或 SigintError 本身,均统一归并为 SigintError
208
+ // 原 if 中额外的「cancellationSource 为 sigint 且 AbortError/aborted/ACTION_CANCELLED」
209
+ // 子句被首项 cancellationSource === "sigint" 完全蕴含,等价化简后移除
211
210
  const isSigint = effectiveControl?.cancellationSource === "sigint" ||
212
- err?.message?.includes("Interrupted by SIGINT");
213
- if (isSigint &&
214
- (err?.name === "AbortError" || effectiveSignal?.aborted || err?.message?.includes("SIGINT") || err instanceof SigintError)) {
211
+ err?.code === "SIGINT_INTERRUPTED" ||
212
+ err instanceof SigintError;
213
+ if (isSigint) {
215
214
  throw new SigintError();
216
215
  }
217
216
  throw err;