@actiondock/core 2.0.12 → 2.2.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 (192) hide show
  1. package/README.md +61 -30
  2. package/dist/app/app.d.ts +73 -0
  3. package/dist/app/app.js +700 -0
  4. package/dist/app/index.d.ts +2 -0
  5. package/dist/app/index.js +2 -0
  6. package/dist/app/types.d.ts +283 -0
  7. package/dist/app/types.js +1 -0
  8. package/dist/catalog/action-index.d.ts +1 -1
  9. package/dist/catalog/action-index.js +93 -0
  10. package/dist/catalog/action-resolver.d.ts +2 -2
  11. package/dist/catalog/action-resolver.js +95 -0
  12. package/dist/catalog/index.d.ts +5 -5
  13. package/dist/catalog/index.js +5 -0
  14. package/dist/catalog/location-registry.d.ts +5 -5
  15. package/dist/catalog/location-registry.js +138 -0
  16. package/dist/catalog/package-catalog.d.ts +2 -2
  17. package/dist/catalog/package-catalog.js +76 -0
  18. package/dist/catalog/types.d.ts +2 -2
  19. package/dist/catalog/types.js +1 -0
  20. package/dist/doctor/doctor.d.ts +2 -1
  21. package/dist/doctor/doctor.js +613 -0
  22. package/dist/doctor/index.d.ts +2 -2
  23. package/dist/doctor/index.js +2 -0
  24. package/dist/doctor/types.js +1 -0
  25. package/dist/errors.d.ts +122 -0
  26. package/dist/errors.js +123 -0
  27. package/dist/execution/index.d.ts +2 -2
  28. package/dist/execution/index.js +2 -0
  29. package/dist/execution/service.d.ts +46 -41
  30. package/dist/execution/service.js +633 -0
  31. package/dist/execution/types.d.ts +59 -2
  32. package/dist/execution/types.js +1 -0
  33. package/dist/export/index.d.ts +1 -1
  34. package/dist/export/index.js +1 -0
  35. package/dist/export/templates.d.ts +45 -7
  36. package/dist/export/templates.js +621 -0
  37. package/dist/filter/index.d.ts +1 -1
  38. package/dist/filter/index.js +1 -0
  39. package/dist/filter/intent.js +123 -0
  40. package/dist/host/host.d.ts +55 -0
  41. package/dist/host/host.js +740 -0
  42. package/dist/host/index.d.ts +2 -0
  43. package/dist/host/index.js +2 -0
  44. package/dist/host/types.d.ts +81 -0
  45. package/dist/host/types.js +1 -0
  46. package/dist/index.d.ts +21 -16
  47. package/dist/index.js +20 -8458
  48. package/dist/ipc/diagnostic.d.ts +65 -0
  49. package/dist/ipc/diagnostic.js +129 -0
  50. package/dist/ipc/host.d.ts +7 -0
  51. package/dist/ipc/host.js +93 -0
  52. package/dist/ipc/index.d.ts +4 -0
  53. package/dist/ipc/index.js +4 -0
  54. package/dist/ipc/target.d.ts +56 -0
  55. package/dist/ipc/target.js +297 -0
  56. package/dist/ipc/types.d.ts +42 -0
  57. package/dist/ipc/types.js +1 -0
  58. package/dist/platform/default.d.ts +32 -0
  59. package/dist/platform/default.js +40 -0
  60. package/dist/platform/index.d.ts +3 -0
  61. package/dist/platform/index.js +3 -0
  62. package/dist/platform/node-fs.d.ts +37 -0
  63. package/dist/platform/node-fs.js +81 -0
  64. package/dist/platform/types.d.ts +104 -0
  65. package/dist/platform/types.js +1 -0
  66. package/dist/profile/client.d.ts +58 -12
  67. package/dist/profile/client.js +467 -0
  68. package/dist/profile/index.d.ts +3 -3
  69. package/dist/profile/index.js +3 -0
  70. package/dist/profile/manager.d.ts +2 -2
  71. package/dist/profile/manager.js +294 -0
  72. package/dist/profile/types.d.ts +1 -1
  73. package/dist/profile/types.js +1 -0
  74. package/dist/project/closure.js +68 -0
  75. package/dist/project/digest.d.ts +29 -0
  76. package/dist/project/digest.js +324 -0
  77. package/dist/project/index.d.ts +10 -5
  78. package/dist/project/index.js +10 -0
  79. package/dist/project/init.d.ts +3 -4
  80. package/dist/project/init.js +175 -0
  81. package/dist/project/loader.d.ts +21 -27
  82. package/dist/project/loader.js +484 -0
  83. package/dist/project/lockfile.d.ts +55 -0
  84. package/dist/project/lockfile.js +175 -0
  85. package/dist/project/manifest.d.ts +6 -28
  86. package/dist/project/manifest.js +220 -0
  87. package/dist/project/resolver.d.ts +116 -0
  88. package/dist/project/resolver.js +376 -0
  89. package/dist/project/transactions.d.ts +103 -0
  90. package/dist/project/transactions.js +1203 -0
  91. package/dist/project/types-generator.d.ts +33 -0
  92. package/dist/project/types-generator.js +168 -0
  93. package/dist/project/types.d.ts +58 -82
  94. package/dist/project/types.js +1 -0
  95. package/dist/registry/index.d.ts +5 -2
  96. package/dist/registry/index.js +5 -0
  97. package/dist/registry/lock.d.ts +27 -0
  98. package/dist/registry/lock.js +93 -0
  99. package/dist/registry/registry.d.ts +32 -11
  100. package/dist/registry/registry.js +611 -0
  101. package/dist/registry/resolve.d.ts +62 -0
  102. package/dist/registry/resolve.js +195 -0
  103. package/dist/registry/scan.d.ts +38 -0
  104. package/dist/registry/scan.js +135 -0
  105. package/dist/registry/types.d.ts +1 -1
  106. package/dist/registry/types.js +1 -0
  107. package/dist/runtime/action-collection.d.ts +27 -0
  108. package/dist/runtime/action-collection.js +57 -0
  109. package/dist/runtime/clock.d.ts +0 -2
  110. package/dist/runtime/clock.js +17 -0
  111. package/dist/runtime/context.d.ts +16 -5
  112. package/dist/runtime/context.js +242 -0
  113. package/dist/runtime/env.d.ts +3 -1
  114. package/dist/runtime/env.js +159 -0
  115. package/dist/runtime/events.d.ts +46 -14
  116. package/dist/runtime/events.js +604 -0
  117. package/dist/runtime/execution-manager.d.ts +1 -1
  118. package/dist/runtime/execution-manager.js +66 -0
  119. package/dist/runtime/index.d.ts +10 -9
  120. package/dist/runtime/index.js +10 -0
  121. package/dist/runtime/module-loader.d.ts +0 -8
  122. package/dist/runtime/module-loader.js +16 -0
  123. package/dist/runtime/process.d.ts +2 -4
  124. package/dist/runtime/process.js +146 -0
  125. package/dist/runtime/runner.d.ts +117 -10
  126. package/dist/runtime/runner.js +984 -0
  127. package/dist/runtime/standalone.d.ts +68 -19
  128. package/dist/runtime/standalone.js +536 -0
  129. package/dist/schema/validator.js +116 -0
  130. package/dist/server/body.js +93 -0
  131. package/dist/server/index.d.ts +5 -6
  132. package/dist/server/index.js +5 -0
  133. package/dist/server/routes/actions.d.ts +2 -2
  134. package/dist/server/routes/actions.js +298 -0
  135. package/dist/server/routes/common.d.ts +28 -17
  136. package/dist/server/routes/common.js +104 -0
  137. package/dist/server/routes/config.d.ts +2 -1
  138. package/dist/server/routes/config.js +173 -0
  139. package/dist/server/routes/doctor.d.ts +2 -2
  140. package/dist/server/routes/doctor.js +63 -0
  141. package/dist/server/routes/health.d.ts +2 -2
  142. package/dist/server/routes/health.js +35 -0
  143. package/dist/server/routes/index.d.ts +9 -9
  144. package/dist/server/routes/index.js +9 -0
  145. package/dist/server/routes/info.d.ts +4 -2
  146. package/dist/server/routes/info.js +158 -0
  147. package/dist/server/routes/playbooks.d.ts +2 -2
  148. package/dist/server/routes/playbooks.js +134 -0
  149. package/dist/server/routes/runs.d.ts +2 -2
  150. package/dist/server/routes/runs.js +343 -0
  151. package/dist/server/routes/state.d.ts +2 -1
  152. package/dist/server/routes/state.js +169 -0
  153. package/dist/server/security.d.ts +11 -4
  154. package/dist/server/security.js +101 -0
  155. package/dist/server/server.d.ts +6 -6
  156. package/dist/server/server.js +355 -0
  157. package/dist/server/types.d.ts +27 -8
  158. package/dist/server/types.js +1 -0
  159. package/dist/storage/data-dir-lock.d.ts +119 -0
  160. package/dist/storage/data-dir-lock.js +1016 -0
  161. package/dist/storage/driver.d.ts +4 -10
  162. package/dist/storage/driver.js +59 -0
  163. package/dist/storage/index.d.ts +30 -10
  164. package/dist/storage/index.js +98 -0
  165. package/dist/storage/lazy.d.ts +8 -0
  166. package/dist/storage/lazy.js +44 -0
  167. package/dist/storage/mask.d.ts +12 -5
  168. package/dist/storage/mask.js +48 -0
  169. package/dist/storage/params.d.ts +13 -0
  170. package/dist/storage/params.js +32 -0
  171. package/dist/storage/sqlite.d.ts +19 -6
  172. package/dist/storage/sqlite.js +689 -0
  173. package/dist/storage/types.d.ts +57 -6
  174. package/dist/storage/types.js +14 -0
  175. package/dist/target/index.d.ts +4 -0
  176. package/dist/target/index.js +4 -0
  177. package/dist/target/local.d.ts +52 -0
  178. package/dist/target/local.js +343 -0
  179. package/dist/target/remote.d.ts +76 -0
  180. package/dist/target/remote.js +839 -0
  181. package/dist/target/target.d.ts +6 -0
  182. package/dist/target/target.js +35 -0
  183. package/dist/target/types.d.ts +246 -0
  184. package/dist/target/types.js +34 -0
  185. package/dist/utils/index.d.ts +22 -2
  186. package/dist/utils/index.js +180 -0
  187. package/dist/version.d.ts +1 -1
  188. package/dist/version.js +4 -0
  189. package/package.json +6 -7
  190. package/dist/build/index.d.ts +0 -1
  191. package/dist/build/templates.d.ts +0 -19
  192. package/dist/server/runtime-registry.d.ts +0 -78
