@actiondock/core 2.0.11 → 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 (258) 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 +9 -0
  9. package/dist/catalog/action-index.js +93 -0
  10. package/dist/catalog/action-resolver.d.ts +20 -0
  11. package/dist/catalog/action-resolver.js +95 -0
  12. package/dist/catalog/index.d.ts +5 -0
  13. package/dist/catalog/index.js +5 -0
  14. package/dist/catalog/location-registry.d.ts +15 -0
  15. package/dist/catalog/location-registry.js +138 -0
  16. package/dist/catalog/package-catalog.d.ts +7 -0
  17. package/dist/catalog/package-catalog.js +76 -0
  18. package/dist/catalog/types.d.ts +68 -0
  19. package/dist/catalog/types.js +1 -0
  20. package/dist/doctor/doctor.d.ts +7 -0
  21. package/dist/doctor/doctor.js +613 -0
  22. package/dist/doctor/index.d.ts +2 -0
  23. package/dist/doctor/index.js +2 -0
  24. package/dist/doctor/types.d.ts +23 -0
  25. package/dist/doctor/types.js +1 -0
  26. package/dist/errors.d.ts +122 -0
  27. package/dist/errors.js +123 -0
  28. package/dist/execution/index.d.ts +2 -0
  29. package/dist/execution/index.js +2 -0
  30. package/dist/execution/service.d.ts +79 -0
  31. package/dist/execution/service.js +633 -0
  32. package/dist/execution/types.d.ts +131 -0
  33. package/dist/execution/types.js +1 -0
  34. package/dist/export/index.d.ts +1 -0
  35. package/dist/export/index.js +1 -0
  36. package/dist/export/templates.d.ts +71 -0
  37. package/dist/export/templates.js +621 -0
  38. package/dist/filter/index.d.ts +1 -0
  39. package/dist/filter/index.js +1 -0
  40. package/dist/filter/intent.d.ts +33 -0
  41. package/dist/filter/intent.js +123 -0
  42. package/dist/host/host.d.ts +55 -0
  43. package/dist/host/host.js +740 -0
  44. package/dist/host/index.d.ts +2 -0
  45. package/dist/host/index.js +2 -0
  46. package/dist/host/types.d.ts +81 -0
  47. package/dist/host/types.js +1 -0
  48. package/dist/index.d.ts +21 -0
  49. package/dist/index.js +20 -0
  50. package/dist/ipc/diagnostic.d.ts +65 -0
  51. package/dist/ipc/diagnostic.js +129 -0
  52. package/dist/ipc/host.d.ts +7 -0
  53. package/dist/ipc/host.js +93 -0
  54. package/dist/ipc/index.d.ts +4 -0
  55. package/dist/ipc/index.js +4 -0
  56. package/dist/ipc/target.d.ts +56 -0
  57. package/dist/ipc/target.js +297 -0
  58. package/dist/ipc/types.d.ts +42 -0
  59. package/dist/ipc/types.js +1 -0
  60. package/dist/platform/default.d.ts +32 -0
  61. package/dist/platform/default.js +40 -0
  62. package/dist/platform/index.d.ts +3 -0
  63. package/dist/platform/index.js +3 -0
  64. package/dist/platform/node-fs.d.ts +37 -0
  65. package/dist/platform/node-fs.js +81 -0
  66. package/dist/platform/types.d.ts +104 -0
  67. package/dist/platform/types.js +1 -0
  68. package/dist/profile/client.d.ts +162 -0
  69. package/dist/profile/client.js +467 -0
  70. package/dist/profile/index.d.ts +3 -0
  71. package/dist/profile/index.js +3 -0
  72. package/dist/profile/manager.d.ts +47 -0
  73. package/dist/profile/manager.js +294 -0
  74. package/dist/profile/types.d.ts +61 -0
  75. package/dist/profile/types.js +1 -0
  76. package/dist/project/closure.d.ts +25 -0
  77. package/dist/project/closure.js +68 -0
  78. package/dist/project/digest.d.ts +29 -0
  79. package/dist/project/digest.js +324 -0
  80. package/dist/project/index.d.ts +10 -0
  81. package/dist/project/index.js +10 -0
  82. package/dist/project/init.d.ts +26 -0
  83. package/dist/project/init.js +175 -0
  84. package/dist/project/loader.d.ts +99 -0
  85. package/dist/project/loader.js +484 -0
  86. package/dist/project/lockfile.d.ts +55 -0
  87. package/dist/project/lockfile.js +175 -0
  88. package/dist/project/manifest.d.ts +22 -0
  89. package/dist/project/manifest.js +220 -0
  90. package/dist/project/resolver.d.ts +116 -0
  91. package/dist/project/resolver.js +376 -0
  92. package/dist/project/transactions.d.ts +103 -0
  93. package/dist/project/transactions.js +1203 -0
  94. package/dist/project/types-generator.d.ts +33 -0
  95. package/dist/project/types-generator.js +168 -0
  96. package/dist/project/types.d.ts +107 -0
  97. package/dist/project/types.js +1 -0
  98. package/dist/registry/index.d.ts +5 -0
  99. package/dist/registry/index.js +5 -0
  100. package/dist/registry/lock.d.ts +27 -0
  101. package/dist/registry/lock.js +93 -0
  102. package/dist/registry/registry.d.ts +45 -0
  103. package/dist/registry/registry.js +611 -0
  104. package/dist/registry/resolve.d.ts +62 -0
  105. package/dist/registry/resolve.js +195 -0
  106. package/dist/registry/scan.d.ts +38 -0
  107. package/dist/registry/scan.js +135 -0
  108. package/dist/registry/types.d.ts +117 -0
  109. package/dist/registry/types.js +1 -0
  110. package/dist/runtime/action-collection.d.ts +27 -0
  111. package/dist/runtime/action-collection.js +57 -0
  112. package/dist/runtime/clock.d.ts +19 -0
  113. package/dist/runtime/clock.js +17 -0
  114. package/dist/runtime/context.d.ts +86 -0
  115. package/dist/runtime/context.js +242 -0
  116. package/dist/runtime/env.d.ts +56 -0
  117. package/dist/runtime/env.js +159 -0
  118. package/dist/runtime/events.d.ts +93 -0
  119. package/dist/runtime/events.js +604 -0
  120. package/dist/runtime/execution-manager.d.ts +48 -0
  121. package/dist/runtime/execution-manager.js +66 -0
  122. package/dist/runtime/index.d.ts +10 -0
  123. package/dist/runtime/index.js +10 -0
  124. package/dist/runtime/module-loader.d.ts +34 -0
  125. package/dist/runtime/module-loader.js +16 -0
  126. package/dist/runtime/process.d.ts +9 -0
  127. package/dist/runtime/process.js +146 -0
  128. package/dist/runtime/runner.d.ts +293 -0
  129. package/dist/runtime/runner.js +984 -0
  130. package/dist/runtime/standalone.d.ts +89 -0
  131. package/dist/runtime/standalone.js +536 -0
  132. package/dist/schema/validator.d.ts +28 -0
  133. package/dist/schema/validator.js +116 -0
  134. package/dist/server/body.d.ts +40 -0
  135. package/dist/server/body.js +93 -0
  136. package/dist/server/index.d.ts +5 -0
  137. package/dist/server/index.js +5 -0
  138. package/dist/server/routes/actions.d.ts +5 -0
  139. package/dist/server/routes/actions.js +298 -0
  140. package/dist/server/routes/common.d.ts +47 -0
  141. package/dist/server/routes/common.js +104 -0
  142. package/dist/server/routes/config.d.ts +6 -0
  143. package/dist/server/routes/config.js +173 -0
  144. package/dist/server/routes/doctor.d.ts +5 -0
  145. package/dist/server/routes/doctor.js +63 -0
  146. package/dist/server/routes/health.d.ts +5 -0
  147. package/dist/server/routes/health.js +35 -0
  148. package/dist/server/routes/index.d.ts +9 -0
  149. package/dist/server/routes/index.js +9 -0
  150. package/dist/server/routes/info.d.ts +7 -0
  151. package/dist/server/routes/info.js +158 -0
  152. package/dist/server/routes/playbooks.d.ts +5 -0
  153. package/dist/server/routes/playbooks.js +134 -0
  154. package/dist/server/routes/runs.d.ts +5 -0
  155. package/dist/server/routes/runs.js +343 -0
  156. package/dist/server/routes/state.d.ts +6 -0
  157. package/dist/server/routes/state.js +169 -0
  158. package/dist/server/security.d.ts +44 -0
  159. package/dist/server/security.js +101 -0
  160. package/dist/server/server.d.ts +15 -0
  161. package/dist/server/server.js +355 -0
  162. package/dist/server/types.d.ts +78 -0
  163. package/dist/server/types.js +1 -0
  164. package/dist/storage/data-dir-lock.d.ts +119 -0
  165. package/dist/storage/data-dir-lock.js +1016 -0
  166. package/dist/storage/driver.d.ts +9 -0
  167. package/dist/storage/driver.js +59 -0
  168. package/dist/storage/index.d.ts +64 -0
  169. package/dist/storage/index.js +98 -0
  170. package/dist/storage/lazy.d.ts +8 -0
  171. package/dist/storage/lazy.js +44 -0
  172. package/dist/storage/mask.d.ts +26 -0
  173. package/dist/storage/mask.js +48 -0
  174. package/dist/storage/params.d.ts +13 -0
  175. package/dist/storage/params.js +32 -0
  176. package/dist/storage/sqlite.d.ts +79 -0
  177. package/dist/storage/sqlite.js +689 -0
  178. package/dist/storage/types.d.ts +172 -0
  179. package/dist/storage/types.js +14 -0
  180. package/dist/target/index.d.ts +4 -0
  181. package/dist/target/index.js +4 -0
  182. package/dist/target/local.d.ts +52 -0
  183. package/dist/target/local.js +343 -0
  184. package/dist/target/remote.d.ts +76 -0
  185. package/dist/target/remote.js +839 -0
  186. package/dist/target/target.d.ts +6 -0
  187. package/dist/target/target.js +35 -0
  188. package/dist/target/types.d.ts +246 -0
  189. package/dist/target/types.js +34 -0
  190. package/dist/utils/index.d.ts +49 -0
  191. package/dist/utils/index.js +180 -0
  192. package/dist/version.d.ts +4 -0
  193. package/dist/version.js +4 -0
  194. package/package.json +13 -13
  195. package/src/build/index.ts +0 -1
  196. package/src/build/templates.ts +0 -59
  197. package/src/catalog/action-index.ts +0 -92
  198. package/src/catalog/action-resolver.ts +0 -120
  199. package/src/catalog/index.ts +0 -5
  200. package/src/catalog/location-registry.ts +0 -134
  201. package/src/catalog/package-catalog.ts +0 -87
  202. package/src/catalog/types.ts +0 -75
  203. package/src/doctor/doctor.ts +0 -419
  204. package/src/doctor/index.ts +0 -2
  205. package/src/doctor/types.ts +0 -25
  206. package/src/execution/index.ts +0 -2
  207. package/src/execution/service.ts +0 -322
  208. package/src/execution/types.ts +0 -73
  209. package/src/export/index.ts +0 -1
  210. package/src/export/templates.ts +0 -590
  211. package/src/filter/index.ts +0 -1
  212. package/src/filter/intent.ts +0 -154
  213. package/src/index.ts +0 -16
  214. package/src/profile/client.ts +0 -552
  215. package/src/profile/index.ts +0 -3
  216. package/src/profile/manager.ts +0 -341
  217. package/src/profile/types.ts +0 -71
  218. package/src/project/index.ts +0 -4
  219. package/src/project/init.ts +0 -242
  220. package/src/project/loader.ts +0 -479
  221. package/src/project/manifest.ts +0 -299
  222. package/src/project/types.ts +0 -141
  223. package/src/registry/index.ts +0 -2
  224. package/src/registry/registry.ts +0 -940
  225. package/src/registry/types.ts +0 -127
  226. package/src/runtime/clock.ts +0 -41
  227. package/src/runtime/context.ts +0 -259
  228. package/src/runtime/env.ts +0 -172
  229. package/src/runtime/events.ts +0 -142
  230. package/src/runtime/execution-manager.ts +0 -74
  231. package/src/runtime/index.ts +0 -10
  232. package/src/runtime/module-loader.ts +0 -67
  233. package/src/runtime/process.ts +0 -245
  234. package/src/runtime/runner.ts +0 -527
  235. package/src/runtime/standalone.ts +0 -429
  236. package/src/schema/validator.ts +0 -61
  237. package/src/server/body.ts +0 -112
  238. package/src/server/index.ts +0 -7
  239. package/src/server/routes/actions.ts +0 -262
  240. package/src/server/routes/common.ts +0 -126
  241. package/src/server/routes/config.ts +0 -147
  242. package/src/server/routes/doctor.ts +0 -29
  243. package/src/server/routes/health.ts +0 -40
  244. package/src/server/routes/index.ts +0 -9
  245. package/src/server/routes/info.ts +0 -221
  246. package/src/server/routes/playbooks.ts +0 -109
  247. package/src/server/routes/runs.ts +0 -301
  248. package/src/server/routes/state.ts +0 -133
  249. package/src/server/runtime-registry.ts +0 -115
  250. package/src/server/security.ts +0 -115
  251. package/src/server/server.ts +0 -251
  252. package/src/server/types.ts +0 -57
  253. package/src/storage/driver.ts +0 -126
  254. package/src/storage/index.ts +0 -81
  255. package/src/storage/mask.ts +0 -34
  256. package/src/storage/sqlite.ts +0 -714
  257. package/src/storage/types.ts +0 -139
  258. package/src/utils/index.ts +0 -62
