@actiondock/cli 2.0.12 → 2.2.1

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 (100) hide show
  1. package/README.md +41 -34
  2. package/bin/ad.js +28 -41
  3. package/dist/commands/action/create.d.ts +9 -1
  4. package/dist/commands/action/create.js +120 -0
  5. package/dist/commands/add.d.ts +7 -0
  6. package/dist/commands/add.js +144 -0
  7. package/dist/commands/build.d.ts +2 -1
  8. package/dist/commands/build.js +83 -0
  9. package/dist/commands/config/delete.d.ts +9 -0
  10. package/dist/commands/config/delete.js +52 -0
  11. package/dist/commands/config/env.d.ts +9 -0
  12. package/dist/commands/config/env.js +85 -0
  13. package/dist/commands/config/get.d.ts +9 -0
  14. package/dist/commands/config/get.js +120 -0
  15. package/dist/commands/config/index.d.ts +9 -0
  16. package/dist/commands/config/index.js +23 -0
  17. package/dist/commands/config/list.d.ts +9 -0
  18. package/dist/commands/config/list.js +137 -0
  19. package/dist/commands/config/schema.d.ts +9 -0
  20. package/dist/commands/config/schema.js +121 -0
  21. package/dist/commands/config/set.d.ts +9 -0
  22. package/dist/commands/config/set.js +85 -0
  23. package/dist/commands/describe.d.ts +9 -0
  24. package/dist/commands/describe.js +68 -0
  25. package/dist/commands/doctor.d.ts +2 -1
  26. package/dist/commands/doctor.js +82 -0
  27. package/dist/commands/export.d.ts +2 -1
  28. package/dist/commands/export.js +212 -0
  29. package/dist/commands/generate.d.ts +9 -0
  30. package/dist/commands/generate.js +44 -0
  31. package/dist/commands/index.d.ts +31 -1
  32. package/dist/commands/index.js +111 -0
  33. package/dist/commands/info.d.ts +9 -2
  34. package/dist/commands/info.js +210 -0
  35. package/dist/commands/init.js +24 -0
  36. package/dist/commands/link.js +71 -0
  37. package/dist/commands/list.d.ts +9 -0
  38. package/dist/commands/list.js +77 -0
  39. package/dist/commands/mcp.d.ts +9 -2
  40. package/dist/commands/mcp.js +173 -0
  41. package/dist/commands/new.d.ts +9 -0
  42. package/dist/commands/new.js +33 -0
  43. package/dist/commands/pack.d.ts +3 -0
  44. package/dist/commands/pack.js +63 -0
  45. package/dist/commands/playbook.d.ts +7 -3
  46. package/dist/commands/playbook.js +433 -0
  47. package/dist/commands/profile.js +253 -0
  48. package/dist/commands/remove.d.ts +7 -0
  49. package/dist/commands/remove.js +118 -0
  50. package/dist/commands/run.d.ts +13 -0
  51. package/dist/commands/run.js +146 -0
  52. package/dist/commands/runs.d.ts +9 -2
  53. package/dist/commands/runs.js +245 -0
  54. package/dist/commands/serve.d.ts +9 -2
  55. package/dist/commands/serve.js +135 -0
  56. package/dist/commands/state-list.d.ts +24 -0
  57. package/dist/commands/state-list.js +82 -0
  58. package/dist/commands/state.d.ts +9 -2
  59. package/dist/commands/state.js +310 -0
  60. package/dist/commands/test.js +44 -0
  61. package/dist/commands/validate.d.ts +9 -0
  62. package/dist/commands/validate.js +106 -0
  63. package/dist/errors.d.ts +43 -0
  64. package/dist/errors.js +103 -0
  65. package/dist/index.d.ts +8 -1
  66. package/dist/index.js +41 -1000
  67. package/dist/prompt.d.ts +53 -0
  68. package/dist/prompt.js +192 -0
  69. package/dist/renderer.d.ts +197 -0
  70. package/dist/renderer.js +502 -0
  71. package/dist/services/index.d.ts +1 -0
  72. package/dist/services/index.js +1 -0
  73. package/dist/services/inspect.d.ts +23 -0
  74. package/dist/services/inspect.js +96 -0
  75. package/dist/standalone/index.d.ts +16 -0
  76. package/dist/standalone/index.js +22 -0
  77. package/dist/types.d.ts +132 -0
  78. package/dist/types.js +9 -0
  79. package/dist/utils/bytes.d.ts +1 -1
  80. package/dist/utils/bytes.js +1 -0
  81. package/dist/utils/duration.js +1 -0
  82. package/dist/utils/filter.d.ts +1 -1
  83. package/dist/utils/filter.js +1 -0
  84. package/dist/utils/index.d.ts +6 -0
  85. package/dist/utils/index.js +6 -0
  86. package/dist/utils/spawn.d.ts +27 -0
  87. package/dist/utils/spawn.js +35 -0
  88. package/dist/utils/target.d.ts +45 -0
  89. package/dist/utils/target.js +78 -0
  90. package/dist/utils/utils.d.ts +38 -0
  91. package/dist/utils/utils.js +111 -0
  92. package/package.json +10 -12
  93. package/dist/commands/action/index.d.ts +0 -16
  94. package/dist/commands/action/list.d.ts +0 -5
  95. package/dist/commands/action/run.d.ts +0 -4
  96. package/dist/commands/action/show.d.ts +0 -5
  97. package/dist/commands/action/sync.d.ts +0 -2
  98. package/dist/commands/action/validate.d.ts +0 -5
  99. package/dist/commands/action.d.ts +0 -1
  100. package/dist/commands/config.d.ts +0 -2
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ActionDock 2.0 官方命令行门面工具链。
4
4
 