@@ -0,0 +1,324 @@
1
+ import { createHash } from "node:crypto";
2
+ /**
3
+ * 解析 JSON 文本并在检测到重复键时抛出错误。
4
+ * 遵循 RFC 8785 清单校验规范:解析阶段必须率先拦截并拒绝重复键。
5
+ */
6
+ export function parseJsonWithoutDuplicates(jsonText) {
7
+ let pos = 0;
8
+ const len = jsonText.length;
9
+ function skipWhitespace() {
10
+ while (pos < len) {
11
+ const ch = jsonText.charCodeAt(pos);
12
+ if (ch === 0x20 || ch === 0x09 || ch === 0x0a || ch === 0x0d) {
13
+ pos++;
14
+ }
15
+ else {
16
+ break;
17
+ }
18
+ }
19
+ }
20
+ function parseString() {
21
+ if (jsonText[pos] !== '"') {
22
+ throw new SyntaxError(`Expected string at position ${pos}`);
23
+ }
24
+ const start = pos;
25
+ pos++; // 跳过开头的引号
26
+ let result = "";
27
+ while (pos < len) {
28
+ const ch = jsonText[pos];
29
+ if (ch === '"') {
30
+ pos++; // 跳过结束的引号
31
+ return result;
32
+ }
33
+ if (ch === "\\") {
34
+ pos++;
35
+ if (pos >= len) {
36
+ throw new SyntaxError(`Unterminated escape sequence in string at ${pos}`);
37
+ }
38
+ const esc = jsonText[pos];
39
+ if (esc === '"' || esc === "\\" || esc === "/") {
40
+ result += esc;
41
+ pos++;
42
+ }
43
+ else if (esc === "b") {
44
+ result += "\b";
45
+ pos++;
46
+ }
47
+ else if (esc === "f") {
48
+ result += "\f";
49
+ pos++;
50
+ }
51
+ else if (esc === "n") {
52
+ result += "\n";
53
+ pos++;
54
+ }
55
+ else if (esc === "r") {
56
+ result += "\r";
57
+ pos++;
58
+ }
59
+ else if (esc === "t") {
60
+ result += "\t";
61
+ pos++;
62
+ }
63
+ else if (esc === "u") {
64
+ pos++;
65
+ const hex = jsonText.slice(pos, pos + 4);
66
+ if (hex.length < 4 || !/^[0-9a-fA-F]{4}$/.test(hex)) {
67
+ throw new SyntaxError(`Invalid unicode escape sequence \\u${hex} at ${pos}`);
68
+ }
69
+ result += String.fromCharCode(parseInt(hex, 16));
70
+ pos += 4;
71
+ }
72
+ else {
73
+ throw new SyntaxError(`Invalid escape character '\\${esc}' at ${pos}`);
74
+ }
75
+ }
76
+ else {
77
+ result += ch;
78
+ pos++;
79
+ }
80
+ }
81
+ throw new SyntaxError(`Unterminated string starting at ${start}`);
82
+ }
83
+ function parseNumber() {
84
+ const start = pos;
85
+ if (jsonText[pos] === "-") {
86
+ pos++;
87
+ }
88
+ if (pos >= len) {
89
+ throw new SyntaxError(`Expected digits at position ${pos}`);
90
+ }
91
+ if (jsonText[pos] === "0") {
92
+ pos++;
93
+ }
94
+ else if (jsonText[pos] >= "1" && jsonText[pos] <= "9") {
95
+ while (pos < len && jsonText[pos] >= "0" && jsonText[pos] <= "9") {
96
+ pos++;
97
+ }
98
+ }
99
+ else {
100
+ throw new SyntaxError(`Invalid number at position ${pos}`);
101
+ }
102
+ if (pos < len && jsonText[pos] === ".") {
103
+ pos++;
104
+ if (pos >= len || jsonText[pos] < "0" || jsonText[pos] > "9") {
105
+ throw new SyntaxError(`Expected digit after decimal point at position ${pos}`);
106
+ }
107
+ while (pos < len && jsonText[pos] >= "0" && jsonText[pos] <= "9") {
108
+ pos++;
109
+ }
110
+ }
111
+ if (pos < len && (jsonText[pos] === "e" || jsonText[pos] === "E")) {
112
+ pos++;
113
+ if (pos < len && (jsonText[pos] === "+" || jsonText[pos] === "-")) {
114
+ pos++;
115
+ }
116
+ if (pos >= len || jsonText[pos] < "0" || jsonText[pos] > "9") {
117
+ throw new SyntaxError(`Expected digit after exponent at position ${pos}`);
118
+ }
119
+ while (pos < len && jsonText[pos] >= "0" && jsonText[pos] <= "9") {
120
+ pos++;
121
+ }
122
+ }
123
+ const numStr = jsonText.slice(start, pos);
124
+ const num = Number(numStr);
125
+ if (!Number.isFinite(num)) {
126
+ throw new SyntaxError(`Number out of range: ${numStr}`);
127
+ }
128
+ return num;
129
+ }
130
+ function parseObject() {
131
+ const obj = {};
132
+ const keys = new Set();
133
+ pos++; // 跳过 '{'
134
+ skipWhitespace();
135
+ if (pos < len && jsonText[pos] === "}") {
136
+ pos++;
137
+ return obj;
138
+ }
139
+ while (pos < len) {
140
+ skipWhitespace();
141
+ if (jsonText[pos] !== '"') {
142
+ throw new SyntaxError(`Expected string key in object at position ${pos}`);
143
+ }
144
+ const key = parseString();
145
+ if (keys.has(key)) {
146
+ throw new Error(`Duplicate key '${key}' detected in JSON`);
147
+ }
148
+ keys.add(key);
149
+ skipWhitespace();
150
+ if (pos >= len || jsonText[pos] !== ":") {
151
+ throw new SyntaxError(`Expected ':' after key '${key}' at position ${pos}`);
152
+ }
153
+ pos++; // 跳过 ':'
154
+ skipWhitespace();
155
+ const val = parseValue();
156
+ obj[key] = val;
157
+ skipWhitespace();
158
+ if (pos >= len) {
159
+ throw new SyntaxError("Unexpected end of JSON in object");
160
+ }
161
+ if (jsonText[pos] === ",") {
162
+ pos++;
163
+ skipWhitespace();
164
+ }
165
+ else if (jsonText[pos] === "}") {
166
+ pos++;
167
+ return obj;
168
+ }
169
+ else {
170
+ throw new SyntaxError(`Expected ',' or '}' in object at position ${pos}`);
171
+ }
172
+ }
173
+ throw new SyntaxError("Unterminated object in JSON");
174
+ }
175
+ function parseArray() {
176
+ const arr = [];
177
+ pos++; // 跳过 '['
178
+ skipWhitespace();
179
+ if (pos < len && jsonText[pos] === "]") {
180
+ pos++;
181
+ return arr;
182
+ }
183
+ while (pos < len) {
184
+ skipWhitespace();
185
+ const val = parseValue();
186
+ arr.push(val);
187
+ skipWhitespace();
188
+ if (pos >= len) {
189
+ throw new SyntaxError("Unexpected end of JSON in array");
190
+ }
191
+ if (jsonText[pos] === ",") {
192
+ pos++;
193
+ skipWhitespace();
194
+ }
195
+ else if (jsonText[pos] === "]") {
196
+ pos++;
197
+ return arr;
198
+ }
199
+ else {
200
+ throw new SyntaxError(`Expected ',' or ']' in array at position ${pos}`);
201
+ }
202
+ }
203
+ throw new SyntaxError("Unterminated array in JSON");
204
+ }
205
+ function parseValue() {
206
+ skipWhitespace();
207
+ if (pos >= len) {
208
+ throw new SyntaxError("Unexpected end of JSON input");
209
+ }
210
+ const ch = jsonText[pos];
211
+ if (ch === "{")
212
+ return parseObject();
213
+ if (ch === "[")
214
+ return parseArray();
215
+ if (ch === '"')
216
+ return parseString();
217
+ if (ch === "-" || (ch >= "0" && ch <= "9"))
218
+ return parseNumber();
219
+ if (jsonText.startsWith("true", pos)) {
220
+ pos += 4;
221
+ return true;
222
+ }
223
+ if (jsonText.startsWith("false", pos)) {
224
+ pos += 5;
225
+ return false;
226
+ }
227
+ if (jsonText.startsWith("null", pos)) {
228
+ pos += 4;
229
+ return null;
230
+ }
231
+ throw new SyntaxError(`Unexpected token '${ch}' at position ${pos}`);
232
+ }
233
+ const result = parseValue();
234
+ skipWhitespace();
235
+ if (pos < len) {
236
+ throw new SyntaxError(`Unexpected trailing characters at position ${pos}`);
237
+ }
238
+ return result;
239
+ }
240
+ /**
241
+ * 依据 RFC 8785 规范化 JSON 数据。
242
+ * 规则:
243
+ * - 对象属性依据 UTF-16 代码单元进行升序排序;
244
+ * - 符号与键值之间不包含多余空白字符;
245
+ * - 浮点数与整数遵循 ECMAScript 规范序列化(负零转为零,拒绝非有限数);
246
+ * - 字符串转义严格遵循标准规范;
247
+ * - 数组保持原有顺序且元素递归规范化。
248
+ */
249
+ export function canonicalizeJson(value) {
250
+ if (value === null) {
251
+ return "null";
252
+ }
253
+ const type = typeof value;
254
+ if (type === "boolean") {
255
+ return value ? "true" : "false";
256
+ }
257
+ if (type === "number") {
258
+ if (!Number.isFinite(value)) {
259
+ throw new TypeError("RFC 8785 JSON 规范化拒绝非有限数值 (NaN 或 Infinity)");
260
+ }
261
+ if (Object.is(value, -0) || value === 0) {
262
+ return "0";
263
+ }
264
+ return JSON.stringify(value);
265
+ }
266
+ if (type === "string") {
267
+ return JSON.stringify(value);
268
+ }
269
+ if (Array.isArray(value)) {
270
+ const items = value.map((item) => {
271
+ if (item === undefined || typeof item === "function" || typeof item === "symbol") {
272
+ return "null";
273
+ }
274
+ return canonicalizeJson(item);
275
+ });
276
+ return `[${items.join(",")}]`;
277
+ }
278
+ if (type === "object") {
279
+ const obj = value;
280
+ const keys = Object.keys(obj).sort();
281
+ const entries = [];
282
+ for (const key of keys) {
283
+ const val = obj[key];
284
+ if (val === undefined || typeof val === "function" || typeof val === "symbol") {
285
+ continue;
286
+ }
287
+ entries.push(`${JSON.stringify(key)}:${canonicalizeJson(val)}`);
288
+ }
289
+ return `{${entries.join(",")}}`;
290
+ }
291
+ return "null";
292
+ }
293
+ /**
294
+ * 计算任意 JSON 结构或 JSON 字符串的 SHA-256 规范化摘要。
295
+ * 输出格式为 sha256-<hex>。
296
+ */
297
+ export function computeDigest(value) {
298
+ let canonicalText;
299
+ if (typeof value === "string") {
300
+ const parsed = parseJsonWithoutDuplicates(value);
301
+ canonicalText = canonicalizeJson(parsed);
302
+ }
303
+ else {
304
+ canonicalText = canonicalizeJson(value);
305
+ }
306
+ const hash = createHash("sha256").update(canonicalText, "utf8").digest("hex");
307
+ return `sha256-${hash}`;
308
+ }
309
+ /**
310
+ * 计算 Action 清单对象的唯一确定性 RFC 8785 摘要。
311
+ * 清单中的等价空格变化与字段顺序不会改变摘要结果。
312
+ */
313
+ export function computeManifestDigest(manifest) {
314
+ return computeDigest(manifest);
315
+ }
316
+ /**
317
+ * 校验给定清单的摘要是否与期望值一致。
318
+ */
319
+ export function verifyManifestDigest(manifest, expectedDigest) {
320
+ if (!expectedDigest || typeof expectedDigest !== "string") {
321
+ return false;
322
+ }
323
+ return computeManifestDigest(manifest) === expectedDigest;
324
+ }
@@ -1,5 +1,10 @@
1
- export * from "./closure";
2
- export * from "./init";
3
- export * from "./loader";
4
- export * from "./manifest";
5
- export * from "./types";
1
+ export * from "./closure.js";
2
+ export * from "./digest.js";
3
+ export * from "./init.js";
4
+ export * from "./loader.js";
5
+ export * from "./lockfile.js";
6
+ export * from "./manifest.js";
7
+ export * from "./resolver.js";
8
+ export { SNAPSHOT_TRACKED_FILES, type TransactionFileRecord, type TransactionMetadata, type ProjectTransaction, isPidAlive, isProjectLockHeld, acquireProjectLock, runFrozenInstall, hasPendingTransactions, beginTransaction, recoverPendingTransactions, } from "./transactions.js";
9
+ export * from "./types.js";
10
+ export * from "./types-generator.js";
@@ -0,0 +1,10 @@
1
+ export * from "./closure.js";
2
+ export * from "./digest.js";
3
+ export * from "./init.js";
4
+ export * from "./loader.js";
5
+ export * from "./lockfile.js";
6
+ export * from "./manifest.js";
7
+ export * from "./resolver.js";
8
+ export { SNAPSHOT_TRACKED_FILES, isPidAlive, isProjectLockHeld, acquireProjectLock, runFrozenInstall, hasPendingTransactions, beginTransaction, recoverPendingTransactions, } from "./transactions.js";
9
+ export * from "./types.js";
10
+ export * from "./types-generator.js";
@@ -12,13 +12,12 @@ export interface InitOptions {
12
12
  /**
13
13
  * 在目标目录初始化一个完整的 ActionDock 2.0 Action Package 脚手架。
14
14
  * 生成内容包括:
15
- * - actiondock.json(项目元数据与配置声明)
16
- * - actiondock.manifest.json(声明式元数据清单事实源)
15
+ * - actiondock.json(项目元数据、Action 与 Playbook 声明的单一事实源)
17
16
  * - package.json(Node.js 标准脚本与依赖声明)
18
17
  * - tsconfig.json(现代 NodeNext 模块规范)
19
18
  * - .gitignore(排除持久化 db、node_modules、dist)
20
- * - actions/greet.ts(标准示例 Action,演示 config、state、log 使用)
21
- * - playbooks/greet-user.md(标准 SOP Playbook 演示)
19
+ * - actions/greet.ts(标准示例 Action 执行 Handler,演示 config、state、log 使用)
20
+ * - playbooks/greet-user.md(纯 Markdown 标准 SOP Playbook 演示)
22
21
  * - tests/greet.test.ts(基于 node:test 与 @actiondock/testing 的测试用例)
23
22
  *
24
23
  * @param targetDir 目标项目目录
@@ -0,0 +1,175 @@
1
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
2
+ import { basename, join, resolve } from "node:path";
3
+ /**
4
+ * 在目标目录初始化一个完整的 ActionDock 2.0 Action Package 脚手架。
5
+ * 生成内容包括:
6
+ * - actiondock.json(项目元数据、Action 与 Playbook 声明的单一事实源)
7
+ * - package.json(Node.js 标准脚本与依赖声明)
8
+ * - tsconfig.json(现代 NodeNext 模块规范)
9
+ * - .gitignore(排除持久化 db、node_modules、dist)
10
+ * - actions/greet.ts(标准示例 Action 执行 Handler,演示 config、state、log 使用)
11
+ * - playbooks/greet-user.md(纯 Markdown 标准 SOP Playbook 演示)
12
+ * - tests/greet.test.ts(基于 node:test 与 @actiondock/testing 的测试用例)
13
+ *
14
+ * @param targetDir 目标项目目录
15
+ * @param options 初始化选项
16
+ */
17
+ export function initProject(targetDir, options = {}) {
18
+ const root = resolve(targetDir);
19
+ if (!existsSync(root)) {
20
+ mkdirSync(root, { recursive: true });
21
+ }
22
+ const dirName = basename(root);
23
+ const id = options.id || dirName;
24
+ const name = options.name || dirName.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
25
+ const description = options.description || "ActionDock AI Agent Actions";
26
+ // 1. actiondock.json(单一事实源)
27
+ const actiondockJson = {
28
+ $schema: "https://actiondock.dev/schema/v2.json",
29
+ schemaVersion: 2,
30
+ id,
31
+ name,
32
+ version: "0.1.0",
33
+ description,
34
+ config: {
35
+ SAMPLE_GREETING: {
36
+ description: "Default greeting message",
37
+ default: "Hello",
38
+ },
39
+ },
40
+ actions: {
41
+ "sample.greet": {
42
+ entry: "actions/greet.ts",
43
+ description: "Greeting action demonstrating basic input, config, and state usage",
44
+ inputSchema: {
45
+ type: "object",
46
+ properties: {
47
+ name: {
48
+ type: "string",
49
+ description: "Name of the person to greet",
50
+ },
51
+ },
52
+ required: ["name"],
53
+ },
54
+ outputSchema: {
55
+ type: "object",
56
+ properties: {
57
+ message: { type: "string" },
58
+ timesGreeted: { type: "number" },
59
+ },
60
+ required: ["message", "timesGreeted"],
61
+ },
62
+ tags: ["sample"],
63
+ uses: [],
64
+ },
65
+ },
66
+ playbooks: {
67
+ "greet-user": {
68
+ entry: "playbooks/greet-user.md",
69
+ description: "SOP for greeting a new user and verifying system health",
70
+ actions: ["sample.greet"],
71
+ },
72
+ },
73
+ files: [],
74
+ };
75
+ writeFileSync(join(root, "actiondock.json"), JSON.stringify(actiondockJson, null, 2) + "\n");
76
+ // 2. package.json
77
+ const packageJson = {
78
+ name: id,
79
+ version: "0.1.0",
80
+ description,
81
+ type: "module",
82
+ scripts: {
83
+ test: "node --import tsx --test tests/*.test.ts",
84
+ },
85
+ engines: {
86
+ node: ">=24.12.0",
87
+ },
88
+ dependencies: {
89
+ "@actiondock/sdk": "^2.2.0",
90
+ },
91
+ devDependencies: {
92
+ "@actiondock/testing": "^2.2.0",
93
+ "@types/node": "^22.13.0",
94
+ "tsx": "^4.19.0",
95
+ "typescript": "^5.7.0",
96
+ },
97
+ };
98
+ writeFileSync(join(root, "package.json"), JSON.stringify(packageJson, null, 2) + "\n");
99
+ // 3. tsconfig.json
100
+ const tsconfigJson = {
101
+ compilerOptions: {
102
+ target: "ES2022",
103
+ module: "NodeNext",
104
+ moduleResolution: "NodeNext",
105
+ strict: true,
106
+ skipLibCheck: true,
107
+ types: ["node"],
108
+ },
109
+ };
110
+ writeFileSync(join(root, "tsconfig.json"), JSON.stringify(tsconfigJson, null, 2) + "\n");
111
+ // 4. .gitignore
112
+ const gitignore = `.actiondock/
113
+ node_modules/
114
+ dist/
115
+ build/
116
+ *.log
117
+ .env
118
+ `;
119
+ writeFileSync(join(root, ".gitignore"), gitignore);
120
+ // 5. actions/
121
+ const actionsDir = join(root, "actions");
122
+ mkdirSync(actionsDir, { recursive: true });
123
+ const sampleAction = `import { defineAction } from "@actiondock/sdk";
124
+
125
+ // 说明:若 Action 需导入包内其他源码目录(如 src/、lib/ 或辅助工具模块),
126
+ // 请务必在 actiondock.json 的 "files" 字段中声明对应目录以确保构建与导出完整。
127
+ export default defineAction(async (input: { name: string }, ctx) => {
128
+ const greeting = ctx.config.get("SAMPLE_GREETING", "Hello");
129
+ const count = ((await ctx.state.get<number>("greet_count")) || 0) + 1;
130
+ await ctx.state.set("greet_count", count);
131
+
132
+ ctx.log.info(\`Greeting \${input.name} (times greeted: \${count})\`);
133
+
134
+ return {
135
+ message: \`\${greeting}, \${input.name}!\`,
136
+ timesGreeted: count,
137
+ };
138
+ });
139
+ `;
140
+ writeFileSync(join(actionsDir, "greet.ts"), sampleAction);
141
+ // 6. playbooks/
142
+ const playbooksDir = join(root, "playbooks");
143
+ mkdirSync(playbooksDir, { recursive: true });
144
+ const samplePlaybook = `# Greeting SOP
145
+
146
+ - Call \`sample.greet\` with the user's name.
147
+ - Confirm the returned greeting message.
148
+ `;
149
+ writeFileSync(join(playbooksDir, "greet-user.md"), samplePlaybook);
150
+ // 7. tests/
151
+ const testsDir = join(root, "tests");
152
+ mkdirSync(testsDir, { recursive: true });
153
+ const sampleTest = `import { describe, it } from "node:test";
154
+ import assert from "node:assert/strict";
155
+ import { createTestRuntime } from "@actiondock/testing";
156
+ import greetAction from "../actions/greet.js";
157
+
158
+ describe("greet action", () => {
159
+ it("should greet user with greeting and increment count", async () => {
160
+ const runtime = createTestRuntime({
161
+ config: { SAMPLE_GREETING: "Hi" },
162
+ });
163
+
164
+ const res1 = await runtime.run(greetAction, { name: "Alice" });
165
+ assert.equal(res1.message, "Hi, Alice!");
166
+ assert.equal(await runtime.state.get("greet_count"), 1);
167
+
168
+ const res2 = await runtime.run(greetAction, { name: "Bob" });
169
+ assert.equal(res2.message, "Hi, Bob!");
170
+ assert.equal(await runtime.state.get("greet_count"), 2);
171
+ });
172
+ });
173
+ `;
174
+ writeFileSync(join(testsDir, "greet.test.ts"), sampleTest);
175
+ }
@@ -1,5 +1,6 @@
1
1
  import type { ActionDefinition } from "@actiondock/sdk";
2
- import type { PlaybookDefinition, ProjectConfig } from "./types";
2
+ import { type ModuleLoader } from "../runtime/module-loader.js";
3
+ import type { ActionDockManifest, PlaybookDefinition, PlaybookManifestEntry, ProjectConfig } from "./types.js";
3
4
  /**
4
5
  * 从指定目录开始向上逐级递归查找包含 `actiondock.json` 的项目根目录。
5
6
  *
@@ -7,8 +8,6 @@ import type { PlaybookDefinition, ProjectConfig } from "./types";
7
8
  * @returns 项目根目录绝对路径,若未找到则返回 null
8
9
  */
9
10
  export declare function findProjectRoot(cwd?: string): string | null;
10
- export declare const ACTION_ID_REGEX: RegExp;
11
- export declare const PLAYBOOK_ID_REGEX: RegExp;
12
11
  /**
13
12
  * 加载并校验指定目录下的 `actiondock.json` 配置文件。
14
13
  *
@@ -59,47 +58,42 @@ export declare function ensureProjectDependencies(projectRoot: string, force?: b
59
58
  export declare function discoverActionFiles(projectRoot: string, actionsDir?: string): string[];
60
59
  /**
61
60
  * 动态导入并加载项目下的所有 Action 定义对象。
61
+ * 以 actiondock.json 为唯一事实源读取 Action 的元数据契约(id, description, inputSchema, outputSchema, tags, annotations, uses),
62
+ * 源码文件仅提供执行 Handler。
62
63
  *
63
64
  * @param projectRoot 项目根目录
64
- * @param actionsDir actions 子目录(默认 "actions")
65
- * @param options 控制是否允许自动安装依赖等选项
65
+ * @param _actionsDir actions 子目录(向后兼容保留参数)
66
+ * @param options 模块加载器等选项
66
67
  * @returns Map<ActionId, ActionDefinition> 映射
67
68
  */
68
- export declare function loadActions(projectRoot: string, actionsDir?: string, options?: {
69
+ export declare function loadActions(projectRoot: string, _actionsDir?: string, options?: {
69
70
  autoInstall?: boolean;
71
+ loader?: ModuleLoader;
70
72
  }): Promise<Map<string, ActionDefinition>>;
71
- /**
72
- * Action 文件映射条目,包含 Action ID、源文件绝对路径与 Action 定义对象。
73
- */
74
- export interface ActionFileEntry {
75
- id: string;
76
- filePath: string;
77
- action: ActionDefinition;
78
- }
79
- /**
80
- * 加载并建立 Action ID 与其物理源码文件路径之间的映射关系(供构建打包器及清单同步使用)。
81
- */
82
- export declare function loadActionFileMap(projectRoot: string, actionsDir?: string, options?: {
83
- autoInstall?: boolean;
84
- strict?: boolean;
85
- }): Promise<Map<string, ActionFileEntry>>;
86
73
  /**
87
74
  * 发现项目 playbooks 目录下的所有 Playbook Markdown 文档(.md)。
88
75
  */
89
76
  export declare function discoverPlaybookFiles(projectRoot: string, playbooksDir?: string): string[];
90
77
  /**
91
- * 解析单个 Playbook Markdown 文件的内容与 YAML Frontmatter 头部元数据。
78
+ * 解析单个 Playbook Markdown 文件的内容与元数据。
79
+ * Playbook 规程正文为纯 Markdown,其元数据(id, description, actions)直接由调用方传入(源自 actiondock.json)。
92
80
  *
93
- * @param content 文件文本内容
81
+ * @param content 文件 Markdown 文本内容
94
82
  * @param filePath 物理文件路径
83
+ * @param metadata Playbook 清单元数据
95
84
  * @returns PlaybookDefinition 对象
96
85
  */
97
- export declare function parsePlaybookContent(content: string, filePath: string): PlaybookDefinition;
86
+ export declare function parsePlaybookContent(content: string, filePath: string, metadata?: Partial<PlaybookManifestEntry> & {
87
+ id?: string;
88
+ name?: string;
89
+ }): PlaybookDefinition;
98
90
  /**
99
- * 加载项目 playbooks 目录下的所有 Playbook SOP 文档。
91
+ * 加载项目声明的 Playbook SOP 规程文档。
92
+ * 以 actiondock.json 中的 playbooks 声明作为唯一事实源。
100
93
  *
101
94
  * @param projectRoot 项目根目录
102
- * @param playbooksDir playbooks 子目录(默认 "playbooks")
95
+ * @param _playbooksDir playbooks 子目录(向后兼容保留参数)
96
+ * @param customManifest 可选的显式清单对象(若未提供则从磁盘加载)
103
97
  * @returns Map<PlaybookId, PlaybookDefinition> 映射
104
98
  */
105
- export declare function loadPlaybooks(projectRoot: string, playbooksDir?: string): Map<string, PlaybookDefinition>;
99
+ export declare function loadPlaybooks(projectRoot: string, _playbooksDir?: string, customManifest?: ActionDockManifest | null): Map<string, PlaybookDefinition>;