@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,33 @@
1
+ import type { ActionDockManifest } from "./types.js";
2
+ export declare const GENERATED_TYPES_REL_PATH: string;
3
+ export declare const GENERATED_TYPES_OUTDATED_CODE = "GENERATED_TYPES_OUTDATED";
4
+ /**
5
+ * 将 JSON Schema 递归转换为 TypeScript 类型声明字符串。
6
+ */
7
+ export declare function jsonSchemaToTypeScript(schema: any, indent?: number): string;
8
+ /**
9
+ * 根据项目清单 Schema 生成类型声明文件内容,头部注入 RFC 8785 Manifest SHA-256 摘要。
10
+ */
11
+ export declare function generateActionTypes(manifest: ActionDockManifest): {
12
+ code: string;
13
+ digest: string;
14
+ };
15
+ /**
16
+ * 将生成的类型声明写入到项目的 .actiondock/generated/actions.d.ts 文件。
17
+ */
18
+ export declare function writeActionTypes(projectRoot: string, manifest?: ActionDockManifest | null): {
19
+ filePath: string;
20
+ digest: string;
21
+ };
22
+ /**
23
+ * 只读检查项目中的 .actiondock/generated/actions.d.ts 是否过期。
24
+ * 若文件不存在,则认为无需校验通过(返回 exists: false, outdated: false)。
25
+ * 若文件存在但摘要与当前 actiondock.json 不一致,则返回 outdated: true。
26
+ */
27
+ export declare function checkGeneratedTypes(projectRoot: string, manifest?: ActionDockManifest | null): {
28
+ exists: boolean;
29
+ outdated: boolean;
30
+ expectedDigest?: string;
31
+ actualDigest?: string;
32
+ filePath: string;
33
+ };
@@ -0,0 +1,168 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { computeManifestDigest } from "./digest.js";
4
+ import { loadManifest } from "./manifest.js";
5
+ export const GENERATED_TYPES_REL_PATH = join(".actiondock", "generated", "actions.d.ts");
6
+ export const GENERATED_TYPES_OUTDATED_CODE = "GENERATED_TYPES_OUTDATED";
7
+ /**
8
+ * 将 JSON Schema 递归转换为 TypeScript 类型声明字符串。
9
+ */
10
+ export function jsonSchemaToTypeScript(schema, indent = 0) {
11
+ if (!schema || typeof schema !== "object" || Object.keys(schema).length === 0) {
12
+ return "unknown";
13
+ }
14
+ if (schema.enum && Array.isArray(schema.enum)) {
15
+ return schema.enum.map((v) => JSON.stringify(v)).join(" | ");
16
+ }
17
+ if (schema.anyOf && Array.isArray(schema.anyOf)) {
18
+ return `(${schema.anyOf.map((s) => jsonSchemaToTypeScript(s, indent)).join(" | ")})`;
19
+ }
20
+ if (schema.oneOf && Array.isArray(schema.oneOf)) {
21
+ return `(${schema.oneOf.map((s) => jsonSchemaToTypeScript(s, indent)).join(" | ")})`;
22
+ }
23
+ if (schema.allOf && Array.isArray(schema.allOf)) {
24
+ return `(${schema.allOf.map((s) => jsonSchemaToTypeScript(s, indent)).join(" & ")})`;
25
+ }
26
+ const type = schema.type;
27
+ if (type === "string")
28
+ return "string";
29
+ if (type === "number" || type === "integer")
30
+ return "number";
31
+ if (type === "boolean")
32
+ return "boolean";
33
+ if (type === "null")
34
+ return "null";
35
+ if (type === "array") {
36
+ const items = schema.items ? jsonSchemaToTypeScript(schema.items, indent) : "unknown";
37
+ return `Array<${items}>`;
38
+ }
39
+ if (type === "object" || schema.properties) {
40
+ const props = schema.properties || {};
41
+ const required = new Set(Array.isArray(schema.required) ? schema.required : []);
42
+ const propKeys = Object.keys(props);
43
+ if (propKeys.length === 0) {
44
+ return "Record<string, unknown>";
45
+ }
46
+ const spaces = " ".repeat(indent + 2);
47
+ const closeSpaces = " ".repeat(indent);
48
+ const lines = propKeys.map((k) => {
49
+ const isReq = required.has(k);
50
+ const safeKey = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(k) ? k : JSON.stringify(k);
51
+ const opt = isReq ? "" : "?";
52
+ const doc = props[k]?.description
53
+ ? `${spaces}/** ${props[k].description.replace(/\*\//g, "* /")} */\n`
54
+ : "";
55
+ return `${doc}${spaces}${safeKey}${opt}: ${jsonSchemaToTypeScript(props[k], indent + 2)};`;
56
+ });
57
+ return `{\n${lines.join("\n")}\n${closeSpaces}}`;
58
+ }
59
+ return "unknown";
60
+ }
61
+ /**
62
+ * 将 Action 标识符转换为合法的 TypeScript 标识符(PascalCase)。
63
+ */
64
+ function toPascalCase(str) {
65
+ return str
66
+ .replace(/[^a-zA-Z0-9]+(.)/g, (_, chr) => chr.toUpperCase())
67
+ .replace(/^[a-z]/, (chr) => chr.toUpperCase())
68
+ .replace(/[^a-zA-Z0-9]/g, "");
69
+ }
70
+ /**
71
+ * 根据项目清单 Schema 生成类型声明文件内容,头部注入 RFC 8785 Manifest SHA-256 摘要。
72
+ */
73
+ export function generateActionTypes(manifest) {
74
+ const digest = computeManifestDigest(manifest);
75
+ const actions = manifest.actions || {};
76
+ const actionEntries = Object.entries(actions);
77
+ const namespaceBlocks = actionEntries.map(([actionId, item]) => {
78
+ const name = toPascalCase(actionId);
79
+ const inType = jsonSchemaToTypeScript(item.inputSchema, 4);
80
+ const outType = jsonSchemaToTypeScript(item.outputSchema, 4);
81
+ const descComment = item.description
82
+ ? ` /**\n * ${item.description.replace(/\*\//g, "* /")}\n */\n`
83
+ : "";
84
+ return ` export namespace ${name} {\n${descComment} export type Input = ${inType};\n export type Output = ${outType};\n }`;
85
+ });
86
+ const mapEntries = actionEntries.map(([actionId]) => {
87
+ const name = toPascalCase(actionId);
88
+ return ` "${actionId}": {\n Input: Actions.${name}.Input;\n Output: Actions.${name}.Output;\n };`;
89
+ });
90
+ const code = `/**
91
+ * AUTO-GENERATED BY ACTIONDOCK. DO NOT EDIT DIRECTLY.
92
+ * @manifest-digest ${digest}
93
+ */
94
+
95
+ import type { ActionContext, ActionDefinition } from "@actiondock/sdk";
96
+
97
+ export namespace Actions {
98
+ ${namespaceBlocks.join("\n\n")}
99
+ }
100
+
101
+ export interface ActionSchemaMap {
102
+ ${mapEntries.join("\n")}
103
+ }
104
+
105
+ export type ActionInput<T extends keyof ActionSchemaMap> = ActionSchemaMap[T]["Input"];
106
+ export type ActionOutput<T extends keyof ActionSchemaMap> = ActionSchemaMap[T]["Output"];
107
+
108
+ export type TypedActionDefinition<T extends keyof ActionSchemaMap> = ActionDefinition<
109
+ ActionSchemaMap[T]["Input"],
110
+ ActionSchemaMap[T]["Output"]
111
+ >;
112
+ `;
113
+ return { code, digest };
114
+ }
115
+ /**
116
+ * 将生成的类型声明写入到项目的 .actiondock/generated/actions.d.ts 文件。
117
+ */
118
+ export function writeActionTypes(projectRoot, manifest) {
119
+ const targetManifest = manifest || loadManifest(projectRoot);
120
+ if (!targetManifest) {
121
+ throw new Error(`actiondock.json not found in ${projectRoot}`);
122
+ }
123
+ const { code, digest } = generateActionTypes(targetManifest);
124
+ const fullPath = join(projectRoot, GENERATED_TYPES_REL_PATH);
125
+ const dir = dirname(fullPath);
126
+ if (!existsSync(dir)) {
127
+ mkdirSync(dir, { recursive: true });
128
+ }
129
+ writeFileSync(fullPath, code, "utf-8");
130
+ return { filePath: fullPath, digest };
131
+ }
132
+ /**
133
+ * 只读检查项目中的 .actiondock/generated/actions.d.ts 是否过期。
134
+ * 若文件不存在,则认为无需校验通过(返回 exists: false, outdated: false)。
135
+ * 若文件存在但摘要与当前 actiondock.json 不一致,则返回 outdated: true。
136
+ */
137
+ export function checkGeneratedTypes(projectRoot, manifest) {
138
+ const fullPath = join(projectRoot, GENERATED_TYPES_REL_PATH);
139
+ if (!existsSync(fullPath)) {
140
+ return { exists: false, outdated: false, filePath: fullPath };
141
+ }
142
+ const targetManifest = manifest || loadManifest(projectRoot);
143
+ if (!targetManifest) {
144
+ return { exists: true, outdated: true, filePath: fullPath };
145
+ }
146
+ const expectedDigest = computeManifestDigest(targetManifest);
147
+ try {
148
+ const content = readFileSync(fullPath, "utf-8");
149
+ const match = content.match(/@manifest-digest\s+([^\s*]+)/);
150
+ const actualDigest = match ? match[1] : undefined;
151
+ const outdated = actualDigest !== expectedDigest;
152
+ return {
153
+ exists: true,
154
+ outdated,
155
+ expectedDigest,
156
+ actualDigest,
157
+ filePath: fullPath,
158
+ };
159
+ }
160
+ catch {
161
+ return {
162
+ exists: true,
163
+ outdated: true,
164
+ expectedDigest,
165
+ filePath: fullPath,
166
+ };
167
+ }
168
+ }
@@ -16,48 +16,13 @@ export interface ConfigItemDefinition {
16
16
  type?: ConfigValueType;
17
17
  /** 显式绑定的外部环境变量名(支持单个或优先级数组) */
18
18
  env?: string | string[];
19
+ /** 是否为必填项 */
20
+ required?: boolean;
21
+ /** 是否允许单次调用覆盖 */
22
+ allowInvocationOverride?: boolean;
19
23
  }
