@actiondock/core 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 (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 +631 -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 +122 -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,613 @@
1
+ import { existsSync, readFileSync, statSync } from "node:fs";
2
+ import { dirname, join, relative } from "node:path";
3
+ import { ActionResolver } from "../catalog/action-resolver.js";
4
+ import { discoverActionFiles, findProjectRoot, loadActions, loadPlaybooks, loadProjectConfig } from "../project/loader.js";
5
+ import { loadManifest, MANIFEST_FILE_NAME } from "../project/manifest.js";
6
+ import { getRegistryStatus, listLinkedPackages, resolvePackageRoot } from "../registry/registry.js";
7
+ import { createGlobalStorage, createStorage, resolveDatabasePath } from "../storage/index.js";
8
+ import { findExecutable, getActionDockHome } from "../utils/index.js";
9
+ function compareSemver(v1, v2) {
10
+ const p1 = v1.replace(/^v/, "").split(".").map(Number);
11
+ const p2 = v2.replace(/^v/, "").split(".").map(Number);
12
+ for (let i = 0; i < Math.max(p1.length, p2.length); i++) {
13
+ const num1 = p1[i] || 0;
14
+ const num2 = p2[i] || 0;
15
+ if (num1 > num2)
16
+ return 1;
17
+ if (num1 < num2)
18
+ return -1;
19
+ }
20
+ return 0;
21
+ }
22
+ export async function runDoctorChecks(options) {
23
+ const cwd = options?.cwd || process.cwd();
24
+ const checks = [];
25
+ // 1. Check Node.js Runtime
26
+ const nodeVersion = process.versions.node;
27
+ if (nodeVersion) {
28
+ const isGte24 = compareSemver(nodeVersion, "24.12.0") >= 0;
29
+ checks.push({
30
+ id: "runtime.node",
31
+ category: "runtime",
32
+ name: "Node.js Runtime",
33
+ status: isGte24 ? "ok" : "warn",
34
+ message: `v${nodeVersion} (${isGte24 ? ">= 24.12.0 supported" : ">= 24.12.0 recommended"})`,
35
+ fix: isGte24 ? undefined : "Upgrade Node.js to v24.12.0 or higher",
36
+ });
37
+ }
38
+ // 2. Check Bun Runtime (Optional environment for cross-runtime compatibility testing)
39
+ const bunVersion = (typeof globalThis.Bun !== "undefined" && globalThis.Bun.version) || process.versions.bun;
40
+ if (bunVersion) {
41
+ checks.push({
42
+ id: "runtime.bun",
43
+ category: "runtime",
44
+ name: "Bun Runtime",
45
+ status: "ok",
46
+ message: `v${bunVersion} (available for cross-environment testing)`,
47
+ });
48
+ }
49
+ else {
50
+ checks.push({
51
+ id: "runtime.bun",
52
+ category: "runtime",
53
+ name: "Bun Runtime",
54
+ status: "ok",
55
+ message: "Bun runtime not detected (optional, used for cross-environment compatibility testing)",
56
+ });
57
+ }
58
+ // 3. Check CLI in PATH
59
+ const adPath = findExecutable("ad");
60
+ if (adPath) {
61
+ checks.push({
62
+ id: "runtime.cli",
63
+ category: "runtime",
64
+ name: "CLI Executable",
65
+ status: "ok",
66
+ message: `Found 'ad' in PATH at ${adPath}`,
67
+ });
68
+ }
69
+ else {
70
+ checks.push({
71
+ id: "runtime.cli",
72
+ category: "runtime",
73
+ name: "CLI Executable",
74
+ status: "warn",
75
+ message: "'ad' command not found in PATH",
76
+ fix: "Run 'npm install -g @actiondock/cli' or in SDK workspace run 'cd packages/cli && npm link'",
77
+ });
78
+ }
79
+ // 3. Check Global Storage
80
+ const globalHome = getActionDockHome(options?.customHome);
81
+ try {
82
+ const globalStorage = createGlobalStorage(options?.customHome);
83
+ await globalStorage.setConfig("_doctor_probe_", "ok");
84
+ await globalStorage.deleteConfig("_doctor_probe_");
85
+ globalStorage.close();
86
+ checks.push({
87
+ id: "storage.global",
88
+ category: "storage",
89
+ name: "Global Storage",
90
+ status: "ok",
91
+ message: `Global SQLite database verified at ${globalHome}`,
92
+ });
93
+ }
94
+ catch (err) {
95
+ checks.push({
96
+ id: "storage.global",
97
+ category: "storage",
98
+ name: "Global Storage",
99
+ status: "error",
100
+ message: `Failed to access global storage: ${err.message}`,
101
+ fix: `Ensure directory '${globalHome}' is writable`,
102
+ });
103
+ }
104
+ // 4. Check Global Registry Health
105
+ try {
106
+ const regStatus = getRegistryStatus(options?.customHome);
107
+ if (regStatus.staleCount > 0) {
108
+ checks.push({
109
+ id: "registry.global",
110
+ category: "registry",
111
+ name: "Global Registry",
112
+ status: "warn",
113
+ message: `${regStatus.totalPackagesCount} package(s), ${regStatus.workspaces.length} workspace(s), but ${regStatus.staleCount} stale path(s) detected`,
114
+ fix: "Run 'ad unlink --prune' to clean up stale entries from registry",
115
+ });
116
+ }
117
+ else {
118
+ checks.push({
119
+ id: "registry.global",
120
+ category: "registry",
121
+ name: "Global Registry",
122
+ status: "ok",
123
+ message: `${regStatus.totalPackagesCount} linked package(s), ${regStatus.workspaces.length} workspace(s) (0 stale)`,
124
+ });
125
+ }
126
+ }
127
+ catch (err) {
128
+ checks.push({
129
+ id: "registry.global",
130
+ category: "registry",
131
+ name: "Global Registry",
132
+ status: "error",
133
+ message: `Failed to read registry: ${err.message}`,
134
+ });
135
+ }
136
+ // 5. Check Linked Package Dependencies (node_modules completeness)
137
+ try {
138
+ let linkedList = listLinkedPackages(options?.customHome);
139
+ if (options?.packageAllowlist && options.packageAllowlist.length > 0) {
140
+ linkedList = linkedList.filter((pkg) => options.packageAllowlist.includes(pkg.id));
141
+ }
142
+ const missingNodeModules = [];
143
+ for (const pkg of linkedList) {
144
+ if (!existsSync(pkg.path))
145
+ continue;
146
+ const pkgJsonPath = join(pkg.path, "package.json");
147
+ if (!existsSync(pkgJsonPath))
148
+ continue;
149
+ try {
150
+ const pkgJson = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
151
+ const hasDeps = (pkgJson.dependencies && Object.keys(pkgJson.dependencies).length > 0) ||
152
+ (pkgJson.devDependencies && Object.keys(pkgJson.devDependencies).length > 0);
153
+ if (hasDeps && !existsSync(join(pkg.path, "node_modules"))) {
154
+ missingNodeModules.push(pkg.id);
155
+ }
156
+ }
157
+ catch {
158
+ // 忽略异常 JSON
159
+ }
160
+ }
161
+ if (missingNodeModules.length > 0) {
162
+ checks.push({
163
+ id: "registry.dependencies",
164
+ category: "registry",
165
+ name: "Linked Package Dependencies",
166
+ status: "warn",
167
+ message: `${missingNodeModules.length} linked package(s) declare dependencies but miss node_modules: ${missingNodeModules.join(", ")}`,
168
+ fix: "Run 'npm install' in the affected package directories, or execute 'ad run' to auto-install",
169
+ });
170
+ }
171
+ else {
172
+ checks.push({
173
+ id: "registry.dependencies",
174
+ category: "registry",
175
+ name: "Linked Package Dependencies",
176
+ status: "ok",
177
+ message: linkedList.length > 0
178
+ ? "All linked packages have dependencies installed"
179
+ : "No linked packages requiring dependency verification",
180
+ });
181
+ }
182
+ }
183
+ catch {
184
+ // 忽略依赖检查异常
185
+ }
186
+ // 6. Check Cross-Package Uses Dependency Closure
187
+ try {
188
+ let linkedList = listLinkedPackages(options?.customHome);
189
+ if (options?.packageAllowlist && options.packageAllowlist.length > 0) {
190
+ linkedList = linkedList.filter((pkg) => options.packageAllowlist.includes(pkg.id));
191
+ }
192
+ let packagesToCheck = linkedList.map((p) => ({
193
+ id: p.id,
194
+ root: p.path,
195
+ }));
196
+ const curProjectRoot = options?.packageIdOrPath
197
+ ? resolvePackageRoot(options.packageIdOrPath, cwd, options?.customHome) || findProjectRoot(options.packageIdOrPath)
198
+ : findProjectRoot(cwd);
199
+ if (curProjectRoot && !packagesToCheck.some((p) => p.root === curProjectRoot)) {
200
+ try {
201
+ const cfg = loadProjectConfig(curProjectRoot);
202
+ packagesToCheck.push({ id: cfg.id, root: curProjectRoot });
203
+ }
204
+ catch {
205
+ // 忽略配置异常
206
+ }
207
+ }
208
+ if (options?.packageAllowlist && options.packageAllowlist.length > 0) {
209
+ packagesToCheck = packagesToCheck.filter((p) => options.packageAllowlist.includes(p.id));
210
+ }
211
+ const unresolvableUses = [];
212
+ for (const pkg of packagesToCheck) {
213
+ if (!existsSync(pkg.root))
214
+ continue;
215
+ const manifest = loadManifest(pkg.root);
216
+ if (!manifest || !manifest.actions)
217
+ continue;
218
+ for (const [actionId, actionEntry] of Object.entries(manifest.actions)) {
219
+ if (!Array.isArray(actionEntry.uses))
220
+ continue;
221
+ for (const rawRef of actionEntry.uses) {
222
+ if (typeof rawRef !== "string" || !rawRef.trim())
223
+ continue;
224
+ let parsed;
225
+ try {
226
+ parsed = ActionResolver.parseRef(rawRef);
227
+ }
228
+ catch {
229
+ unresolvableUses.push(`${pkg.id}/${actionId} -> '${rawRef}'`);
230
+ continue;
231
+ }
232
+ if (parsed.packageId) {
233
+ const depRoot = resolvePackageRoot(parsed.packageId, pkg.root, options?.customHome);
234
+ if (!depRoot || !existsSync(depRoot)) {
235
+ unresolvableUses.push(`${pkg.id}/${actionId} -> '${rawRef}'`);
236
+ }
237
+ }
238
+ }
239
+ }
240
+ }
241
+ if (unresolvableUses.length > 0) {
242
+ checks.push({
243
+ id: "registry.uses_closure",
244
+ category: "registry",
245
+ name: "Cross-Package Uses Dependencies",
246
+ status: "warn",
247
+ message: `${unresolvableUses.length} unresolvable cross-package dependency reference(s) found in manifest uses: ${unresolvableUses.join(", ")}`,
248
+ fix: "Link missing packages with 'ad link', or correct unresolvable uses declarations",
249
+ });
250
+ }
251
+ else {
252
+ checks.push({
253
+ id: "registry.uses_closure",
254
+ category: "registry",
255
+ name: "Cross-Package Uses Dependencies",
256
+ status: "ok",
257
+ message: "All cross-package uses declarations in manifests resolved successfully",
258
+ });
259
+ }
260
+ }
261
+ catch {
262
+ // 忽略闭包检查异常
263
+ }
264
+ // 7. Check Project Context
265
+ let projectRoot = null;
266
+ if (options?.packageIdOrPath) {
267
+ projectRoot =
268
+ resolvePackageRoot(options.packageIdOrPath, cwd, options?.customHome) ||
269
+ findProjectRoot(options.packageIdOrPath);
270
+ }
271
+ else {
272
+ projectRoot = findProjectRoot(cwd);
273
+ }
274
+ let packageId;
275
+ if (projectRoot) {
276
+ try {
277
+ const config = loadProjectConfig(projectRoot);
278
+ if (options?.packageAllowlist &&
279
+ Array.isArray(options.packageAllowlist) &&
280
+ options.packageAllowlist.length > 0 &&
281
+ !options.packageAllowlist.includes(config.id)) {
282
+ projectRoot = null;
283
+ }
284
+ }
285
+ catch {
286
+ // 忽略预校验异常,交由后续详细诊断处理
287
+ }
288
+ }
289
+ if (projectRoot) {
290
+ try {
291
+ const config = loadProjectConfig(projectRoot);
292
+ packageId = config.id;
293
+ // Project Config Check
294
+ checks.push({
295
+ id: "project.config",
296
+ category: "project",
297
+ name: "Project Configuration",
298
+ status: "ok",
299
+ message: `Valid (${config.id} v${config.version})`,
300
+ });
301
+ // SDK Resolution Check
302
+ const hasSdkInNodeModules = existsSync(join(projectRoot, "node_modules", "@actiondock", "sdk")) ||
303
+ existsSync(join(projectRoot, "node_modules", "@actiondock", "sdk", "package.json"));
304
+ if (hasSdkInNodeModules) {
305
+ checks.push({
306
+ id: "project.sdk",
307
+ category: "project",
308
+ name: "SDK Dependency",
309
+ status: "ok",
310
+ message: "Resolved @actiondock/sdk in node_modules",
311
+ });
312
+ }
313
+ else {
314
+ checks.push({
315
+ id: "project.sdk",
316
+ category: "project",
317
+ name: "SDK Dependency",
318
+ status: "warn",
319
+ message: "@actiondock/sdk not found in project node_modules",
320
+ fix: "Run 'npm link @actiondock/sdk' or 'npm install' in project directory",
321
+ });
322
+ }
323
+ // Project Runtime Database Check
324
+ const dbPath = resolveDatabasePath(config.id, { customHome: options?.customHome });
325
+ try {
326
+ const projectStorage = createStorage(config.id, { customHome: options?.customHome });
327
+ await projectStorage.setConfig("_doctor_probe_", "ok");
328
+ await projectStorage.deleteConfig("_doctor_probe_");
329
+ projectStorage.close();
330
+ checks.push({
331
+ id: "project.storage",
332
+ category: "project",
333
+ name: "Project Database",
334
+ status: "ok",
335
+ message: `Database writable at ${dbPath}`,
336
+ });
337
+ }
338
+ catch (err) {
339
+ checks.push({
340
+ id: "project.storage",
341
+ category: "project",
342
+ name: "Project Database",
343
+ status: "error",
344
+ message: `Failed to write project runtime database: ${err.message}`,
345
+ fix: `Check write permissions for '${dirname(dbPath)}'`,
346
+ });
347
+ }
348
+ // Actions Check
349
+ try {
350
+ const manifest = loadManifest(projectRoot);
351
+ let actionsCount = 0;
352
+ if (manifest?.actions) {
353
+ actionsCount = Object.keys(manifest.actions).length;
354
+ }
355
+ else {
356
+ const actions = await loadActions(projectRoot, config.actionsDir, { autoInstall: false });
357
+ actionsCount = actions.size;
358
+ }
359
+ if (actionsCount === 0) {
360
+ checks.push({
361
+ id: "project.actions",
362
+ category: "project",
363
+ name: "Actions",
364
+ status: "warn",
365
+ message: `No actions found in '${config.actionsDir || "actions"}'`,
366
+ fix: "Run 'ad action new <id>' to create your first action",
367
+ });
368
+ }
369
+ else {
370
+ checks.push({
371
+ id: "project.actions",
372
+ category: "project",
373
+ name: "Actions",
374
+ status: "ok",
375
+ message: `${actionsCount} action(s) valid and loaded`,
376
+ });
377
+ }
378
+ }
379
+ catch (err) {
380
+ checks.push({
381
+ id: "project.actions",
382
+ category: "project",
383
+ name: "Actions",
384
+ status: "error",
385
+ message: `Failed to load actions: ${err.message}`,
386
+ });
387
+ }
388
+ // Project Manifest Check (Lightweight Static Detection)
389
+ try {
390
+ const manifestPath = join(projectRoot, MANIFEST_FILE_NAME);
391
+ const manifest = loadManifest(projectRoot);
392
+ const actionFiles = discoverActionFiles(projectRoot, config.actionsDir || "actions");
393
+ if (!existsSync(manifestPath)) {
394
+ if (actionFiles.length > 0) {
395
+ checks.push({
396
+ id: "project.manifest",
397
+ category: "project",
398
+ name: "Action Manifest",
399
+ status: "warn",
400
+ message: `${MANIFEST_FILE_NAME} not found (${actionFiles.length} action source file(s) exist)`,
401
+ fix: "Run 'ad validate' to generate or check manifest",
402
+ });
403
+ }
404
+ }
405
+ else if (manifest && manifest.actions) {
406
+ const missingFiles = Object.entries(manifest.actions)
407
+ .filter(([_, item]) => !existsSync(join(projectRoot, item.entry)))
408
+ .map(([id, item]) => `${id} (${item.entry})`);
409
+ const manifestEntries = new Set(Object.values(manifest.actions).map((a) => a.entry.replace(/\\/g, "/")));
410
+ const untracked = actionFiles
411
+ .map((f) => relative(projectRoot, f).replace(/\\/g, "/"))
412
+ .filter((rel) => !manifestEntries.has(rel));
413
+ if (missingFiles.length > 0) {
414
+ checks.push({
415
+ id: "project.manifest",
416
+ category: "project",
417
+ name: "Action Manifest",
418
+ status: "warn",
419
+ message: `${missingFiles.length} action(s) in manifest point to missing files: ${missingFiles.join(", ")}`,
420
+ fix: "Run 'ad validate' to check or update actiondock.json",
421
+ });
422
+ }
423
+ else if (untracked.length > 0) {
424
+ checks.push({
425
+ id: "project.manifest",
426
+ category: "project",
427
+ name: "Action Manifest",
428
+ status: "warn",
429
+ message: `${untracked.length} action file(s) not declared in manifest: ${untracked.join(", ")}`,
430
+ fix: "Run 'ad validate' to check or update actiondock.json",
431
+ });
432
+ }
433
+ else {
434
+ const manifestStat = statSync(manifestPath);
435
+ const newerFiles = actionFiles.filter((f) => statSync(f).mtimeMs > manifestStat.mtimeMs + 2000);
436
+ if (newerFiles.length > 0) {
437
+ checks.push({
438
+ id: "project.manifest",
439
+ category: "project",
440
+ name: "Action Manifest",
441
+ status: "ok",
442
+ message: `Manifest valid (Note: ${newerFiles.length} action file(s) modified after manifest; run 'ad validate' if definitions changed)`,
443
+ });
444
+ }
445
+ else {
446
+ checks.push({
447
+ id: "project.manifest",
448
+ category: "project",
449
+ name: "Action Manifest",
450
+ status: "ok",
451
+ message: "Manifest synchronized with action files",
452
+ });
453
+ }
454
+ }
455
+ }
456
+ }
457
+ catch (err) {
458
+ checks.push({
459
+ id: "project.manifest",
460
+ category: "project",
461
+ name: "Action Manifest",
462
+ status: "error",
463
+ message: `Failed to inspect manifest: ${err.message}`,
464
+ });
465
+ }
466
+ // Project Declared Files & Source Boundaries Check
467
+ try {
468
+ const declaredFiles = config.files || [];
469
+ const hasSrc = existsSync(join(projectRoot, "src"));
470
+ const hasLib = existsSync(join(projectRoot, "lib"));
471
+ if (declaredFiles.length > 0) {
472
+ const missingDeclared = declaredFiles.filter((f) => !existsSync(join(projectRoot, f)));
473
+ if (missingDeclared.length > 0) {
474
+ checks.push({
475
+ id: "project.files",
476
+ category: "project",
477
+ name: "Declared Files",
478
+ status: "error",
479
+ message: `${missingDeclared.length} path(s) declared in 'files' do not exist: ${missingDeclared.join(", ")}`,
480
+ fix: `Verify and update 'files' in ${MANIFEST_FILE_NAME}`,
481
+ });
482
+ }
483
+ else {
484
+ checks.push({
485
+ id: "project.files",
486
+ category: "project",
487
+ name: "Declared Files",
488
+ status: "ok",
489
+ message: `All ${declaredFiles.length} declared file/directory boundaries verified`,
490
+ });
491
+ }
492
+ }
493
+ else if (hasSrc || hasLib) {
494
+ const actionFiles = discoverActionFiles(projectRoot, config.actionsDir || "actions");
495
+ let hasReferenceToSrcOrLib = false;
496
+ for (const actFile of actionFiles) {
497
+ try {
498
+ const src = readFileSync(actFile, "utf-8");
499
+ if (/['"](?:\.\.\/(?:src|lib)|\.\/(?:src|lib))[^'"]*['"]/.test(src)) {
500
+ hasReferenceToSrcOrLib = true;
501
+ break;
502
+ }
503
+ }
504
+ catch {
505
+ // 忽略单个文件读取异常
506
+ }
507
+ }
508
+ if (hasReferenceToSrcOrLib) {
509
+ checks.push({
510
+ id: "project.files",
511
+ category: "project",
512
+ name: "Declared Files",
513
+ status: "error",
514
+ message: `Actions import modules from ${hasSrc ? "'src/'" : ""}${hasSrc && hasLib ? " and " : ""}${hasLib ? "'lib/'" : ""}, but 'files' is not declared in ${MANIFEST_FILE_NAME}`,
515
+ fix: `Add "files": [${hasSrc ? '"src"' : ""}${hasSrc && hasLib ? ', "lib"' : hasLib && !hasSrc ? '"lib"' : ""}] to ${MANIFEST_FILE_NAME}`,
516
+ });
517
+ }
518
+ else {
519
+ checks.push({
520
+ id: "project.files",
521
+ category: "project",
522
+ name: "Declared Files",
523
+ status: "ok",
524
+ message: `Project source directories clean (no undeclared references to ${hasSrc ? "src/" : "lib/"})`,
525
+ });
526
+ }
527
+ }
528
+ }
529
+ catch {
530
+ // 忽略文件边界检测异常
531
+ }
532
+ // Playbooks Check
533
+ try {
534
+ const playbooks = loadPlaybooks(projectRoot, config.playbooksDir);
535
+ checks.push({
536
+ id: "project.playbooks",
537
+ category: "project",
538
+ name: "Playbooks",
539
+ status: "ok",
540
+ message: `${playbooks.size} playbook(s) valid`,
541
+ });
542
+ }
543
+ catch (err) {
544
+ checks.push({
545
+ id: "project.playbooks",
546
+ category: "project",
547
+ name: "Playbooks",
548
+ status: "warn",
549
+ message: `Playbooks issue: ${err.message}`,
550
+ });
551
+ }
552
+ // Config readiness check
553
+ if (config.config && Object.keys(config.config).length > 0) {
554
+ const missingKeys = [];
555
+ const projectStorage = createStorage(config.id, { customHome: options?.customHome });
556
+ for (const [key, def] of Object.entries(config.config)) {
557
+ const inStorage = await projectStorage.getConfig(key);
558
+ const envNames = Array.isArray(def.env) ? def.env : def.env ? [def.env] : [key];
559
+ const inEnv = envNames.some((e) => process.env[e] !== undefined);
560
+ const isRequired = def.required || (def.default === undefined && def.secret);
561
+ if ((isRequired || def.default === undefined) && inStorage === undefined && !inEnv) {
562
+ missingKeys.push(key);
563
+ }
564
+ }
565
+ projectStorage.close();
566
+ if (missingKeys.length > 0) {
567
+ checks.push({
568
+ id: "project.config_readiness",
569
+ category: "project",
570
+ name: "Config Readiness",
571
+ status: "warn",
572
+ message: `Required config item(s) missing: ${missingKeys.join(", ")}`,
573
+ fix: `Run 'ad config set <KEY> <VALUE>' to configure missing keys`,
574
+ });
575
+ }
576
+ else {
577
+ checks.push({
578
+ id: "project.config_readiness",
579
+ category: "project",
580
+ name: "Config Readiness",
581
+ status: "ok",
582
+ message: "All declared configuration dependencies satisfied",
583
+ });
584
+ }
585
+ }
586
+ }
587
+ catch (err) {
588
+ checks.push({
589
+ id: "project.config",
590
+ category: "project",
591
+ name: "Project Configuration",
592
+ status: "error",
593
+ message: `Invalid actiondock.json: ${err.message}`,
594
+ });
595
+ }
596
+ }
597
+ const okCount = checks.filter((c) => c.status === "ok").length;
598
+ const warnCount = checks.filter((c) => c.status === "warn").length;
599
+ const errorCount = checks.filter((c) => c.status === "error").length;
600
+ return {
601
+ ok: errorCount === 0,
602
+ hasProject: !!projectRoot,
603
+ projectRoot: projectRoot || undefined,
604
+ packageId,
605
+ summary: {
606
+ total: checks.length,
607
+ ok: okCount,
608
+ warn: warnCount,
609
+ error: errorCount,
610
+ },
611
+ checks,
612
+ };
613
+ }
@@ -1,2 +1,2 @@
1
- export * from "./doctor";
2
- export * from "./types";
1
+ export * from "./doctor.js";
2
+ export * from "./types.js";
@@ -0,0 +1,2 @@
1
+ export * from "./doctor.js";
2
+ export * from "./types.js";
@@ -0,0 +1 @@
1
+ export {};