@actiondock/cli 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 (123) hide show
  1. package/README.md +41 -21
  2. package/bin/ad.js +29 -31
  3. package/dist/commands/action/create.d.ts +10 -0
  4. package/dist/commands/action/create.js +120 -0
  5. package/dist/commands/add.d.ts +7 -0
  6. package/dist/commands/add.js +144 -0
  7. package/dist/commands/build.d.ts +3 -0
  8. package/dist/commands/build.js +83 -0
  9. package/dist/commands/config/delete.d.ts +9 -0
  10. package/dist/commands/config/delete.js +52 -0
  11. package/dist/commands/config/env.d.ts +9 -0
  12. package/dist/commands/config/env.js +85 -0
  13. package/dist/commands/config/get.d.ts +9 -0
  14. package/dist/commands/config/get.js +120 -0
  15. package/dist/commands/config/index.d.ts +9 -0
  16. package/dist/commands/config/index.js +23 -0
  17. package/dist/commands/config/list.d.ts +9 -0
  18. package/dist/commands/config/list.js +137 -0
  19. package/dist/commands/config/schema.d.ts +9 -0
  20. package/dist/commands/config/schema.js +121 -0
  21. package/dist/commands/config/set.d.ts +9 -0
  22. package/dist/commands/config/set.js +85 -0
  23. package/dist/commands/describe.d.ts +9 -0
  24. package/dist/commands/describe.js +68 -0
  25. package/dist/commands/doctor.d.ts +3 -0
  26. package/dist/commands/doctor.js +82 -0
  27. package/dist/commands/export.d.ts +3 -0
  28. package/dist/commands/export.js +212 -0
  29. package/dist/commands/generate.d.ts +9 -0
  30. package/dist/commands/generate.js +44 -0
  31. package/dist/commands/index.d.ts +32 -0
  32. package/dist/commands/index.js +111 -0
  33. package/dist/commands/info.d.ts +9 -0
  34. package/dist/commands/info.js +210 -0
  35. package/dist/commands/init.d.ts +2 -0
  36. package/dist/commands/init.js +24 -0
  37. package/dist/commands/link.d.ts +2 -0
  38. package/dist/commands/link.js +71 -0
  39. package/dist/commands/list.d.ts +9 -0
  40. package/dist/commands/list.js +77 -0
  41. package/dist/commands/mcp.d.ts +9 -0
  42. package/dist/commands/mcp.js +172 -0
  43. package/dist/commands/new.d.ts +9 -0
  44. package/dist/commands/new.js +33 -0
  45. package/dist/commands/pack.d.ts +3 -0
  46. package/dist/commands/pack.js +63 -0
  47. package/dist/commands/playbook.d.ts +10 -0
  48. package/dist/commands/playbook.js +433 -0
  49. package/dist/commands/profile.d.ts +2 -0
  50. package/dist/commands/profile.js +253 -0
  51. package/dist/commands/remove.d.ts +7 -0
  52. package/dist/commands/remove.js +118 -0
  53. package/dist/commands/run.d.ts +13 -0
  54. package/dist/commands/run.js +146 -0
  55. package/dist/commands/runs.d.ts +9 -0
  56. package/dist/commands/runs.js +245 -0
  57. package/dist/commands/serve.d.ts +9 -0
  58. package/dist/commands/serve.js +129 -0
  59. package/dist/commands/state-list.d.ts +24 -0
  60. package/dist/commands/state-list.js +82 -0
  61. package/dist/commands/state.d.ts +9 -0
  62. package/dist/commands/state.js +310 -0
  63. package/dist/commands/test.d.ts +2 -0
  64. package/dist/commands/test.js +44 -0
  65. package/dist/commands/validate.d.ts +9 -0
  66. package/dist/commands/validate.js +106 -0
  67. package/dist/errors.d.ts +43 -0
  68. package/dist/errors.js +103 -0
  69. package/dist/index.d.ts +15 -0
  70. package/dist/index.js +47 -65969
  71. package/dist/prompt.d.ts +53 -0
  72. package/dist/prompt.js +192 -0
  73. package/dist/renderer.d.ts +197 -0
  74. package/dist/renderer.js +502 -0
  75. package/dist/services/index.d.ts +1 -0
  76. package/dist/services/index.js +1 -0
  77. package/dist/services/inspect.d.ts +23 -0
  78. package/dist/services/inspect.js +96 -0
  79. package/dist/standalone/index.d.ts +16 -0
  80. package/dist/standalone/index.js +22 -0
  81. package/dist/types.d.ts +132 -0
  82. package/dist/types.js +9 -0
  83. package/dist/utils/bytes.d.ts +1 -0
  84. package/dist/utils/bytes.js +1 -0
  85. package/dist/utils/duration.js +1 -0
  86. package/dist/utils/filter.d.ts +1 -0
  87. package/dist/utils/filter.js +1 -0
  88. package/dist/utils/index.d.ts +6 -0
  89. package/dist/utils/index.js +6 -0
  90. package/dist/utils/spawn.d.ts +27 -0
  91. package/dist/utils/spawn.js +35 -0
  92. package/dist/utils/target.d.ts +45 -0
  93. package/dist/utils/target.js +78 -0
  94. package/dist/utils/utils.d.ts +38 -0
  95. package/dist/utils/utils.js +111 -0
  96. package/package.json +20 -17
  97. package/src/commands/action/create.ts +0 -129
  98. package/src/commands/action/index.ts +0 -27
  99. package/src/commands/action/list.ts +0 -247
  100. package/src/commands/action/run.ts +0 -197
  101. package/src/commands/action/show.ts +0 -128
  102. package/src/commands/action/sync.ts +0 -76
  103. package/src/commands/action/validate.ts +0 -102
  104. package/src/commands/action.ts +0 -1
  105. package/src/commands/build.ts +0 -56
  106. package/src/commands/config.ts +0 -583
  107. package/src/commands/doctor.ts +0 -84
  108. package/src/commands/export.ts +0 -196
  109. package/src/commands/index.ts +0 -75
  110. package/src/commands/info.ts +0 -537
  111. package/src/commands/init.ts +0 -24
  112. package/src/commands/link.ts +0 -73
  113. package/src/commands/mcp.ts +0 -195
  114. package/src/commands/playbook.ts +0 -566
  115. package/src/commands/profile.ts +0 -296
  116. package/src/commands/runs.ts +0 -452
  117. package/src/commands/serve.ts +0 -132
  118. package/src/commands/state.ts +0 -590
  119. package/src/commands/test.ts +0 -48
  120. package/src/index.ts +0 -58
  121. package/src/utils/bytes.ts +0 -2
  122. package/src/utils/filter.ts +0 -2
  123. /package/{src/utils/duration.ts → dist/utils/duration.d.ts} +0 -0