20
24
  /**
21
- * ActionDock 项目根配置文件契约(对应 actiondock.json)。
22
- */
23
- export interface ProjectConfig {
24
- /** 项目全局唯一 ID(例如 "team4u.github-tools") */
25
- id: string;
26
- /** 项目展示名称(例如 "GitHub Tools") */
27
- name: string;
28
- /** 项目版本号(遵循语义化版本 Semantic Versioning,如 "1.0.0") */
29
- version: string;
30
- /** 项目描述信息 */
31
- description?: string;
32
- /** Action 脚本文件存放目录(相对于项目根目录,默认为 "actions") */
33
- actionsDir?: string;
34
- /** Playbook SOP 文档存放目录(相对于项目根目录,默认为 "playbooks") */
35
- playbooksDir?: string;
36
- /** 声明的项目依赖配置项清单 */
37
- config?: Record<string, ConfigItemDefinition>;
38
- }
39
- /**
40
- * Playbook Markdown 文档头部 YAML Frontmatter 元数据。
41
- */
42
- export interface PlaybookFrontmatter {
43
- /** Playbook 唯一标识符(例如 "review-pr") */
44
- id: string;
45
- /** Playbook 任务描述 */
46
- description?: string;
47
- /** 该 Playbook SOP 所依赖/调用的 Action ID 列表(用于最小化构建与 Tree-shaking 导出) */
48
- actions?: string[];
49
- }
50
- /**
51
- * 解析后的完整 Playbook 定义对象。
52
- */
53
- export interface PlaybookDefinition extends PlaybookFrontmatter {
54
- /** Markdown 正文内容(去除了头部 YAML Frontmatter 后的 SOP 指南内容) */
55
- content: string;
56
- /** Playbook 源文件的绝对物理路径 */
57
- filePath: string;
58
- }
59
- /**
60
- * 单个 Action 在清单中的声明项。
25
+ * 单个 Action actiondock.json 清单中的声明项。
61
26
  */
