@actiondock/cli 2.0.11-beta.0 → 2.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +15 -2
  2. package/bin/ad.js +13 -2
  3. package/dist/commands/action/create.d.ts +2 -0
  4. package/dist/commands/action/index.d.ts +16 -0
  5. package/dist/commands/action/list.d.ts +5 -0
  6. package/dist/commands/action/run.d.ts +4 -0
  7. package/dist/commands/action/show.d.ts +5 -0
  8. package/dist/commands/action/sync.d.ts +2 -0
  9. package/dist/commands/action/validate.d.ts +5 -0
  10. package/dist/commands/build.d.ts +2 -0
  11. package/dist/commands/config.d.ts +2 -0
  12. package/dist/commands/doctor.d.ts +2 -0
  13. package/dist/commands/export.d.ts +2 -0
  14. package/dist/commands/index.d.ts +2 -0
  15. package/dist/commands/info.d.ts +2 -0
  16. package/dist/commands/init.d.ts +2 -0
  17. package/dist/commands/link.d.ts +2 -0
  18. package/dist/commands/mcp.d.ts +2 -0
  19. package/dist/commands/playbook.d.ts +6 -0
  20. package/dist/commands/profile.d.ts +2 -0
  21. package/dist/commands/runs.d.ts +2 -0
  22. package/dist/commands/serve.d.ts +2 -0
  23. package/dist/commands/state.d.ts +2 -0
  24. package/dist/commands/test.d.ts +2 -0
  25. package/dist/index.d.ts +8 -0
  26. package/dist/index.js +745 -65708
  27. package/{src/utils/bytes.ts → dist/utils/bytes.d.ts} +0 -1
  28. package/{src/utils/filter.ts → dist/utils/filter.d.ts} +0 -1
  29. package/package.json +20 -15
  30. package/src/commands/action/create.ts +0 -129
  31. package/src/commands/action/index.ts +0 -27
  32. package/src/commands/action/list.ts +0 -247
  33. package/src/commands/action/run.ts +0 -197
  34. package/src/commands/action/show.ts +0 -128
  35. package/src/commands/action/sync.ts +0 -76
  36. package/src/commands/action/validate.ts +0 -102
  37. package/src/commands/build.ts +0 -56
  38. package/src/commands/config.ts +0 -583
  39. package/src/commands/doctor.ts +0 -84
  40. package/src/commands/export.ts +0 -196
  41. package/src/commands/index.ts +0 -75
  42. package/src/commands/info.ts +0 -537
  43. package/src/commands/init.ts +0 -24
  44. package/src/commands/link.ts +0 -73
  45. package/src/commands/mcp.ts +0 -195
  46. package/src/commands/playbook.ts +0 -566
  47. package/src/commands/profile.ts +0 -296
  48. package/src/commands/runs.ts +0 -452
  49. package/src/commands/serve.ts +0 -132
  50. package/src/commands/state.ts +0 -590
  51. package/src/commands/test.ts +0 -48
  52. package/src/index.ts +0 -58
  53. /package/{src/commands/action.ts → dist/commands/action.d.ts} +0 -0
  54. /package/{src/utils/duration.ts → dist/utils/duration.d.ts} +0 -0