@@ -1,452 +0,0 @@
1
- import { existsSync } from "node:fs";
2
- import {
3
- cancelRemoteRun,
4
- clearRemoteRuns,
5
- createStorage,
6
- fetchRemoteRun,
7
- fetchRemoteRuns,
8
- filterWithFallbackInfo,
9
- findProjectRoot,
10
- listLinkedPackages,
11
- loadProjectConfig,
12
- resolvePackageRoot,
13
- resolveTarget,
14
- } from "@actiondock/core";
15
- import { Command } from "commander";
16
- import { ArgumentError, CliError, ExecutionError, getEffectiveOptions, renderResult } from "@actiondock/runtime-cli";
17
- import { resolveIntent } from "../utils/filter";
18
-
19
- function printRunRecord(run: any): void {
20
- console.log(`Run: ${run.id}`);
21
- console.log(`Action: ${run.actionId}`);
22
- if (run.packageId) console.log(`Package: ${run.packageId}`);
23
- console.log(`Status: ${run.status}`);
24
- if (run.parentRunId) console.log(`Parent Run: ${run.parentRunId}`);
25
- console.log(`Started: ${run.startedAt}`);
26
- if (run.finishedAt) console.log(`Finished: ${run.finishedAt}`);
27
-
28
- console.log("\nInput:");
29
- console.log(JSON.stringify(run.input, null, 2));
30
-
31
- if (run.output !== undefined) {
32
- console.log("\nOutput:");
33
- console.log(JSON.stringify(run.output, null, 2));
34
- }
35
-
36
- if (run.error) {
37
- console.log("\nError:");
38
- console.log(JSON.stringify(run.error, null, 2));
39
- }
40
- }
41
-
42
- export function registerRunsCommands(program: Command): void {
43
- const runsCmd = program
44
- .command("runs")
45
- .description("Inspect action execution history");
46
-
47
- // runs list
48
- runsCmd
49
- .command("list [patterns...]")
50
- .description("List recent execution records (in current project or linked packages)")
51
- .option("-P, --package <id>", "Target package ID or path")
52
- .option("-i, --intent <pattern>", "Regex or fuzzy intent filter; falls back to full list when no match")
53
- .option("-a, --action <actionId>", "Filter by action ID")
54
- .option("-n, --limit <count>", "Maximum number of records to return", "20")
55
- .option("-p, --profile <name>", "Query against a specific profile")
56
- .option("-s, --server <url>", "Remote server URL")
57
- .option("-t, --token <token>", "Auth token for remote server")
58
- .option("--no-fallback", "Disable fallback to full list when no items match intent")
59
- .option("--data-dir <path>", "Custom database storage directory")
60
- .option("--json", "Output as JSON")
61
- .option("--envelope", "Wrap JSON output in standard envelope")
62
- .action(async (patterns, rawOptions, cmd) => {
63
- try {
64
- const options = getEffectiveOptions(rawOptions, cmd);
65
- const effectiveIntent = resolveIntent(options.intent, patterns);
66
- const shouldFallback = options.fallback !== false;
67
- const limit = Number.parseInt(options.limit, 10) || 20;
68
- const isMachine = Boolean(options.json || options.envelope);
69
-
70
- const target = resolveTarget({
71
- profile: options.profile,
72
- server: options.server,
73
- token: options.token,
74
- });
75
-
76
- if (target.type === "remote") {
77
- const res = await fetchRemoteRuns(target.serverUrl!, target.token, {
78
- packageId: options.package,
79
- actionId: options.action,
80
- intent: effectiveIntent,
81
- limit,
82
- });
83
- if (isMachine) {
84
- renderResult(res.items, { json: options.json, envelope: options.envelope });
85
- return;
86
- }
87
- console.log(
88
- `Execution Runs on remote server ${target.serverUrl}${target.profileName ? ` (Profile: ${target.profileName})` : ""} (${res.items.length}):\n`
89
- );
90
- console.log(
91
- ` ${"RUN ID".padEnd(38)} ${"ACTION".padEnd(24)} ${"STATUS".padEnd(10)} ${"STARTED"}`
92
- );
93
- console.log(" " + "-".repeat(90));
94
- for (const r of res.items) {
95
- const time = (r.startedAt || "").replace("T", " ").slice(0, 19);
96
- console.log(
97
- ` ${r.id.padEnd(38)} ${r.actionId.padEnd(24)} ${r.status.padEnd(10)} ${time}`
98
- );
99
- }
100
- return;
101
- }
102
-
103
- const targetRoot = options.package
104
- ? resolvePackageRoot(options.package)
105
- : findProjectRoot();
106
-
107
- if (options.package && !targetRoot) {
108
- throw new ArgumentError(`Package '${options.package}' not found in linked packages or path`);
109
- }
110
-
111
- if (targetRoot) {
112
- const projConfig = loadProjectConfig(targetRoot);
113
- const storage = createStorage(projConfig.id, {
114
- projectRoot: targetRoot,
115
- dataDir: options.dataDir,
116
- });
117
- const records = storage.listRuns({
118
- actionId: options.action,
119
- limit,
120
- });
121
- storage.close();
122
-
123
- const filterRes = filterWithFallbackInfo(
124
- records,
125
- effectiveIntent,
126
- [(r) => r.id, (r) => r.actionId, (r) => r.status, (r) => r.error?.message],
127
- shouldFallback
128
- );
129
-
130
- if (isMachine) {
131
- renderResult(filterRes.items, { json: options.json, envelope: options.envelope });
132
- } else {
133
- console.log(`Execution Runs in ${projConfig.id} (${filterRes.items.length}):\n`);
134
- if (filterRes.isFallback && effectiveIntent) {
135
- console.log(`(No runs matched intent '${effectiveIntent}', showing all runs)\n`);
136
- }
137
- console.log(
138
- ` ${"RUN ID".padEnd(38)} ${"ACTION".padEnd(24)} ${"STATUS".padEnd(10)} ${"STARTED"}`
139
- );
140
- console.log(" " + "-".repeat(90));
141
- for (const r of filterRes.items) {
142
- const time = r.startedAt.replace("T", " ").slice(0, 19);
143
- console.log(
144
- ` ${r.id.padEnd(38)} ${r.actionId.padEnd(24)} ${r.status.padEnd(10)} ${time}`
145
- );
146
- }
147
- }
148
- return;
149
- }
150
-
151
- // Outside project: List recent runs across all linked packages
152
- const linkedList = listLinkedPackages();
153
- if (linkedList.length === 0) {
154
- if (isMachine) {
155
- renderResult([], { json: options.json, envelope: options.envelope });
156
- return;
157
- }
158
- console.log("No ActionDock project in current directory, and no packages linked.");
159
- console.log("Run 'ad link' inside an Action package to register it.");
160
- return;
161
- }
162
-
163
- let allRecords: any[] = [];
164
- for (const pkg of linkedList) {
165
- if (!existsSync(pkg.path)) continue;
166
- try {
167
- const projConfig = loadProjectConfig(pkg.path);
168
- const storage = createStorage(projConfig.id, {
169
- projectRoot: pkg.path,
170
- dataDir: options.dataDir,
171
- });
172
- const records = storage.listRuns({
173
- actionId: options.action,
174
- limit,
175
- });
176
- storage.close();
177
- for (const r of records) {
178
- allRecords.push({ ...r, packageId: projConfig.id });
179
- }
180
- } catch {
181
- // Ignore broken linked package
182
- }
183
- }
184
-
185
- // Sort by startedAt desc
186
- allRecords.sort((a, b) => (b.startedAt > a.startedAt ? 1 : b.startedAt < a.startedAt ? -1 : 0));
187
- allRecords = allRecords.slice(0, limit);
188
-
189
- const filterRes = filterWithFallbackInfo(
190
- allRecords,
191
- effectiveIntent,
192
- [(r) => r.id, (r) => r.actionId, (r) => r.packageId, (r) => r.status, (r) => r.error?.message],
193
- shouldFallback
194
- );
195
-
196
- if (isMachine) {
197
- renderResult(filterRes.items, { json: options.json, envelope: options.envelope });
198
- } else {
199
- console.log(`Execution Runs across Linked Packages (${filterRes.items.length}):\n`);
200
- if (filterRes.isFallback && effectiveIntent) {
201
- console.log(`(No runs matched intent '${effectiveIntent}', showing all runs)\n`);
202
- }
203
- console.log(
204
- ` ${"RUN ID".padEnd(38)} ${"PACKAGE".padEnd(20)} ${"ACTION".padEnd(22)} ${"STATUS".padEnd(10)} ${"STARTED"}`
205
- );
206
- console.log(" " + "-".repeat(110));
207
- for (const r of filterRes.items) {
208
- const time = r.startedAt.replace("T", " ").slice(0, 19);
209
- console.log(
210
- ` ${r.id.padEnd(38)} ${(r.packageId || "").padEnd(20)} ${r.actionId.padEnd(22)} ${r.status.padEnd(10)} ${time}`
211
- );
212
- }
213
- }
214
- } catch (err: any) {
215
- if (err instanceof CliError) {
216
- throw err;
217
- }
218
- throw new ExecutionError(err.message);
219
- }
220
- });
221
-
222
- // runs show
223
- runsCmd
224
- .command("show <id>")
225
- .description("Show details of a specific execution run (searches current project or linked packages)")
226
- .option("-P, --package <id>", "Target package ID or path")
227
- .option("-p, --profile <name>", "Query run against a specific profile")
228
- .option("-s, --server <url>", "Remote server URL")
229
- .option("-t, --token <token>", "Auth token for remote server")
230
- .option("--data-dir <path>", "Custom database storage directory")
231
- .option("--json", "Output as JSON")
232
- .option("--envelope", "Wrap JSON output in standard envelope")
233
- .action(async (id, rawOptions, cmd) => {
234
- let target;
235
- const options = getEffectiveOptions(rawOptions, cmd);
236
- const isMachine = Boolean(options.json || options.envelope);
237
- try {
238
- target = resolveTarget({
239
- profile: options.profile,
240
- server: options.server,
241
- token: options.token,
242
- });
243
- } catch (err: any) {
244
- throw new ExecutionError(err.message);
245
- }
246
-
247
- if (target.type === "remote") {
248
- try {
249
- const run = await fetchRemoteRun(target.serverUrl!, id, target.token);
250
- if (isMachine) {
251
- renderResult(run, { json: options.json, envelope: options.envelope });
252
- } else {
253
- printRunRecord(run);
254
- }
255
- return;
256
- } catch (err: any) {
257
- throw new ExecutionError(err.message);
258
- }
259
- }
260
-
261
- // Local show
262
- try {
263
- let foundRun: any = null;
264
-
265
- if (options.package) {
266
- const targetRoot = resolvePackageRoot(options.package);
267
- if (!targetRoot) {
268
- throw new ArgumentError(`Package '${options.package}' not found in linked packages or path`);
269
- }
270
- const projConfig = loadProjectConfig(targetRoot);
271
- const storage = createStorage(projConfig.id, {
272
- projectRoot: targetRoot,
273
- dataDir: options.dataDir,
274
- });
275
- foundRun = storage.getRun(id);
276
- storage.close();
277
- if (foundRun && !foundRun.packageId) {
278
- foundRun.packageId = projConfig.id;
279
- }
280
- } else {
281
- // 1. Try current project root if exists
282
- const currentRoot = findProjectRoot();
283
- if (currentRoot) {
284
- try {
285
- const projConfig = loadProjectConfig(currentRoot);
286
- const storage = createStorage(projConfig.id, {
287
- projectRoot: currentRoot,
288
- dataDir: options.dataDir,
289
- });
290
- foundRun = storage.getRun(id);
291
- storage.close();
292
- if (foundRun && !foundRun.packageId) {
293
- foundRun.packageId = projConfig.id;
294
- }
295
- } catch {}
296
- }
297
-
298
- // 2. If not found in current project, search across all linked packages
299
- if (!foundRun) {
300
- const linkedList = listLinkedPackages();
301
- for (const pkg of linkedList) {
302
- if (!existsSync(pkg.path)) continue;
303
- try {
304
- const projConfig = loadProjectConfig(pkg.path);
305
- const storage = createStorage(projConfig.id, {
306
- projectRoot: pkg.path,
307
- dataDir: options.dataDir,
308
- });
309
- const r = storage.getRun(id);
310
- storage.close();
311
- if (r) {
312
- foundRun = { ...r, packageId: projConfig.id };
313
- break;
314
- }
315
- } catch {}
316
- }
317
- }
318
- }
319
-
320
- if (!foundRun) {
321
- throw new ExecutionError(`Run record '${id}' not found in current project or any linked packages`);
322
- }
323
-
324
- if (isMachine) {
325
- renderResult(foundRun, { json: options.json, envelope: options.envelope });
326
- } else {
327
- printRunRecord(foundRun);
328
- }
329
- } catch (err: any) {
330
- if (err instanceof CliError) {
331
- throw err;
332
- }
333
- throw new ExecutionError(err.message);
334
- }
335
- });
336
-
337
- // runs cancel
338
- runsCmd
339
- .command("cancel <id>")
340
- .description("Cancel a running action execution on a remote server")
341
- .option("-p, --profile <name>", "Execute cancel against a specific profile")
342
- .option("-s, --server <url>", "Remote server URL")
343
- .option("-t, --token <token>", "Auth token for remote server")
344
- .option("-r, --reason <reason>", "Reason for cancellation")
345
- .option("--json", "Output as JSON")
346
- .option("--envelope", "Wrap JSON output in standard envelope")
347
- .action(async (id, rawOptions, cmd) => {
348
- let target;
349
- const options = getEffectiveOptions(rawOptions, cmd);
350
- const isMachine = Boolean(options.json || options.envelope);
351
- try {
352
- target = resolveTarget({
353
- profile: options.profile,
354
- server: options.server,
355
- token: options.token,
356
- });
357
- } catch (err: any) {
358
- throw new ExecutionError(err.message);
359
- }
360
-
361
- if (target.type === "local") {
362
- throw new ArgumentError(
363
- "'ad runs cancel' is only supported for remote execution targets. Use --profile <name> or --server <url>."
364
- );
365
- }
366
-
367
- try {
368
- const result = await cancelRemoteRun(
369
- target.serverUrl!,
370
- id,
371
- target.token,
372
- options.reason
373
- );
374
- if (isMachine) {
375
- renderResult(result, { json: options.json, envelope: options.envelope });
376
- } else {
377
- console.log(`Run '${id}' cancellation requested (Status: ${result.status}).`);
378
- }
379
- } catch (err: any) {
380
- throw new ExecutionError(err.message);
381
- }
382
- });
383
-
384
- // runs clear
385
- runsCmd
386
- .command("clear")
387
- .description("Clear execution run records (locally or on remote server)")
388
- .option("-P, --package <id>", "Target package ID or path")
389
- .option("-a, --action <actionId>", "Filter by action ID")
390
- .option("-p, --profile <name>", "Target profile")
391
- .option("-s, --server <url>", "Remote server URL")
392
- .option("-t, --token <token>", "Auth token for remote server")
393
- .option("--data-dir <path>", "Custom database storage directory")
394
- .option("--json", "Output as JSON")
395
- .option("--envelope", "Wrap JSON output in standard envelope")
396
- .action(async (rawOptions, cmd) => {
397
- try {
398
- const options = getEffectiveOptions(rawOptions, cmd);
399
- const isMachine = Boolean(options.json || options.envelope);
400
- const target = resolveTarget({
401
- profile: options.profile,
402
- server: options.server,
403
- token: options.token,
404
- });
405
-
406
- if (target.type === "remote") {
407
- const res = await clearRemoteRuns(target.serverUrl!, target.token, {
408
- packageId: options.package,
409
- actionId: options.action,
410
- });
411
- if (isMachine) {
412
- renderResult(res, { json: options.json, envelope: options.envelope });
413
- } else {
414
- console.log(`Cleared ${res.clearedCount} execution run(s) on remote server.`);
415
- }
416
- return;
417
- }
418
-
419
- const targetRoot = options.package
420
- ? resolvePackageRoot(options.package)
421
- : findProjectRoot();
422
-
423
- if (options.package && !targetRoot) {
424
- throw new ArgumentError(`Package '${options.package}' not found in linked packages or path`);
425
- }
426
-
427
- if (targetRoot) {
428
- const projConfig = loadProjectConfig(targetRoot);
429
- const storage = createStorage(projConfig.id, {
430
- projectRoot: targetRoot,
431
- dataDir: options.dataDir,
432
- });
433
- const count = storage.clearRuns({ actionId: options.action });
434
- storage.close();
435
- if (isMachine) {
436
- renderResult({ ok: true, clearedCount: count, packageId: projConfig.id }, { json: options.json, envelope: options.envelope });
437
- } else {
438
- console.log(`Cleared ${count} execution run(s) in package '${projConfig.id}'.`);
439
- }
440
- return;
441
- }
442
-
443
- throw new ArgumentError(
444
- "Not in an ActionDock project. Please specify -P, --package <id> or cd into a project directory."
445
- );
446
- } catch (err: any) {
447
- if (err instanceof CliError) throw err;
448
- throw new ExecutionError(err.message);
449
- }
450
- });
451
- }
452
-
@@ -1,132 +0,0 @@
1
- import { resolve } from "node:path";
2
- import { findProjectRoot, loadProjectConfig, startActionDockServer } from "@actiondock/core";
3
- import { createActionDockMcpServer } from "@actiondock/mcp";
4
- import { createMcpHandler } from "@modelcontextprotocol/server";
5
- import { Command } from "commander";
6
- import { ExecutionError } from "@actiondock/runtime-cli";
7
- import { parseByteSize } from "../utils/bytes";
8
-
9
- export function registerServeCommand(program: Command): void {
10
- program
11
- .command("serve")
12
- .description("Start the ActionDock lightweight HTTP Runner server for remote execution")
13
- .option("-p, --port <port>", "Port to listen on (default: 5177)", "5177")
14
- .option("-H, --host <host>", "Host address to bind to (default: 127.0.0.1)", "127.0.0.1")
15
- .option("-t, --token <token>", "Authentication token for securing the endpoint (or set ACTIONDOCK_TOKEN)")
16
- .option("--allow-insecure-no-auth", "Allow non-loopback host binding without authentication token (INSECURE)")
17
- .option(
18
- "--cors-origin <origin>",
19
- "Allowed CORS origin (can be specified multiple times)",
20
- (val: string, prev: string[] = []) => [...prev, val],
21
- []
22
- )
23
- .option("--max-body <size>", "Maximum allowed JSON request body size (e.g. 1mb, 500kb)", "1mb")
24
- .option("--expose-debug-info", "Expose project root path in health and info responses")
25
- .option("--no-mcp", "Disable unified MCP protocol endpoint at /mcp")
26
- .option("-d, --dir <path>", "Project root directory (default: current working directory)")
27
- .action(async (options) => {
28
- const port = parseInt(options.port, 10) || 5177;
29
- const host = options.host || "127.0.0.1";
30
- const token = options.token || process.env.ACTIONDOCK_TOKEN;
31
- const allowInsecureNoAuth = Boolean(options.allowInsecureNoAuth);
32
- const corsOrigins = options.corsOrigin && options.corsOrigin.length > 0 ? options.corsOrigin : undefined;
33
- const exposeDebugInfo = Boolean(options.exposeDebugInfo);
34
-
35
- let maxBodyBytes: number | undefined;
36
- if (options.maxBody) {
37
- try {
38
- maxBodyBytes = parseByteSize(options.maxBody);
39
- } catch (err: any) {
40
- throw new ExecutionError(err.message);
41
- }
42
- }
43
-
44
- const projectRoot = options.dir
45
- ? resolve(options.dir)
46
- : findProjectRoot(process.cwd());
47
-
48
- let projectName = "Global Registry Mode";
49
- let packageId = "none";
50
-
51
- if (projectRoot) {
52
- try {
53
- const config = loadProjectConfig(projectRoot);
54
- projectName = `${config.name} (${config.id})`;
55
- packageId = config.id;
56
- } catch {
57
- // fallback
58
- }
59
- }
60
-
61
- let mcpHandler: ((req: Request) => Promise<Response | null | undefined>) | undefined;
62
- const enableMcp = options.mcp !== false;
63
- if (enableMcp) {
64
- try {
65
- const handler = createMcpHandler(
66
- () => {
67
- return createActionDockMcpServer({
68
- projectRoot: projectRoot || undefined,
69
- });
70
- },
71
- {
72
- onerror: (err) => {
73
- process.stderr.write(`[MCP HTTP Error] ${err?.message || String(err)}\n`);
74
- },
75
- }
76
- );
77
- mcpHandler = async (req: Request) => {
78
- return handler.fetch(req);
79
- };
80
- } catch {
81
- // 忽略 MCP 初始化异常
82
- }
83
- }
84
-
85
- try {
86
- const server = startActionDockServer({
87
- port,
88
- host,
89
- token,
90
- allowInsecureNoAuth,
91
- corsOrigins,
92
- maxBodyBytes,
93
- exposeDebugInfo,
94
- enableMcp,
95
- mcpHandler,
96
- projectRoot: projectRoot || undefined,
97
- });
98
-
99
- console.log(`\n======================================================`);
100
- console.log(` ActionDock 2.0 HTTP Runner Server`);
101
- console.log(`======================================================`);
102
- console.log(` * Listening on: http://${host}:${server.port}`);
103
- console.log(` * Project: ${projectName}`);
104
- if (projectRoot && exposeDebugInfo) {
105
- console.log(` * Root Path: ${projectRoot}`);
106
- }
107
- console.log(` * Authentication: ${token ? "Bearer Token / Query Token Enabled" : "Disabled (Public/Local)"}`);
108
- console.log(` * CORS Origins: ${corsOrigins ? corsOrigins.join(", ") : "Disabled (Default)"}`);
109
- console.log(` * Max Body Size: ${options.maxBody || "1mb"}`);
110
- console.log(` * Health Endpoint: http://${host === "0.0.0.0" ? "127.0.0.1" : host}:${server.port}/api/v1/health`);
111
- if (enableMcp) {
112
- console.log(` * MCP Endpoint: http://${host === "0.0.0.0" ? "127.0.0.1" : host}:${server.port}/mcp`);
113
- }
114
- console.log(`======================================================\n`);
115
- console.log(`Server is ready to accept remote 'ad run' requests.`);
116
- console.log(`Press Ctrl+C to terminate.\n`);
117
-
118
- // Keep process running
119
- process.on("SIGINT", () => {
120
- console.log("\nStopping ActionDock server...");
121
- server.stop();
122
- process.exit(0);
123
- });
124
- process.on("SIGTERM", () => {
125
- server.stop();
126
- process.exit(0);
127
- });
128
- } catch (err: any) {
129
- throw new ExecutionError(`Failed to start ActionDock server: ${err.message}`);
130
- }
131
- });
132
- }