62
27
  export interface ActionManifestEntry {
63
28
  /** Action 入口文件相对路径(如 "actions/greet.ts") */
@@ -76,56 +41,67 @@ export interface ActionManifestEntry {
76
41
  annotations?: Record<string, unknown>;
77
42
  }
78
43
  /**
79
- * ActionDock 声明式清单(actiondock.manifest.json)规范。
80
- * 作为元数据的单一事实源,实现无副作用的模块发现与构建规划。
44
+ * 单个 Playbook 在 actiondock.json 清单中的声明项。
81
45
  */
82
- export interface ActionDockManifest {
83
- schemaVersion: number;
84
- actions: Record<string, ActionManifestEntry>;
85
- assets?: string[];
46
+ export interface PlaybookManifestEntry {
47
+ /** Playbook Markdown 入口文件相对路径(如 "playbooks/greet-user.md") */
48
+ entry: string;
49
+ /** Playbook 任务描述 */
50
+ description?: string;
51
+ /** 该 Playbook 所依赖/调用的 Action ID 列表 */
52
+ actions?: string[];
86
53
  }
87
54
  /**
88
- * 清单同步操作选项。
55
+ * ActionDock 声明式元数据清单契约(actiondock.json 为唯一事实源)。
89
56
  */
90
- export interface SyncManifestOptions {
91
- /** 自定义 actions 源码目录(默认为工程配置中的 actionsDir 或 "actions") */
57
+ export interface ActionDockManifest {
58
+ /** JSON Schema 声明 URL */
59
+ $schema?: string;
60
+ /** 清单规范版本号(默认 2) */
61
+ schemaVersion?: number;
62
+ /** 项目全局唯一逻辑 ID(例如 "team4u.github-tools") */
63
+ id: string;
64
+ /** 项目展示名称(例如 "GitHub Tools") */
65
+ name?: string;
66
+ /** 项目版本号(如 "1.0.0") */
67
+ version?: string;
68
+ /** 项目描述信息 */
69
+ description?: string;
70
+ /** 声明的项目依赖配置项清单 */
71
+ config?: Record<string, ConfigItemDefinition>;
72
+ /** 声明的 Actions 集合(唯一事实源) */
73
+ actions?: Record<string, ActionManifestEntry>;
74
+ /** 声明的 Playbooks 集合(唯一事实源) */
75
+ playbooks?: Record<string, PlaybookManifestEntry>;
76
+ /** 跨包外部依赖映射(逻辑包 ID 到 npm 包名) */
77
+ dependencies?: Record<string, string>;
78
+ /** 框架导出与分发边界文件列表 */
79
+ files?: string[];
80
+ /** 静态资产列表 */
81
+ assets?: string[];
82
+ /** Action 源码存放目录(向后兼容过渡配置) */
92
83
  actionsDir?: string;
93
- /** 是否仅检查同步状态而不保存写入文件(默认 false) */
94
- check?: boolean;
95
- /** 是否自动移除源码中已不存在的 Action(默认 true) */
96
- prune?: boolean;
97
- /** 缺失依赖时是否自动执行安装(默认 true) */
98
- autoInstall?: boolean;
84
+ /** Playbook 规程文档存放目录(向后兼容过渡配置) */
85
+ playbooksDir?: string;
99
86
  }
100
87
  /**
101
- * 单个 Action 的清单同步变更项。
88
+ * ActionDock 项目根配置文件契约(等同于 ActionDockManifest,actiondock.json 为单一事实源)。
102
89
  */
103
- export interface ManifestSyncChange {
104
- /** Action 标识符 */
105
- actionId: string;
106
- /** 变更类型:新增、更新、删除或未变 */
107
- type: "added" | "updated" | "removed" | "unchanged";
108
- /** 源码入口文件相对路径 */
109
- entry?: string;
110
- /** 发生变更的字段列表(例如 ["description", "inputSchema"]) */
111
- changedFields?: string[];
112
- }
90
+ export type ProjectConfig = ActionDockManifest;
113
91
  /**
114
- * 清单同步执行结果。
92
+ * 解析后的完整 Playbook 定义对象。
115
93
  */
116
- export interface ManifestSyncResult {
117
- /** 清单当前是否已与源码完全一致 */
118
- inSync: boolean;
119
- /** 清单文件的物理绝对路径 */
120
- manifestPath: string;
121
- /** 所有 Action 的变更详情列表 */
122
- changes: ManifestSyncChange[];
123
- /** 新增的 Action 标识列表 */
124
- added: string[];
125
- /** 更新的 Action 标识列表 */
126
- updated: string[];
127
- /** 移除的 Action 标识列表 */
128
- removed: string[];
129
- /** 未改变的 Action 标识列表 */
130
- unchanged: string[];
94
+ export interface PlaybookDefinition {
95
+ /** Playbook 唯一标识符(例如 "review-pr") */
96
+ id: string;
97
+ /** Playbook 显示名称 */
98
+ name?: string;
99
+ /** Playbook 任务描述 */
100
+ description?: string;
101
+ /** Playbook 所依赖/调用的 Action ID 列表 */
102
+ actions: string[];
103
+ /** Markdown 正文内容(纯 Markdown 规程内容) */
104
+ content: string;
105
+ /** Playbook 源文件的绝对物理路径 */
106
+ filePath: string;
131
107
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,2 +1,5 @@
1
- export * from "./registry";
2
- export * from "./types";
1
+ export * from "./lock.js";
2
+ export * from "./registry.js";
3
+ export * from "./resolve.js";
4
+ export * from "./scan.js";
5
+ export * from "./types.js";
@@ -0,0 +1,5 @@
1
+ export * from "./lock.js";
2
+ export * from "./registry.js";
3
+ export * from "./resolve.js";
4
+ export * from "./scan.js";
5
+ export * from "./types.js";
@@ -0,0 +1,27 @@
1
+ /**
2
+ * 锁目录创建后超过该时长仍未释放,视为持有进程异常退出的残留锁。
3
+ */
4
+ export declare const REGISTRY_LOCK_STALE_MS = 10000;
5
+ /**
6
+ * 获取锁的最长等待时间,必须大于 REGISTRY_LOCK_STALE_MS,
7
+ * 保证等待方至少有机会观察到残留锁被回收后再判定超时。
8
+ */
9
+ export declare const REGISTRY_LOCK_ACQUIRE_TIMEOUT_MS = 15000;
10
+ /**
11
+ * 两次获取尝试之间的退避间隔,休眠让出事件循环,避免忙等阻塞。
12
+ */
13
+ export declare const REGISTRY_LOCK_RETRY_DELAY_MS = 25;
14
+ /**
15
+ * 异步注册表锁(防止多进程并发读写导致 registry.json 损坏)。
16
+ *
17
+ * 获取方式:fs/promises mkdir 的原子性保证同一时刻仅一个进程能创建锁目录。
18
+ * 冲突处理:EEXIST 时 stat 检查锁目录年龄,超过 stale 阈值则先原子 rename 到
19
+ * 隔离名再删除——rename 仅有一个进程能成功,失败方回到等待重试,消除
20
+ * 「stat 检查后删除再重建」的竞态窗口;未过期的锁通过 sleep 让出事件循环后重试。
21
+ *
22
+ * @param filePath 注册表文件路径,锁目录为其同级 `${filePath}.lock`
23
+ * @param fn 持锁期间执行的操作(同步或异步)
24
+ * @returns fn 的返回值
25
+ * @throws 超过 REGISTRY_LOCK_ACQUIRE_TIMEOUT_MS 仍未获取时抛出描述性错误
26
+ */
27
+ export declare function withRegistryLock<T>(filePath: string, fn: () => T | Promise<T>): Promise<T>;
@@ -0,0 +1,93 @@
1
+ import { mkdir, rename, rm, stat } from "node:fs/promises";
2
+ /**
3
+ * 锁目录创建后超过该时长仍未释放,视为持有进程异常退出的残留锁。
4
+ */
5
+ export const REGISTRY_LOCK_STALE_MS = 10000;
6
+ /**
7
+ * 获取锁的最长等待时间,必须大于 REGISTRY_LOCK_STALE_MS,
8
+ * 保证等待方至少有机会观察到残留锁被回收后再判定超时。
9
+ */
10
+ export const REGISTRY_LOCK_ACQUIRE_TIMEOUT_MS = 15000;
11
+ /**
12
+ * 两次获取尝试之间的退避间隔,休眠让出事件循环,避免忙等阻塞。
13
+ */
14
+ export const REGISTRY_LOCK_RETRY_DELAY_MS = 25;
15
+ function sleep(ms) {
16
+ return new Promise((r) => setTimeout(r, ms));
17
+ }
18
+ /**
19
+ * 异步注册表锁(防止多进程并发读写导致 registry.json 损坏)。
20
+ *
21
+ * 获取方式:fs/promises mkdir 的原子性保证同一时刻仅一个进程能创建锁目录。
22
+ * 冲突处理:EEXIST 时 stat 检查锁目录年龄,超过 stale 阈值则先原子 rename 到
23
+ * 隔离名再删除——rename 仅有一个进程能成功,失败方回到等待重试,消除
24
+ * 「stat 检查后删除再重建」的竞态窗口;未过期的锁通过 sleep 让出事件循环后重试。
25
+ *
26
+ * @param filePath 注册表文件路径,锁目录为其同级 `${filePath}.lock`
27
+ * @param fn 持锁期间执行的操作(同步或异步)
28
+ * @returns fn 的返回值
29
+ * @throws 超过 REGISTRY_LOCK_ACQUIRE_TIMEOUT_MS 仍未获取时抛出描述性错误
30
+ */
31
+ export async function withRegistryLock(filePath, fn) {
32
+ const lockDir = `${filePath}.lock`;
33
+ const deadline = Date.now() + REGISTRY_LOCK_ACQUIRE_TIMEOUT_MS;
34
+ while (true) {
35
+ try {
36
+ // 原子获取:目录创建成功即持有锁,无需二次确认
37
+ await mkdir(lockDir);
38
+ break;
39
+ }
40
+ catch (err) {
41
+ if (err.code !== "EEXIST") {
42
+ throw err;
43
+ }
44
+ }
45
+ // 锁被占用,检查是否为残留的过期锁
46
+ let lockAgeMs;
47
+ try {
48
+ const info = await stat(lockDir);
49
+ lockAgeMs = Date.now() - info.mtimeMs;
50
+ }
51
+ catch (err) {
52
+ if (err.code === "ENOENT") {
53
+ // 持有者恰好在两次探测之间释放,立即重试获取
54
+ continue;
55
+ }
56
+ throw err;
57
+ }
58
+ if (lockAgeMs > REGISTRY_LOCK_STALE_MS) {
59
+ // 原子抢占:rename 到含进程号与时间戳的隔离名,仅一个进程能成功;
60
+ // 失败方说明已被其他等待者抢先处理,回到等待重试即可
61
+ const quarantine = `${lockDir}.stale.${process.pid}.${Date.now()}`;
62
+ try {
63
+ await rename(lockDir, quarantine);
64
+ await rm(quarantine, { recursive: true, force: true });
65
+ continue;
66
+ }
67
+ catch (err) {
68
+ if (err.code === "ENOENT") {
69
+ // 其他等待者已抢先回收该残留锁
70
+ continue;
71
+ }
72
+ // 其他原因的 rename 失败(如瞬时权限问题)不视为致命,退回等待重试
73
+ }
74
+ }
75
+ if (Date.now() > deadline) {
76
+ throw new Error(`Failed to acquire registry lock '${lockDir}' within ${REGISTRY_LOCK_ACQUIRE_TIMEOUT_MS}ms. ` +
77
+ `The lock may be held by another ActionDock process; retry after it exits or remove the stale lock directory manually.`);
78
+ }
79
+ await sleep(REGISTRY_LOCK_RETRY_DELAY_MS);
80
+ }
81
+ try {
82
+ return await fn();
83
+ }
84
+ finally {
85
+ try {
86
+ await rm(lockDir, { recursive: true, force: true });
87
+ }
88
+ catch {
89
+ // 释放失败可忽略:进程未清理的锁目录会随时间超过 stale 阈值,
90
+ // 被后续获取方的原子 rename 抢占回收,不会造成永久死锁
91
+ }
92
+ }
93
+ }
@@ -1,24 +1,45 @@
1
- import type { GlobalRegistryData, LinkedPackageEntry, LinkedWorkspaceEntry, LinkResult, PruneResult, RegistryStatusReport, ResolvedActionProject, ResolvedPlaybookProject, UnlinkResult } from "./types";
1
+ import type { GlobalRegistryData, LinkedPackageEntry, LinkedWorkspaceEntry, LinkResult, PruneResult, RegistryStatusReport, ResolvedActionProject, ResolvedPlaybookProject, UnlinkResult } from "./types.js";
2
2
  /**
3
- * 跨平台注册表写锁控制函数(防止多进程并发读写导致 registry.json 损坏)。
3
+ * 注册表内容解析与 links 迁移(纯函数,同步与异步加载共享的唯一事实源)。
4
+ *
5
+ * @param raw 文件原始文本
6
+ * @param filePath 注册表文件路径(用于错误信息定位)
7
+ * @returns 解析后的注册表数据
8
+ * @throws JSON 非法或顶层非对象时抛出带恢复指引的错误(调用方负责留档原文件)
4
9
  */
5
- export declare function withRegistryLock<T>(filePath: string, fn: () => T): T;
10
+ export declare function parseRegistryContent(raw: string, filePath: string): GlobalRegistryData;
11
+ export declare function getRegistryFilePath(customHome?: string): string;
6
12
  /**
7
- * 递归扫描包含 actiondock.json 的子项目根目录
13
+ * 同步加载注册表(保留同步签名供 CLI 与诊断路径使用)。
14
+ *
15
+ * @throws 注册表文件损坏时抛出带恢复指引的错误,绝不静默返回空表,
16
+ * 否则下次 save 会用空数据覆盖原文件导致全部链接记录丢失
8
17
  */
9
- export declare function discoverProjects(dir: string, maxDepth?: number): string[];
10
- export declare function getRegistryFilePath(customHome?: string): string;
11
18
  export declare function loadRegistry(customHome?: string): GlobalRegistryData;
12
- export declare function saveRegistry(data: GlobalRegistryData, customHome?: string): void;
19
+ /**
20
+ * 异步加载注册表(损坏语义与同步版一致:留档后抛错,不静默清空)。
21
+ */
22
+ export declare function loadRegistryAsync(customHome?: string): Promise<GlobalRegistryData>;
23
+ /**
24
+ * 原子写入注册表:临时文件写盘后 rename,持锁执行,避免并发撕裂。
25
+ */
26
+ export declare function saveRegistry(data: GlobalRegistryData, customHome?: string): Promise<void>;
27
+ /**
28
+ * 在已持有注册表锁的上下文中直接原子落盘(供复合读改写流程使用,
29
+ * 避免与 saveRegistry 的锁重入死等);调用方必须已持有对应锁。
30
+ */
31
+ export declare function writeRegistryLocked(data: GlobalRegistryData, customHome?: string): Promise<void>;
13
32
  export declare function linkPackage(targetPath?: string, customHome?: string, options?: {
14
33
  recursive?: boolean;
15
- }): LinkResult;
16
- export declare function unlinkPackage(identifier?: string, customHome?: string): UnlinkResult | null;
34
+ }): Promise<LinkResult>;
35
+ export declare function unlinkPackage(identifier?: string, customHome?: string): Promise<UnlinkResult | null>;
17
36
  export declare function listLinkedPackages(customHome?: string): LinkedPackageEntry[];
37
+ export declare function listLinkedPackagesAsync(customHome?: string): Promise<LinkedPackageEntry[]>;
18
38
  export declare function listLinkedWorkspaces(customHome?: string): LinkedWorkspaceEntry[];
19
39
  export declare function resolveActionProjectSync(actionIdentifier: string, cwd?: string, customHome?: string): ResolvedActionProject;
20
40
  export declare function resolveActionProject(actionIdentifier: string, cwd?: string, customHome?: string): Promise<ResolvedActionProject>;
21
- export declare function resolvePackageRoot(packageIdOrPath?: string, cwd?: string, customHome?: string): string | null;
22
41
  export declare function resolvePlaybookProject(playbookIdentifier: string, cwd?: string, customHome?: string): ResolvedPlaybookProject;
42
+ export declare function resolvePackageRoot(packageIdOrPath?: string, cwd?: string, customHome?: string): string | null;
23
43
  export declare function getRegistryStatus(customHome?: string): RegistryStatusReport;
24
- export declare function pruneRegistry(customHome?: string): PruneResult;
44
+ export declare function pruneRegistry(customHome?: string): Promise<PruneResult>;
45
+ export { discoverProjects, IGNORED_SCAN_DIRS } from "./scan.js";