@actiondock/core 2.0.11 → 2.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build/templates.d.ts +19 -0
- package/dist/catalog/action-index.d.ts +9 -0
- package/dist/catalog/action-resolver.d.ts +20 -0
- package/dist/catalog/location-registry.d.ts +15 -0
- package/dist/catalog/package-catalog.d.ts +7 -0
- package/dist/catalog/types.d.ts +68 -0
- package/dist/doctor/doctor.d.ts +6 -0
- package/dist/doctor/types.d.ts +23 -0
- package/dist/execution/index.d.ts +2 -0
- package/dist/execution/service.d.ts +74 -0
- package/dist/execution/types.d.ts +74 -0
- package/dist/export/templates.d.ts +33 -0
- package/dist/filter/intent.d.ts +33 -0
- package/{src/index.ts → dist/index.d.ts} +1 -1
- package/dist/index.js +8458 -0
- package/dist/profile/client.d.ts +116 -0
- package/dist/profile/manager.d.ts +47 -0
- package/dist/profile/types.d.ts +61 -0
- package/dist/project/closure.d.ts +25 -0
- package/{src/project/index.ts → dist/project/index.d.ts} +1 -0
- package/dist/project/init.d.ts +27 -0
- package/dist/project/loader.d.ts +105 -0
- package/dist/project/manifest.d.ts +44 -0
- package/dist/project/types.d.ts +131 -0
- package/dist/registry/registry.d.ts +24 -0
- package/dist/registry/types.d.ts +117 -0
- package/dist/runtime/clock.d.ts +21 -0
- package/dist/runtime/context.d.ts +75 -0
- package/dist/runtime/env.d.ts +54 -0
- package/dist/runtime/events.d.ts +61 -0
- package/dist/runtime/execution-manager.d.ts +48 -0
- package/{src/runtime/index.ts → dist/runtime/index.d.ts} +0 -1
- package/dist/runtime/module-loader.d.ts +42 -0
- package/dist/runtime/process.d.ts +11 -0
- package/dist/runtime/runner.d.ts +186 -0
- package/dist/runtime/standalone.d.ts +40 -0
- package/dist/schema/validator.d.ts +28 -0
- package/dist/server/body.d.ts +40 -0
- package/{src/server/index.ts → dist/server/index.d.ts} +0 -1
- package/dist/server/routes/actions.d.ts +5 -0
- package/dist/server/routes/common.d.ts +36 -0
- package/dist/server/routes/config.d.ts +5 -0
- package/dist/server/routes/doctor.d.ts +5 -0
- package/dist/server/routes/health.d.ts +5 -0
- package/dist/server/routes/info.d.ts +5 -0
- package/dist/server/routes/playbooks.d.ts +5 -0
- package/dist/server/routes/runs.d.ts +5 -0
- package/dist/server/routes/state.d.ts +5 -0
- package/dist/server/runtime-registry.d.ts +78 -0
- package/dist/server/security.d.ts +37 -0
- package/dist/server/server.d.ts +15 -0
- package/dist/server/types.d.ts +59 -0
- package/dist/storage/driver.d.ts +15 -0
- package/dist/storage/index.d.ts +44 -0
- package/{src/storage/mask.ts → dist/storage/mask.d.ts} +5 -20
- package/dist/storage/sqlite.d.ts +66 -0
- package/dist/storage/types.d.ts +121 -0
- package/dist/utils/index.d.ts +29 -0
- package/dist/version.d.ts +4 -0
- package/package.json +11 -10
- package/src/build/templates.ts +0 -59
- package/src/catalog/action-index.ts +0 -92
- package/src/catalog/action-resolver.ts +0 -120
- package/src/catalog/location-registry.ts +0 -134
- package/src/catalog/package-catalog.ts +0 -87
- package/src/catalog/types.ts +0 -75
- package/src/doctor/doctor.ts +0 -419
- package/src/doctor/types.ts +0 -25
- package/src/execution/index.ts +0 -2
- package/src/execution/service.ts +0 -322
- package/src/execution/types.ts +0 -73
- package/src/export/templates.ts +0 -590
- package/src/filter/intent.ts +0 -154
- package/src/profile/client.ts +0 -552
- package/src/profile/manager.ts +0 -341
- package/src/profile/types.ts +0 -71
- package/src/project/init.ts +0 -242
- package/src/project/loader.ts +0 -479
- package/src/project/manifest.ts +0 -299
- package/src/project/types.ts +0 -141
- package/src/registry/registry.ts +0 -940
- package/src/registry/types.ts +0 -127
- package/src/runtime/clock.ts +0 -41
- package/src/runtime/context.ts +0 -259
- package/src/runtime/env.ts +0 -172
- package/src/runtime/events.ts +0 -142
- package/src/runtime/execution-manager.ts +0 -74
- package/src/runtime/module-loader.ts +0 -67
- package/src/runtime/process.ts +0 -245
- package/src/runtime/runner.ts +0 -527
- package/src/runtime/standalone.ts +0 -429
- package/src/schema/validator.ts +0 -61
- package/src/server/body.ts +0 -112
- package/src/server/routes/actions.ts +0 -262
- package/src/server/routes/common.ts +0 -126
- package/src/server/routes/config.ts +0 -147
- package/src/server/routes/doctor.ts +0 -29
- package/src/server/routes/health.ts +0 -40
- package/src/server/routes/info.ts +0 -221
- package/src/server/routes/playbooks.ts +0 -109
- package/src/server/routes/runs.ts +0 -301
- package/src/server/routes/state.ts +0 -133
- package/src/server/runtime-registry.ts +0 -115
- package/src/server/security.ts +0 -115
- package/src/server/server.ts +0 -251
- package/src/server/types.ts +0 -57
- package/src/storage/driver.ts +0 -126
- package/src/storage/index.ts +0 -81
- package/src/storage/sqlite.ts +0 -714
- package/src/storage/types.ts +0 -139
- package/src/utils/index.ts +0 -62
- /package/{src/build/index.ts → dist/build/index.d.ts} +0 -0
- /package/{src/catalog/index.ts → dist/catalog/index.d.ts} +0 -0
- /package/{src/doctor/index.ts → dist/doctor/index.d.ts} +0 -0
- /package/{src/export/index.ts → dist/export/index.d.ts} +0 -0
- /package/{src/filter/index.ts → dist/filter/index.d.ts} +0 -0
- /package/{src/profile/index.ts → dist/profile/index.d.ts} +0 -0
- /package/{src/registry/index.ts → dist/registry/index.d.ts} +0 -0
- /package/{src/server/routes/index.ts → dist/server/routes/index.d.ts} +0 -0
package/src/registry/registry.ts
DELETED
|
@@ -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
|
-
|