@@ -1,940 +0,0 @@
1
- import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
2
- import { basename, dirname, join, resolve } from "node:path";
3
- import { findProjectRoot, loadActions, loadPlaybooks, loadProjectConfig } from "../project/loader";
4
- import { loadManifest } from "../project/manifest";
5
- import { getActionDockHome, getPackageSlug } from "../utils";
6
- import type {
7
- GlobalRegistryData,
8
- LinkedPackageEntry,
9
- LinkedWorkspaceEntry,
10
- LinkResult,
11
- PruneResult,
12
- RegistryStatusReport,
13
- RegistryTreeItem,
14
- ResolvedActionProject,
15
- ResolvedPlaybookProject,
16
- UnlinkResult,
17
- } from "./types";
18
-
19
- const IGNORED_SCAN_DIRS = new Set([
20
- "node_modules",
21
- ".git",
22
- "dist",
23
- "build",
24
- ".gemini",
25
- ".actiondock",
26
- ".claude",
27
- ".idea",
28
- ".vscode",
29
- ]);
30
-
31
- /**
32
- * 递归扫描包含 actiondock.json 的子项目根目录
33
- */
34
- export function discoverProjects(dir: string, maxDepth: number = 3): string[] {
35
- const results: string[] = [];
36
- const resolvedDir = resolve(dir);
37
-
38
- function walk(currentDir: string, currentDepth: number) {
39
- if (currentDepth > maxDepth) return;
40
- try {
41
- const entries = readdirSync(currentDir, { withFileTypes: true });
42
- const hasActiondock = entries.some((e) => e.isFile() && e.name === "actiondock.json");
43
-
44
- if (hasActiondock && currentDir !== resolvedDir) {
45
- results.push(currentDir);
46
- return; // 不再向项目内部子目录递归
47
- }
48
-
49
- for (const entry of entries) {
50
- if (entry.isDirectory() && !IGNORED_SCAN_DIRS.has(entry.name)) {
51
- walk(join(currentDir, entry.name), currentDepth + 1);
52
- }
53
- }
54
- } catch {
55
- // 忽略无法读取的目录
56
- }
57
- }
58
-
59
- walk(resolvedDir, 1);
60
- return results;
61
- }
62
-
63
- export function getRegistryFilePath(customHome?: string): string {
64
- const baseDir = getActionDockHome(customHome);
65
- return join(baseDir, ".actiondock", "registry.json");
66
- }
67
-
68
- export function loadRegistry(customHome?: string): GlobalRegistryData {
69
- const filePath = getRegistryFilePath(customHome);
70
- if (!existsSync(filePath)) {
71
- return { version: "2.0.0", packages: {}, workspaces: {} };
72
- }
73
- try {
74
- const raw = readFileSync(filePath, "utf-8");
75
- const parsed = JSON.parse(raw);
76
- if (!parsed || typeof parsed !== "object" || !parsed.packages) {
77
- return { version: "2.0.0", packages: {}, workspaces: {} };
78
- }
79
- return {
80
- version: "2.0.0",
81
- packages: parsed.packages || {},
82
- workspaces: parsed.workspaces || {},
83
- };
84
- } catch {
85
- return { version: "2.0.0", packages: {}, workspaces: {} };
86
- }
87
- }
88
-
89
- export function saveRegistry(data: GlobalRegistryData, customHome?: string): void {
90
- const filePath = getRegistryFilePath(customHome);
91
- mkdirSync(dirname(filePath), { recursive: true });
92
- writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8");
93
- }
94
-
95
- export function linkPackage(
96
- targetPath: string = process.cwd(),
97
- customHome?: string,
98
- options?: { recursive?: boolean }
99
- ): LinkResult {
100
- const absPath = resolve(targetPath);
101
- const directHasConfig = existsSync(join(absPath, "actiondock.json"));
102
-
103
- // 1. 如果当前目录直接包含 actiondock.json 且未强制递归,按单包链接
104
- if (directHasConfig && !options?.recursive) {
105
- const config = loadProjectConfig(absPath);
106
- const registry = loadRegistry(customHome);
107
-
108
- const entry: LinkedPackageEntry = {
109
- id: config.id,
110
- name: config.name,
111
- version: config.version,
112
- path: absPath,
113
- linkedAt: new Date().toISOString(),
114
- };
115
-
116
- registry.packages[config.id] = entry;
117
- saveRegistry(registry, customHome);
118
-
119
- return {
120
- id: config.id,
121
- name: config.name,
122
- version: config.version,
123
- path: absPath,
124
- linkedAt: entry.linkedAt,
125
- isWorkspace: false,
126
- entries: [entry],
127
- };
128
- }
129
-
130
- // 2. 尝试扫描子目录发现多个 ActionDock 子项目(Workspace 模式)
131
- const discoveredRoots = discoverProjects(absPath);
132
-
133
- if (discoveredRoots.length > 0) {
134
- const registry = loadRegistry(customHome);
135
- const now = new Date().toISOString();
136
- const wsEntry: LinkedWorkspaceEntry = {
137
- path: absPath,
138
- linkedAt: now,
139
- };
140
-
141
- registry.workspaces = registry.workspaces || {};
142
- registry.workspaces[absPath] = wsEntry;
143
-
144
- const linkedEntries: LinkedPackageEntry[] = [];
145
-
146
- for (const root of discoveredRoots) {
147
- try {
148
- const config = loadProjectConfig(root);
149
- const entry: LinkedPackageEntry = {
150
- id: config.id,
151
- name: config.name,
152
- version: config.version,
153
- path: root,
154
- linkedAt: now,
155
- workspaceRoot: absPath,
156
- };
157
- registry.packages[config.id] = entry;
158
- linkedEntries.push(entry);
159
- } catch {
160
- // 忽略异常项目
161
- }
162
- }
163
-
164
- saveRegistry(registry, customHome);
165
-
166
- const wsName = basename(absPath);
167
- return {
168
- id: wsName,
169
- name: wsName,
170
- version: "2.0.0",
171
- path: absPath,
172
- linkedAt: now,
173
- isWorkspace: true,
174
- entries: linkedEntries,
175
- workspace: wsEntry,
176
- };
177
- }
178
-
179
- // 3. 回退检查:如果在子目录执行(例如在 package 的 actions/ 目录下),查找父级项目根目录
180
- const parentRoot = findProjectRoot(absPath);
181
- if (parentRoot) {
182
- const config = loadProjectConfig(parentRoot);
183
- const registry = loadRegistry(customHome);
184
-
185
- const entry: LinkedPackageEntry = {
186
- id: config.id,
187
- name: config.name,
188
- version: config.version,
189
- path: parentRoot,
190
- linkedAt: new Date().toISOString(),
191
- };
192
-
193
- registry.packages[config.id] = entry;
194
- saveRegistry(registry, customHome);
195
-
196
- return {
197
- id: config.id,
198
- name: config.name,
199
- version: config.version,
200
- path: parentRoot,
201
- linkedAt: entry.linkedAt,
202
- isWorkspace: false,
203
- entries: [entry],
204
- };
205
- }
206
-
207
- throw new Error(`Cannot link: actiondock.json not found in '${absPath}' or its subdirectories`);
208
- }
209
-
210
- export function unlinkPackage(
211
- identifier: string = process.cwd(),
212
- customHome?: string
213
- ): UnlinkResult | null {
214
- const registry = loadRegistry(customHome);
215
- const absPath = resolve(identifier);
216
-
217
- // 1. 检查是否匹配 Workspace 绝对路径
218
- if (registry.workspaces && registry.workspaces[absPath]) {
219
- const removedWs = registry.workspaces[absPath];
220
- delete registry.workspaces[absPath];
221
-
222
- let removedCount = 0;
223
- for (const [id, entry] of Object.entries(registry.packages)) {
224
- if (entry.workspaceRoot === absPath || entry.path.startsWith(absPath)) {
225
- delete registry.packages[id];
226
- removedCount++;
227
- }
228
- }
229
- saveRegistry(registry, customHome);
230
- return {
231
- type: "workspace",
232
- id: basename(absPath),
233
- path: absPath,
234
- packagesCount: removedCount,
235
- removedWorkspace: removedWs,
236
- };
237
- }
238
-
239
- // 2. 检查是否匹配 Workspace 目录别名
240
- if (registry.workspaces) {
241
- for (const [wsPath, wsEntry] of Object.entries(registry.workspaces)) {
242
- if (basename(wsPath) === identifier) {
243
- delete registry.workspaces[wsPath];
244
- let removedCount = 0;
245
- for (const [id, entry] of Object.entries(registry.packages)) {
246
- if (entry.workspaceRoot === wsPath || entry.path.startsWith(wsPath)) {
247
- delete registry.packages[id];
248
- removedCount++;
249
- }
250
- }
251
- saveRegistry(registry, customHome);
252
- return {
253
- type: "workspace",
254
- id: basename(wsPath),
255
- path: wsPath,
256
- packagesCount: removedCount,
257
- removedWorkspace: wsEntry,
258
- };
259
- }
260
- }
261
- }
262
-
263
- // 3. 检查是否直接匹配 Package ID
264
- let targetKey: string | undefined;
265
- if (registry.packages[identifier]) {
266
- targetKey = identifier;
267
- } else {
268
- // 匹配路径或短 slug
269
- for (const [id, entry] of Object.entries(registry.packages)) {
270
- if (
271
- entry.path === absPath ||
272
- entry.id === identifier ||
273
- getPackageSlug(entry.id) === identifier
274
- ) {
275
- targetKey = id;
276
- break;
277
- }
278
- }
279
- }
280
-
281
- if (!targetKey) {
282
- return null;
283
- }
284
-
285
- const removed = registry.packages[targetKey];
286
- delete registry.packages[targetKey];
287
- saveRegistry(registry, customHome);
288
- return {
289
- type: "package",
290
- id: removed.id,
291
- path: removed.path,
292
- packagesCount: 1,
293
- removedPackage: removed,
294
- };
295
- }
296
-
297
- export function listLinkedPackages(customHome?: string): LinkedPackageEntry[] {
298
- const registry = loadRegistry(customHome);
299
- const result: Record<string, LinkedPackageEntry> = { ...registry.packages };
300
-
301
- // 动态扫描已挂载的 Workspace 目录,确保新拉取/新建的子包即时感知
302
- if (registry.workspaces) {
303
- for (const ws of Object.values(registry.workspaces)) {
304
- if (!existsSync(ws.path)) continue;
305
- const discovered = discoverProjects(ws.path);
306
- for (const root of discovered) {
307
- try {
308
- const config = loadProjectConfig(root);
309
- if (!result[config.id] || result[config.id].workspaceRoot === ws.path) {
310
- result[config.id] = {
311
- id: config.id,
312
- name: config.name,
313
- version: config.version,
314
- path: root,
315
- linkedAt: ws.linkedAt,
316
- workspaceRoot: ws.path,
317
- };
318
- }
319
- } catch {
320
- // 忽略异常项目
321
- }
322
- }
323
- }
324
- }
325
-
326
- return Object.values(result);
327
- }
328
-
329
- export function listLinkedWorkspaces(customHome?: string): LinkedWorkspaceEntry[] {
330
- const registry = loadRegistry(customHome);
331
- return Object.values(registry.workspaces || {});
332
- }
333
-
334
- async function projectHasAction(
335
- projectRoot: string,
336
- actionsDir: string | undefined,
337
- actionId: string
338
- ): Promise<boolean> {
339
- const manifest = loadManifest(projectRoot);
340
- if (manifest?.actions && actionId in manifest.actions) {
341
- return true;
342
- }
343
- try {
344
- const actions = await loadActions(projectRoot, actionsDir, { autoInstall: false });
345
- return actions.has(actionId);
346
- } catch {
347
- return false;
348
- }
349
- }
350
-
351
- function projectHasActionSync(
352
- projectRoot: string,
353
- actionsDir: string | undefined,
354
- actionId: string
355
- ): boolean {
356
- const manifest = loadManifest(projectRoot);
357
- if (manifest?.actions && actionId in manifest.actions) {
358
- return true;
359
- }
360
- const dir = join(projectRoot, actionsDir || "actions");
361
- if (!existsSync(dir)) {
362
- return false;
363
- }
364
- if (actionId.includes("..") || actionId.startsWith("/") || actionId.startsWith("\\")) {
365
- return false;
366
- }
367
- if (existsSync(join(dir, `${actionId}.ts`)) || existsSync(join(dir, `${actionId}.js`))) {
368
- return true;
369
- }
370
- const relFile = actionId.replace(/\./g, "/");
371
- if (existsSync(join(dir, `${relFile}.ts`)) || existsSync(join(dir, `${relFile}.js`))) {
372
- return true;
373
- }
374
- return false;
375
- }
376
-
377
- export function resolveActionProjectSync(
378
- actionIdentifier: string,
379
- cwd: string = process.cwd(),
380
- customHome?: string
381
- ): ResolvedActionProject {
382
- // 1. Check current directory / parent project
383
- const currentRoot = findProjectRoot(cwd);
384
- if (currentRoot) {
385
- try {
386
- const config = loadProjectConfig(currentRoot);
387
- if (projectHasActionSync(currentRoot, config.actionsDir, actionIdentifier)) {
388
- return {
389
- projectRoot: currentRoot,
390
- packageId: config.id,
391
- actionId: actionIdentifier,
392
- };
393
- }
394
- } catch {
395
- // Ignore and proceed to registry lookup
396
- }
397
- }
398
-
399
- // 2. Check if scoped format: <package-id>/<action-id> or <package-id>:<action-id>
400
- let targetPackage: string | undefined;
401
- let pureActionId = actionIdentifier;
402
-
403
- if (actionIdentifier.includes("/")) {
404
- const slashIdx = actionIdentifier.lastIndexOf("/");
405
- targetPackage = actionIdentifier.slice(0, slashIdx);
406
- pureActionId = actionIdentifier.slice(slashIdx + 1);
407
- } else if (actionIdentifier.includes(":")) {
408
- const colonIdx = actionIdentifier.lastIndexOf(":");
409
- targetPackage = actionIdentifier.slice(0, colonIdx);
410
- pureActionId = actionIdentifier.slice(colonIdx + 1);
411
- }
412
-
413
- const linkedList = listLinkedPackages(customHome);
414
-
415
- if (targetPackage) {
416
- let targetRoot: string | undefined;
417
- let targetPkgId = targetPackage;
418
-
419
- const pkg = linkedList.find(
420
- (p) => p.id === targetPackage || getPackageSlug(p.id) === targetPackage
421
- );
422
- if (pkg && existsSync(pkg.path)) {
423
- targetRoot = pkg.path;
424
- targetPkgId = pkg.id;
425
- } else if (currentRoot) {
426
- try {
427
- const config = loadProjectConfig(currentRoot);
428
- if (config.id === targetPackage || getPackageSlug(config.id) === targetPackage) {
429
- targetRoot = currentRoot;
430
- targetPkgId = config.id;
431
- }
432
- } catch {
433
- // Ignore
434
- }
435
- }
436
-
437
- if (!targetRoot || !existsSync(targetRoot)) {
438
- throw new Error(
439
- `Linked package '${targetPackage}' not found or path no longer exists (${pkg?.path || "unregistered"}). Run 'ad link' in the package directory.`
440
- );
441
- }
442
-
443
- const config = loadProjectConfig(targetRoot);
444
- if (!projectHasActionSync(targetRoot, config.actionsDir, pureActionId)) {
445
- throw new Error(`Action '${pureActionId}' not found in package '${targetPkgId}' (${targetRoot})`);
446
- }
447
-
448
- return {
449
- projectRoot: targetRoot,
450
- packageId: targetPkgId,
451
- actionId: pureActionId,
452
- };
453
- }
454
-
455
- // 3. Search across all linked packages
456
- const matches: Array<{ entry: LinkedPackageEntry; actionId: string }> = [];
457
-
458
- for (const pkg of linkedList) {
459
- if (!existsSync(pkg.path)) continue;
460
- try {
461
- const config = loadProjectConfig(pkg.path);
462
- if (projectHasActionSync(pkg.path, config.actionsDir, actionIdentifier)) {
463
- matches.push({ entry: pkg, actionId: actionIdentifier });
464
- }
465
- } catch {
466
- // Ignore invalid linked package
467
- }
468
- }
469
-
470
- if (matches.length === 1) {
471
- return {
472
- projectRoot: matches[0].entry.path,
473
- packageId: matches[0].entry.id,
474
- actionId: matches[0].actionId,
475
- };
476
- }
477
-
478
- if (matches.length > 1) {
479
- const pkgList = matches.map((m) => `'${m.entry.id}'`).join(", ");
480
- throw new Error(
481
- `Action '${actionIdentifier}' is provided by multiple linked packages: ${pkgList}. Please specify using '<package-id>/${actionIdentifier}'.`
482
- );
483
- }
484
-
485
- if (currentRoot) {
486
- throw new Error(`Action '${actionIdentifier}' not found in current project or any linked packages`);
487
- } else {
488
- throw new Error(
489
- `Action '${actionIdentifier}' not found. You are not in an ActionDock project, and no linked package provides '${actionIdentifier}'. Use 'ad link' to register your package.`
490
- );
491
- }
492
- }
493
-
494
- export async function resolveActionProject(
495
- actionIdentifier: string,
496
- cwd: string = process.cwd(),
497
- customHome?: string
498
- ): Promise<ResolvedActionProject> {
499
- // 1. Check current directory / parent project
500
- const currentRoot = findProjectRoot(cwd);
501
- if (currentRoot) {
502
- try {
503
- const config = loadProjectConfig(currentRoot);
504
- if (await projectHasAction(currentRoot, config.actionsDir, actionIdentifier)) {
505
- return {
506
- projectRoot: currentRoot,
507
- packageId: config.id,
508
- actionId: actionIdentifier,
509
- };
510
- }
511
- } catch {
512
- // Ignore and proceed to registry lookup
513
- }
514
- }
515
-
516
- // 2. Check if scoped format: <package-id>/<action-id> or <package-id>:<action-id>
517
- let targetPackage: string | undefined;
518
- let pureActionId = actionIdentifier;
519
-
520
- if (actionIdentifier.includes("/")) {
521
- const slashIdx = actionIdentifier.lastIndexOf("/");
522
- targetPackage = actionIdentifier.slice(0, slashIdx);
523
- pureActionId = actionIdentifier.slice(slashIdx + 1);
524
- } else if (actionIdentifier.includes(":")) {
525
- const colonIdx = actionIdentifier.lastIndexOf(":");
526
- targetPackage = actionIdentifier.slice(0, colonIdx);
527
- pureActionId = actionIdentifier.slice(colonIdx + 1);
528
- }
529
-
530
- const linkedList = listLinkedPackages(customHome);
531
-
532
- if (targetPackage) {
533
- let targetRoot: string | undefined;
534
- let targetPkgId = targetPackage;
535
-
536
- const pkg = linkedList.find(
537
- (p) => p.id === targetPackage || getPackageSlug(p.id) === targetPackage
538
- );
539
- if (pkg && existsSync(pkg.path)) {
540
- targetRoot = pkg.path;
541
- targetPkgId = pkg.id;
542
- } else if (currentRoot) {
543
- try {
544
- const config = loadProjectConfig(currentRoot);
545
- if (config.id === targetPackage || getPackageSlug(config.id) === targetPackage) {
546
- targetRoot = currentRoot;
547
- targetPkgId = config.id;
548
- }
549
- } catch {
550
- // Ignore
551
- }
552
- }
553
-
554
- if (!targetRoot || !existsSync(targetRoot)) {
555
- throw new Error(
556
- `Linked package '${targetPackage}' not found or path no longer exists (${pkg?.path || "unregistered"}). Run 'ad link' in the package directory.`
557
- );
558
- }
559
-
560
- const config = loadProjectConfig(targetRoot);
561
- if (!(await projectHasAction(targetRoot, config.actionsDir, pureActionId))) {
562
- throw new Error(`Action '${pureActionId}' not found in package '${targetPkgId}' (${targetRoot})`);
563
- }
564
-
565
- return {
566
- projectRoot: targetRoot,
567
- packageId: targetPkgId,
568
- actionId: pureActionId,
569
- };
570
- }
571
-
572
- // 3. Search across all linked packages
573
- const matches: Array<{ entry: LinkedPackageEntry; actionId: string }> = [];
574
-
575
- for (const pkg of linkedList) {
576
- if (!existsSync(pkg.path)) continue;
577
- try {
578
- const config = loadProjectConfig(pkg.path);
579
- if (await projectHasAction(pkg.path, config.actionsDir, actionIdentifier)) {
580
- matches.push({ entry: pkg, actionId: actionIdentifier });
581
- }
582
- } catch {
583
- // Ignore invalid linked package
584
- }
585
- }
586
-
587
- if (matches.length === 1) {
588
- return {
589
- projectRoot: matches[0].entry.path,
590
- packageId: matches[0].entry.id,
591
- actionId: matches[0].actionId,
592
- };
593
- }
594
-
595
- if (matches.length > 1) {
596
- const pkgList = matches.map((m) => `'${m.entry.id}'`).join(", ");
597
- throw new Error(
598
- `Action '${actionIdentifier}' is provided by multiple linked packages: ${pkgList}. Please specify using '<package-id>/${actionIdentifier}'.`
599
- );
600
- }
601
-
602
- if (currentRoot) {
603
- throw new Error(`Action '${actionIdentifier}' not found in current project or any linked packages`);
604
- } else {
605
- throw new Error(
606
- `Action '${actionIdentifier}' not found. You are not in an ActionDock project, and no linked package provides '${actionIdentifier}'. Use 'ad link' to register your package.`
607
- );
608
- }
609
- }
610
-
611
- export function resolvePackageRoot(
612
- packageIdOrPath?: string,
613
- cwd?: string,
614
- customHome?: string
615
- ): string | null {
616
- if (!packageIdOrPath) {
617
- return findProjectRoot(cwd);
618
- }
619
-
620
- const baseDir = cwd || process.cwd();
621
- const resolvedPath = resolve(baseDir, packageIdOrPath);
622
-
623
- // 1. Check if packageIdOrPath is an existing directory or file path on disk
624
- if (existsSync(resolvedPath)) {
625
- try {
626
- const stat = statSync(resolvedPath);
627
- const targetDir = stat.isDirectory() ? resolvedPath : dirname(resolvedPath);
628
- if (existsSync(join(targetDir, "actiondock.json"))) {
629
- return targetDir;
630
- }
631
- const parentRoot = findProjectRoot(targetDir);
632
- if (parentRoot) {
633
- return parentRoot;
634
- }
635
- } catch {
636
- // ignore
637
- }
638
- }
639
-
640
- // If it was explicitly a path (starts with . or / or ~ or contains / or \), and did not resolve above:
641
- const isExplicitPath =
642
- packageIdOrPath.startsWith(".") ||
643
- packageIdOrPath.startsWith("/") ||
644
- packageIdOrPath.startsWith("~") ||
645
- packageIdOrPath.includes("/") ||
646
- packageIdOrPath.includes("\\");
647
-
648
- if (isExplicitPath) {
649
- // An explicit path that does not exist or is not an ActionDock project must fail
650
- return null;
651
- }
652
-
653
- // 2. Check linked packages in registry
654
- const linkedList = listLinkedPackages(customHome);
655
- const found = linkedList.find(
656
- (p) =>
657
- p.id === packageIdOrPath ||
658
- getPackageSlug(p.id) === packageIdOrPath ||
659
- p.path === resolvedPath
660
- );
661
- if (found) {
662
- return found.path;
663
- }
664
-
665
- // 3. Check current project (from cwd)
666
- const currentRoot = findProjectRoot(cwd);
667
- if (currentRoot) {
668
- try {
669
- const config = loadProjectConfig(currentRoot);
670
- if (config.id === packageIdOrPath || getPackageSlug(config.id) === packageIdOrPath) {
671
- return currentRoot;
672
- }
673
- } catch {
674
- // ignore broken config
675
- }
676
- }
677
-
678
- return null;
679
- }
680
-
681
- export function resolvePlaybookProject(
682
- playbookIdentifier: string,
683
- cwd: string = process.cwd(),
684
- customHome?: string
685
- ): ResolvedPlaybookProject {
686
- // 1. Check current directory / parent project
687
- const currentRoot = findProjectRoot(cwd);
688
- if (currentRoot) {
689
- try {
690
- const config = loadProjectConfig(currentRoot);
691
- const playbooks = loadPlaybooks(currentRoot, config.playbooksDir);
692
- if (playbooks.has(playbookIdentifier)) {
693
- return {
694
- projectRoot: currentRoot,
695
- packageId: config.id,
696
- playbookId: playbookIdentifier,
697
- playbook: playbooks.get(playbookIdentifier)!,
698
- };
699
- }
700
- } catch {
701
- // Ignore and proceed to registry lookup
702
- }
703
- }
704
-
705
- // 2. Check if scoped format: <package-id>/<playbook-id> or <package-id>:<playbook-id>
706
- let targetPackage: string | undefined;
707
- let purePlaybookId = playbookIdentifier;
708
-
709
- if (playbookIdentifier.includes("/")) {
710
- const slashIdx = playbookIdentifier.indexOf("/");
711
- targetPackage = playbookIdentifier.slice(0, slashIdx);
712
- purePlaybookId = playbookIdentifier.slice(slashIdx + 1);
713
- } else if (playbookIdentifier.includes(":")) {
714
- const colonIdx = playbookIdentifier.indexOf(":");
715
- targetPackage = playbookIdentifier.slice(0, colonIdx);
716
- purePlaybookId = playbookIdentifier.slice(colonIdx + 1);
717
- }
718
-
719
- const linkedList = listLinkedPackages(customHome);
720
-
721
- if (targetPackage) {
722
- let targetRoot: string | undefined;
723
- let targetPkgId = targetPackage;
724
-
725
- const pkg = linkedList.find(
726
- (p) => p.id === targetPackage || getPackageSlug(p.id) === targetPackage
727
- );
728
- if (pkg && existsSync(pkg.path)) {
729
- targetRoot = pkg.path;
730
- targetPkgId = pkg.id;
731
- } else if (currentRoot) {
732
- try {
733
- const config = loadProjectConfig(currentRoot);
734
- if (config.id === targetPackage || getPackageSlug(config.id) === targetPackage) {
735
- targetRoot = currentRoot;
736
- targetPkgId = config.id;
737
- }
738
- } catch {
739
- // Ignore
740
- }
741
- }
742
-
743
- if (!targetRoot || !existsSync(targetRoot)) {
744
- throw new Error(
745
- `Linked package '${targetPackage}' not found or path no longer exists (${pkg?.path || "unregistered"}). Run 'ad link' in the package directory.`
746
- );
747
- }
748
-
749
- const config = loadProjectConfig(targetRoot);
750
- const playbooks = loadPlaybooks(targetRoot, config.playbooksDir);
751
- const pb = playbooks.get(purePlaybookId);
752
- if (!pb) {
753
- throw new Error(`Playbook '${purePlaybookId}' not found in package '${targetPkgId}' (${targetRoot})`);
754
- }
755
-
756
- return {
757
- projectRoot: targetRoot,
758
- packageId: targetPkgId,
759
- playbookId: purePlaybookId,
760
- playbook: pb,
761
- };
762
- }
763
-
764
- // 3. Search across all linked packages
765
- const matches: Array<{ entry: LinkedPackageEntry; playbookId: string; playbook: import("../project/types").PlaybookDefinition }> = [];
766
-
767
- for (const pkg of linkedList) {
768
- if (!existsSync(pkg.path)) continue;
769
- try {
770
- const config = loadProjectConfig(pkg.path);
771
- const playbooks = loadPlaybooks(pkg.path, config.playbooksDir);
772
- if (playbooks.has(playbookIdentifier)) {
773
- matches.push({
774
- entry: pkg,
775
- playbookId: playbookIdentifier,
776
- playbook: playbooks.get(playbookIdentifier)!,
777
- });
778
- }
779
- } catch {
780
- // Ignore invalid linked package
781
- }
782
- }
783
-
784
- if (matches.length === 1) {
785
- return {
786
- projectRoot: matches[0].entry.path,
787
- packageId: matches[0].entry.id,
788
- playbookId: matches[0].playbookId,
789
- playbook: matches[0].playbook,
790
- };
791
- }
792
-
793
- if (matches.length > 1) {
794
- const pkgList = matches.map((m) => `'${m.entry.id}'`).join(", ");
795
- throw new Error(
796
- `Playbook '${playbookIdentifier}' is provided by multiple linked packages: ${pkgList}. Please specify using '<package-id>/${playbookIdentifier}'.`
797
- );
798
- }
799
-
800
- if (currentRoot) {
801
- throw new Error(`Playbook '${playbookIdentifier}' not found in current project or any linked packages`);
802
- } else {
803
- throw new Error(
804
- `Playbook '${playbookIdentifier}' not found. You are not in an ActionDock project, and no linked package provides '${playbookIdentifier}'. Use 'ad link' to register your package.`
805
- );
806
- }
807
- }
808
-
809
- export function getRegistryStatus(customHome?: string): RegistryStatusReport {
810
- const registry = loadRegistry(customHome);
811
- const workspaces: RegistryTreeItem[] = [];
812
- const packages: RegistryTreeItem[] = [];
813
- let staleCount = 0;
814
- const seenPackageIds = new Set<string>();
815
-
816
- // 1. Process workspaces
817
- if (registry.workspaces) {
818
- for (const [wsPath, wsEntry] of Object.entries(registry.workspaces)) {
819
- const isWsActive = existsSync(wsPath);
820
- if (!isWsActive) {
821
- staleCount++;
822
- workspaces.push({
823
- type: "workspace",
824
- id: basename(wsPath),
825
- path: wsPath,
826
- status: "stale",
827
- packagesCount: 0,
828
- children: [],
829
- });
830
- continue;
831
- }
832
-
833
- const discoveredRoots = discoverProjects(wsPath);
834
- const children: NonNullable<RegistryTreeItem["children"]> = [];
835
-
836
- for (const root of discoveredRoots) {
837
- try {
838
- const config = loadProjectConfig(root);
839
- seenPackageIds.add(config.id);
840
- children.push({
841
- id: config.id,
842
- name: config.name,
843
- version: config.version,
844
- path: root,
845
- status: "active",
846
- });
847
- } catch {
848
- // ignore broken project
849
- }
850
- }
851
-
852
- workspaces.push({
853
- type: "workspace",
854
- id: basename(wsPath),
855
- path: wsPath,
856
- status: "active",
857
- packagesCount: children.length,
858
- children,
859
- });
860
- }
861
- }
862
-
863
- // 2. Process standalone packages (not part of an active workspace)
864
- for (const [pkgId, pkgEntry] of Object.entries(registry.packages)) {
865
- if (pkgEntry.workspaceRoot && registry.workspaces && registry.workspaces[pkgEntry.workspaceRoot]) {
866
- continue;
867
- }
868
- if (seenPackageIds.has(pkgId)) {
869
- continue;
870
- }
871
-
872
- const isPkgActive = existsSync(pkgEntry.path);
873
- if (!isPkgActive) {
874
- staleCount++;
875
- packages.push({
876
- type: "package",
877
- id: pkgEntry.id,
878
- name: pkgEntry.name,
879
- version: pkgEntry.version,
880
- path: pkgEntry.path,
881
- status: "stale",
882
- });
883
- } else {
884
- packages.push({
885
- type: "package",
886
- id: pkgEntry.id,
887
- name: pkgEntry.name,
888
- version: pkgEntry.version,
889
- path: pkgEntry.path,
890
- status: "active",
891
- });
892
- }
893
- }
894
-
895
- const totalPackagesCount =
896
- workspaces.reduce((acc, ws) => acc + (ws.children?.length || 0), 0) +
897
- packages.filter((p) => p.status === "active").length;
898
-
899
- return {
900
- workspaces,
901
- packages,
902
- staleCount,
903
- totalPackagesCount,
904
- };
905
- }
906
-
907
- export function pruneRegistry(customHome?: string): PruneResult {
908
- const registry = loadRegistry(customHome);
909
- const prunedWorkspaces: LinkedWorkspaceEntry[] = [];
910
- const prunedPackages: LinkedPackageEntry[] = [];
911
-
912
- // 1. Prune workspaces
913
- if (registry.workspaces) {
914
- for (const [wsPath, wsEntry] of Object.entries(registry.workspaces)) {
915
- if (!existsSync(wsPath)) {
916
- prunedWorkspaces.push(wsEntry);
917
- delete registry.workspaces[wsPath];
918
- }
919
- }
920
- }
921
-
922
- // 2. Prune packages
923
- for (const [pkgId, pkgEntry] of Object.entries(registry.packages)) {
924
- if (!existsSync(pkgEntry.path)) {
925
- prunedPackages.push(pkgEntry);
926
- delete registry.packages[pkgId];
927
- }
928
- }
929
-
930
- if (prunedWorkspaces.length > 0 || prunedPackages.length > 0) {
931
- saveRegistry(registry, customHome);
932
- }
933
-
934
- return {
935
- prunedPackages,
936
- prunedWorkspaces,
937
- };
938
- }
939
-
940
-