@@ -1,537 +0,0 @@
1
- import { existsSync } from "node:fs";
2
- import {
3
- fetchRemoteInfo,
4
- filterWithFallbackInfo,
5
- findProjectRoot,
6
- getRegistryStatus,
7
- listLinkedPackages,
8
- loadManifest,
9
- loadPlaybooks,
10
- loadProjectConfig,
11
- resolvePackageRoot,
12
- resolveTarget,
13
- } from "@actiondock/core";
14
- import { Command } from "commander";
15
- import { ArgumentError, CliError, ExecutionError, getEffectiveOptions, renderResult } from "@actiondock/runtime-cli";
16
- import { resolveIntent } from "../utils/filter";
17
-
18
- interface AggregatedPackage {
19
- id: string;
20
- name: string;
21
- version: string;
22
- description?: string;
23
- path: string;
24
- actionsCount: number;
25
- playbooksCount: number;
26
- actions: string[];
27
- playbooks: string[];
28
- configDeclared: string[];
29
- }
30
-
31
- async function getProjectDetailInfo(root: string) {
32
- const config = loadProjectConfig(root);
33
- const manifest = loadManifest(root);
34
- const actionsMap = new Map<string, { id: string; description?: string }>();
35
- if (manifest?.actions) {
36
- for (const [id, item] of Object.entries(manifest.actions)) {
37
- actionsMap.set(id, {
38
- id,
39
- description: item.description,
40
- });
41
- }
42
- }
43
- const playbooks = loadPlaybooks(root, config.playbooksDir);
44
-
45
- return {
46
- id: config.id,
47
- name: config.name,
48
- version: config.version,
49
- description: config.description,
50
- projectRoot: root,
51
- actionsDir: config.actionsDir || "actions",
52
- playbooksDir: config.playbooksDir || "playbooks",
53
- actionsCount: actionsMap.size,
54
- playbooksCount: playbooks.size,
55
- actions: Array.from(actionsMap.keys()),
56
- playbooks: Array.from(playbooks.keys()),
57
- configDeclared: config.config ? Object.keys(config.config) : [],
58
- configDef: config.config,
59
- actionsMap,
60
- playbooksMap: playbooks,
61
- };
62
- }
63
-
64
- function projectDetailToJson(info: Awaited<ReturnType<typeof getProjectDetailInfo>>) {
65
- return {
66
- id: info.id,
67
- name: info.name,
68
- version: info.version,
69
- description: info.description,
70
- projectRoot: info.projectRoot,
71
- actionsDir: info.actionsDir,
72
- playbooksDir: info.playbooksDir,
73
- actionsCount: info.actionsCount,
74
- playbooksCount: info.playbooksCount,
75
- actions: info.actions,
76
- playbooks: info.playbooks,
77
- configDeclared: info.configDeclared,
78
- };
79
- }
80
-
81
- function printProjectDetail(info: Awaited<ReturnType<typeof getProjectDetailInfo>>) {
82
- console.log(`ActionDock Project: ${info.name} (${info.id})`);
83
- console.log(`Version: ${info.version}`);
84
- if (info.description) console.log(`Description: ${info.description}`);
85
- console.log(`Root: ${info.projectRoot}`);
86
- console.log(`\nActions (${info.actionsCount}):`);
87
- for (const [id, act] of info.actionsMap.entries()) {
88
- console.log(` - ${id.padEnd(28)} ${act.description || ""}`);
89
- }
90
- console.log(`\nPlaybooks (${info.playbooksCount}):`);
91
- for (const [id, pb] of info.playbooksMap.entries()) {
92
- console.log(` - ${id.padEnd(28)} ${pb.description || ""}`);
93
- }
94
- if (info.configDeclared.length > 0) {
95
- console.log(`\nDeclared Config Keys:`);
96
- for (const k of info.configDeclared) {
97
- const item = info.configDef?.[k];
98
- const isSec = item?.secret ? " [secret]" : "";
99
- const def =
100
- item?.default !== undefined
101
- ? ` (default: ${JSON.stringify(item.default)})`
102
- : "";
103
- console.log(
104
- ` - ${k.padEnd(24)} ${item?.description || ""}${def}${isSec}`
105
- );
106
- }
107
- }
108
- }
109
-
110
- function printAggregatedPackages(
111
- packages: AggregatedPackage[],
112
- options?: { header?: string; showTip?: boolean }
113
- ) {
114
- if (options?.header) {
115
- console.log(options.header);
116
- } else {
117
- console.log(`ActionDock Linked Packages (${packages.length}):\n`);
118
- }
119
- for (const p of packages) {
120
- console.log(`* ${p.name} (${p.id}) v${p.version}`);
121
- console.log(` Path: ${p.path}`);
122
- if (p.description) {
123
- console.log(` Desc: ${p.description}`);
124
- }
125
- console.log(
126
- ` Actions (${p.actionsCount}): ${p.actions.join(", ") || "(none)"}`
127
- );
128
- console.log(
129
- ` Playbooks (${p.playbooksCount}): ${p.playbooks.join(", ") || "(none)"}`
130
- );
131
- console.log("");
132
- }
133
- if (options?.showTip !== false) {
134
- console.log(
135
- "Tip: Run 'ad info <package-id>' to view detailed package configuration and schema."
136
- );
137
- }
138
- }
139
-
140
- function printRegistryTree(status: ReturnType<typeof getRegistryStatus>): void {
141
- const hasWorkspaces = status.workspaces.length > 0;
142
- const hasPackages = status.packages.length > 0;
143
-
144
- if (!hasWorkspaces && !hasPackages) {
145
- console.log("[INFO] No ActionDock packages or workspaces currently linked.");
146
- console.log(" Run 'ad link' inside an Action package or workspace to register it.");
147
- return;
148
- }
149
-
150
- console.log("[ActionDock Workspace & Package Tree]\n");
151
-
152
- if (hasWorkspaces) {
153
- console.log("Workspaces:");
154
- for (const ws of status.workspaces) {
155
- const tag = ws.status === "active" ? "[OK]" : "[STALE]";
156
- console.log(` ${tag} ${ws.path} (${ws.packagesCount} package${ws.packagesCount === 1 ? "" : "s"})`);
157
- if (ws.children && ws.children.length > 0) {
158
- ws.children.forEach((child: any, idx: number) => {
159
- const isLast = idx === ws.children!.length - 1;
160
- const prefix = isLast ? " +-- " : " |-- ";
161
- console.log(`${prefix}${child.id} (v${child.version}) -> ${child.path}`);
162
- });
163
- }
164
- }
165
- }
166
-
167
- if (hasPackages) {
168
- if (hasWorkspaces) console.log("");
169
- console.log("Standalone Packages:");
170
- for (const pkg of status.packages) {
171
- const tag = pkg.status === "active" ? "[OK]" : "[STALE]";
172
- console.log(` ${tag} ${pkg.id} (v${pkg.version || "unknown"}) -> ${pkg.path}`);
173
- }
174
- }
175
-
176
- console.log(`\n[Summary] Total: ${status.totalPackagesCount} active package(s), ${status.workspaces.length} workspace(s)`);
177
- if (status.staleCount > 0) {
178
- console.log(`[WARN] ${status.staleCount} stale entry/entries detected. Run 'ad unlink --prune' to clean up.`);
179
- }
180
- }
181
-
182
- export function registerInfoCommand(program: Command): void {
183
- program
184
- .command("info [patterns...]")
185
- .description(
186
- "Display information about current project, linked package, or remote target"
187
- )
188
- .option(
189
- "-i, --intent <pattern>",
190
- "Regex or fuzzy intent filter; falls back to full list when no match"
191
- )
192
- .option("-P, --package <id>", "Target package ID or path")
193
- .option("--tree", "Display packages in hierarchical tree view grouped by workspace")
194
- .option("-p, --profile <name>", "Query against a specific profile")
195
- .option("-s, --server <url>", "Remote server URL")
196
- .option("-t, --token <token>", "Auth token for remote server")
197
- .option("--fallback", "Enable fallback to full list when no items match intent")
198
- .option(
199
- "--no-fallback",
200
- "Disable fallback to full list when no items match intent"
201
- )
202
- .option("--json", "Output information as JSON")
203
- .option("--envelope", "Wrap JSON output in standard envelope")
204
- .option("--data-dir <path>", "Custom database storage directory")
205
- .action(async (patterns: string[] = [], rawOptions: any, cmd: any) => {
206
- try {
207
- const options = getEffectiveOptions(rawOptions, cmd);
208
- const effectiveIntent = resolveIntent(options.intent, patterns);
209
- const isMachine = Boolean(options.json || options.envelope);
210
- const fallbackExplicit = options.fallback === true || (Array.isArray(process.argv) && process.argv.includes("--fallback"));
211
- const shouldFallback = isMachine ? fallbackExplicit : options.fallback !== false;
212
-
213
- const target = resolveTarget({
214
- profile: options.profile,
215
- server: options.server,
216
- token: options.token,
217
- });
218
-
219
- // 1. Remote target handling
220
- if (target.type === "remote") {
221
- const remoteInfo = await fetchRemoteInfo(
222
- target.serverUrl!,
223
- target.token,
224
- {
225
- intent: effectiveIntent,
226
- package: options.package,
227
- tree: Boolean(options.tree),
228
- }
229
- );
230
-
231
- if (isMachine) {
232
- renderResult(remoteInfo, { json: options.json, envelope: options.envelope });
233
- return;
234
- }
235
-
236
- console.log(
237
- `Remote ActionDock Server: ${target.serverUrl}${target.profileName ? ` (Profile: ${target.profileName})` : ""}\n`
238
- );
239
-
240
- // Remote Tree
241
- if (remoteInfo.type === "tree" || (options.tree && remoteInfo.workspaces)) {
242
- printRegistryTree(remoteInfo);
243
- return;
244
- }
245
-
246
- // Remote Package Detail
247
- if (remoteInfo.type === "package_detail" || (remoteInfo.id && !remoteInfo.packages)) {
248
- console.log(`ActionDock Project: ${remoteInfo.name || remoteInfo.id} (${remoteInfo.id})`);
249
- console.log(`Version: ${remoteInfo.version || "unknown"}`);
250
- if (remoteInfo.description) console.log(`Description: ${remoteInfo.description}`);
251
- if (remoteInfo.path || remoteInfo.projectRoot) {
252
- console.log(`Root: ${remoteInfo.path || remoteInfo.projectRoot}`);
253
- }
254
-
255
- const actions = remoteInfo.actionsDetail || remoteInfo.actions || [];
256
- console.log(`\nActions (${remoteInfo.actionsCount || actions.length}):`);
257
- for (const act of actions) {
258
- if (typeof act === "string") {
259
- console.log(` - ${act}`);
260
- } else {
261
- console.log(` - ${(act.id || "").padEnd(28)} ${act.description || ""}`);
262
- }
263
- }
264
-
265
- const playbooks = remoteInfo.playbooksDetail || remoteInfo.playbooks || [];
266
- console.log(`\nPlaybooks (${remoteInfo.playbooksCount || playbooks.length}):`);
267
- for (const pb of playbooks) {
268
- if (typeof pb === "string") {
269
- console.log(` - ${pb}`);
270
- } else {
271
- console.log(` - ${(pb.id || "").padEnd(28)} ${pb.description || ""}`);
272
- }
273
- }
274
-
275
- const declared = remoteInfo.configDeclared || {};
276
- const declaredKeys = Object.keys(declared);
277
- if (declaredKeys.length > 0) {
278
- console.log(`\nDeclared Config Keys:`);
279
- for (const k of declaredKeys) {
280
- const item = declared[k];
281
- const isSec = item?.secret ? " [secret]" : "";
282
- const def =
283
- item?.default !== undefined
284
- ? ` (default: ${JSON.stringify(item.default)})`
285
- : "";
286
- console.log(
287
- ` - ${k.padEnd(24)} ${item?.description || ""}${def}${isSec}`
288
- );
289
- }
290
- }
291
- return;
292
- }
293
-
294
- // Remote Package List
295
- const packages = remoteInfo.packages || remoteInfo.linkedPackages || [];
296
- if (Array.isArray(packages)) {
297
- console.log(`Remote ActionDock Packages (${packages.length}):\n`);
298
- for (const p of packages) {
299
- console.log(`* ${p.name || p.id} (${p.id}) v${p.version || "unknown"}`);
300
- if (p.path) console.log(` Path: ${p.path}`);
301
- if (p.description) console.log(` Desc: ${p.description}`);
302
- const actNames = Array.isArray(p.actions)
303
- ? p.actions.map((a: any) => (typeof a === "string" ? a : a.id)).join(", ")
304
- : "";
305
- const pbNames = Array.isArray(p.playbooks)
306
- ? p.playbooks.map((pb: any) => (typeof pb === "string" ? pb : pb.id)).join(", ")
307
- : "";
308
- console.log(` Actions (${p.actionsCount || (p.actions ? p.actions.length : 0)}): ${actNames || "(none)"}`);
309
- console.log(` Playbooks (${p.playbooksCount || (p.playbooks ? p.playbooks.length : 0)}): ${pbNames || "(none)"}`);
310
- console.log("");
311
- }
312
- console.log("Tip: Run 'ad info <package-id> --server <url>' to view detailed package configuration and schema.");
313
- return;
314
- }
315
-
316
- console.log(JSON.stringify(remoteInfo, null, 2));
317
- return;
318
- }
319
-
320
- // Local Tree
321
- if (options.tree) {
322
- const status = getRegistryStatus();
323
- if (isMachine) {
324
- renderResult(status, { json: options.json, envelope: options.envelope });
325
- } else {
326
- printRegistryTree(status);
327
- }
328
- return;
329
- }
330
-
331
- // 2. Explicit package option (-P, --package)
332
- if (options.package) {
333
- const directRoot = resolvePackageRoot(options.package);
334
- if (directRoot) {
335
- const detail = await getProjectDetailInfo(directRoot);
336
- if (isMachine) {
337
- renderResult(projectDetailToJson(detail), { json: options.json, envelope: options.envelope });
338
- } else {
339
- printProjectDetail(detail);
340
- }
341
- return;
342
- }
343
- throw new ArgumentError(
344
- `Package '${options.package}' not found in linked packages or path`
345
- );
346
- }
347
-
348
- // 3. Collect local candidate packages (current project + linked packages)
349
- const currentRoot = findProjectRoot();
350
- const linkedList = listLinkedPackages();
351
- const aggregated: AggregatedPackage[] = [];
352
- const seenPaths = new Set<string>();
353
-
354
- if (currentRoot) {
355
- try {
356
- const config = loadProjectConfig(currentRoot);
357
- const manifest = loadManifest(currentRoot);
358
- const manifestActionIds = manifest?.actions ? Object.keys(manifest.actions) : [];
359
- const playbooks = loadPlaybooks(currentRoot, config.playbooksDir);
360
- aggregated.push({
361
- id: config.id,
362
- name: config.name,
363
- version: config.version,
364
- description: config.description,
365
- path: currentRoot,
366
- actionsCount: manifestActionIds.length,
367
- playbooksCount: playbooks.size,
368
- actions: manifestActionIds,
369
- playbooks: Array.from(playbooks.keys()),
370
- configDeclared: config.config ? Object.keys(config.config) : [],
371
- });
372
- seenPaths.add(currentRoot);
373
- } catch {
374
- // Ignore broken project root
375
- }
376
- }
377
-
378
- for (const pkg of linkedList) {
379
- if (!existsSync(pkg.path)) continue;
380
- if (seenPaths.has(pkg.path)) continue;
381
- try {
382
- const config = loadProjectConfig(pkg.path);
383
- const manifest = loadManifest(pkg.path);
384
- const manifestActionIds = manifest?.actions ? Object.keys(manifest.actions) : [];
385
- const playbooks = loadPlaybooks(pkg.path, config.playbooksDir);
386
-
387
- aggregated.push({
388
- id: config.id,
389
- name: config.name,
390
- version: config.version,
391
- description: config.description,
392
- path: pkg.path,
393
- actionsCount: manifestActionIds.length,
394
- playbooksCount: playbooks.size,
395
- actions: manifestActionIds,
396
- playbooks: Array.from(playbooks.keys()),
397
- configDeclared: config.config ? Object.keys(config.config) : [],
398
- });
399
- seenPaths.add(pkg.path);
400
- } catch {
401
- // Ignore broken linked package
402
- }
403
- }
404
-
405
- // 4. Case: No pattern / intent specified
406
- if (!effectiveIntent) {
407
- if (currentRoot) {
408
- const detail = await getProjectDetailInfo(currentRoot);
409
- if (isMachine) {
410
- renderResult(projectDetailToJson(detail), { json: options.json, envelope: options.envelope });
411
- } else {
412
- printProjectDetail(detail);
413
- }
414
- return;
415
- }
416
-
417
- if (aggregated.length === 0) {
418
- if (isMachine) {
419
- renderResult({ linkedPackages: [] }, { json: options.json, envelope: options.envelope });
420
- return;
421
- }
422
- console.log(
423
- "No ActionDock project in current directory, and no packages linked."
424
- );
425
- console.log(
426
- "Run 'ad link' inside an Action package or specify a target package."
427
- );
428
- return;
429
- }
430
-
431
- if (isMachine) {
432
- renderResult({ linkedPackages: aggregated }, { json: options.json, envelope: options.envelope });
433
- } else {
434
- printAggregatedPackages(aggregated);
435
- }
436
- return;
437
- }
438
-
439
- // 5. Case: Pattern / intent specified
440
- // 5.1 Check if single positional argument directly matches an exact path or package ID/slug
441
- if (patterns.length === 1 && !options.intent) {
442
- const directRoot = resolvePackageRoot(patterns[0]);
443
- if (directRoot) {
444
- const detail = await getProjectDetailInfo(directRoot);
445
- if (isMachine) {
446
- renderResult(projectDetailToJson(detail), { json: options.json, envelope: options.envelope });
447
- } else {
448
- printProjectDetail(detail);
449
- }
450
- return;
451
- }
452
- }
453
-
454
- if (aggregated.length === 0) {
455
- if (isMachine) {
456
- renderResult({ linkedPackages: [], matchedCount: 0, isFallback: false }, { json: options.json, envelope: options.envelope });
457
- return;
458
- }
459
- throw new ExecutionError(
460
- `No ActionDock project or linked packages available to match '${effectiveIntent}'`
461
- );
462
- }
463
-
464
- // 5.2 Intent filtering across candidate packages
465
- const filterRes = filterWithFallbackInfo(
466
- aggregated,
467
- effectiveIntent,
468
- [
469
- (p) => p.id,
470
- (p) => p.name,
471
- (p) => p.description,
472
- (p) => p.path,
473
- (p) => p.actions,
474
- (p) => p.playbooks,
475
- ],
476
- shouldFallback
477
- );
478
-
479
- // Subcase A: 0 items matched
480
- if (filterRes.matchedCount === 0) {
481
- if (!filterRes.isFallback) {
482
- if (isMachine) {
483
- renderResult({ linkedPackages: [], matchedCount: 0, isFallback: false }, { json: options.json, envelope: options.envelope });
484
- return;
485
- }
486
- throw new ExecutionError(
487
- `No packages matched intent '${effectiveIntent}'`
488
- );
489
- }
490
-
491
- // Fallback enabled: display all packages with a notice
492
- if (isMachine) {
493
- renderResult(
494
- { linkedPackages: filterRes.items, isFallback: true, matchedCount: 0 },
495
- { json: options.json, envelope: options.envelope }
496
- );
497
- return;
498
- }
499
- console.log(
500
- `(No linked packages matched intent '${effectiveIntent}', showing all packages)\n`
501
- );
502
- printAggregatedPackages(filterRes.items);
503
- return;
504
- }
505
-
506
- // Subcase B: Matched items in machine mode
507
- if (isMachine) {
508
- renderResult(
509
- { linkedPackages: filterRes.items, matchedCount: filterRes.matchedCount, isFallback: false },
510
- { json: options.json, envelope: options.envelope }
511
- );
512
- return;
513
- }
514
-
515
- // Subcase C: Exactly 1 item matched in human terminal mode -> Display full project detail
516
- if (filterRes.matchedCount === 1) {
517
- const matchedPkg = filterRes.items[0];
518
- const detail = await getProjectDetailInfo(matchedPkg.path);
519
- printProjectDetail(detail);
520
- return;
521
- }
522
-
523
- // Subcase D: Multiple items matched in human terminal mode -> Display filtered summary list
524
- printAggregatedPackages(filterRes.items, {
525
- header: `ActionDock Linked Packages (${filterRes.matchedCount} matches for '${effectiveIntent}'):\n`,
526
- showTip: true,
527
- });
528
- } catch (err: any) {
529
- if (err instanceof CliError) {
530
- throw err;
531
- }
532
- throw new ExecutionError(err.message);
533
- }
534
- });
535
- }
536
-
537
-
@@ -1,24 +0,0 @@
1
- import { initProject } from "@actiondock/core";
2
- import { ExecutionError } from "@actiondock/runtime-cli";
3
- import { Command } from "commander";
4
-
5
- export function registerInitCommand(program: Command): void {
6
- program
7
- .command("init [directory]")
8
- .description("Initialize a new ActionDock project")
9
- .option("-i, --id <id>", "Project identifier")
10
- .option("-n, --name <name>", "Project display name")
11
- .option("-d, --desc <description>", "Project description")
12
- .action((dir = ".", options) => {
13
- try {
14
- initProject(dir, {
15
- id: options.id,
16
- name: options.name,
17
- description: options.desc,
18
- });
19
- console.log(`Successfully initialized ActionDock project in ${dir}`);
20
- } catch (err: any) {
21
- throw new ExecutionError(err.message);
22
- }
23
- });
24
- }
@@ -1,73 +0,0 @@
1
- import {
2
- linkPackage,
3
- pruneRegistry,
4
- unlinkPackage,
5
- } from "@actiondock/core";
6
- import { ExecutionError } from "@actiondock/runtime-cli";
7
- import { Command } from "commander";
8
-
9
- export function registerLinkCommands(program: Command): void {
10
- // ad link [path]
11
- program
12
- .command("link [path]")
13
- .option("-r, --recursive", "Recursively discover and link packages in subdirectories")
14
- .description("Link local Action package(s) or workspace directory into global registry for instant cross-directory execution")
15
- .action(async (targetPath, options) => {
16
- try {
17
- const result = linkPackage(targetPath || process.cwd(), undefined, {
18
- recursive: options.recursive,
19
- });
20
-
21
- if (result.isWorkspace) {
22
- console.log(`[OK] Linked workspace '${result.path}' (${result.entries.length} package${result.entries.length > 1 ? "s" : ""}):`);
23
- for (const e of result.entries) {
24
- console.log(` - ${e.id} (v${e.version}) -> ${e.path}`);
25
- }
26
- console.log("[INFO] Sub-packages added to this workspace will be automatically discovered.");
27
- } else {
28
- console.log(`[OK] Linked package '${result.id}' (v${result.version}) from ${result.path}`);
29
- }
30
- } catch (err: any) {
31
- throw new ExecutionError(err.message);
32
- }
33
- });
34
-
35
- // ad unlink [id|path]
36
- program
37
- .command("unlink [identifier]")
38
- .option("-p, --prune", "Automatically scan and prune any stale/missing paths from registry")
39
- .description("Unlink a package, workspace, or prune stale entries from global developer registry")
40
- .action((identifier, options) => {
41
- try {
42
- if (options.prune || identifier === "--prune") {
43
- const result = pruneRegistry();
44
- const totalPruned = result.prunedPackages.length + result.prunedWorkspaces.length;
45
- if (totalPruned === 0) {
46
- console.log("[OK] No stale registry entries found.");
47
- } else {
48
- console.log(`[OK] Pruned ${result.prunedWorkspaces.length} workspace(s) and ${result.prunedPackages.length} package(s) from registry.`);
49
- for (const ws of result.prunedWorkspaces) {
50
- console.log(` - [workspace] ${ws.path}`);
51
- }
52
- for (const pkg of result.prunedPackages) {
53
- console.log(` - [package] ${pkg.id} (${pkg.path})`);
54
- }
55
- }
56
- return;
57
- }
58
-
59
- const removed = unlinkPackage(identifier || process.cwd());
60
- if (removed) {
61
- if (removed.type === "workspace") {
62
- console.log(`[OK] Unlinked workspace '${removed.path}' (${removed.packagesCount || 0} package${(removed.packagesCount || 0) > 1 ? "s" : ""} unlinked)`);
63
- } else {
64
- console.log(`[OK] Unlinked package '${removed.id}' (${removed.path})`);
65
- }
66
- } else {
67
- console.log(`Package or workspace '${identifier || process.cwd()}' was not linked in registry`);
68
- }
69
- } catch (err: any) {
70
- throw new ExecutionError(err.message);
71
- }
72
- });
73
- }