@actiondock/core 2.0.11 → 2.0.12

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 (119) hide show
  1. package/dist/build/templates.d.ts +19 -0
  2. package/dist/catalog/action-index.d.ts +9 -0
  3. package/dist/catalog/action-resolver.d.ts +20 -0
  4. package/dist/catalog/location-registry.d.ts +15 -0
  5. package/dist/catalog/package-catalog.d.ts +7 -0
  6. package/dist/catalog/types.d.ts +68 -0
  7. package/dist/doctor/doctor.d.ts +6 -0
  8. package/dist/doctor/types.d.ts +23 -0
  9. package/dist/execution/index.d.ts +2 -0
  10. package/dist/execution/service.d.ts +74 -0
  11. package/dist/execution/types.d.ts +74 -0
  12. package/dist/export/templates.d.ts +33 -0
  13. package/dist/filter/intent.d.ts +33 -0
  14. package/{src/index.ts → dist/index.d.ts} +1 -1
  15. package/dist/index.js +8458 -0
  16. package/dist/profile/client.d.ts +116 -0
  17. package/dist/profile/manager.d.ts +47 -0
  18. package/dist/profile/types.d.ts +61 -0
  19. package/dist/project/closure.d.ts +25 -0
  20. package/{src/project/index.ts → dist/project/index.d.ts} +1 -0
  21. package/dist/project/init.d.ts +27 -0
  22. package/dist/project/loader.d.ts +105 -0
  23. package/dist/project/manifest.d.ts +44 -0
  24. package/dist/project/types.d.ts +131 -0
  25. package/dist/registry/registry.d.ts +24 -0
  26. package/dist/registry/types.d.ts +117 -0
  27. package/dist/runtime/clock.d.ts +21 -0
  28. package/dist/runtime/context.d.ts +75 -0
  29. package/dist/runtime/env.d.ts +54 -0
  30. package/dist/runtime/events.d.ts +61 -0
  31. package/dist/runtime/execution-manager.d.ts +48 -0
  32. package/{src/runtime/index.ts → dist/runtime/index.d.ts} +0 -1
  33. package/dist/runtime/module-loader.d.ts +42 -0
  34. package/dist/runtime/process.d.ts +11 -0
  35. package/dist/runtime/runner.d.ts +186 -0
  36. package/dist/runtime/standalone.d.ts +40 -0
  37. package/dist/schema/validator.d.ts +28 -0
  38. package/dist/server/body.d.ts +40 -0
  39. package/{src/server/index.ts → dist/server/index.d.ts} +0 -1
  40. package/dist/server/routes/actions.d.ts +5 -0
  41. package/dist/server/routes/common.d.ts +36 -0
  42. package/dist/server/routes/config.d.ts +5 -0
  43. package/dist/server/routes/doctor.d.ts +5 -0
  44. package/dist/server/routes/health.d.ts +5 -0
  45. package/dist/server/routes/info.d.ts +5 -0
  46. package/dist/server/routes/playbooks.d.ts +5 -0
  47. package/dist/server/routes/runs.d.ts +5 -0
  48. package/dist/server/routes/state.d.ts +5 -0
  49. package/dist/server/runtime-registry.d.ts +78 -0
  50. package/dist/server/security.d.ts +37 -0
  51. package/dist/server/server.d.ts +15 -0
  52. package/dist/server/types.d.ts +59 -0
  53. package/dist/storage/driver.d.ts +15 -0
  54. package/dist/storage/index.d.ts +44 -0
  55. package/{src/storage/mask.ts → dist/storage/mask.d.ts} +5 -20
  56. package/dist/storage/sqlite.d.ts +66 -0
  57. package/dist/storage/types.d.ts +121 -0
  58. package/dist/utils/index.d.ts +29 -0
  59. package/dist/version.d.ts +4 -0
  60. package/package.json +11 -10
  61. package/src/build/templates.ts +0 -59
  62. package/src/catalog/action-index.ts +0 -92
  63. package/src/catalog/action-resolver.ts +0 -120
  64. package/src/catalog/location-registry.ts +0 -134
  65. package/src/catalog/package-catalog.ts +0 -87
  66. package/src/catalog/types.ts +0 -75
  67. package/src/doctor/doctor.ts +0 -419
  68. package/src/doctor/types.ts +0 -25
  69. package/src/execution/index.ts +0 -2
  70. package/src/execution/service.ts +0 -322
  71. package/src/execution/types.ts +0 -73
  72. package/src/export/templates.ts +0 -590
  73. package/src/filter/intent.ts +0 -154
  74. package/src/profile/client.ts +0 -552
  75. package/src/profile/manager.ts +0 -341
  76. package/src/profile/types.ts +0 -71
  77. package/src/project/init.ts +0 -242
  78. package/src/project/loader.ts +0 -479
  79. package/src/project/manifest.ts +0 -299
  80. package/src/project/types.ts +0 -141
  81. package/src/registry/registry.ts +0 -940
  82. package/src/registry/types.ts +0 -127
  83. package/src/runtime/clock.ts +0 -41
  84. package/src/runtime/context.ts +0 -259
  85. package/src/runtime/env.ts +0 -172
  86. package/src/runtime/events.ts +0 -142
  87. package/src/runtime/execution-manager.ts +0 -74
  88. package/src/runtime/module-loader.ts +0 -67
  89. package/src/runtime/process.ts +0 -245
  90. package/src/runtime/runner.ts +0 -527
  91. package/src/runtime/standalone.ts +0 -429
  92. package/src/schema/validator.ts +0 -61
  93. package/src/server/body.ts +0 -112
  94. package/src/server/routes/actions.ts +0 -262
  95. package/src/server/routes/common.ts +0 -126
  96. package/src/server/routes/config.ts +0 -147
  97. package/src/server/routes/doctor.ts +0 -29
  98. package/src/server/routes/health.ts +0 -40
  99. package/src/server/routes/info.ts +0 -221
  100. package/src/server/routes/playbooks.ts +0 -109
  101. package/src/server/routes/runs.ts +0 -301
  102. package/src/server/routes/state.ts +0 -133
  103. package/src/server/runtime-registry.ts +0 -115
  104. package/src/server/security.ts +0 -115
  105. package/src/server/server.ts +0 -251
  106. package/src/server/types.ts +0 -57
  107. package/src/storage/driver.ts +0 -126
  108. package/src/storage/index.ts +0 -81
  109. package/src/storage/sqlite.ts +0 -714
  110. package/src/storage/types.ts +0 -139
  111. package/src/utils/index.ts +0 -62
  112. /package/{src/build/index.ts → dist/build/index.d.ts} +0 -0
  113. /package/{src/catalog/index.ts → dist/catalog/index.d.ts} +0 -0
  114. /package/{src/doctor/index.ts → dist/doctor/index.d.ts} +0 -0
  115. /package/{src/export/index.ts → dist/export/index.d.ts} +0 -0
  116. /package/{src/filter/index.ts → dist/filter/index.d.ts} +0 -0
  117. /package/{src/profile/index.ts → dist/profile/index.d.ts} +0 -0
  118. /package/{src/registry/index.ts → dist/registry/index.d.ts} +0 -0
  119. /package/{src/server/routes/index.ts → dist/server/routes/index.d.ts} +0 -0
