@actiondock/cli 2.0.9 → 2.0.10
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/README.md +17 -0
- package/dist/index.js +45803 -35787
- package/package.json +7 -7
- package/src/commands/action/create.ts +129 -0
- package/src/commands/action/index.ts +27 -0
- package/src/commands/action/list.ts +247 -0
- package/src/commands/action/run.ts +197 -0
- package/src/commands/action/show.ts +128 -0
- package/src/commands/action/sync.ts +76 -0
- package/src/commands/action/validate.ts +102 -0
- package/src/commands/action.ts +1 -633
- package/src/commands/build.ts +19 -17
- package/src/commands/config.ts +168 -69
- package/src/commands/doctor.ts +44 -42
- package/src/commands/export.ts +9 -9
- package/src/commands/index.ts +33 -1
- package/src/commands/info.ts +93 -74
- package/src/commands/init.ts +2 -2
- package/src/commands/link.ts +3 -4
- package/src/commands/mcp.ts +6 -10
- package/src/commands/playbook.ts +289 -161
- package/src/commands/profile.ts +125 -92
- package/src/commands/runs.ts +92 -49
- package/src/commands/serve.ts +3 -4
- package/src/commands/state.ts +145 -64
- package/src/commands/test.ts +1 -1
- package/src/index.ts +39 -3
- package/src/utils/bytes.ts +1 -35
- package/src/utils/filter.ts +2 -24
package/src/commands/profile.ts
CHANGED
|
@@ -13,6 +13,13 @@ import {
|
|
|
13
13
|
useProfile,
|
|
14
14
|
} from "@actiondock/core";
|
|
15
15
|
import { Command } from "commander";
|
|
16
|
+
import {
|
|
17
|
+
ArgumentError,
|
|
18
|
+
CliError,
|
|
19
|
+
ExecutionError,
|
|
20
|
+
getEffectiveOptions,
|
|
21
|
+
renderResult,
|
|
22
|
+
} from "@actiondock/runtime-cli";
|
|
16
23
|
import { resolveIntent } from "../utils/filter";
|
|
17
24
|
|
|
18
25
|
export function registerProfileCommands(program: Command): void {
|
|
@@ -26,12 +33,17 @@ export function registerProfileCommands(program: Command): void {
|
|
|
26
33
|
.description("List all configured profiles")
|
|
27
34
|
.option("-i, --intent <pattern>", "Regex or fuzzy intent filter; falls back to full list when no match")
|
|
28
35
|
.option("--reveal, --show-secrets", "Reveal plain text values for tokens")
|
|
36
|
+
.option("--fallback", "Enable fallback to full list when no items match intent")
|
|
29
37
|
.option("--no-fallback", "Disable fallback to full list when no items match intent")
|
|
30
38
|
.option("--json", "Output as JSON")
|
|
31
|
-
.
|
|
39
|
+
.option("--envelope", "Wrap JSON output in standard envelope")
|
|
40
|
+
.action((patterns, rawOptions, cmd) => {
|
|
32
41
|
try {
|
|
42
|
+
const options = getEffectiveOptions(rawOptions, cmd);
|
|
33
43
|
const effectiveIntent = resolveIntent(options.intent, patterns);
|
|
34
|
-
const
|
|
44
|
+
const isMachine = Boolean(options.json || options.envelope);
|
|
45
|
+
const fallbackExplicit = options.fallback === true || (Array.isArray(process.argv) && process.argv.includes("--fallback"));
|
|
46
|
+
const shouldFallback = isMachine ? fallbackExplicit : options.fallback !== false;
|
|
35
47
|
const reveal = Boolean(options.reveal || options.showSecrets);
|
|
36
48
|
|
|
37
49
|
const list = listProfiles();
|
|
@@ -56,42 +68,53 @@ export function registerProfileCommands(program: Command): void {
|
|
|
56
68
|
shouldFallback
|
|
57
69
|
);
|
|
58
70
|
|
|
59
|
-
if (
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
for (const item of filterRes.items) {
|
|
67
|
-
const currentMarker = item.isCurrent ? "* " : " ";
|
|
68
|
-
let tokenInfo = "";
|
|
69
|
-
if (item.tokenSource === "tokenEnv") {
|
|
70
|
-
tokenInfo = ` [token: env(${item.tokenEnv})]`;
|
|
71
|
-
} else if (item.tokenSource === "profileEnv") {
|
|
72
|
-
tokenInfo = ` [token: env(ACTIONDOCK_${toSnakeUpperCase(item.name)}_TOKEN)]`;
|
|
73
|
-
} else if (item.tokenSource === "profile") {
|
|
74
|
-
tokenInfo = ` [token: stored in profile]`;
|
|
75
|
-
} else if (item.tokenSource === "globalEnv") {
|
|
76
|
-
tokenInfo = ` [token: env(ACTIONDOCK_TOKEN)]`;
|
|
77
|
-
}
|
|
71
|
+
if (filterRes.isFallback && isMachine) {
|
|
72
|
+
renderResult(
|
|
73
|
+
{ items: filterRes.items, isFallback: true, matchedCount: 0 },
|
|
74
|
+
{ json: options.json, envelope: options.envelope }
|
|
75
|
+
);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
78
|
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
renderResult(filterRes.items, {
|
|
80
|
+
json: options.json,
|
|
81
|
+
envelope: options.envelope,
|
|
82
|
+
humanFormatter: () => {
|
|
83
|
+
const lines = ["ActionDock Execution Profiles:\n"];
|
|
84
|
+
if (filterRes.isFallback && effectiveIntent) {
|
|
85
|
+
lines.push(`(No profiles matched intent '${effectiveIntent}', showing all profiles)\n`);
|
|
81
86
|
}
|
|
87
|
+
for (const item of filterRes.items) {
|
|
88
|
+
const currentMarker = item.isCurrent ? "* " : " ";
|
|
89
|
+
let tokenInfo = "";
|
|
90
|
+
if (item.tokenSource === "tokenEnv") {
|
|
91
|
+
tokenInfo = ` [token: env(${item.tokenEnv})]`;
|
|
92
|
+
} else if (item.tokenSource === "profileEnv") {
|
|
93
|
+
tokenInfo = ` [token: env(ACTIONDOCK_${toSnakeUpperCase(item.name)}_TOKEN)]`;
|
|
94
|
+
} else if (item.tokenSource === "profile") {
|
|
95
|
+
tokenInfo = ` [token: stored in profile]`;
|
|
96
|
+
} else if (item.tokenSource === "globalEnv") {
|
|
97
|
+
tokenInfo = ` [token: env(ACTIONDOCK_TOKEN)]`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (reveal && item.token) {
|
|
101
|
+
tokenInfo += ` = ${item.token}`;
|
|
102
|
+
}
|
|
82
103
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
104
|
+
const desc = item.description ? ` - ${item.description}` : "";
|
|
105
|
+
lines.push(
|
|
106
|
+
`${currentMarker}${item.name.padEnd(20)} ${item.serverUrl}${tokenInfo}${desc}`
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
lines.push(
|
|
110
|
+
"\nUse 'ad profile use <name>' to switch or 'ad run <action> --profile <name>' to execute on a specific target."
|
|
86
111
|
);
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
);
|
|
91
|
-
}
|
|
112
|
+
return lines.join("\n");
|
|
113
|
+
},
|
|
114
|
+
});
|
|
92
115
|
} catch (err: any) {
|
|
93
|
-
|
|
94
|
-
|
|
116
|
+
if (err instanceof ArgumentError || err instanceof CliError) throw err;
|
|
117
|
+
throw new ExecutionError(err.message);
|
|
95
118
|
}
|
|
96
119
|
});
|
|
97
120
|
|
|
@@ -118,8 +141,7 @@ export function registerProfileCommands(program: Command): void {
|
|
|
118
141
|
});
|
|
119
142
|
console.log(`[OK] Profile '${name}' configured for server: ${options.server}`);
|
|
120
143
|
} catch (err: any) {
|
|
121
|
-
|
|
122
|
-
process.exit(1);
|
|
144
|
+
throw new ExecutionError(err.message);
|
|
123
145
|
}
|
|
124
146
|
});
|
|
125
147
|
|
|
@@ -132,8 +154,7 @@ export function registerProfileCommands(program: Command): void {
|
|
|
132
154
|
useProfile(name);
|
|
133
155
|
console.log(`[OK] Active profile switched to '${name}'`);
|
|
134
156
|
} catch (err: any) {
|
|
135
|
-
|
|
136
|
-
process.exit(1);
|
|
157
|
+
throw new ExecutionError(err.message);
|
|
137
158
|
}
|
|
138
159
|
});
|
|
139
160
|
|
|
@@ -143,16 +164,17 @@ export function registerProfileCommands(program: Command): void {
|
|
|
143
164
|
.description("Display details of a profile (defaults to active profile)")
|
|
144
165
|
.option("--reveal, --show-secrets", "Reveal plain text values for tokens")
|
|
145
166
|
.option("--json", "Output as JSON")
|
|
146
|
-
.
|
|
167
|
+
.option("--envelope", "Wrap JSON output in standard envelope")
|
|
168
|
+
.action((name, rawOptions, cmd) => {
|
|
147
169
|
try {
|
|
170
|
+
const options = getEffectiveOptions(rawOptions, cmd);
|
|
148
171
|
const config = loadProfiles();
|
|
149
172
|
const targetName = name || config.currentProfile || "local";
|
|
150
173
|
const entry = getProfile(targetName);
|
|
151
174
|
const reveal = Boolean(options.reveal || options.showSecrets);
|
|
152
175
|
|
|
153
176
|
if (!entry && targetName !== "local") {
|
|
154
|
-
|
|
155
|
-
process.exit(1);
|
|
177
|
+
throw new ArgumentError(`Profile '${targetName}' not found.`);
|
|
156
178
|
}
|
|
157
179
|
|
|
158
180
|
const resolved = resolveProfileToken(targetName, entry);
|
|
@@ -171,32 +193,38 @@ export function registerProfileCommands(program: Command): void {
|
|
|
171
193
|
description: entry?.description || "",
|
|
172
194
|
};
|
|
173
195
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
sourceDetail =
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
196
|
+
renderResult(data, {
|
|
197
|
+
json: options.json,
|
|
198
|
+
envelope: options.envelope,
|
|
199
|
+
humanFormatter: () => {
|
|
200
|
+
const lines: string[] = [];
|
|
201
|
+
lines.push(`Profile: ${data.name}${data.isCurrent ? " (Active)" : ""}`);
|
|
202
|
+
lines.push(`Server URL: ${data.serverUrl}`);
|
|
203
|
+
let sourceDetail = "None";
|
|
204
|
+
if (data.tokenSource === "tokenEnv") {
|
|
205
|
+
sourceDetail = `Environment Variable ($${data.tokenEnv})`;
|
|
206
|
+
} else if (data.tokenSource === "profileEnv") {
|
|
207
|
+
sourceDetail = `Profile Environment Variable ($ACTIONDOCK_${toSnakeUpperCase(data.name)}_TOKEN)`;
|
|
208
|
+
} else if (data.tokenSource === "profile") {
|
|
209
|
+
sourceDetail = "Stored in profiles.json (Deprecated)";
|
|
210
|
+
} else if (data.tokenSource === "globalEnv") {
|
|
211
|
+
sourceDetail = "Global Environment Variable ($ACTIONDOCK_TOKEN)";
|
|
212
|
+
}
|
|
213
|
+
lines.push(`Auth Source: ${sourceDetail}`);
|
|
214
|
+
if (data.tokenConfigured) {
|
|
215
|
+
lines.push(`Token Value: ${data.token}`);
|
|
216
|
+
}
|
|
217
|
+
if (data.description) {
|
|
218
|
+
lines.push(`Description: ${data.description}`);
|
|
219
|
+
}
|
|
220
|
+
return lines.join("\n");
|
|
221
|
+
},
|
|
222
|
+
});
|
|
197
223
|
} catch (err: any) {
|
|
198
|
-
|
|
199
|
-
|
|
224
|
+
if (err instanceof ArgumentError || err instanceof CliError) {
|
|
225
|
+
throw err;
|
|
226
|
+
}
|
|
227
|
+
throw new ExecutionError(err.message);
|
|
200
228
|
}
|
|
201
229
|
});
|
|
202
230
|
|
|
@@ -208,18 +236,18 @@ export function registerProfileCommands(program: Command): void {
|
|
|
208
236
|
.action((name) => {
|
|
209
237
|
try {
|
|
210
238
|
if (name === "local") {
|
|
211
|
-
|
|
212
|
-
process.exit(1);
|
|
239
|
+
throw new ExecutionError("Cannot remove built-in 'local' profile");
|
|
213
240
|
}
|
|
214
241
|
const removed = removeProfile(name);
|
|
215
242
|
if (!removed) {
|
|
216
|
-
|
|
217
|
-
process.exit(1);
|
|
243
|
+
throw new ExecutionError(`Profile '${name}' not found`);
|
|
218
244
|
}
|
|
219
245
|
console.log(`[OK] Profile '${name}' removed`);
|
|
220
246
|
} catch (err: any) {
|
|
221
|
-
|
|
222
|
-
|
|
247
|
+
if (err instanceof CliError) {
|
|
248
|
+
throw err;
|
|
249
|
+
}
|
|
250
|
+
throw new ExecutionError(err.message);
|
|
223
251
|
}
|
|
224
252
|
});
|
|
225
253
|
|
|
@@ -228,36 +256,41 @@ export function registerProfileCommands(program: Command): void {
|
|
|
228
256
|
.command("test [name]")
|
|
229
257
|
.description("Test connection latency and health of a profile")
|
|
230
258
|
.option("--json", "Output as JSON")
|
|
231
|
-
.
|
|
259
|
+
.option("--envelope", "Wrap JSON output in standard envelope")
|
|
260
|
+
.action(async (name, rawOptions, cmd) => {
|
|
232
261
|
try {
|
|
262
|
+
const options = getEffectiveOptions(rawOptions, cmd);
|
|
233
263
|
const target = resolveTarget({ profile: name });
|
|
234
264
|
if (target.type === "local") {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
265
|
+
renderResult(
|
|
266
|
+
{ ok: true, type: "local", message: "Local execution" },
|
|
267
|
+
{
|
|
268
|
+
json: options.json,
|
|
269
|
+
envelope: options.envelope,
|
|
270
|
+
humanFormatter: () =>
|
|
271
|
+
`Target profile '${target.profileName || "local"}' is local (runs in local runtime).`,
|
|
272
|
+
}
|
|
273
|
+
);
|
|
240
274
|
return;
|
|
241
275
|
}
|
|
242
276
|
|
|
243
277
|
const health = await checkRemoteHealth(target.serverUrl!, target.token);
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
`[OK] Connected to ${target.serverUrl} (${health.latencyMs}ms) - Version: ${health.version}, Status: ${health.status}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
}
|
|
278
|
+
renderResult(health, {
|
|
279
|
+
json: options.json,
|
|
280
|
+
envelope: options.envelope,
|
|
281
|
+
humanFormatter: () => {
|
|
282
|
+
if (health.ok) {
|
|
283
|
+
return `[OK] Connected to ${target.serverUrl} (${health.latencyMs}ms) - Version: ${health.version}, Status: ${health.status}`;
|
|
284
|
+
}
|
|
285
|
+
return `[FAIL] Connection to ${target.serverUrl} failed (${health.latencyMs}ms): ${health.error}`;
|
|
286
|
+
},
|
|
287
|
+
});
|
|
288
|
+
if (!health.ok) {
|
|
289
|
+
process.exitCode = 1;
|
|
257
290
|
}
|
|
258
291
|
} catch (err: any) {
|
|
259
|
-
|
|
260
|
-
|
|
292
|
+
if (err instanceof ArgumentError || err instanceof CliError) throw err;
|
|
293
|
+
throw new ExecutionError(err.message);
|
|
261
294
|
}
|
|
262
295
|
});
|
|
263
296
|
}
|
package/src/commands/runs.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
resolveTarget,
|
|
14
14
|
} from "@actiondock/core";
|
|
15
15
|
import { Command } from "commander";
|
|
16
|
+
import { ArgumentError, CliError, ExecutionError, getEffectiveOptions, renderResult } from "@actiondock/runtime-cli";
|
|
16
17
|
import { resolveIntent } from "../utils/filter";
|
|
17
18
|
|
|
18
19
|
function printRunRecord(run: any): void {
|
|
@@ -55,12 +56,16 @@ export function registerRunsCommands(program: Command): void {
|
|
|
55
56
|
.option("-s, --server <url>", "Remote server URL")
|
|
56
57
|
.option("-t, --token <token>", "Auth token for remote server")
|
|
57
58
|
.option("--no-fallback", "Disable fallback to full list when no items match intent")
|
|
59
|
+
.option("--data-dir <path>", "Custom database storage directory")
|
|
58
60
|
.option("--json", "Output as JSON")
|
|
59
|
-
.
|
|
61
|
+
.option("--envelope", "Wrap JSON output in standard envelope")
|
|
62
|
+
.action(async (patterns, rawOptions, cmd) => {
|
|
60
63
|
try {
|
|
64
|
+
const options = getEffectiveOptions(rawOptions, cmd);
|
|
61
65
|
const effectiveIntent = resolveIntent(options.intent, patterns);
|
|
62
66
|
const shouldFallback = options.fallback !== false;
|
|
63
67
|
const limit = Number.parseInt(options.limit, 10) || 20;
|
|
68
|
+
const isMachine = Boolean(options.json || options.envelope);
|
|
64
69
|
|
|
65
70
|
const target = resolveTarget({
|
|
66
71
|
profile: options.profile,
|
|
@@ -75,8 +80,8 @@ export function registerRunsCommands(program: Command): void {
|
|
|
75
80
|
intent: effectiveIntent,
|
|
76
81
|
limit,
|
|
77
82
|
});
|
|
78
|
-
if (
|
|
79
|
-
|
|
83
|
+
if (isMachine) {
|
|
84
|
+
renderResult(res.items, { json: options.json, envelope: options.envelope });
|
|
80
85
|
return;
|
|
81
86
|
}
|
|
82
87
|
console.log(
|
|
@@ -100,13 +105,15 @@ export function registerRunsCommands(program: Command): void {
|
|
|
100
105
|
: findProjectRoot();
|
|
101
106
|
|
|
102
107
|
if (options.package && !targetRoot) {
|
|
103
|
-
|
|
104
|
-
process.exit(1);
|
|
108
|
+
throw new ArgumentError(`Package '${options.package}' not found in linked packages or path`);
|
|
105
109
|
}
|
|
106
110
|
|
|
107
111
|
if (targetRoot) {
|
|
108
112
|
const projConfig = loadProjectConfig(targetRoot);
|
|
109
|
-
const storage = createStorage(projConfig.id, {
|
|
113
|
+
const storage = createStorage(projConfig.id, {
|
|
114
|
+
projectRoot: targetRoot,
|
|
115
|
+
dataDir: options.dataDir,
|
|
116
|
+
});
|
|
110
117
|
const records = storage.listRuns({
|
|
111
118
|
actionId: options.action,
|
|
112
119
|
limit,
|
|
@@ -120,8 +127,8 @@ export function registerRunsCommands(program: Command): void {
|
|
|
120
127
|
shouldFallback
|
|
121
128
|
);
|
|
122
129
|
|
|
123
|
-
if (
|
|
124
|
-
|
|
130
|
+
if (isMachine) {
|
|
131
|
+
renderResult(filterRes.items, { json: options.json, envelope: options.envelope });
|
|
125
132
|
} else {
|
|
126
133
|
console.log(`Execution Runs in ${projConfig.id} (${filterRes.items.length}):\n`);
|
|
127
134
|
if (filterRes.isFallback && effectiveIntent) {
|
|
@@ -144,6 +151,10 @@ export function registerRunsCommands(program: Command): void {
|
|
|
144
151
|
// Outside project: List recent runs across all linked packages
|
|
145
152
|
const linkedList = listLinkedPackages();
|
|
146
153
|
if (linkedList.length === 0) {
|
|
154
|
+
if (isMachine) {
|
|
155
|
+
renderResult([], { json: options.json, envelope: options.envelope });
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
147
158
|
console.log("No ActionDock project in current directory, and no packages linked.");
|
|
148
159
|
console.log("Run 'ad link' inside an Action package to register it.");
|
|
149
160
|
return;
|
|
@@ -154,7 +165,10 @@ export function registerRunsCommands(program: Command): void {
|
|
|
154
165
|
if (!existsSync(pkg.path)) continue;
|
|
155
166
|
try {
|
|
156
167
|
const projConfig = loadProjectConfig(pkg.path);
|
|
157
|
-
const storage = createStorage(projConfig.id, {
|
|
168
|
+
const storage = createStorage(projConfig.id, {
|
|
169
|
+
projectRoot: pkg.path,
|
|
170
|
+
dataDir: options.dataDir,
|
|
171
|
+
});
|
|
158
172
|
const records = storage.listRuns({
|
|
159
173
|
actionId: options.action,
|
|
160
174
|
limit,
|
|
@@ -179,8 +193,8 @@ export function registerRunsCommands(program: Command): void {
|
|
|
179
193
|
shouldFallback
|
|
180
194
|
);
|
|
181
195
|
|
|
182
|
-
if (
|
|
183
|
-
|
|
196
|
+
if (isMachine) {
|
|
197
|
+
renderResult(filterRes.items, { json: options.json, envelope: options.envelope });
|
|
184
198
|
} else {
|
|
185
199
|
console.log(`Execution Runs across Linked Packages (${filterRes.items.length}):\n`);
|
|
186
200
|
if (filterRes.isFallback && effectiveIntent) {
|
|
@@ -198,8 +212,10 @@ export function registerRunsCommands(program: Command): void {
|
|
|
198
212
|
}
|
|
199
213
|
}
|
|
200
214
|
} catch (err: any) {
|
|
201
|
-
|
|
202
|
-
|
|
215
|
+
if (err instanceof CliError) {
|
|
216
|
+
throw err;
|
|
217
|
+
}
|
|
218
|
+
throw new ExecutionError(err.message);
|
|
203
219
|
}
|
|
204
220
|
});
|
|
205
221
|
|
|
@@ -211,9 +227,13 @@ export function registerRunsCommands(program: Command): void {
|
|
|
211
227
|
.option("-p, --profile <name>", "Query run against a specific profile")
|
|
212
228
|
.option("-s, --server <url>", "Remote server URL")
|
|
213
229
|
.option("-t, --token <token>", "Auth token for remote server")
|
|
230
|
+
.option("--data-dir <path>", "Custom database storage directory")
|
|
214
231
|
.option("--json", "Output as JSON")
|
|
215
|
-
.
|
|
232
|
+
.option("--envelope", "Wrap JSON output in standard envelope")
|
|
233
|
+
.action(async (id, rawOptions, cmd) => {
|
|
216
234
|
let target;
|
|
235
|
+
const options = getEffectiveOptions(rawOptions, cmd);
|
|
236
|
+
const isMachine = Boolean(options.json || options.envelope);
|
|
217
237
|
try {
|
|
218
238
|
target = resolveTarget({
|
|
219
239
|
profile: options.profile,
|
|
@@ -221,22 +241,20 @@ export function registerRunsCommands(program: Command): void {
|
|
|
221
241
|
token: options.token,
|
|
222
242
|
});
|
|
223
243
|
} catch (err: any) {
|
|
224
|
-
|
|
225
|
-
process.exit(1);
|
|
244
|
+
throw new ExecutionError(err.message);
|
|
226
245
|
}
|
|
227
246
|
|
|
228
247
|
if (target.type === "remote") {
|
|
229
248
|
try {
|
|
230
249
|
const run = await fetchRemoteRun(target.serverUrl!, id, target.token);
|
|
231
|
-
if (
|
|
232
|
-
|
|
250
|
+
if (isMachine) {
|
|
251
|
+
renderResult(run, { json: options.json, envelope: options.envelope });
|
|
233
252
|
} else {
|
|
234
253
|
printRunRecord(run);
|
|
235
254
|
}
|
|
236
255
|
return;
|
|
237
256
|
} catch (err: any) {
|
|
238
|
-
|
|
239
|
-
process.exit(1);
|
|
257
|
+
throw new ExecutionError(err.message);
|
|
240
258
|
}
|
|
241
259
|
}
|
|
242
260
|
|
|
@@ -247,11 +265,13 @@ export function registerRunsCommands(program: Command): void {
|
|
|
247
265
|
if (options.package) {
|
|
248
266
|
const targetRoot = resolvePackageRoot(options.package);
|
|
249
267
|
if (!targetRoot) {
|
|
250
|
-
|
|
251
|
-
process.exit(1);
|
|
268
|
+
throw new ArgumentError(`Package '${options.package}' not found in linked packages or path`);
|
|
252
269
|
}
|
|
253
270
|
const projConfig = loadProjectConfig(targetRoot);
|
|
254
|
-
const storage = createStorage(projConfig.id, {
|
|
271
|
+
const storage = createStorage(projConfig.id, {
|
|
272
|
+
projectRoot: targetRoot,
|
|
273
|
+
dataDir: options.dataDir,
|
|
274
|
+
});
|
|
255
275
|
foundRun = storage.getRun(id);
|
|
256
276
|
storage.close();
|
|
257
277
|
if (foundRun && !foundRun.packageId) {
|
|
@@ -263,7 +283,10 @@ export function registerRunsCommands(program: Command): void {
|
|
|
263
283
|
if (currentRoot) {
|
|
264
284
|
try {
|
|
265
285
|
const projConfig = loadProjectConfig(currentRoot);
|
|
266
|
-
const storage = createStorage(projConfig.id, {
|
|
286
|
+
const storage = createStorage(projConfig.id, {
|
|
287
|
+
projectRoot: currentRoot,
|
|
288
|
+
dataDir: options.dataDir,
|
|
289
|
+
});
|
|
267
290
|
foundRun = storage.getRun(id);
|
|
268
291
|
storage.close();
|
|
269
292
|
if (foundRun && !foundRun.packageId) {
|
|
@@ -279,7 +302,10 @@ export function registerRunsCommands(program: Command): void {
|
|
|
279
302
|
if (!existsSync(pkg.path)) continue;
|
|
280
303
|
try {
|
|
281
304
|
const projConfig = loadProjectConfig(pkg.path);
|
|
282
|
-
const storage = createStorage(projConfig.id, {
|
|
305
|
+
const storage = createStorage(projConfig.id, {
|
|
306
|
+
projectRoot: pkg.path,
|
|
307
|
+
dataDir: options.dataDir,
|
|
308
|
+
});
|
|
283
309
|
const r = storage.getRun(id);
|
|
284
310
|
storage.close();
|
|
285
311
|
if (r) {
|
|
@@ -292,18 +318,19 @@ export function registerRunsCommands(program: Command): void {
|
|
|
292
318
|
}
|
|
293
319
|
|
|
294
320
|
if (!foundRun) {
|
|
295
|
-
|
|
296
|
-
process.exit(1);
|
|
321
|
+
throw new ExecutionError(`Run record '${id}' not found in current project or any linked packages`);
|
|
297
322
|
}
|
|
298
323
|
|
|
299
|
-
if (
|
|
300
|
-
|
|
324
|
+
if (isMachine) {
|
|
325
|
+
renderResult(foundRun, { json: options.json, envelope: options.envelope });
|
|
301
326
|
} else {
|
|
302
327
|
printRunRecord(foundRun);
|
|
303
328
|
}
|
|
304
329
|
} catch (err: any) {
|
|
305
|
-
|
|
306
|
-
|
|
330
|
+
if (err instanceof CliError) {
|
|
331
|
+
throw err;
|
|
332
|
+
}
|
|
333
|
+
throw new ExecutionError(err.message);
|
|
307
334
|
}
|
|
308
335
|
});
|
|
309
336
|
|
|
@@ -316,8 +343,11 @@ export function registerRunsCommands(program: Command): void {
|
|
|
316
343
|
.option("-t, --token <token>", "Auth token for remote server")
|
|
317
344
|
.option("-r, --reason <reason>", "Reason for cancellation")
|
|
318
345
|
.option("--json", "Output as JSON")
|
|
319
|
-
.
|
|
346
|
+
.option("--envelope", "Wrap JSON output in standard envelope")
|
|
347
|
+
.action(async (id, rawOptions, cmd) => {
|
|
320
348
|
let target;
|
|
349
|
+
const options = getEffectiveOptions(rawOptions, cmd);
|
|
350
|
+
const isMachine = Boolean(options.json || options.envelope);
|
|
321
351
|
try {
|
|
322
352
|
target = resolveTarget({
|
|
323
353
|
profile: options.profile,
|
|
@@ -325,15 +355,13 @@ export function registerRunsCommands(program: Command): void {
|
|
|
325
355
|
token: options.token,
|
|
326
356
|
});
|
|
327
357
|
} catch (err: any) {
|
|
328
|
-
|
|
329
|
-
process.exit(1);
|
|
358
|
+
throw new ExecutionError(err.message);
|
|
330
359
|
}
|
|
331
360
|
|
|
332
361
|
if (target.type === "local") {
|
|
333
|
-
|
|
334
|
-
"
|
|
362
|
+
throw new ArgumentError(
|
|
363
|
+
"'ad runs cancel' is only supported for remote execution targets. Use --profile <name> or --server <url>."
|
|
335
364
|
);
|
|
336
|
-
process.exit(1);
|
|
337
365
|
}
|
|
338
366
|
|
|
339
367
|
try {
|
|
@@ -343,14 +371,13 @@ export function registerRunsCommands(program: Command): void {
|
|
|
343
371
|
target.token,
|
|
344
372
|
options.reason
|
|
345
373
|
);
|
|
346
|
-
if (
|
|
347
|
-
|
|
374
|
+
if (isMachine) {
|
|
375
|
+
renderResult(result, { json: options.json, envelope: options.envelope });
|
|
348
376
|
} else {
|
|
349
377
|
console.log(`Run '${id}' cancellation requested (Status: ${result.status}).`);
|
|
350
378
|
}
|
|
351
379
|
} catch (err: any) {
|
|
352
|
-
|
|
353
|
-
process.exit(1);
|
|
380
|
+
throw new ExecutionError(err.message);
|
|
354
381
|
}
|
|
355
382
|
});
|
|
356
383
|
|
|
@@ -363,9 +390,13 @@ export function registerRunsCommands(program: Command): void {
|
|
|
363
390
|
.option("-p, --profile <name>", "Target profile")
|
|
364
391
|
.option("-s, --server <url>", "Remote server URL")
|
|
365
392
|
.option("-t, --token <token>", "Auth token for remote server")
|
|
393
|
+
.option("--data-dir <path>", "Custom database storage directory")
|
|
366
394
|
.option("--json", "Output as JSON")
|
|
367
|
-
.
|
|
395
|
+
.option("--envelope", "Wrap JSON output in standard envelope")
|
|
396
|
+
.action(async (rawOptions, cmd) => {
|
|
368
397
|
try {
|
|
398
|
+
const options = getEffectiveOptions(rawOptions, cmd);
|
|
399
|
+
const isMachine = Boolean(options.json || options.envelope);
|
|
369
400
|
const target = resolveTarget({
|
|
370
401
|
profile: options.profile,
|
|
371
402
|
server: options.server,
|
|
@@ -377,8 +408,8 @@ export function registerRunsCommands(program: Command): void {
|
|
|
377
408
|
packageId: options.package,
|
|
378
409
|
actionId: options.action,
|
|
379
410
|
});
|
|
380
|
-
if (
|
|
381
|
-
|
|
411
|
+
if (isMachine) {
|
|
412
|
+
renderResult(res, { json: options.json, envelope: options.envelope });
|
|
382
413
|
} else {
|
|
383
414
|
console.log(`Cleared ${res.clearedCount} execution run(s) on remote server.`);
|
|
384
415
|
}
|
|
@@ -389,20 +420,32 @@ export function registerRunsCommands(program: Command): void {
|
|
|
389
420
|
? resolvePackageRoot(options.package)
|
|
390
421
|
: findProjectRoot();
|
|
391
422
|
|
|
423
|
+
if (options.package && !targetRoot) {
|
|
424
|
+
throw new ArgumentError(`Package '${options.package}' not found in linked packages or path`);
|
|
425
|
+
}
|
|
426
|
+
|
|
392
427
|
if (targetRoot) {
|
|
393
428
|
const projConfig = loadProjectConfig(targetRoot);
|
|
394
|
-
const storage = createStorage(projConfig.id, {
|
|
429
|
+
const storage = createStorage(projConfig.id, {
|
|
430
|
+
projectRoot: targetRoot,
|
|
431
|
+
dataDir: options.dataDir,
|
|
432
|
+
});
|
|
395
433
|
const count = storage.clearRuns({ actionId: options.action });
|
|
396
434
|
storage.close();
|
|
397
|
-
if (
|
|
398
|
-
|
|
435
|
+
if (isMachine) {
|
|
436
|
+
renderResult({ ok: true, clearedCount: count, packageId: projConfig.id }, { json: options.json, envelope: options.envelope });
|
|
399
437
|
} else {
|
|
400
438
|
console.log(`Cleared ${count} execution run(s) in package '${projConfig.id}'.`);
|
|
401
439
|
}
|
|
440
|
+
return;
|
|
402
441
|
}
|
|
442
|
+
|
|
443
|
+
throw new ArgumentError(
|
|
444
|
+
"Not in an ActionDock project. Please specify -P, --package <id> or cd into a project directory."
|
|
445
|
+
);
|
|
403
446
|
} catch (err: any) {
|
|
404
|
-
|
|
405
|
-
|
|
447
|
+
if (err instanceof CliError) throw err;
|
|
448
|
+
throw new ExecutionError(err.message);
|
|
406
449
|
}
|
|
407
450
|
});
|
|
408
451
|
}
|