@actiondock/cli 2.0.1 → 2.0.2
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/package.json +1 -1
- package/src/commands/config.ts +96 -10
- package/src/commands/doctor.ts +24 -6
- package/src/commands/index.ts +1 -1
- package/src/commands/info.ts +94 -115
- package/src/commands/playbook.ts +63 -2
- package/src/commands/runs.ts +91 -1
- package/src/commands/serve.ts +32 -0
- package/src/commands/state.ts +124 -11
package/package.json
CHANGED
package/src/commands/config.ts
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createGlobalStorage,
|
|
3
3
|
createStorage,
|
|
4
|
+
deleteRemoteConfig,
|
|
5
|
+
fetchRemoteConfig,
|
|
4
6
|
filterWithFallbackInfo,
|
|
5
7
|
isSecretConfigKey,
|
|
6
8
|
loadProjectConfig,
|
|
7
9
|
maskSecretValue,
|
|
8
10
|
resolvePackageRoot,
|
|
11
|
+
resolveTarget,
|
|
12
|
+
setRemoteConfig,
|
|
9
13
|
type ConfigItemDefinition,
|
|
10
14
|
} from "@actiondock/core";
|
|
11
15
|
import { Command } from "commander";
|
|
@@ -69,18 +73,17 @@ export function registerConfigCommands(program: Command): void {
|
|
|
69
73
|
resolvedValue = itemDef.default;
|
|
70
74
|
source = "default";
|
|
71
75
|
status = "DEFAULT";
|
|
72
|
-
} else {
|
|
73
|
-
status = "MISSING";
|
|
74
76
|
}
|
|
75
77
|
|
|
78
|
+
const displayValue = isSecret && resolvedValue !== undefined ? maskSecretValue(resolvedValue) : resolvedValue;
|
|
79
|
+
|
|
76
80
|
return {
|
|
77
81
|
key,
|
|
78
|
-
|
|
79
|
-
secret: isSecret,
|
|
80
|
-
default: itemDef.default,
|
|
82
|
+
value: displayValue,
|
|
81
83
|
status,
|
|
82
84
|
source,
|
|
83
|
-
|
|
85
|
+
secret: isSecret,
|
|
86
|
+
description: itemDef.description || "",
|
|
84
87
|
required: itemDef.default === undefined,
|
|
85
88
|
};
|
|
86
89
|
});
|
|
@@ -141,16 +144,45 @@ export function registerConfigCommands(program: Command): void {
|
|
|
141
144
|
.description("List configuration entries (Global & Project)")
|
|
142
145
|
.option("-P, --package <id>", "Target package ID or path")
|
|
143
146
|
.option("-g, --global", "Show only global configurations")
|
|
147
|
+
.option("-p, --profile <name>", "Query config on a remote target")
|
|
148
|
+
.option("-s, --server <url>", "Remote server URL")
|
|
149
|
+
.option("-t, --token <token>", "Auth token for remote server")
|
|
144
150
|
.option("-i, --intent <pattern>", "Regex or fuzzy intent filter; falls back to full list when no match")
|
|
145
151
|
.option("--reveal, --show-secrets", "Reveal plain text values for secrets")
|
|
146
152
|
.option("--no-fallback", "Disable fallback to full list when no items match intent")
|
|
147
153
|
.option("--json", "Output as JSON")
|
|
148
|
-
.action((patterns, options) => {
|
|
154
|
+
.action(async (patterns, options) => {
|
|
149
155
|
try {
|
|
150
156
|
const effectiveIntent = resolveIntent(options.intent, patterns);
|
|
151
157
|
const shouldFallback = options.fallback !== false;
|
|
152
158
|
const reveal = options.reveal || options.showSecrets;
|
|
153
159
|
|
|
160
|
+
const target = resolveTarget({
|
|
161
|
+
profile: options.profile,
|
|
162
|
+
server: options.server,
|
|
163
|
+
token: options.token,
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
if (target.type === "remote") {
|
|
167
|
+
const res = await fetchRemoteConfig(target.serverUrl!, target.token, options.package);
|
|
168
|
+
if (options.json) {
|
|
169
|
+
console.log(JSON.stringify(res, null, 2));
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
console.log(
|
|
173
|
+
`Config on remote server ${target.serverUrl}${target.profileName ? ` (Profile: ${target.profileName})` : ""}:\n`
|
|
174
|
+
);
|
|
175
|
+
const entries = Object.entries(res.values || {});
|
|
176
|
+
if (entries.length === 0) {
|
|
177
|
+
console.log(" (No config entries stored)");
|
|
178
|
+
} else {
|
|
179
|
+
for (const [k, v] of entries) {
|
|
180
|
+
console.log(` ${k.padEnd(24)} = ${JSON.stringify(v)}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
154
186
|
const globalStorage = createGlobalStorage();
|
|
155
187
|
const globalConfig = globalStorage.listConfig();
|
|
156
188
|
|
|
@@ -246,10 +278,30 @@ export function registerConfigCommands(program: Command): void {
|
|
|
246
278
|
.description("Get a configuration value")
|
|
247
279
|
.option("-P, --package <id>", "Target package ID or path")
|
|
248
280
|
.option("-g, --global", "Get from global configuration only")
|
|
281
|
+
.option("-p, --profile <name>", "Query config on a remote target")
|
|
282
|
+
.option("-s, --server <url>", "Remote server URL")
|
|
283
|
+
.option("-t, --token <token>", "Auth token for remote server")
|
|
249
284
|
.option("--reveal, --show-secrets", "Reveal plain text value for secret")
|
|
250
285
|
.option("--json", "Output as JSON")
|
|
251
|
-
.action((key, options) => {
|
|
286
|
+
.action(async (key, options) => {
|
|
252
287
|
try {
|
|
288
|
+
const target = resolveTarget({
|
|
289
|
+
profile: options.profile,
|
|
290
|
+
server: options.server,
|
|
291
|
+
token: options.token,
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
if (target.type === "remote") {
|
|
295
|
+
const res = await fetchRemoteConfig(target.serverUrl!, target.token, options.package);
|
|
296
|
+
const val = res.values?.[key];
|
|
297
|
+
if (options.json) {
|
|
298
|
+
console.log(JSON.stringify({ key, value: val }, null, 2));
|
|
299
|
+
} else {
|
|
300
|
+
console.log(val !== undefined ? JSON.stringify(val) : "undefined");
|
|
301
|
+
}
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
|
|
253
305
|
const reveal = options.reveal || options.showSecrets;
|
|
254
306
|
const globalStorage = createGlobalStorage();
|
|
255
307
|
const globalVal = globalStorage.getConfig(key);
|
|
@@ -326,7 +378,10 @@ export function registerConfigCommands(program: Command): void {
|
|
|
326
378
|
.description("Set a configuration value (Global by default outside project, or use -g for global)")
|
|
327
379
|
.option("-P, --package <id>", "Target package ID or path")
|
|
328
380
|
.option("-g, --global", "Set globally across all packages")
|
|
329
|
-
.
|
|
381
|
+
.option("-p, --profile <name>", "Set config on a remote target")
|
|
382
|
+
.option("-s, --server <url>", "Remote server URL")
|
|
383
|
+
.option("-t, --token <token>", "Auth token for remote server")
|
|
384
|
+
.action(async (key, rawValue, options) => {
|
|
330
385
|
try {
|
|
331
386
|
let parsed: unknown = rawValue;
|
|
332
387
|
try {
|
|
@@ -335,6 +390,18 @@ export function registerConfigCommands(program: Command): void {
|
|
|
335
390
|
parsed = rawValue;
|
|
336
391
|
}
|
|
337
392
|
|
|
393
|
+
const target = resolveTarget({
|
|
394
|
+
profile: options.profile,
|
|
395
|
+
server: options.server,
|
|
396
|
+
token: options.token,
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
if (target.type === "remote") {
|
|
400
|
+
await setRemoteConfig(target.serverUrl!, key, parsed, target.token, options.package);
|
|
401
|
+
console.log(`[OK] Remote config '${key}' updated on ${target.serverUrl}`);
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
|
|
338
405
|
const projectRoot = !options.global ? resolvePackageRoot(options.package) : null;
|
|
339
406
|
const isSecret = isSecretConfigKey(key);
|
|
340
407
|
const displayVal = isSecret ? maskSecretValue(parsed) : JSON.stringify(parsed);
|
|
@@ -366,8 +433,27 @@ export function registerConfigCommands(program: Command): void {
|
|
|
366
433
|
.description("Delete a configuration value")
|
|
367
434
|
.option("-P, --package <id>", "Target package ID or path")
|
|
368
435
|
.option("-g, --global", "Delete from global configuration")
|
|
369
|
-
.
|
|
436
|
+
.option("-p, --profile <name>", "Delete config on a remote target")
|
|
437
|
+
.option("-s, --server <url>", "Remote server URL")
|
|
438
|
+
.option("-t, --token <token>", "Auth token for remote server")
|
|
439
|
+
.action(async (key, options) => {
|
|
370
440
|
try {
|
|
441
|
+
const target = resolveTarget({
|
|
442
|
+
profile: options.profile,
|
|
443
|
+
server: options.server,
|
|
444
|
+
token: options.token,
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
if (target.type === "remote") {
|
|
448
|
+
const res = await deleteRemoteConfig(target.serverUrl!, key, target.token, options.package);
|
|
449
|
+
if (res.deleted) {
|
|
450
|
+
console.log(`[OK] Remote config '${key}' deleted from ${target.serverUrl}`);
|
|
451
|
+
} else {
|
|
452
|
+
console.log(`Remote config '${key}' not found on ${target.serverUrl}`);
|
|
453
|
+
}
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
|
|
371
457
|
const projectRoot = !options.global ? resolvePackageRoot(options.package) : null;
|
|
372
458
|
|
|
373
459
|
if (options.global || !projectRoot) {
|
package/src/commands/doctor.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { runDoctorChecks } from "@actiondock/core";
|
|
1
|
+
import { fetchRemoteDoctor, resolveTarget, runDoctorChecks } from "@actiondock/core";
|
|
2
2
|
import { Command } from "commander";
|
|
3
3
|
|
|
4
4
|
export function registerDoctorCommand(program: Command): void {
|
|
@@ -6,13 +6,28 @@ export function registerDoctorCommand(program: Command): void {
|
|
|
6
6
|
.command("doctor")
|
|
7
7
|
.description("Check ActionDock environment, registry health, and project diagnostics")
|
|
8
8
|
.option("-P, --package <id|path>", "Target package ID or directory path for project diagnostics")
|
|
9
|
+
.option("-p, --profile <name>", "Query doctor diagnostics on a remote target")
|
|
10
|
+
.option("-s, --server <url>", "Remote server URL")
|
|
11
|
+
.option("-t, --token <token>", "Auth token for remote server")
|
|
9
12
|
.option("--json", "Output diagnostics report in JSON format")
|
|
10
13
|
.action(async (options) => {
|
|
11
14
|
try {
|
|
12
|
-
const
|
|
13
|
-
|
|
15
|
+
const target = resolveTarget({
|
|
16
|
+
profile: options.profile,
|
|
17
|
+
server: options.server,
|
|
18
|
+
token: options.token,
|
|
14
19
|
});
|
|
15
20
|
|
|
21
|
+
let report;
|
|
22
|
+
if (target.type === "remote") {
|
|
23
|
+
const remoteRes = await fetchRemoteDoctor(target.serverUrl!, target.token, options.package);
|
|
24
|
+
report = remoteRes.report || remoteRes;
|
|
25
|
+
} else {
|
|
26
|
+
report = await runDoctorChecks({
|
|
27
|
+
packageIdOrPath: options.package,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
16
31
|
if (options.json) {
|
|
17
32
|
console.log(JSON.stringify(report, null, 2));
|
|
18
33
|
if (!report.ok) {
|
|
@@ -21,11 +36,14 @@ export function registerDoctorCommand(program: Command): void {
|
|
|
21
36
|
return;
|
|
22
37
|
}
|
|
23
38
|
|
|
24
|
-
|
|
39
|
+
const title = target.type === "remote"
|
|
40
|
+
? `[DOCTOR] Remote ActionDock Server Diagnostics (${target.serverUrl}${target.profileName ? ` - Profile: ${target.profileName}` : ""})\n`
|
|
41
|
+
: "[DOCTOR] ActionDock System & Project Diagnostics\n";
|
|
42
|
+
console.log(title);
|
|
25
43
|
|
|
26
44
|
// 1. Runtime & Environment Group
|
|
27
45
|
console.log("[Runtime & Environment]");
|
|
28
|
-
const envChecks = report.checks.filter((c) => c.category === "runtime" || c.category === "storage" || c.category === "registry");
|
|
46
|
+
const envChecks = (report.checks || []).filter((c: any) => c.category === "runtime" || c.category === "storage" || c.category === "registry");
|
|
29
47
|
for (const c of envChecks) {
|
|
30
48
|
const tag = c.status === "ok" ? "[OK]" : c.status === "warn" ? "[WARN]" : "[ERROR]";
|
|
31
49
|
console.log(` ${tag} ${c.name}: ${c.message}`);
|
|
@@ -37,7 +55,7 @@ export function registerDoctorCommand(program: Command): void {
|
|
|
37
55
|
// 2. Project Group
|
|
38
56
|
if (report.hasProject) {
|
|
39
57
|
console.log(`\n[Project: ${report.packageId || "unknown"}] (${report.projectRoot})`);
|
|
40
|
-
const projChecks = report.checks.filter((c) => c.category === "project");
|
|
58
|
+
const projChecks = (report.checks || []).filter((c: any) => c.category === "project");
|
|
41
59
|
for (const c of projChecks) {
|
|
42
60
|
const tag = c.status === "ok" ? "[OK]" : c.status === "warn" ? "[WARN]" : "[ERROR]";
|
|
43
61
|
console.log(` ${tag} ${c.name}: ${c.message}`);
|
package/src/commands/index.ts
CHANGED
|
@@ -21,7 +21,7 @@ export function createCliProgram(): Command {
|
|
|
21
21
|
program
|
|
22
22
|
.name("ad")
|
|
23
23
|
.description("ActionDock (ad) 2.0 - Toolchain for building and shipping standalone AI Agent Actions & Skills")
|
|
24
|
-
.version("2.0.
|
|
24
|
+
.version("2.0.2");
|
|
25
25
|
|
|
26
26
|
registerInitCommand(program);
|
|
27
27
|
registerInfoCommand(program);
|
package/src/commands/info.ts
CHANGED
|
@@ -191,16 +191,6 @@ export function registerInfoCommand(program: Command): void {
|
|
|
191
191
|
.option("--json", "Output information as JSON")
|
|
192
192
|
.action(async (patterns: string[] = [], options: any) => {
|
|
193
193
|
try {
|
|
194
|
-
if (options.tree) {
|
|
195
|
-
const status = getRegistryStatus();
|
|
196
|
-
if (options.json) {
|
|
197
|
-
console.log(JSON.stringify(status, null, 2));
|
|
198
|
-
} else {
|
|
199
|
-
printRegistryTree(status);
|
|
200
|
-
}
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
194
|
const effectiveIntent = resolveIntent(options.intent, patterns);
|
|
205
195
|
const shouldFallback = options.fallback !== false;
|
|
206
196
|
|
|
@@ -214,121 +204,110 @@ export function registerInfoCommand(program: Command): void {
|
|
|
214
204
|
if (target.type === "remote") {
|
|
215
205
|
const remoteInfo = await fetchRemoteInfo(
|
|
216
206
|
target.serverUrl!,
|
|
217
|
-
target.token
|
|
207
|
+
target.token,
|
|
208
|
+
{
|
|
209
|
+
intent: effectiveIntent,
|
|
210
|
+
package: options.package,
|
|
211
|
+
tree: Boolean(options.tree),
|
|
212
|
+
}
|
|
218
213
|
);
|
|
219
214
|
|
|
220
215
|
if (options.json) {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
null,
|
|
243
|
-
2
|
|
244
|
-
)
|
|
245
|
-
);
|
|
246
|
-
} else {
|
|
247
|
-
console.log(JSON.stringify(remoteInfo, null, 2));
|
|
216
|
+
console.log(JSON.stringify(remoteInfo, null, 2));
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
console.log(
|
|
221
|
+
`Remote ActionDock Server: ${target.serverUrl}${target.profileName ? ` (Profile: ${target.profileName})` : ""}\n`
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
// Remote Tree
|
|
225
|
+
if (remoteInfo.type === "tree" || (options.tree && remoteInfo.workspaces)) {
|
|
226
|
+
printRegistryTree(remoteInfo);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Remote Package Detail
|
|
231
|
+
if (remoteInfo.type === "package_detail" || (remoteInfo.id && !remoteInfo.packages)) {
|
|
232
|
+
console.log(`ActionDock Project: ${remoteInfo.name || remoteInfo.id} (${remoteInfo.id})`);
|
|
233
|
+
console.log(`Version: ${remoteInfo.version || "unknown"}`);
|
|
234
|
+
if (remoteInfo.description) console.log(`Description: ${remoteInfo.description}`);
|
|
235
|
+
if (remoteInfo.path || remoteInfo.projectRoot) {
|
|
236
|
+
console.log(`Root: ${remoteInfo.path || remoteInfo.projectRoot}`);
|
|
248
237
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
)
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
effectiveIntent,
|
|
258
|
-
[
|
|
259
|
-
(r: any) => r.id,
|
|
260
|
-
(r: any) => r.name,
|
|
261
|
-
(r: any) => r.description,
|
|
262
|
-
(r: any) => r.actions,
|
|
263
|
-
(r: any) => r.playbooks,
|
|
264
|
-
],
|
|
265
|
-
shouldFallback
|
|
266
|
-
);
|
|
267
|
-
if (match.matchedCount === 0 && !shouldFallback) {
|
|
268
|
-
console.error(
|
|
269
|
-
`Error: Remote project '${remoteInfo.id}' did not match intent '${effectiveIntent}'`
|
|
270
|
-
);
|
|
271
|
-
process.exit(1);
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
console.log(
|
|
275
|
-
`Project: ${remoteInfo.name || remoteInfo.id} (${remoteInfo.id})`
|
|
276
|
-
);
|
|
277
|
-
console.log(`Version: ${remoteInfo.version || "unknown"}`);
|
|
278
|
-
if (remoteInfo.description) {
|
|
279
|
-
console.log(`Description: ${remoteInfo.description}`);
|
|
280
|
-
}
|
|
281
|
-
if (Array.isArray(remoteInfo.actions)) {
|
|
282
|
-
console.log(`\nActions (${remoteInfo.actions.length}):`);
|
|
283
|
-
for (const actId of remoteInfo.actions) {
|
|
284
|
-
console.log(` - ${actId}`);
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
if (Array.isArray(remoteInfo.playbooks)) {
|
|
288
|
-
console.log(`\nPlaybooks (${remoteInfo.playbooks.length}):`);
|
|
289
|
-
for (const pbId of remoteInfo.playbooks) {
|
|
290
|
-
console.log(` - ${pbId}`);
|
|
291
|
-
}
|
|
238
|
+
|
|
239
|
+
const actions = remoteInfo.actionsDetail || remoteInfo.actions || [];
|
|
240
|
+
console.log(`\nActions (${remoteInfo.actionsCount || actions.length}):`);
|
|
241
|
+
for (const act of actions) {
|
|
242
|
+
if (typeof act === "string") {
|
|
243
|
+
console.log(` - ${act}`);
|
|
244
|
+
} else {
|
|
245
|
+
console.log(` - ${(act.id || "").padEnd(28)} ${act.description || ""}`);
|
|
292
246
|
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
(p: any) => p.name,
|
|
303
|
-
(p: any) => p.description,
|
|
304
|
-
(p: any) => p.path,
|
|
305
|
-
(p: any) => p.actions,
|
|
306
|
-
(p: any) => p.playbooks,
|
|
307
|
-
],
|
|
308
|
-
shouldFallback
|
|
309
|
-
);
|
|
310
|
-
if (filterRes.matchedCount === 0 && !shouldFallback) {
|
|
311
|
-
console.error(
|
|
312
|
-
`Error: No remote packages matched intent '${effectiveIntent}'`
|
|
313
|
-
);
|
|
314
|
-
process.exit(1);
|
|
315
|
-
}
|
|
316
|
-
list = filterRes.items;
|
|
317
|
-
isFallback = filterRes.isFallback;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const playbooks = remoteInfo.playbooksDetail || remoteInfo.playbooks || [];
|
|
250
|
+
console.log(`\nPlaybooks (${remoteInfo.playbooksCount || playbooks.length}):`);
|
|
251
|
+
for (const pb of playbooks) {
|
|
252
|
+
if (typeof pb === "string") {
|
|
253
|
+
console.log(` - ${pb}`);
|
|
254
|
+
} else {
|
|
255
|
+
console.log(` - ${(pb.id || "").padEnd(28)} ${pb.description || ""}`);
|
|
318
256
|
}
|
|
319
|
-
|
|
320
|
-
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const declared = remoteInfo.configDeclared || {};
|
|
260
|
+
const declaredKeys = Object.keys(declared);
|
|
261
|
+
if (declaredKeys.length > 0) {
|
|
262
|
+
console.log(`\nDeclared Config Keys:`);
|
|
263
|
+
for (const k of declaredKeys) {
|
|
264
|
+
const item = declared[k];
|
|
265
|
+
const isSec = item?.secret ? " [secret]" : "";
|
|
266
|
+
const def =
|
|
267
|
+
item?.default !== undefined
|
|
268
|
+
? ` (default: ${JSON.stringify(item.default)})`
|
|
269
|
+
: "";
|
|
321
270
|
console.log(
|
|
322
|
-
|
|
271
|
+
` - ${k.padEnd(24)} ${item?.description || ""}${def}${isSec}`
|
|
323
272
|
);
|
|
324
273
|
}
|
|
325
|
-
console.log(`\nLinked Packages (${list.length}):`);
|
|
326
|
-
for (const pkg of list) {
|
|
327
|
-
console.log(` - ${pkg.id} (${pkg.path})`);
|
|
328
|
-
}
|
|
329
|
-
} else {
|
|
330
|
-
console.log(JSON.stringify(remoteInfo, null, 2));
|
|
331
274
|
}
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Remote Package List
|
|
279
|
+
const packages = remoteInfo.packages || remoteInfo.linkedPackages || [];
|
|
280
|
+
if (Array.isArray(packages)) {
|
|
281
|
+
console.log(`Remote ActionDock Packages (${packages.length}):\n`);
|
|
282
|
+
for (const p of packages) {
|
|
283
|
+
console.log(`* ${p.name || p.id} (${p.id}) v${p.version || "unknown"}`);
|
|
284
|
+
if (p.path) console.log(` Path: ${p.path}`);
|
|
285
|
+
if (p.description) console.log(` Desc: ${p.description}`);
|
|
286
|
+
const actNames = Array.isArray(p.actions)
|
|
287
|
+
? p.actions.map((a: any) => (typeof a === "string" ? a : a.id)).join(", ")
|
|
288
|
+
: "";
|
|
289
|
+
const pbNames = Array.isArray(p.playbooks)
|
|
290
|
+
? p.playbooks.map((pb: any) => (typeof pb === "string" ? pb : pb.id)).join(", ")
|
|
291
|
+
: "";
|
|
292
|
+
console.log(` Actions (${p.actionsCount || (p.actions ? p.actions.length : 0)}): ${actNames || "(none)"}`);
|
|
293
|
+
console.log(` Playbooks (${p.playbooksCount || (p.playbooks ? p.playbooks.length : 0)}): ${pbNames || "(none)"}`);
|
|
294
|
+
console.log("");
|
|
295
|
+
}
|
|
296
|
+
console.log("Tip: Run 'ad info <package-id> --server <url>' to view detailed package configuration and schema.");
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
console.log(JSON.stringify(remoteInfo, null, 2));
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Local Tree
|
|
305
|
+
if (options.tree) {
|
|
306
|
+
const status = getRegistryStatus();
|
|
307
|
+
if (options.json) {
|
|
308
|
+
console.log(JSON.stringify(status, null, 2));
|
|
309
|
+
} else {
|
|
310
|
+
printRegistryTree(status);
|
|
332
311
|
}
|
|
333
312
|
return;
|
|
334
313
|
}
|
package/src/commands/playbook.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { dirname, resolve } from "node:path";
|
|
3
3
|
import {
|
|
4
|
+
fetchRemotePlaybooks,
|
|
5
|
+
fetchRemotePlaybookShow,
|
|
4
6
|
filterWithFallbackInfo,
|
|
5
7
|
findProjectRoot,
|
|
6
8
|
listLinkedPackages,
|
|
@@ -8,6 +10,7 @@ import {
|
|
|
8
10
|
loadPlaybooks,
|
|
9
11
|
loadProjectConfig,
|
|
10
12
|
resolvePlaybookProject,
|
|
13
|
+
resolveTarget,
|
|
11
14
|
} from "@actiondock/core";
|
|
12
15
|
import type { PlaybookDefinition } from "@actiondock/core";
|
|
13
16
|
import { Command } from "commander";
|
|
@@ -23,13 +26,45 @@ export function registerPlaybookCommands(program: Command): void {
|
|
|
23
26
|
.command("list [patterns...]")
|
|
24
27
|
.description("List playbooks in current project or linked packages")
|
|
25
28
|
.option("-i, --intent <pattern>", "Regex or fuzzy intent filter; falls back to full list when no match")
|
|
29
|
+
.option("-P, --package <id>", "Target package ID or path")
|
|
30
|
+
.option("-p, --profile <name>", "Query against a specific profile")
|
|
31
|
+
.option("-s, --server <url>", "Remote server URL")
|
|
32
|
+
.option("-t, --token <token>", "Auth token for remote server")
|
|
26
33
|
.option("--no-fallback", "Disable fallback to full list when no items match intent")
|
|
27
34
|
.option("--json", "Output as JSON")
|
|
28
|
-
.action((patterns, options) => {
|
|
35
|
+
.action(async (patterns, options) => {
|
|
29
36
|
try {
|
|
30
37
|
const effectiveIntent = resolveIntent(options.intent, patterns);
|
|
31
38
|
const shouldFallback = options.fallback !== false;
|
|
32
39
|
|
|
40
|
+
const target = resolveTarget({
|
|
41
|
+
profile: options.profile,
|
|
42
|
+
server: options.server,
|
|
43
|
+
token: options.token,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
if (target.type === "remote") {
|
|
47
|
+
const remotePbs = await fetchRemotePlaybooks(target.serverUrl!, target.token, {
|
|
48
|
+
intent: effectiveIntent,
|
|
49
|
+
package: options.package,
|
|
50
|
+
});
|
|
51
|
+
if (options.json) {
|
|
52
|
+
console.log(JSON.stringify(remotePbs, null, 2));
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
console.log(
|
|
56
|
+
`Playbooks on remote server ${target.serverUrl}${target.profileName ? ` (Profile: ${target.profileName})` : ""}:\n`
|
|
57
|
+
);
|
|
58
|
+
if (remotePbs.length === 0) {
|
|
59
|
+
console.log(" (No playbooks found)");
|
|
60
|
+
} else {
|
|
61
|
+
for (const p of remotePbs) {
|
|
62
|
+
console.log(` ${p.id.padEnd(24)} ${p.description} (Package: ${p.packageId})`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
33
68
|
const root = findProjectRoot();
|
|
34
69
|
if (root) {
|
|
35
70
|
const config = loadProjectConfig(root);
|
|
@@ -228,9 +263,35 @@ This playbook provides task execution guidance for AI Agents.
|
|
|
228
263
|
pbCmd
|
|
229
264
|
.command("show <id>")
|
|
230
265
|
.description("Show playbook content and metadata (from current project or linked packages)")
|
|
266
|
+
.option("-p, --profile <name>", "Query against a specific profile")
|
|
267
|
+
.option("-s, --server <url>", "Remote server URL")
|
|
268
|
+
.option("-t, --token <token>", "Auth token for remote server")
|
|
231
269
|
.option("--json", "Output as JSON")
|
|
232
|
-
.action((id, options) => {
|
|
270
|
+
.action(async (id, options) => {
|
|
233
271
|
try {
|
|
272
|
+
const target = resolveTarget({
|
|
273
|
+
profile: options.profile,
|
|
274
|
+
server: options.server,
|
|
275
|
+
token: options.token,
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
if (target.type === "remote") {
|
|
279
|
+
const pb = await fetchRemotePlaybookShow(target.serverUrl!, id, target.token);
|
|
280
|
+
if (options.json) {
|
|
281
|
+
console.log(JSON.stringify(pb, null, 2));
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
console.log(`Playbook: ${pb.id} (Package: ${pb.packageId})`);
|
|
285
|
+
if (pb.description) console.log(`Description: ${pb.description}`);
|
|
286
|
+
if (pb.actions && pb.actions.length > 0) {
|
|
287
|
+
console.log(`Actions: ${pb.actions.join(", ")}`);
|
|
288
|
+
}
|
|
289
|
+
if (pb.filePath) console.log(`File: ${pb.filePath}\n`);
|
|
290
|
+
console.log("--- Content ---");
|
|
291
|
+
console.log(pb.content);
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
|
|
234
295
|
const resolved = resolvePlaybookProject(id);
|
|
235
296
|
const pb = resolved.playbook;
|
|
236
297
|
|
package/src/commands/runs.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import {
|
|
3
3
|
cancelRemoteRun,
|
|
4
|
+
clearRemoteRuns,
|
|
4
5
|
createStorage,
|
|
5
6
|
fetchRemoteRun,
|
|
7
|
+
fetchRemoteRuns,
|
|
6
8
|
filterWithFallbackInfo,
|
|
7
9
|
findProjectRoot,
|
|
8
10
|
listLinkedPackages,
|
|
@@ -49,14 +51,50 @@ export function registerRunsCommands(program: Command): void {
|
|
|
49
51
|
.option("-i, --intent <pattern>", "Regex or fuzzy intent filter; falls back to full list when no match")
|
|
50
52
|
.option("-a, --action <actionId>", "Filter by action ID")
|
|
51
53
|
.option("-n, --limit <count>", "Maximum number of records to return", "20")
|
|
54
|
+
.option("-p, --profile <name>", "Query against a specific profile")
|
|
55
|
+
.option("-s, --server <url>", "Remote server URL")
|
|
56
|
+
.option("-t, --token <token>", "Auth token for remote server")
|
|
52
57
|
.option("--no-fallback", "Disable fallback to full list when no items match intent")
|
|
53
58
|
.option("--json", "Output as JSON")
|
|
54
|
-
.action((patterns, options) => {
|
|
59
|
+
.action(async (patterns, options) => {
|
|
55
60
|
try {
|
|
56
61
|
const effectiveIntent = resolveIntent(options.intent, patterns);
|
|
57
62
|
const shouldFallback = options.fallback !== false;
|
|
58
63
|
const limit = Number.parseInt(options.limit, 10) || 20;
|
|
59
64
|
|
|
65
|
+
const target = resolveTarget({
|
|
66
|
+
profile: options.profile,
|
|
67
|
+
server: options.server,
|
|
68
|
+
token: options.token,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
if (target.type === "remote") {
|
|
72
|
+
const res = await fetchRemoteRuns(target.serverUrl!, target.token, {
|
|
73
|
+
packageId: options.package,
|
|
74
|
+
actionId: options.action,
|
|
75
|
+
intent: effectiveIntent,
|
|
76
|
+
limit,
|
|
77
|
+
});
|
|
78
|
+
if (options.json) {
|
|
79
|
+
console.log(JSON.stringify(res.items, null, 2));
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
console.log(
|
|
83
|
+
`Execution Runs on remote server ${target.serverUrl}${target.profileName ? ` (Profile: ${target.profileName})` : ""} (${res.items.length}):\n`
|
|
84
|
+
);
|
|
85
|
+
console.log(
|
|
86
|
+
` ${"RUN ID".padEnd(38)} ${"ACTION".padEnd(24)} ${"STATUS".padEnd(10)} ${"STARTED"}`
|
|
87
|
+
);
|
|
88
|
+
console.log(" " + "-".repeat(90));
|
|
89
|
+
for (const r of res.items) {
|
|
90
|
+
const time = (r.startedAt || "").replace("T", " ").slice(0, 19);
|
|
91
|
+
console.log(
|
|
92
|
+
` ${r.id.padEnd(38)} ${r.actionId.padEnd(24)} ${r.status.padEnd(10)} ${time}`
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
60
98
|
const targetRoot = options.package
|
|
61
99
|
? resolvePackageRoot(options.package)
|
|
62
100
|
: findProjectRoot();
|
|
@@ -315,5 +353,57 @@ export function registerRunsCommands(program: Command): void {
|
|
|
315
353
|
process.exit(1);
|
|
316
354
|
}
|
|
317
355
|
});
|
|
356
|
+
|
|
357
|
+
// runs clear
|
|
358
|
+
runsCmd
|
|
359
|
+
.command("clear")
|
|
360
|
+
.description("Clear execution run records (locally or on remote server)")
|
|
361
|
+
.option("-P, --package <id>", "Target package ID or path")
|
|
362
|
+
.option("-a, --action <actionId>", "Filter by action ID")
|
|
363
|
+
.option("-p, --profile <name>", "Target profile")
|
|
364
|
+
.option("-s, --server <url>", "Remote server URL")
|
|
365
|
+
.option("-t, --token <token>", "Auth token for remote server")
|
|
366
|
+
.option("--json", "Output as JSON")
|
|
367
|
+
.action(async (options) => {
|
|
368
|
+
try {
|
|
369
|
+
const target = resolveTarget({
|
|
370
|
+
profile: options.profile,
|
|
371
|
+
server: options.server,
|
|
372
|
+
token: options.token,
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
if (target.type === "remote") {
|
|
376
|
+
const res = await clearRemoteRuns(target.serverUrl!, target.token, {
|
|
377
|
+
packageId: options.package,
|
|
378
|
+
actionId: options.action,
|
|
379
|
+
});
|
|
380
|
+
if (options.json) {
|
|
381
|
+
console.log(JSON.stringify(res, null, 2));
|
|
382
|
+
} else {
|
|
383
|
+
console.log(`Cleared ${res.clearedCount} execution run(s) on remote server.`);
|
|
384
|
+
}
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const targetRoot = options.package
|
|
389
|
+
? resolvePackageRoot(options.package)
|
|
390
|
+
: findProjectRoot();
|
|
391
|
+
|
|
392
|
+
if (targetRoot) {
|
|
393
|
+
const projConfig = loadProjectConfig(targetRoot);
|
|
394
|
+
const storage = createStorage(projConfig.id, { projectRoot: targetRoot });
|
|
395
|
+
const count = storage.clearRuns({ actionId: options.action });
|
|
396
|
+
storage.close();
|
|
397
|
+
if (options.json) {
|
|
398
|
+
console.log(JSON.stringify({ ok: true, clearedCount: count }, null, 2));
|
|
399
|
+
} else {
|
|
400
|
+
console.log(`Cleared ${count} execution run(s) in package '${projConfig.id}'.`);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
} catch (err: any) {
|
|
404
|
+
console.error(`Error: ${err.message}`);
|
|
405
|
+
process.exit(1);
|
|
406
|
+
}
|
|
407
|
+
});
|
|
318
408
|
}
|
|
319
409
|
|
package/src/commands/serve.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { resolve } from "node:path";
|
|
2
2
|
import { findProjectRoot, loadProjectConfig, startActionDockServer } from "@actiondock/core";
|
|
3
|
+
import { createActionDockMcpServer } from "@actiondock/mcp";
|
|
4
|
+
import { createMcpHandler } from "@modelcontextprotocol/server";
|
|
3
5
|
import { Command } from "commander";
|
|
4
6
|
import { parseByteSize } from "../utils/bytes";
|
|
5
7
|
|
|
@@ -19,6 +21,7 @@ export function registerServeCommand(program: Command): void {
|
|
|
19
21
|
)
|
|
20
22
|
.option("--max-body <size>", "Maximum allowed JSON request body size (e.g. 1mb, 500kb)", "1mb")
|
|
21
23
|
.option("--expose-debug-info", "Expose project root path in health and info responses")
|
|
24
|
+
.option("--no-mcp", "Disable unified MCP protocol endpoint at /mcp")
|
|
22
25
|
.option("-d, --dir <path>", "Project root directory (default: current working directory)")
|
|
23
26
|
.action(async (options) => {
|
|
24
27
|
const port = parseInt(options.port, 10) || 5177;
|
|
@@ -55,6 +58,30 @@ export function registerServeCommand(program: Command): void {
|
|
|
55
58
|
}
|
|
56
59
|
}
|
|
57
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
|
+
|
|
58
85
|
try {
|
|
59
86
|
const server = startActionDockServer({
|
|
60
87
|
port,
|
|
@@ -64,6 +91,8 @@ export function registerServeCommand(program: Command): void {
|
|
|
64
91
|
corsOrigins,
|
|
65
92
|
maxBodyBytes,
|
|
66
93
|
exposeDebugInfo,
|
|
94
|
+
enableMcp,
|
|
95
|
+
mcpHandler,
|
|
67
96
|
projectRoot: projectRoot || undefined,
|
|
68
97
|
});
|
|
69
98
|
|
|
@@ -79,6 +108,9 @@ export function registerServeCommand(program: Command): void {
|
|
|
79
108
|
console.log(` * CORS Origins: ${corsOrigins ? corsOrigins.join(", ") : "Disabled (Default)"}`);
|
|
80
109
|
console.log(` * Max Body Size: ${options.maxBody || "1mb"}`);
|
|
81
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
|
+
}
|
|
82
114
|
console.log(`======================================================\n`);
|
|
83
115
|
console.log(`Server is ready to accept remote 'ad run' requests.`);
|
|
84
116
|
console.log(`Press Ctrl+C to terminate.\n`);
|
package/src/commands/state.ts
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import {
|
|
3
|
+
clearRemoteState,
|
|
3
4
|
createStorage,
|
|
5
|
+
deleteRemoteStateKey,
|
|
6
|
+
fetchRemoteStateList,
|
|
4
7
|
filterWithFallbackInfo,
|
|
5
8
|
findProjectRoot,
|
|
9
|
+
getRemoteStateKey,
|
|
6
10
|
listLinkedPackages,
|
|
7
11
|
loadProjectConfig,
|
|
8
12
|
resolvePackageRoot,
|
|
13
|
+
resolveTarget,
|
|
14
|
+
setRemoteStateKey,
|
|
9
15
|
} from "@actiondock/core";
|
|
10
16
|
import { Command } from "commander";
|
|
11
17
|
import { resolveIntent } from "../utils/filter";
|
|
@@ -47,6 +53,9 @@ export function registerStateCommands(program: Command): void {
|
|
|
47
53
|
.description("List state keys matching prefix, namespace, or intent pattern (in current project or linked packages)")
|
|
48
54
|
.option("-P, --package <id>", "Target package ID or path")
|
|
49
55
|
.option("-n, --namespace <ns>", "Filter by specific namespace")
|
|
56
|
+
.option("-p, --profile <name>", "Query state on a remote target")
|
|
57
|
+
.option("-s, --server <url>", "Remote server URL")
|
|
58
|
+
.option("-t, --token <token>", "Auth token for remote server")
|
|
50
59
|
.option("-i, --intent <pattern>", "Regex or fuzzy intent filter; falls back to full list when no match")
|
|
51
60
|
.option("--no-fallback", "Disable fallback to full list when no items match intent")
|
|
52
61
|
.option("-d, --detail", "Show detailed state entry objects (including namespace, expiration, update time)")
|
|
@@ -56,6 +65,31 @@ export function registerStateCommands(program: Command): void {
|
|
|
56
65
|
const effectiveIntent = resolveIntent(options.intent, prefix ? [prefix] : []);
|
|
57
66
|
const shouldFallback = options.fallback !== false;
|
|
58
67
|
|
|
68
|
+
const target = resolveTarget({
|
|
69
|
+
profile: options.profile,
|
|
70
|
+
server: options.server,
|
|
71
|
+
token: options.token,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
if (target.type === "remote") {
|
|
75
|
+
const res = await fetchRemoteStateList(target.serverUrl!, target.token, {
|
|
76
|
+
package: options.package,
|
|
77
|
+
namespace: options.namespace,
|
|
78
|
+
prefix,
|
|
79
|
+
});
|
|
80
|
+
if (options.json) {
|
|
81
|
+
console.log(JSON.stringify(res.keys, null, 2));
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
console.log(
|
|
85
|
+
`State keys on remote server ${target.serverUrl}${target.profileName ? ` (Profile: ${target.profileName})` : ""} (${res.keys.length}):\n`
|
|
86
|
+
);
|
|
87
|
+
for (const k of res.keys) {
|
|
88
|
+
console.log(` - ${k}`);
|
|
89
|
+
}
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
59
93
|
const targetRoot = options.package
|
|
60
94
|
? resolvePackageRoot(options.package)
|
|
61
95
|
: findProjectRoot();
|
|
@@ -236,10 +270,32 @@ export function registerStateCommands(program: Command): void {
|
|
|
236
270
|
.description("Get a state value by key, namespace:key, or package/namespace:key")
|
|
237
271
|
.option("-P, --package <id>", "Target package ID or path")
|
|
238
272
|
.option("-n, --namespace <ns>", "Target namespace")
|
|
273
|
+
.option("-p, --profile <name>", "Query state on a remote target")
|
|
274
|
+
.option("-s, --server <url>", "Remote server URL")
|
|
275
|
+
.option("-t, --token <token>", "Auth token for remote server")
|
|
239
276
|
.option("--json", "Output as JSON")
|
|
240
277
|
.action(async (rawKey, options) => {
|
|
241
|
-
const { root, key } = getTargetRoot(options.package, rawKey);
|
|
242
278
|
try {
|
|
279
|
+
const target = resolveTarget({
|
|
280
|
+
profile: options.profile,
|
|
281
|
+
server: options.server,
|
|
282
|
+
token: options.token,
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
if (target.type === "remote") {
|
|
286
|
+
const res = await getRemoteStateKey(target.serverUrl!, rawKey, target.token, {
|
|
287
|
+
package: options.package,
|
|
288
|
+
namespace: options.namespace,
|
|
289
|
+
});
|
|
290
|
+
if (options.json) {
|
|
291
|
+
console.log(JSON.stringify(res, null, 2));
|
|
292
|
+
} else {
|
|
293
|
+
console.log(res.value !== undefined ? JSON.stringify(res.value, null, 2) : "undefined");
|
|
294
|
+
}
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const { root, key } = getTargetRoot(options.package, rawKey);
|
|
243
299
|
const projConfig = loadProjectConfig(root);
|
|
244
300
|
const storage = createStorage(projConfig.id, { projectRoot: root });
|
|
245
301
|
|
|
@@ -290,10 +346,36 @@ export function registerStateCommands(program: Command): void {
|
|
|
290
346
|
.description("Set a state value by key, namespace:key, or package/namespace:key")
|
|
291
347
|
.option("-P, --package <id>", "Target package ID or path")
|
|
292
348
|
.option("-n, --namespace <ns>", "Target namespace")
|
|
349
|
+
.option("-p, --profile <name>", "Set state on a remote target")
|
|
350
|
+
.option("-s, --server <url>", "Remote server URL")
|
|
351
|
+
.option("-t, --token <token>", "Auth token for remote server")
|
|
293
352
|
.option("--ttl <seconds>", "Time to live in seconds", (v) => parseInt(v, 10))
|
|
294
353
|
.action(async (rawKey, rawValue, options) => {
|
|
295
|
-
const { root, key } = getTargetRoot(options.package, rawKey);
|
|
296
354
|
try {
|
|
355
|
+
let parsed: unknown = rawValue;
|
|
356
|
+
try {
|
|
357
|
+
parsed = JSON.parse(rawValue);
|
|
358
|
+
} catch {
|
|
359
|
+
parsed = rawValue;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
const target = resolveTarget({
|
|
363
|
+
profile: options.profile,
|
|
364
|
+
server: options.server,
|
|
365
|
+
token: options.token,
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
if (target.type === "remote") {
|
|
369
|
+
await setRemoteStateKey(target.serverUrl!, rawKey, parsed, target.token, {
|
|
370
|
+
package: options.package,
|
|
371
|
+
namespace: options.namespace,
|
|
372
|
+
ttl: options.ttl,
|
|
373
|
+
});
|
|
374
|
+
console.log(`[OK] State '${rawKey}' updated on remote server ${target.serverUrl}`);
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
const { root, key } = getTargetRoot(options.package, rawKey);
|
|
297
379
|
const projConfig = loadProjectConfig(root);
|
|
298
380
|
const storage = createStorage(projConfig.id, { projectRoot: root });
|
|
299
381
|
|
|
@@ -309,13 +391,6 @@ export function registerStateCommands(program: Command): void {
|
|
|
309
391
|
actualKey = key.slice(colonIdx + 1);
|
|
310
392
|
}
|
|
311
393
|
|
|
312
|
-
let parsed: unknown = rawValue;
|
|
313
|
-
try {
|
|
314
|
-
parsed = JSON.parse(rawValue);
|
|
315
|
-
} catch {
|
|
316
|
-
parsed = rawValue;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
394
|
await storage.setState(ns, actualKey, parsed, options.ttl);
|
|
320
395
|
storage.close();
|
|
321
396
|
|
|
@@ -336,10 +411,28 @@ export function registerStateCommands(program: Command): void {
|
|
|
336
411
|
.description("Delete a state value by key, namespace:key, or package/namespace:key")
|
|
337
412
|
.option("-P, --package <id>", "Target package ID or path")
|
|
338
413
|
.option("-n, --namespace <ns>", "Target namespace")
|
|
414
|
+
.option("-p, --profile <name>", "Delete state on a remote target")
|
|
415
|
+
.option("-s, --server <url>", "Remote server URL")
|
|
416
|
+
.option("-t, --token <token>", "Auth token for remote server")
|
|
339
417
|
.option("--silent", "Do not exit with error if key is not found")
|
|
340
418
|
.action(async (rawKey, options) => {
|
|
341
|
-
const { root, key } = getTargetRoot(options.package, rawKey);
|
|
342
419
|
try {
|
|
420
|
+
const target = resolveTarget({
|
|
421
|
+
profile: options.profile,
|
|
422
|
+
server: options.server,
|
|
423
|
+
token: options.token,
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
if (target.type === "remote") {
|
|
427
|
+
await deleteRemoteStateKey(target.serverUrl!, rawKey, target.token, {
|
|
428
|
+
package: options.package,
|
|
429
|
+
namespace: options.namespace,
|
|
430
|
+
});
|
|
431
|
+
console.log(`[OK] State '${rawKey}' deleted from remote server ${target.serverUrl}`);
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const { root, key } = getTargetRoot(options.package, rawKey);
|
|
343
436
|
const projConfig = loadProjectConfig(root);
|
|
344
437
|
const storage = createStorage(projConfig.id, { projectRoot: root });
|
|
345
438
|
const deleted = await storage.deleteStateSmart(key, options.namespace);
|
|
@@ -366,10 +459,30 @@ export function registerStateCommands(program: Command): void {
|
|
|
366
459
|
.description("Clear state entries by namespace, prefix, or all")
|
|
367
460
|
.option("-P, --package <id>", "Target package ID or path")
|
|
368
461
|
.option("-n, --namespace <ns>", "Target namespace to clear")
|
|
462
|
+
.option("-p, --profile <name>", "Clear state on a remote target")
|
|
463
|
+
.option("-s, --server <url>", "Remote server URL")
|
|
464
|
+
.option("-t, --token <token>", "Auth token for remote server")
|
|
369
465
|
.option("-a, --all", "Clear all state entries across all namespaces in this package")
|
|
370
466
|
.action(async (prefix = "", options) => {
|
|
371
|
-
const { root } = getTargetRoot(options.package);
|
|
372
467
|
try {
|
|
468
|
+
const target = resolveTarget({
|
|
469
|
+
profile: options.profile,
|
|
470
|
+
server: options.server,
|
|
471
|
+
token: options.token,
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
if (target.type === "remote") {
|
|
475
|
+
const res = await clearRemoteState(target.serverUrl!, target.token, {
|
|
476
|
+
package: options.package,
|
|
477
|
+
namespace: options.namespace,
|
|
478
|
+
prefix,
|
|
479
|
+
all: Boolean(options.all),
|
|
480
|
+
});
|
|
481
|
+
console.log(`[OK] Cleared ${res.clearedCount} state entry(s) on remote server ${target.serverUrl}`);
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
const { root } = getTargetRoot(options.package);
|
|
373
486
|
const projConfig = loadProjectConfig(root);
|
|
374
487
|
const storage = createStorage(projConfig.id, { projectRoot: root });
|
|
375
488
|
const count = await storage.clearState({
|