@@ -1,479 +0,0 @@
1
- import { spawnSync } from "node:child_process";
2
- import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
3
- import { homedir } from "node:os";
4
- import { basename, dirname, join, resolve } from "node:path";
5
- import { pathToFileURL } from "node:url";
6
- import YAML from "yaml";
7
- import type { ActionDefinition } from "@actiondock/sdk";
8
- import { getModuleLoader } from "../runtime/module-loader";
9
- import type { PlaybookDefinition, PlaybookFrontmatter, ProjectConfig } from "./types";
10
-
11
- /**
12
- * 将绝对路径规范化为 ESM 动态导入可用的 file:// URL 标识符。
13
- * Windows 绝对路径(如 D:\a\b.ts)不是合法的 ESM 标识符,会被解析为 "d:" 协议导致
14
- * 加载失败;POSIX 绝对路径虽可直接导入,统一转换后跨平台行为一致。
15
- */
16
- function toImportSpecifier(filePath: string): string {
17
- return pathToFileURL(filePath).href;
18
- }
19
-
20
- /**
21
- * 从指定目录开始向上逐级递归查找包含 `actiondock.json` 的项目根目录。
22
- *
23
- * @param cwd 起始搜索目录(默认为 process.cwd())
24
- * @returns 项目根目录绝对路径,若未找到则返回 null
25
- */
26
- export function findProjectRoot(cwd?: string): string | null {
27
- let current: string;
28
- try {
29
- current = resolve(cwd || process.cwd());
30
- } catch {
31
- return null;
32
- }
33
- while (true) {
34
- try {
35
- const configPath = join(current, "actiondock.json");
36
- if (existsSync(configPath)) {
37
- return current;
38
- }
39
- } catch {
40
- return null;
41
- }
42
- const parent = dirname(current);
43
- if (parent === current) {
44
- break;
45
- }
46
- current = parent;
47
- }
48
- return null;
49
- }
50
-
51
- /**
52
- * 加载并校验指定目录下的 `actiondock.json` 配置文件。
53
- *
54
- * @param projectRoot 项目根目录绝对路径
55
- * @returns 解析后的 ProjectConfig 对象
56
- * @throws {Error} 若文件不存在或 JSON 格式错误、缺失必要字段
57
- */
58
- export function loadProjectConfig(projectRoot: string): ProjectConfig {
59
- const configPath = join(projectRoot, "actiondock.json");
60
- if (!existsSync(configPath)) {
61
- throw new Error(`actiondock.json not found in ${projectRoot}`);
62
- }
63
- const content = readFileSync(configPath, "utf-8");
64
- try {
65
- const parsed = JSON.parse(content);
66
- if (!parsed.id || typeof parsed.id !== "string") {
67
- throw new Error("actiondock.json missing required 'id' field");
68
- }
69
- if (!parsed.name || typeof parsed.name !== "string") {
70
- parsed.name = parsed.id;
71
- }
72
- if (!parsed.version || typeof parsed.version !== "string") {
73
- parsed.version = "0.1.0";
74
- }
75
- parsed.actionsDir = parsed.actionsDir || "actions";
76
- parsed.playbooksDir = parsed.playbooksDir || "playbooks";
77
- return parsed as ProjectConfig;
78
- } catch (err: any) {
79
- throw new Error(`Failed to parse actiondock.json: ${err.message}`);
80
- }
81
- }
82
-
83
- /**
84
- * 探测宿主系统中可用的包管理工具(优先级:pnpm > npm > yarn > bun)。
85
- * 可通过环境变量 ACTIONDOCK_INSTALLER 强制指定(如 npm、pnpm、yarn、bun)。
86
- * 探测必须经 shell 执行:Windows 下 npm/pnpm/yarn 均为 .cmd 垫片,
87
- * 不经 shell 的 spawnSync 无法解析,将错误地回退到原生 exe 的 bun。
88
- */
89
- function getInstallCommand(): string[] {
90
- const preferred = process.env.ACTIONDOCK_INSTALLER?.trim();
91
- if (preferred) {
92
- return [preferred, "install"];
93
- }
94
- const candidates: [string, string][] = [
95
- ["pnpm", "install"],
96
- ["npm", "install"],
97
- ["yarn", "install"],
98
- ["bun", "install"],
99
- ];
100
- for (const [pm, action] of candidates) {
101
- try {
102
- const check = spawnSync(`${pm} --version`, {
103
- stdio: "pipe",
104
- shell: true,
105
- });
106
- if (check.status === 0) {
107
- return [pm, action];
108
- }
109
- } catch {
110
- // 继续探测下一个候选包管理器
111
- }
112
- }
113
- return ["npm", "install"];
114
- }
115
-
116
- /**
117
- * 解析 npm 风格 .npmrc 中的 strict-ssl 配置(项目级优先于用户级)。
118
- * 返回 undefined 表示各级配置均未声明该键。
119
- */
120
- function resolveNpmStrictSsl(projectRoot: string): boolean | undefined {
121
- const configPaths = [join(projectRoot, ".npmrc"), join(homedir(), ".npmrc")];
122
- for (const configPath of configPaths) {
123
- try {
124
- if (!existsSync(configPath)) continue;
125
- const raw = readFileSync(configPath, "utf-8");
126
- for (const line of raw.split(/\r?\n/)) {
127
- const matched = line.match(/^\s*strict-ssl\s*=\s*(\S+)\s*$/i);
128
- if (matched) {
129
- const falsy = ["false", "0", "no", "off"];
130
- return !falsy.includes(matched[1].toLowerCase());
131
- }
132
- }
133
- } catch {
134
- // 配置不可读时继续检查下一级
135
- }
136
- }
137
- return undefined;
138
- }
139
-
140
- /**
141
- * 确保项目依赖(node_modules)已正确安装。
142
- * 若尚未安装或加载失败时,自动触发包管理器执行依赖安装。
143
- *
144
- * @param projectRoot 项目根目录
145
- * @param force 是否强制重新安装
146
- * @returns 是否成功执行了安装
147
- */
148
- export function ensureProjectDependencies(projectRoot: string, force = false): boolean {
149
- if (process.env.ACTIONDOCK_AUTO_INSTALL === "false") {
150
- return false;
151
- }
152
- const pkgJsonPath = join(projectRoot, "package.json");
153
- if (!existsSync(pkgJsonPath)) {
154
- return false;
155
- }
156
-
157
- const nodeModulesPath = join(projectRoot, "node_modules");
158
- if (!force && existsSync(nodeModulesPath)) {
159
- return false;
160
- }
161
-
162
- try {
163
- const raw = readFileSync(pkgJsonPath, "utf-8");
164
- const pkg = JSON.parse(raw);
165
- const hasDeps =
166
- (pkg.dependencies && Object.keys(pkg.dependencies).length > 0) ||
167
- (pkg.devDependencies && Object.keys(pkg.devDependencies).length > 0);
168
-
169
- if (!hasDeps && !force) {
170
- return false;
171
- }
172
-
173
- const installCmd = getInstallCommand();
174
- process.stderr.write(
175
- `[actiondock] Installing dependencies using ${installCmd[0]} for '${pkg.name || basename(projectRoot)}'...\n`
176
- );
177
-
178
- // bun 不读取 .npmrc 的 strict-ssl 配置;当 npm 侧已声明 strict-ssl=false 时,
179
- // 桥接为 bun 子进程的 NODE_TLS_REJECT_UNAUTHORIZED=0,保证内网自签名证书源下行为一致
180
- const childEnv = { ...process.env };
181
- if (installCmd[0] === "bun" && resolveNpmStrictSsl(projectRoot) === false) {
182
- childEnv.NODE_TLS_REJECT_UNAUTHORIZED = "0";
183
- }
184
-
185
- // shell 模式下须传入单一命令字符串;候选命令均来自内置白名单,无注入面
186
- const proc = spawnSync(installCmd.join(" "), {
187
- cwd: projectRoot,
188
- stdio: "pipe",
189
- shell: true,
190
- env: childEnv,
191
- });
192
-
193
- if (proc.status !== 0) {
194
- const errText = proc.stderr?.toString() || `Unknown error during ${installCmd[0]} install`;
195
- process.stderr.write(`[actiondock] Warning: Dependency installation failed: ${errText}\n`);
196
- if (installCmd[0] === "bun" && /SELF_SIGNED_CERT|CERT_|UNABLE_TO_VERIFY|ERR_TLS/i.test(errText)) {
197
- process.stderr.write(
198
- `[actiondock] Hint: bun ignores 'strict-ssl=false' from .npmrc. ` +
199
- `Add 'strict-ssl=false' to the project or user .npmrc, or set ACTIONDOCK_INSTALLER=npm.\n`
200
- );
201
- }
202
- return false;
203
- }
204
-
205
- process.stderr.write(`[actiondock] Dependencies installed successfully.\n`);
206
- return true;
207
- } catch (err: any) {
208
- process.stderr.write(`[actiondock] Warning: Failed to run auto-install: ${err.message}\n`);
209
- return false;
210
- }
211
- }
212
-
213
- /**
214
- * 递归扫描指定目录下的特定后缀文件(自动排除测试文件 *.test.ts, *.spec.ts 和 *.d.ts)。
215
- */
216
- function scanFiles(dir: string, extension: string): string[] {
217
- if (!existsSync(dir)) return [];
218
- const results: string[] = [];
219
-
220
- function walk(current: string) {
221
- const entries = readdirSync(current);
222
- for (const entry of entries) {
223
- const fullPath = join(current, entry);
224
- const stat = statSync(fullPath);
225
- if (stat.isDirectory()) {
226
- walk(fullPath);
227
- } else if (stat.isFile() && fullPath.endsWith(extension)) {
228
- // 排除测试与类型声明文件
229
- if (
230
- !fullPath.endsWith(".test.ts") &&
231
- !fullPath.endsWith(".spec.ts") &&
232
- !fullPath.endsWith(".d.ts")
233
- ) {
234
- results.push(fullPath);
235
- }
236
- }
237
- }
238
- }
239
-
240
- walk(dir);
241
- return results;
242
- }
243
-
244
- /**
245
- * 发现并检索项目 actions 目录下的所有 Action 源码文件(.ts)。
246
- *
247
- * @param projectRoot 项目根目录
248
- * @param actionsDir actions 子目录名称(默认 "actions")
249
- */
250
- export function discoverActionFiles(
251
- projectRoot: string,
252
- actionsDir = "actions"
253
- ): string[] {
254
- const fullDir = join(projectRoot, actionsDir);
255
- return scanFiles(fullDir, ".ts");
256
- }
257
-
258
- /**
259
- * 动态导入并加载项目下的所有 Action 定义对象。
260
- *
261
- * @param projectRoot 项目根目录
262
- * @param actionsDir actions 子目录(默认 "actions")
263
- * @param options 控制是否允许自动安装依赖等选项
264
- * @returns Map<ActionId, ActionDefinition> 映射
265
- */
266
- export async function loadActions(
267
- projectRoot: string,
268
- actionsDir = "actions",
269
- options: { autoInstall?: boolean } = { autoInstall: true }
270
- ): Promise<Map<string, ActionDefinition>> {
271
- if (options.autoInstall !== false) {
272
- ensureProjectDependencies(projectRoot);
273
- }
274
-
275
- const files = discoverActionFiles(projectRoot, actionsDir);
276
- const actions = new Map<string, ActionDefinition>();
277
- const loader = getModuleLoader();
278
-
279
- for (const file of files) {
280
- try {
281
- // 动态导入,若缺失模块则自动触发依赖重装与二次重试
282
- let imported: any;
283
- try {
284
- imported = await loader.load(file);
285
- } catch (err: any) {
286
- const msg = String(err.message || "");
287
- if (
288
- options.autoInstall !== false &&
289
- (msg.includes("Cannot find package") ||
290
- msg.includes("Cannot find module") ||
291
- msg.includes("ERR_MODULE_NOT_FOUND") ||
292
- msg.includes("Could not resolve"))
293
- ) {
294
- const installed = ensureProjectDependencies(projectRoot, true);
295
- if (installed) {
296
- imported = await loader.load(file);
297
- } else {
298
- throw err;
299
- }
300
- } else {
301
- throw err;
302
- }
303
- }
304
-
305
- const action = imported.default || imported.action;
306
- if (action && typeof action === "object" && typeof action.id === "string") {
307
- if (actions.has(action.id)) {
308
- throw new Error(
309
- `Duplicate action ID '${action.id}' found in ${file} (previously loaded)`
310
- );
311
- }
312
- actions.set(action.id, action);
313
- } else {
314
- console.warn(
315
- `[WARN] File ${file} does not export a valid default ActionDefinition`
316
- );
317
- }
318
- } catch (err: any) {
319
- throw new Error(`Failed to load action from ${file}: ${err.message}`);
320
- }
321
- }
322
-
323
- return actions;
324
- }
325
-
326
- /**
327
- * Action 文件映射条目,包含 Action ID、源文件绝对路径与 Action 定义对象。
328
- */
329
- export interface ActionFileEntry {
330
- id: string;
331
- filePath: string;
332
- action: ActionDefinition;
333
- }
334
-
335
- /**
336
- * 加载并建立 Action ID 与其物理源码文件路径之间的映射关系(供构建打包器及清单同步使用)。
337
- */
338
- export async function loadActionFileMap(
339
- projectRoot: string,
340
- actionsDir = "actions",
341
- options: { autoInstall?: boolean; strict?: boolean } = { autoInstall: true, strict: false }
342
- ): Promise<Map<string, ActionFileEntry>> {
343
- if (options.autoInstall !== false) {
344
- ensureProjectDependencies(projectRoot);
345
- }
346
-
347
- const files = discoverActionFiles(projectRoot, actionsDir);
348
- const map = new Map<string, ActionFileEntry>();
349
- const loader = getModuleLoader();
350
-
351
- for (const file of files) {
352
- try {
353
- let imported: any;
354
- try {
355
- imported = await loader.load(file);
356
- } catch (err: any) {
357
- const msg = String(err.message || "");
358
- if (
359
- options.autoInstall !== false &&
360
- (msg.includes("Cannot find package") ||
361
- msg.includes("Cannot find module") ||
362
- msg.includes("ERR_MODULE_NOT_FOUND") ||
363
- msg.includes("Could not resolve"))
364
- ) {
365
- const installed = ensureProjectDependencies(projectRoot, true);
366
- if (installed) {
367
- imported = await loader.load(file);
368
- } else {
369
- throw err;
370
- }
371
- } else {
372
- throw err;
373
- }
374
- }
375
-
376
- const act = imported.default || imported.action;
377
- if (act && typeof act === "object" && typeof act.id === "string") {
378
- if (map.has(act.id)) {
379
- throw new Error(
380
- `Duplicate action ID '${act.id}' found in ${file} (previously loaded from ${map.get(act.id)!.filePath})`
381
- );
382
- }
383
- map.set(act.id, {
384
- id: act.id,
385
- filePath: resolve(file),
386
- action: act,
387
- });
388
- } else if (options.strict) {
389
- console.warn(`[WARN] File ${file} does not export a valid default ActionDefinition`);
390
- }
391
- } catch (err: any) {
392
- if (options.strict) {
393
- throw new Error(`Failed to load action from ${file}: ${err.message}`);
394
- }
395
- }
396
- }
397
-
398
- return map;
399
- }
400
-
401
- /**
402
- * 发现项目 playbooks 目录下的所有 Playbook Markdown 文档(.md)。
403
- */
404
- export function discoverPlaybookFiles(
405
- projectRoot: string,
406
- playbooksDir = "playbooks"
407
- ): string[] {
408
- const fullDir = join(projectRoot, playbooksDir);
409
- return scanFiles(fullDir, ".md");
410
- }
411
-
412
- /**
413
- * 解析单个 Playbook Markdown 文件的内容与 YAML Frontmatter 头部元数据。
414
- *
415
- * @param content 文件文本内容
416
- * @param filePath 物理文件路径
417
- * @returns PlaybookDefinition 对象
418
- */
419
- export function parsePlaybookContent(
420
- content: string,
421
- filePath: string
422
- ): PlaybookDefinition {
423
- let frontmatter: Partial<PlaybookFrontmatter> = {};
424
- let body = content;
425
-
426
- // 正则提取以 --- 包裹的 YAML Frontmatter
427
- const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/);
428
- if (match) {
429
- try {
430
- frontmatter = YAML.parse(match[1]) || {};
431
- body = match[2];
432
- } catch (err: any) {
433
- console.warn(`[WARN] Failed to parse frontmatter in ${filePath}: ${err.message}`);
434
- }
435
- }
436
-
437
- const filename = filePath.split("/").pop() || "unknown";
438
- const defaultId = filename.replace(/\.md$/, "");
439
-
440
- return {
441
- id: frontmatter.id || defaultId,
442
- description: frontmatter.description,
443
- actions: Array.isArray(frontmatter.actions) ? frontmatter.actions : [],
444
- content: body.trim(),
445
- filePath,
446
- };
447
- }
448
-
449
- /**
450
- * 加载项目 playbooks 目录下的所有 Playbook SOP 文档。
451
- *
452
- * @param projectRoot 项目根目录
453
- * @param playbooksDir playbooks 子目录(默认 "playbooks")
454
- * @returns Map<PlaybookId, PlaybookDefinition> 映射
455
- */
456
- export function loadPlaybooks(
457
- projectRoot: string,
458
- playbooksDir = "playbooks"
459
- ): Map<string, PlaybookDefinition> {
460
- const files = discoverPlaybookFiles(projectRoot, playbooksDir);
461
- const playbooks = new Map<string, PlaybookDefinition>();
462
-
463
- for (const file of files) {
464
- try {
465
- const content = readFileSync(file, "utf-8");
466
- const playbook = parsePlaybookContent(content, file);
467
- if (playbooks.has(playbook.id)) {
468
- throw new Error(
469
- `Duplicate playbook ID '${playbook.id}' found in ${file}`
470
- );
471
- }
472
- playbooks.set(playbook.id, playbook);
473
- } catch (err: any) {
474
- throw new Error(`Failed to load playbook from ${file}: ${err.message}`);
475
- }
476
- }
477
-
478
- return playbooks;
479
- }