5
- [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D22.13.0-green?logo=node.js)](https://nodejs.org/)
5
+ [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D24.12.0-green?logo=node.js)](https://nodejs.org/)
6
6
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue?logo=typescript)](https://www.typescriptlang.org/)
7
7
  [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
8
8
 
@@ -12,10 +12,10 @@ ActionDock 2.0 官方命令行门面工具链。
12
12
 
13
13
  ## 运行环境
14
14
 
15
- - 原生基于 Node.js 22.13.0 或更高版本运行。
16
- - 支持 npm、pnpm 与 yarn 进行全局安装或项目级管理。
17
- - 日常开发、调试、测试、MCP 服务启动与 HTTP 部署完全基于 Node.js,无需安装 Bun。
18
- - 仅在使用 `ad build` 编译单文件独立二进制产物时,需要系统安装外部 Bun 编译器。
15
+ - 原生基于 Node.js >=24.12.0 运行,充分依托原生类型擦除、内置 SQLite 与原生 HTTP 特性。
16
+ - 支持 npm、pnpm 与 yarn 标准包管理器进行全局安装与项目级管理。
17
+ - 日常开发、调试、测试、构建、打包、MCP 服务启动与 HTTP 部署完全基于 Node.js 与 npm 标准工作流,无需安装外部编译器。
18
+ - 单文件独立二进制构建(`--target`、`--bytecode`、`--standalone`)与 `actiondock.manifest.json` 已在 2.0 中彻底移除,由标准的 Node.js 目录交付产物与 npm 打包取代。
19
19
 
20
20
  ---
21
21
 
@@ -33,19 +33,6 @@ npm install -g @actiondock/cli
33
33
  npx ad --help
34
34
  ```
35
35
 
36
- ### 从源码安装本地开发版
37
-
38
- 若需从仓库源码调试最新开发版本:
39
-
40
- ```bash
41
- git clone https://github.com/team4u/actiondock.git
42
- cd actiondock
43
- bun install
44
- bun run build
45
- cd packages/cli
46
- npm link
47
- ```
48
-
49
36
  ---
50
37
 
51
38
  ## 快速上手流程
@@ -57,6 +44,11 @@ cd my-tools
57
44
  npm install
58
45
  ```
59
46
 
47
+ - 安装并锁定依赖(基于 actiondock.lock.json 与原子事务):
48
+ ```bash
49
+ ad add @actiondock/example-tools
50
+ ```
51
+
60
52
  - 本地执行 Action:
61
53
  ```bash
62
54
  ad run sample.greet --input '{"name": "Alice"}'
@@ -74,30 +66,39 @@ ad mcp
74
66
 
75
67
  - 导出为便携式 Agent Skill 技能包:
76
68
  ```bash
69
+ # 导出源码模式技能
77
70
  ad export skill
71
+
72
+ # 导出自包含 Node.js 目录模式技能
73
+ ad export skill --mode node
78
74
  ```
79
75
 
80
- - 编译为零依赖单文件独立二进制产物(需外部安装 Bun):
76
+ - 构建 Node.js 目录交付产物:
81
77
  ```bash
82
78
  ad build
83
79
  ```
84
80
 
81
+ - 打包为标准 npm tarball:
82
+ ```bash
83
+ ad pack
84
+ ```
85
+
85
86
  ---
86
87
 
87
- ## 通用选项与机器调用契约
88
+ ## 通用选项与调用契约
88
89
 
89
- 所有 CLI 子命令均支持前置或后置传入通用控制选项:
90
+ 所有 CLI 子命令均支持传入通用控制选项:
90
91
 
91
- - `-v, -V, --version`:打印版本号并以状态码 0 退出。
92
+ - `-v, -V, --version`:打印版本号并退出。
92
93
  - `-h, --help`:打印命令帮助并退出。
93
94
  - `--json`:以标准 JSON 格式输出结果。
94
95
  - `--envelope`:将输出包装为标准信封结构对象(包含 `ok: true, data: T` 或 `ok: false, error: { code, message, details }`)。
95
96
  - `--data-dir <path>`:指定自定义数据存储目录,实现多测试或多任务数据隔离。
96
97
 
97
- ### 精确目标解析机制
98
+ ### 严格目标解析机制
98
99
 
99
- - **严格目标定位**:通过 `-P, --package <id|path>` 指定目标包。系统严格区分物理路径与注册表包标识符,若目标不存在则坚决不向当前目录或父级目录隐式回退,直接以参数校验错误退出(状态码 2)。
100
- - **多目标检索契约**:在执行多目标检索(如 `ad info [keywords]` 或 `ad action list [keywords]`)时,若无任何匹配项,在机器模式下始终返回确定性空数组结构并以状态码 0 退出,绝不因空搜索产生异常中断。
100
+ - 严格目标定位:通过 `-P, --package <id|path>` 指定目标包。系统严格区分物理路径与注册表包标识符,若目标不存在则不向当前目录或父级目录隐式回退,直接以参数校验错误退出。
101
+ - 多目标检索契约:在执行多目标检索(如 `ad info` 或 `ad list`)时,若无任何匹配项,在机器模式下始终返回确定性空数组结构并以状态码 0 退出,不因空搜索产生异常中断。
101
102
 
102
103
  ---
103
104
 
@@ -106,17 +107,23 @@ ad build
106
107
  | 命令 | 说明 |
107
108
  |---|---|
108
109
  | `ad init [dir]` | 初始化 Action Package 项目脚手架 |
110
+ | `ad add <package>` | 安装并锁定 Action 依赖,更新 actiondock.lock.json 并提供原子回滚保护 |
111
+ | `ad remove <package>` | 卸载并更新锁定依赖,提供原子回滚保护 |
109
112
  | `ad info [patterns...]` | 检索包元数据与能力清单,支持模式匹配与树形展示 |
113
+ | `ad list [patterns...]` | 列出包内所有已注册的 Action |
114
+ | `ad describe <id>` | 查看 Action 的详情、参数与模式规范(别名 `ad show`) |
115
+ | `ad run <id>` | 本地或远程执行指定 Action 并输出标准信封结果 |
116
+ | `ad validate [id]` | 校验 Action 规范与模式规范 |
110
117
  | `ad doctor` | 执行运行环境与项目结构健康诊断 |
111
- | `ad run <id>` | 本地执行指定 Action 并输出标准信封结果 |
112
- | `ad action list` | 列出包内所有已注册的 Action |
118
+ | `ad action create <id>` | 创建新 Action 源码(别名 `ad action new`) |
113
119
  | `ad playbook list` / `show` | 查看智能体操作规程 Playbook |
114
120
  | `ad config list` / `get` / `set` | 管理包运行时配置与环境变量绑定 |
115
121
  | `ad state list` / `get` / `set` / `delete` / `clear` | 查看与维护 SQLite 持久化状态 |
116
122
  | `ad runs list` / `show` | 查询任务执行历史与追踪记录 |
117
123
  | `ad test` | 执行快速单元测试 |
118
- | `ad build` | 调度外部 Bun 编译器打包单文件独立二进制产物 |
119
- | `ad export skill` | 导出包含操作规程的 Agent Skill 技能包 |
124
+ | `ad build` | 构建 Node.js 目录交付产物(支持 `--vendor-deps` 与 `--archive`) |
125
+ | `ad pack` | 打包为标准 npm tarball(`.tgz`),支持 `--dry-run` |
126
+ | `ad export skill` | 导出 Agent Skill 技能包(支持 `--mode source` 与 `--mode node`) |
120
127
  | `ad link` / `unlink` | 注册或注销工作区全局路由与符号链接 |
121
128
  | `ad profile` | 管理远程执行节点凭证与环境配置 |
122
129
  | `ad serve` | 启动远程 HTTP 执行调度微服务 |
@@ -126,12 +133,12 @@ ad build
126
133
 
127
134
  ## 架构集成
128
135
 
129
- 作为顶层门面,`@actiondock/cli` 串联以下子包:
136
+ 作为顶层门面与独立运行分发器,`@actiondock/cli` 串联以下子包:
130
137
 
131
- - 运行时适配:依赖 `@actiondock/runtime-node`,在启动时自动注入基于 Node.js 原生能力的驱动实现。
132
- - 共享命令集:复用 `@actiondock/runtime-cli` 的命令处理器与标准信封输出渲染器。
133
- - 构建与打包:调用 `@actiondock/builder` 完成依赖闭包分析与产物装配。
134
- - 协议服务:通过 `@actiondock/mcp` 与 `@actiondock/core` 启动协议监听。
138
+ - 运行时适配:依赖 [@actiondock/runtime-node](../runtime-node/README.md),在启动时自动注入基于 Node.js 原生能力的驱动实现。
139
+ - 领域内核:依赖 [@actiondock/core](../core/README.md),调度统一调用门面 ActionDockTarget、数据目录锁 DataDirLock 与依赖事务管理器。
140
+ - 构建与打包:依赖 [@actiondock/builder](../builder/README.md),完成目录交付构建、npm 打包与技能导出。
141
+ - 协议服务:通过 [@actiondock/mcp](../mcp/README.md) 启动协议监听。
135
142
 
136
143
  ---
137
144
 
package/bin/ad.js CHANGED
@@ -1,55 +1,42 @@
1
1
  #!/usr/bin/env node
2
2
  import { spawnSync } from "node:child_process";
3
+ import { existsSync } from "node:fs";
3
4
  import { createRequire } from "node:module";
5
+ import { resolve } from "node:path";
4
6
  import { fileURLToPath, pathToFileURL } from "node:url";
5
7
 
6
- const isBun = typeof process.versions.bun !== "undefined";
8
+ const distEntry = resolve(import.meta.dirname, "../dist/index.js");
7
9
 
8
- // 本进程是否已挂载 tsx 引导器。execArgv 不会被子进程继承,必须逐进程检测;
9
- // 严禁改用环境变量判断:Action 内嵌套调度的 ad 子进程会继承父进程环境变量,
10
- // 却不继承 --import,误判为已引导后会直接 import node_modules 下的 TS 源码,
11
- // 触发 ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING。
12
- // 注意也不能依赖 Node 原生类型剥离:它拒绝 node_modules 下的文件,且不支持源码中的目录导入。
13
- const hasTsx =
14
- process.execArgv.some((arg, i) => arg === "--import" && process.execArgv[i + 1]?.includes("tsx")) ||
15
- process.execArgv.some((arg) => arg.includes("tsx"));
10
+ if (existsSync(distEntry)) {
11
+ const { main } = await import(pathToFileURL(distEntry).href);
12
+ await main(process.argv);
13
+ } else {
14
+ const isBun = typeof process.versions.bun !== "undefined";
15
+ const hasTsx =
16
+ process.execArgv.some((arg, i) => arg === "--import" && process.execArgv[i + 1]?.includes("tsx")) ||
17
+ process.execArgv.some((arg) => arg.includes("tsx"));
16
18
 
17
- if (!isBun && !hasTsx) {
18
- const require = createRequire(import.meta.url);
19
- let tsxSpecifier = "tsx";
20
- try {
21
- tsxSpecifier = pathToFileURL(require.resolve("tsx")).href;
22
- } catch {
19
+ if (!isBun && !hasTsx) {
20
+ const require = createRequire(import.meta.url);
21
+ let tsxSpecifier = null;
23
22
  try {
24
- const runtimeNodePkg = require.resolve("@actiondock/runtime-node/package.json");
25
- const runtimeReq = createRequire(runtimeNodePkg);
26
- tsxSpecifier = pathToFileURL(runtimeReq.resolve("tsx")).href;
23
+ tsxSpecifier = pathToFileURL(require.resolve("tsx")).href;
27
24
  } catch {
28
- // 回退为裸模块名
25
+ // 未检测到 tsx 模块
29
26
  }
30
- }
31
-
32
- const extraArgs = [];
33
- const [major, minor] = (process.versions.node || "").split(".").map(Number);
34
- if (major === 22 && minor < 13) {
35
- extraArgs.push("--experimental-sqlite");
36
- }
37
27
 
38
- const res = spawnSync(
39
- process.execPath,
40
- ["--import", tsxSpecifier, ...extraArgs, fileURLToPath(import.meta.url), ...process.argv.slice(2)],
41
- {
42
- stdio: "inherit",
28
+ if (tsxSpecifier) {
29
+ const res = spawnSync(
30
+ process.execPath,
31
+ ["--import", tsxSpecifier, fileURLToPath(import.meta.url), ...process.argv.slice(2)],
32
+ {
33
+ stdio: "inherit",
34
+ }
35
+ );
36
+ process.exit(res.status ?? (res.signal ? 1 : 0));
43
37
  }
44
- );
45
- process.exit(res.status ?? (res.signal ? 1 : 0));
46
- }
38
+ }
47
39
 
48
- let main;
49
- try {
50
- ({ main } = await import("../dist/index.js"));
51
- } catch {
52
- ({ main } = await import("../src/index.ts"));
40
+ const { main } = await import("../src/index.ts");
41
+ await main(process.argv);
53
42
  }
54
- await main(process.argv);
55
-
@@ -1,2 +1,10 @@
1
1
  import type { Command } from "commander";
2
- export declare function registerActionCreateCommand(actionCmd: Command): void;
2
+ import type { CliContext } from "../../types.js";
3
+ /**
4
+ * 注册 action 命令组(action create, action new)。
5
+ *
6
+ * @param program Commander 根程序对象
7
+ * @param context 命令行上下文
8
+ */
9
+ export declare function registerActionCommands(program: Command, context?: CliContext): void;
10
+ export declare function handleActionCreate(id: string, options: any, context?: CliContext): Promise<void>;
@@ -0,0 +1,120 @@
1
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
2
+ import { isAbsolute, join, resolve } from "node:path";
3
+ import { assertPathWithinRoot, checkGeneratedTypes, findProjectRoot, getPackageSlug, loadManifest, loadProjectConfig, saveManifest, writeActionTypes, } from "@actiondock/core";
4
+ import { ExecutionError } from "../../errors.js";
5
+ import { writeStdout } from "../../renderer.js";
6
+ /**
7
+ * 注册 action 命令组(action create, action new)。
8
+ *
9
+ * @param program Commander 根程序对象
10
+ * @param context 命令行上下文
11
+ */
12
+ export function registerActionCommands(program, context) {
13
+ const actionCmd = program
14
+ .command("action")
15
+ .description("Manage Action definitions (create, scaffold)");
16
+ actionCmd
17
+ .command("create <id>")
18
+ .alias("new")
19
+ .description("Scaffold a new Action definition file")
20
+ .option("-d, --desc <description>", "Action description")
21
+ .option("-f, --file <filePath>", "Target file path relative to actions dir")
22
+ .action(async (id, options) => {
23
+ await handleActionCreate(id, options, context);
24
+ });
25
+ }
26
+ export async function handleActionCreate(id, options, context) {
27
+ const root = findProjectRoot();
28
+ if (!root) {
29
+ throw new ExecutionError("Not in an ActionDock project (actiondock.json not found)");
30
+ }
31
+ try {
32
+ const config = loadProjectConfig(root);
33
+ const actionsDir = resolve(root, config.actionsDir || "actions");
34
+ if (!existsSync(actionsDir)) {
35
+ mkdirSync(actionsDir, { recursive: true });
36
+ }
37
+ if (options.file && isAbsolute(options.file)) {
38
+ throw new ExecutionError(`--file option must be a relative path, received: ${options.file}`);
39
+ }
40
+ const cleanName = getPackageSlug(id);
41
+ const targetRelFile = options.file || `${cleanName}.ts`;
42
+ const targetFullFile = resolve(actionsDir, targetRelFile);
43
+ assertPathWithinRoot(actionsDir, targetFullFile, "action file");
44
+ if (existsSync(targetFullFile)) {
45
+ throw new ExecutionError(`Target action file already exists at ${targetFullFile}`);
46
+ }
47
+ const desc = options.desc || `Action ${id}`;
48
+ const template = `import { defineAction } from "@actiondock/sdk";
49
+
50
+ export interface Input {
51
+ exampleParam?: string;
52
+ }
53
+
54
+ export interface Output {
55
+ success: boolean;
56
+ result?: unknown;
57
+ }
58
+
59
+ export default defineAction<Input, Output>(async (input, ctx) => {
60
+ ctx.log.info("Running ${id}", input);
61
+
62
+ // Access config: ctx.config.get("MY_CONFIG")
63
+ // Access state: await ctx.state.get("my_key") / await ctx.state.set("my_key", val)
64
+ // Call action: await ctx.actions.invoke(otherAction, input)
65
+
66
+ return {
67
+ success: true,
68
+ result: input.exampleParam || "done",
69
+ };
70
+ });
71
+ `;
72
+ writeFileSync(targetFullFile, template, "utf-8");
73
+ const manifest = loadManifest(root) || {
74
+ $schema: "https://actiondock.dev/schema/v2/actiondock.json",
75
+ id: config.id,
76
+ name: config.name,
77
+ version: config.version,
78
+ actions: {},
79
+ };
80
+ manifest.actions = manifest.actions || {};
81
+ manifest.actions[id] = {
82
+ entry: join(config.actionsDir || "actions", targetRelFile).replace(/\\/g, "/"),
83
+ description: desc,
84
+ inputSchema: {
85
+ type: "object",
86
+ properties: {
87
+ exampleParam: {
88
+ type: "string",
89
+ description: "Example parameter description",
90
+ },
91
+ },
92
+ required: [],
93
+ },
94
+ outputSchema: {
95
+ type: "object",
96
+ properties: {
97
+ success: { type: "boolean" },
98
+ result: {},
99
+ },
100
+ required: ["success"],
101
+ },
102
+ uses: [],
103
+ tags: [],
104
+ };
105
+ saveManifest(root, manifest);
106
+ // 若当前项目已存在类型生成文件,则同步自动刷新类型声明
107
+ const typeCheck = checkGeneratedTypes(root, manifest);
108
+ if (typeCheck.exists) {
109
+ writeActionTypes(root, manifest);
110
+ }
111
+ writeStdout(`[OK] Created Action '${id}' at ${targetFullFile}`, context);
112
+ writeStdout(`\nTo run this action:`, context);
113
+ writeStdout(` ad run ${id} --input '{"exampleParam": "hello"}'`, context);
114
+ }
115
+ catch (err) {
116
+ if (err instanceof ExecutionError)
117
+ throw err;
118
+ throw new ExecutionError(err.message);
119
+ }
120
+ }
@@ -0,0 +1,7 @@
1
+ import { Command } from "commander";
2
+ import type { CliContext } from "../types.js";
3
+ /**
4
+ * 注册 ad add 依赖安装命令。
5
+ * 遵循规范:安装并锁定依赖,更新 package.json、actiondock.json 与 actiondock.lock.json,执行原子事务快照保护。
6
+ */
7
+ export declare function registerAddCommand(program: Command, context?: CliContext): void;
@@ -0,0 +1,144 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { join, resolve } from "node:path";
3
+ import { ActionPackageResolver, beginTransaction, computeManifestDigest, findProjectRoot, getInstallCommand, loadLockfile, loadManifest, MANIFEST_FILE_NAME, parseJsonWithoutDuplicates, saveLockfile, saveManifest, } from "@actiondock/core";
4
+ import { ArgumentError, ExecutionError } from "../errors.js";
5
+ import { renderResult } from "../renderer.js";
6
+ import { getEffectiveOptions, spawnAsync } from "../utils/index.js";
7
+ /**
8
+ * 从安装说明符中提取基础 npm 包名(去除版本号及前缀范围)。
9
+ */
10
+ function extractNpmPackageName(spec) {
11
+ const clean = spec.trim();
12
+ if (clean.startsWith("@")) {
13
+ const slashIdx = clean.indexOf("/");
14
+ if (slashIdx !== -1) {
15
+ const atIdx = clean.indexOf("@", slashIdx + 1);
16
+ return atIdx !== -1 ? clean.slice(0, atIdx) : clean;
17
+ }
18
+ }
19
+ else {
20
+ const atIdx = clean.indexOf("@");
21
+ return atIdx !== -1 ? clean.slice(0, atIdx) : clean;
22
+ }
23
+ return clean;
24
+ }
25
+ /**
26
+ * 注册 ad add 依赖安装命令。
27
+ * 遵循规范:安装并锁定依赖,更新 package.json、actiondock.json 与 actiondock.lock.json,执行原子事务快照保护。
28
+ */
29
+ export function registerAddCommand(program, context) {
30
+ program
31
+ .command("add <package>")
32
+ .description("Install and lock an Action package dependency into current project")
33
+ .option("--allow-install-scripts", "Explicitly allow lifecycle install scripts from third-party packages")
34
+ .option("-D, --dev", "Install package as development dependency")
35
+ .option("-P, --package <path>", "Target project directory path")
36
+ .option("--json", "Output as JSON")
37
+ .option("--envelope", "Wrap JSON output in standard envelope")
38
+ .action(async (packageSpec, rawOptions, cmd) => {
39
+ const options = getEffectiveOptions(rawOptions, cmd);
40
+ const root = options.package ? resolve(options.package) : findProjectRoot();
41
+ if (!root) {
42
+ throw new ArgumentError("Not in an ActionDock project (actiondock.json not found).\nPlease specify -P, --package <path> or cd into a project directory.");
43
+ }
44
+ const manifest = loadManifest(root);
45
+ if (!manifest) {
46
+ throw new ArgumentError(`actiondock.json not found in ${root}`);
47
+ }
48
+ const npmPackageName = extractNpmPackageName(packageSpec);
49
+ // 获取排他修改锁并创建事务快照
50
+ const tx = await beginTransaction(root, `ad add ${packageSpec}`);
51
+ try {
52
+ // 调用包管理器执行安装(默认禁用生命周期安装脚本)
53
+ const installCmd = getInstallCommand(root);
54
+ const pm = installCmd[0];
55
+ const args = [];
56
+ if (pm === "bun") {
57
+ args.push("add", packageSpec);
58
+ if (options.dev) {
59
+ args.push("--dev");
60
+ }
61
+ if (!options.allowInstallScripts) {
62
+ args.push("--ignore-scripts");
63
+ }
64
+ }
65
+ else {
66
+ args.push("install", packageSpec);
67
+ if (options.dev) {
68
+ args.push("--save-dev");
69
+ }
70
+ if (!options.allowInstallScripts) {
71
+ args.push("--ignore-scripts");
72
+ }
73
+ }
74
+ const installProc = await spawnAsync(pm, args, {
75
+ cwd: root,
76
+ stdio: "pipe",
77
+ });
78
+ if (installProc.status !== 0) {
79
+ const errMsg = installProc.stderr?.toString() || installProc.stdout?.toString() || "Unknown error";
80
+ throw new Error(`Failed to install '${packageSpec}' using ${pm}: ${errMsg}`);
81
+ }
82
+ // 寻址已安装包的 actiondock.json 清单
83
+ let targetManifestPath = join(root, "node_modules", npmPackageName, MANIFEST_FILE_NAME);
84
+ if (!existsSync(targetManifestPath)) {
85
+ targetManifestPath = join(root, "node_modules", packageSpec, MANIFEST_FILE_NAME);
86
+ }
87
+ if (!existsSync(targetManifestPath)) {
88
+ throw new Error(`Target package '${npmPackageName}' does not contain '${MANIFEST_FILE_NAME}'. It is not a valid ActionDock package.`);
89
+ }
90
+ const targetRaw = readFileSync(targetManifestPath, "utf-8");
91
+ const targetManifest = parseJsonWithoutDuplicates(targetRaw);
92
+ const targetDigest = computeManifestDigest(targetManifest);
93
+ const targetPackageId = targetManifest.id;
94
+ const targetVersion = targetManifest.version || "0.1.0";
95
+ // 更新 actiondock.json.dependencies 映射
96
+ manifest.dependencies = manifest.dependencies || {};
97
+ manifest.dependencies[targetPackageId] =
98
+ targetPackageId === npmPackageName ? `^${targetVersion}` : npmPackageName;
99
+ // 触发依赖图解析及版本冲突检测
100
+ const resolver = new ActionPackageResolver({ projectRoot: root, manifest });
101
+ const graph = resolver.resolveSync();
102
+ // 更新 actiondock.lock.json
103
+ const lockfile = loadLockfile(root) || {
104
+ lockfileVersion: 1,
105
+ packages: {},
106
+ };
107
+ for (const pkg of graph.packages.values()) {
108
+ if (!pkg.isRoot) {
109
+ lockfile.packages[pkg.packageId] = {
110
+ package: pkg.npmPackage,
111
+ resolved: pkg.version,
112
+ source: "npm",
113
+ packageId: pkg.packageId,
114
+ npmPackage: pkg.npmPackage,
115
+ version: pkg.version,
116
+ manifestDigest: pkg.manifestDigest,
117
+ dependencies: pkg.manifest.dependencies || {},
118
+ };
119
+ }
120
+ }
121
+ // 写入清单与锁文件
122
+ saveManifest(root, manifest);
123
+ saveLockfile(root, lockfile);
124
+ // 提交事务
125
+ await tx.commit();
126
+ const resultPayload = {
127
+ packageId: targetPackageId,
128
+ npmPackage: npmPackageName,
129
+ version: targetVersion,
130
+ manifestDigest: targetDigest,
131
+ };
132
+ renderResult(resultPayload, {
133
+ json: options.json,
134
+ envelope: options.envelope,
135
+ humanFormatter: () => `Successfully added and locked '${resultPayload.packageId}' (v${resultPayload.version}) from ${resultPayload.npmPackage}.`,
136
+ context,
137
+ });
138
+ }
139
+ catch (err) {
140
+ await tx.rollback({ frozenInstall: false });
141
+ throw new ExecutionError(err.message, err, err.code || "ADD_DEPENDENCY_FAILED");
142
+ }
143
+ });
144
+ }
@@ -1,2 +1,3 @@
1
1
  import { Command } from "commander";
2
- export declare function registerBuildCommand(program: Command): void;
2
+ import type { CliContext } from "../types.js";
3
+ export declare function registerBuildCommand(program: Command, context?: CliContext): void;
@@ -0,0 +1,83 @@
1
+ import { findProjectRoot, resolvePackageRoot } from "@actiondock/core";
2
+ import { ExecutionError } from "../errors.js";
3
+ import { renderResult, writeStdout } from "../renderer.js";
4
+ import { getEffectiveOptions, parseListOption } from "../utils/index.js";
5
+ export function registerBuildCommand(program, context) {
6
+ program
7
+ .command("build")
8
+ .description("Build project actions into a runnable Node.js delivery directory")
9
+ .option("-P, --package <id>", "Target package ID or path")
10
+ .option("-o, --out <path>", "Output directory (or archive path)")
11
+ .option("-a, --actions <actions...>", "Only build specific action(s)", parseListOption)
12
+ .option("-p, --playbooks <playbooks...>", "Only build specific playbook(s)", parseListOption)
13
+ .option("-z, --archive", "Create a standard zip archive of the build directory")
14
+ .option("--vendor-deps", "Materialize locked production dependencies into the output")
15
+ .option("--allow-install-scripts", "Allow lifecycle install scripts to run during vendor-deps")
16
+ .option("--require-reproducible", "Require reproducible build and fail if install scripts must run")
17
+ .option("-t, --target <target>", "Target compilation platform (removed)")
18
+ .option("--bytecode", "Bytecode compilation (removed)")
19
+ .action(async (rawOptions, cmd) => {
20
+ const options = getEffectiveOptions(rawOptions, cmd);
21
+ // 彻底删除原有单文件二进制输出语义
22
+ if (options.target !== undefined || options.bytecode !== undefined) {
23
+ throw new ExecutionError("Unsupported build mode: '--target' and '--bytecode' standalone single-file binary compilation have been removed in ActionDock 2.0. Directory-based Node.js builds are now the standard distribution format.", undefined, "UNSUPPORTED_BUILD_MODE");
24
+ }
25
+ const root = options.package
26
+ ? resolvePackageRoot(options.package)
27
+ : findProjectRoot();
28
+ if (!root) {
29
+ if (options.package) {
30
+ throw new ExecutionError(`Package '${options.package}' not found in linked packages or path`);
31
+ }
32
+ throw new ExecutionError("Not in an ActionDock project (actiondock.json not found).\nPlease specify -P, --package <id> or cd into a project directory.");
33
+ }
34
+ const isMachine = Boolean(options.json || options.envelope);
35
+ if (!isMachine) {
36
+ writeStdout("Building Node.js delivery artifact...");
37
+ }
38
+ let result;
39
+ try {
40
+ const { buildProject } = await import("@actiondock/builder");
41
+ result = await buildProject({
42
+ projectRoot: root,
43
+ outDir: options.out,
44
+ actions: options.actions,
45
+ playbooks: options.playbooks,
46
+ archive: options.archive,
47
+ vendorDeps: options.vendorDeps,
48
+ allowInstallScripts: options.allowInstallScripts,
49
+ requireReproducible: options.requireReproducible,
50
+ });
51
+ }
52
+ catch (err) {
53
+ if (err?.code === "UNSUPPORTED_BUILD_MODE") {
54
+ throw new ExecutionError(err.message, undefined, "UNSUPPORTED_BUILD_MODE");
55
+ }
56
+ throw new ExecutionError(`Build failed: ${err.message}`);
57
+ }
58
+ renderResult(result, {
59
+ json: isMachine,
60
+ envelope: options.envelope,
61
+ humanFormatter: () => {
62
+ const lines = [];
63
+ lines.push(`[OK] Successfully built ${result.packageId} (v${result.version})`);
64
+ lines.push(` Output: ${result.outputDir}`);
65
+ lines.push(` Entrypoint: ${result.entrypointPath}`);
66
+ lines.push(` Actions: ${result.actions.join(", ")}`);
67
+ if (result.playbooks.length > 0) {
68
+ lines.push(` Playbooks: ${result.playbooks.join(", ")}`);
69
+ }
70
+ lines.push(` Vendor Deps: ${result.vendorDeps}`);
71
+ lines.push(` Reproducible: ${result.reproducible}`);
72
+ if (result.archivePath) {
73
+ lines.push(` Archive: ${result.archivePath}`);
74
+ }
75
+ if (result.metadataPath) {
76
+ lines.push(` Metadata: ${result.metadataPath}`);
77
+ }
78
+ return lines.join("\n");
79
+ },
80
+ context,
81
+ });
82
+ });
83
+ }
@@ -0,0 +1,9 @@
1
+ import type { Command } from "commander";
2
+ import type { CliContext } from "../../types.js";
3
+ /**
4
+ * 注册 config delete 子命令:删除配置项。
5
+ *
6
+ * @param configCmd config 命令实例
7
+ * @param context 命令行上下文
8
+ */
9
+ export declare function registerConfigDeleteCommand(configCmd: Command, context?: CliContext): void;