@actiondock/cli 2.0.11-beta.0 → 2.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -2
- package/bin/ad.js +13 -2
- package/dist/commands/action/create.d.ts +2 -0
- package/dist/commands/action/index.d.ts +16 -0
- package/dist/commands/action/list.d.ts +5 -0
- package/dist/commands/action/run.d.ts +4 -0
- package/dist/commands/action/show.d.ts +5 -0
- package/dist/commands/action/sync.d.ts +2 -0
- package/dist/commands/action/validate.d.ts +5 -0
- package/dist/commands/build.d.ts +2 -0
- package/dist/commands/config.d.ts +2 -0
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/export.d.ts +2 -0
- package/dist/commands/index.d.ts +2 -0
- package/dist/commands/info.d.ts +2 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/link.d.ts +2 -0
- package/dist/commands/mcp.d.ts +2 -0
- package/dist/commands/playbook.d.ts +6 -0
- package/dist/commands/profile.d.ts +2 -0
- package/dist/commands/runs.d.ts +2 -0
- package/dist/commands/serve.d.ts +2 -0
- package/dist/commands/state.d.ts +2 -0
- package/dist/commands/test.d.ts +2 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +745 -65708
- package/{src/utils/bytes.ts → dist/utils/bytes.d.ts} +0 -1
- package/{src/utils/filter.ts → dist/utils/filter.d.ts} +0 -1
- package/package.json +20 -15
- package/src/commands/action/create.ts +0 -129
- package/src/commands/action/index.ts +0 -27
- package/src/commands/action/list.ts +0 -247
- package/src/commands/action/run.ts +0 -197
- package/src/commands/action/show.ts +0 -128
- package/src/commands/action/sync.ts +0 -76
- package/src/commands/action/validate.ts +0 -102
- package/src/commands/build.ts +0 -56
- package/src/commands/config.ts +0 -583
- package/src/commands/doctor.ts +0 -84
- package/src/commands/export.ts +0 -196
- package/src/commands/index.ts +0 -75
- package/src/commands/info.ts +0 -537
- package/src/commands/init.ts +0 -24
- package/src/commands/link.ts +0 -73
- package/src/commands/mcp.ts +0 -195
- package/src/commands/playbook.ts +0 -566
- package/src/commands/profile.ts +0 -296
- package/src/commands/runs.ts +0 -452
- package/src/commands/serve.ts +0 -132
- package/src/commands/state.ts +0 -590
- package/src/commands/test.ts +0 -48
- package/src/index.ts +0 -58
- /package/{src/commands/action.ts → dist/commands/action.d.ts} +0 -0
- /package/{src/utils/duration.ts → dist/utils/duration.d.ts} +0 -0
package/src/commands/state.ts
DELETED
|
@@ -1,590 +0,0 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
2
|
-
import {
|
|
3
|
-
clearRemoteState,
|
|
4
|
-
createStorage,
|
|
5
|
-
deleteRemoteStateKey,
|
|
6
|
-
fetchRemoteStateList,
|
|
7
|
-
filterWithFallbackInfo,
|
|
8
|
-
findProjectRoot,
|
|
9
|
-
getRemoteStateKey,
|
|
10
|
-
listLinkedPackages,
|
|
11
|
-
loadProjectConfig,
|
|
12
|
-
resolvePackageRoot,
|
|
13
|
-
resolveTarget,
|
|
14
|
-
setRemoteStateKey,
|
|
15
|
-
} from "@actiondock/core";
|
|
16
|
-
import { ArgumentError, ExecutionError, getEffectiveOptions, renderResult } from "@actiondock/runtime-cli";
|
|
17
|
-
import { Command } from "commander";
|
|
18
|
-
import { resolveIntent } from "../utils/filter";
|
|
19
|
-
|
|
20
|
-
function getTargetRoot(packageOption?: string, keyHint?: string): { root: string; key: string } {
|
|
21
|
-
let targetPackage = packageOption;
|
|
22
|
-
let effectiveKey = keyHint || "";
|
|
23
|
-
|
|
24
|
-
if (!targetPackage && keyHint) {
|
|
25
|
-
if (keyHint.includes("/")) {
|
|
26
|
-
const slashIdx = keyHint.indexOf("/");
|
|
27
|
-
targetPackage = keyHint.slice(0, slashIdx);
|
|
28
|
-
effectiveKey = keyHint.slice(slashIdx + 1);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const root = resolvePackageRoot(targetPackage);
|
|
33
|
-
if (!root) {
|
|
34
|
-
if (targetPackage) {
|
|
35
|
-
throw new ArgumentError(`Package '${targetPackage}' not found in linked packages or path`);
|
|
36
|
-
}
|
|
37
|
-
throw new ArgumentError(
|
|
38
|
-
"Not in an ActionDock project (actiondock.json not found).\nPlease specify -P, --package <id> or cd into a project directory."
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
return { root, key: effectiveKey };
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export function registerStateCommands(program: Command): void {
|
|
45
|
-
const stateCmd = program
|
|
46
|
-
.command("state")
|
|
47
|
-
.description("Inspect and manage Shared State store");
|
|
48
|
-
|
|
49
|
-
// state list
|
|
50
|
-
stateCmd
|
|
51
|
-
.command("list [prefix]")
|
|
52
|
-
.description("List state keys matching prefix, namespace, or intent pattern (in current project or linked packages)")
|
|
53
|
-
.option("-P, --package <id>", "Target package ID or path")
|
|
54
|
-
.option("-n, --namespace <ns>", "Filter by specific namespace")
|
|
55
|
-
.option("-p, --profile <name>", "Query state on a remote target")
|
|
56
|
-
.option("-s, --server <url>", "Remote server URL")
|
|
57
|
-
.option("-t, --token <token>", "Auth token for remote server")
|
|
58
|
-
.option("-i, --intent <pattern>", "Regex or fuzzy intent filter; falls back to full list when no match")
|
|
59
|
-
.option("--no-fallback", "Disable fallback to full list when no items match intent")
|
|
60
|
-
.option("-d, --detail", "Show detailed state entry objects (including namespace, expiration, update time)")
|
|
61
|
-
.option("--data-dir <path>", "Custom database storage directory")
|
|
62
|
-
.option("--json", "Output as JSON")
|
|
63
|
-
.option("--envelope", "Wrap JSON output in standard envelope")
|
|
64
|
-
.action(async (prefix = "", rawOptions, cmd) => {
|
|
65
|
-
try {
|
|
66
|
-
const options = getEffectiveOptions(rawOptions, cmd);
|
|
67
|
-
const effectiveIntent = resolveIntent(options.intent, prefix ? [prefix] : []);
|
|
68
|
-
const shouldFallback = options.fallback !== false;
|
|
69
|
-
const isMachine = Boolean(options.json || options.envelope);
|
|
70
|
-
|
|
71
|
-
const target = resolveTarget({
|
|
72
|
-
profile: options.profile,
|
|
73
|
-
server: options.server,
|
|
74
|
-
token: options.token,
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
if (target.type === "remote") {
|
|
78
|
-
const res = await fetchRemoteStateList(target.serverUrl!, target.token, {
|
|
79
|
-
package: options.package,
|
|
80
|
-
namespace: options.namespace,
|
|
81
|
-
prefix,
|
|
82
|
-
});
|
|
83
|
-
if (isMachine) {
|
|
84
|
-
renderResult(res.keys, { json: options.json, envelope: options.envelope });
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
console.log(
|
|
88
|
-
`State keys on remote server ${target.serverUrl}${target.profileName ? ` (Profile: ${target.profileName})` : ""} (${res.keys.length}):\n`
|
|
89
|
-
);
|
|
90
|
-
for (const k of res.keys) {
|
|
91
|
-
console.log(` - ${k}`);
|
|
92
|
-
}
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const targetRoot = options.package
|
|
97
|
-
? resolvePackageRoot(options.package)
|
|
98
|
-
: findProjectRoot();
|
|
99
|
-
|
|
100
|
-
if (options.package && !targetRoot) {
|
|
101
|
-
throw new ArgumentError(`Package '${options.package}' not found in linked packages or path`);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (targetRoot) {
|
|
105
|
-
const projConfig = loadProjectConfig(targetRoot);
|
|
106
|
-
const storage = createStorage(projConfig.id, {
|
|
107
|
-
projectRoot: targetRoot,
|
|
108
|
-
dataDir: options.dataDir,
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
if (options.detail && isMachine) {
|
|
112
|
-
const entries = await storage.listStateEntries({
|
|
113
|
-
namespace: options.namespace,
|
|
114
|
-
prefix: prefix || undefined,
|
|
115
|
-
});
|
|
116
|
-
storage.close();
|
|
117
|
-
|
|
118
|
-
const filterRes = filterWithFallbackInfo(
|
|
119
|
-
entries,
|
|
120
|
-
effectiveIntent,
|
|
121
|
-
[(e) => e.fullKey, (e) => e.key, (e) => e.namespace],
|
|
122
|
-
shouldFallback
|
|
123
|
-
);
|
|
124
|
-
|
|
125
|
-
renderResult(filterRes.items, { json: options.json, envelope: options.envelope });
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const allKeys = await storage.listStateKeys(
|
|
130
|
-
options.namespace !== undefined ? options.namespace : null,
|
|
131
|
-
prefix
|
|
132
|
-
);
|
|
133
|
-
storage.close();
|
|
134
|
-
|
|
135
|
-
const filterRes = filterWithFallbackInfo(
|
|
136
|
-
allKeys,
|
|
137
|
-
effectiveIntent,
|
|
138
|
-
[(k) => k],
|
|
139
|
-
shouldFallback
|
|
140
|
-
);
|
|
141
|
-
|
|
142
|
-
if (isMachine) {
|
|
143
|
-
renderResult(filterRes.items, { json: options.json, envelope: options.envelope });
|
|
144
|
-
} else {
|
|
145
|
-
const nsDesc = options.namespace ? ` [namespace: ${options.namespace}]` : "";
|
|
146
|
-
const filterDesc = prefix ? ` (filter: ${prefix})` : "";
|
|
147
|
-
console.log(`State keys for ${projConfig.id} (${targetRoot})${nsDesc}${filterDesc}:\n`);
|
|
148
|
-
if (filterRes.isFallback && effectiveIntent) {
|
|
149
|
-
console.log(`(No state keys matched intent '${effectiveIntent}', showing all keys)\n`);
|
|
150
|
-
}
|
|
151
|
-
if (filterRes.items.length === 0) {
|
|
152
|
-
console.log(" (no state keys found)\n");
|
|
153
|
-
} else {
|
|
154
|
-
for (const k of filterRes.items) {
|
|
155
|
-
console.log(` - ${k}`);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// Outside project: list state keys across all linked packages
|
|
163
|
-
const linkedList = listLinkedPackages();
|
|
164
|
-
if (linkedList.length === 0) {
|
|
165
|
-
if (isMachine) {
|
|
166
|
-
renderResult([], { json: options.json, envelope: options.envelope });
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
console.log("No ActionDock project in current directory, and no packages linked.");
|
|
170
|
-
console.log("Run 'ad link' inside an Action package to register it.");
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
const aggregated: Array<{
|
|
175
|
-
packageId: string;
|
|
176
|
-
packageName: string;
|
|
177
|
-
path: string;
|
|
178
|
-
keys: string[];
|
|
179
|
-
entries?: any[];
|
|
180
|
-
}> = [];
|
|
181
|
-
|
|
182
|
-
for (const pkg of linkedList) {
|
|
183
|
-
if (!existsSync(pkg.path)) continue;
|
|
184
|
-
try {
|
|
185
|
-
const projConfig = loadProjectConfig(pkg.path);
|
|
186
|
-
const storage = createStorage(projConfig.id, {
|
|
187
|
-
projectRoot: pkg.path,
|
|
188
|
-
dataDir: options.dataDir,
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
if (options.detail && isMachine) {
|
|
192
|
-
const entries = await storage.listStateEntries({
|
|
193
|
-
namespace: options.namespace,
|
|
194
|
-
prefix: prefix || undefined,
|
|
195
|
-
});
|
|
196
|
-
storage.close();
|
|
197
|
-
aggregated.push({
|
|
198
|
-
packageId: pkg.id,
|
|
199
|
-
packageName: pkg.name,
|
|
200
|
-
path: pkg.path,
|
|
201
|
-
keys: entries.map((e) => e.fullKey),
|
|
202
|
-
entries,
|
|
203
|
-
});
|
|
204
|
-
} else {
|
|
205
|
-
const keys = await storage.listStateKeys(
|
|
206
|
-
options.namespace !== undefined ? options.namespace : null,
|
|
207
|
-
prefix
|
|
208
|
-
);
|
|
209
|
-
storage.close();
|
|
210
|
-
aggregated.push({
|
|
211
|
-
packageId: pkg.id,
|
|
212
|
-
packageName: pkg.name,
|
|
213
|
-
path: pkg.path,
|
|
214
|
-
keys,
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
} catch {
|
|
218
|
-
// Ignore broken linked package
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
let filteredPackages: typeof aggregated = [];
|
|
223
|
-
if (!effectiveIntent) {
|
|
224
|
-
filteredPackages = aggregated;
|
|
225
|
-
} else {
|
|
226
|
-
for (const pkg of aggregated) {
|
|
227
|
-
const pkgMatches = filterWithFallbackInfo(
|
|
228
|
-
[pkg],
|
|
229
|
-
effectiveIntent,
|
|
230
|
-
[(p) => p.packageId, (p) => p.packageName, (p) => p.path],
|
|
231
|
-
false
|
|
232
|
-
).matchedCount > 0;
|
|
233
|
-
|
|
234
|
-
if (pkgMatches) {
|
|
235
|
-
filteredPackages.push(pkg);
|
|
236
|
-
} else {
|
|
237
|
-
const matchedKeys = filterWithFallbackInfo(
|
|
238
|
-
pkg.keys,
|
|
239
|
-
effectiveIntent,
|
|
240
|
-
[(k) => k],
|
|
241
|
-
false
|
|
242
|
-
).items;
|
|
243
|
-
|
|
244
|
-
if (matchedKeys.length > 0) {
|
|
245
|
-
filteredPackages.push({
|
|
246
|
-
...pkg,
|
|
247
|
-
keys: matchedKeys,
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
if (filteredPackages.length === 0 && shouldFallback) {
|
|
254
|
-
filteredPackages = aggregated;
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
if (isMachine) {
|
|
259
|
-
renderResult(filteredPackages, { json: options.json, envelope: options.envelope });
|
|
260
|
-
} else {
|
|
261
|
-
console.log("State Keys in Linked Packages:\n");
|
|
262
|
-
for (const pkg of filteredPackages) {
|
|
263
|
-
console.log(`* Package: ${pkg.packageId} (${pkg.path})`);
|
|
264
|
-
if (pkg.keys.length === 0) {
|
|
265
|
-
console.log(" (no state keys found)");
|
|
266
|
-
} else {
|
|
267
|
-
for (const k of pkg.keys) {
|
|
268
|
-
console.log(` - ${k}`);
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
} catch (err: any) {
|
|
274
|
-
if (err instanceof ArgumentError || err instanceof ExecutionError) throw err;
|
|
275
|
-
throw new ExecutionError(err.message);
|
|
276
|
-
}
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
// state get
|
|
280
|
-
stateCmd
|
|
281
|
-
.command("get <key>")
|
|
282
|
-
.description("Get a state value by key, namespace:key, or package/namespace:key")
|
|
283
|
-
.option("-P, --package <id>", "Target package ID or path")
|
|
284
|
-
.option("-n, --namespace <ns>", "Target namespace")
|
|
285
|
-
.option("-p, --profile <name>", "Query state on a remote target")
|
|
286
|
-
.option("-s, --server <url>", "Remote server URL")
|
|
287
|
-
.option("-t, --token <token>", "Auth token for remote server")
|
|
288
|
-
.option("--data-dir <path>", "Custom database storage directory")
|
|
289
|
-
.option("--json", "Output as JSON")
|
|
290
|
-
.option("--envelope", "Wrap JSON output in standard envelope")
|
|
291
|
-
.action(async (rawKey, rawOptions, cmd) => {
|
|
292
|
-
try {
|
|
293
|
-
const options = getEffectiveOptions(rawOptions, cmd);
|
|
294
|
-
const isMachine = Boolean(options.json || options.envelope);
|
|
295
|
-
const target = resolveTarget({
|
|
296
|
-
profile: options.profile,
|
|
297
|
-
server: options.server,
|
|
298
|
-
token: options.token,
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
if (target.type === "remote") {
|
|
302
|
-
const res = await getRemoteStateKey(target.serverUrl!, rawKey, target.token, {
|
|
303
|
-
package: options.package,
|
|
304
|
-
namespace: options.namespace,
|
|
305
|
-
});
|
|
306
|
-
if (isMachine) {
|
|
307
|
-
renderResult(res, { json: options.json, envelope: options.envelope });
|
|
308
|
-
} else {
|
|
309
|
-
console.log(res.value !== undefined ? JSON.stringify(res.value, null, 2) : "undefined");
|
|
310
|
-
}
|
|
311
|
-
return;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
const { root, key } = getTargetRoot(options.package, rawKey);
|
|
315
|
-
const projConfig = loadProjectConfig(root);
|
|
316
|
-
const storage = createStorage(projConfig.id, {
|
|
317
|
-
projectRoot: root,
|
|
318
|
-
dataDir: options.dataDir,
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
let val: unknown;
|
|
322
|
-
let entryNamespace = options.namespace || "";
|
|
323
|
-
|
|
324
|
-
if (options.namespace !== undefined) {
|
|
325
|
-
val = await storage.getState(options.namespace, key);
|
|
326
|
-
} else {
|
|
327
|
-
const entry = await storage.findState(key);
|
|
328
|
-
if (entry) {
|
|
329
|
-
val = entry.value;
|
|
330
|
-
entryNamespace = entry.namespace;
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
storage.close();
|
|
334
|
-
|
|
335
|
-
if (val === undefined) {
|
|
336
|
-
throw new ExecutionError(`State key '${key}' not found in ${projConfig.id}`);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
if (isMachine) {
|
|
340
|
-
renderResult(
|
|
341
|
-
{
|
|
342
|
-
key,
|
|
343
|
-
packageId: projConfig.id,
|
|
344
|
-
namespace: entryNamespace,
|
|
345
|
-
value: val,
|
|
346
|
-
},
|
|
347
|
-
{ json: options.json, envelope: options.envelope }
|
|
348
|
-
);
|
|
349
|
-
} else {
|
|
350
|
-
console.log(val !== undefined ? JSON.stringify(val, null, 2) : "undefined");
|
|
351
|
-
}
|
|
352
|
-
} catch (err: any) {
|
|
353
|
-
if (err instanceof ArgumentError || err instanceof ExecutionError) throw err;
|
|
354
|
-
throw new ExecutionError(err.message);
|
|
355
|
-
}
|
|
356
|
-
});
|
|
357
|
-
|
|
358
|
-
// state set
|
|
359
|
-
stateCmd
|
|
360
|
-
.command("set <key> <value>")
|
|
361
|
-
.description("Set a state value by key, namespace:key, or package/namespace:key")
|
|
362
|
-
.option("-P, --package <id>", "Target package ID or path")
|
|
363
|
-
.option("-n, --namespace <ns>", "Target namespace")
|
|
364
|
-
.option("-p, --profile <name>", "Set state on a remote target")
|
|
365
|
-
.option("-s, --server <url>", "Remote server URL")
|
|
366
|
-
.option("-t, --token <token>", "Auth token for remote server")
|
|
367
|
-
.option("--ttl <seconds>", "Time to live in seconds", (v) => parseInt(v, 10))
|
|
368
|
-
.option("--data-dir <path>", "Custom database storage directory")
|
|
369
|
-
.option("--json", "Output as JSON")
|
|
370
|
-
.option("--envelope", "Wrap JSON output in standard envelope")
|
|
371
|
-
.action(async (rawKey, rawValue, rawOptions, cmd) => {
|
|
372
|
-
try {
|
|
373
|
-
const options = getEffectiveOptions(rawOptions, cmd);
|
|
374
|
-
const isMachine = Boolean(options.json || options.envelope);
|
|
375
|
-
let parsed: unknown = rawValue;
|
|
376
|
-
try {
|
|
377
|
-
parsed = JSON.parse(rawValue);
|
|
378
|
-
} catch {
|
|
379
|
-
parsed = rawValue;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
const target = resolveTarget({
|
|
383
|
-
profile: options.profile,
|
|
384
|
-
server: options.server,
|
|
385
|
-
token: options.token,
|
|
386
|
-
});
|
|
387
|
-
|
|
388
|
-
if (target.type === "remote") {
|
|
389
|
-
await setRemoteStateKey(target.serverUrl!, rawKey, parsed, target.token, {
|
|
390
|
-
package: options.package,
|
|
391
|
-
namespace: options.namespace,
|
|
392
|
-
ttl: options.ttl,
|
|
393
|
-
});
|
|
394
|
-
if (isMachine) {
|
|
395
|
-
renderResult(
|
|
396
|
-
{ ok: true, key: rawKey, value: parsed, ttl: options.ttl },
|
|
397
|
-
{ json: options.json, envelope: options.envelope }
|
|
398
|
-
);
|
|
399
|
-
} else {
|
|
400
|
-
console.log(`[OK] State '${rawKey}' updated on remote server ${target.serverUrl}`);
|
|
401
|
-
}
|
|
402
|
-
return;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
const { root, key } = getTargetRoot(options.package, rawKey);
|
|
406
|
-
const projConfig = loadProjectConfig(root);
|
|
407
|
-
const storage = createStorage(projConfig.id, {
|
|
408
|
-
projectRoot: root,
|
|
409
|
-
dataDir: options.dataDir,
|
|
410
|
-
});
|
|
411
|
-
|
|
412
|
-
let ns = "";
|
|
413
|
-
let actualKey = key;
|
|
414
|
-
|
|
415
|
-
if (options.namespace !== undefined) {
|
|
416
|
-
ns = options.namespace;
|
|
417
|
-
actualKey = key;
|
|
418
|
-
} else if (key.includes(":")) {
|
|
419
|
-
const colonIdx = key.indexOf(":");
|
|
420
|
-
ns = key.slice(0, colonIdx);
|
|
421
|
-
actualKey = key.slice(colonIdx + 1);
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
await storage.setState(ns, actualKey, parsed, options.ttl);
|
|
425
|
-
storage.close();
|
|
426
|
-
|
|
427
|
-
const displayKey = ns ? `${ns}:${actualKey}` : actualKey;
|
|
428
|
-
if (isMachine) {
|
|
429
|
-
renderResult(
|
|
430
|
-
{
|
|
431
|
-
ok: true,
|
|
432
|
-
key: displayKey,
|
|
433
|
-
value: parsed,
|
|
434
|
-
ttl: options.ttl,
|
|
435
|
-
packageId: projConfig.id,
|
|
436
|
-
},
|
|
437
|
-
{ json: options.json, envelope: options.envelope }
|
|
438
|
-
);
|
|
439
|
-
} else {
|
|
440
|
-
console.log(
|
|
441
|
-
`[OK] State '${displayKey}' set to ${JSON.stringify(parsed)}${options.ttl ? ` (TTL: ${options.ttl}s)` : ""} in ${projConfig.id}`
|
|
442
|
-
);
|
|
443
|
-
}
|
|
444
|
-
} catch (err: any) {
|
|
445
|
-
if (err instanceof ArgumentError || err instanceof ExecutionError) throw err;
|
|
446
|
-
throw new ExecutionError(err.message);
|
|
447
|
-
}
|
|
448
|
-
});
|
|
449
|
-
|
|
450
|
-
// state delete
|
|
451
|
-
stateCmd
|
|
452
|
-
.command("delete <key>")
|
|
453
|
-
.alias("rm")
|
|
454
|
-
.description("Delete a state value by key, namespace:key, or package/namespace:key")
|
|
455
|
-
.option("-P, --package <id>", "Target package ID or path")
|
|
456
|
-
.option("-n, --namespace <ns>", "Target namespace")
|
|
457
|
-
.option("-p, --profile <name>", "Delete state on a remote target")
|
|
458
|
-
.option("-s, --server <url>", "Remote server URL")
|
|
459
|
-
.option("-t, --token <token>", "Auth token for remote server")
|
|
460
|
-
.option("--silent", "Do not exit with error if key is not found")
|
|
461
|
-
.option("--data-dir <path>", "Custom database storage directory")
|
|
462
|
-
.option("--json", "Output as JSON")
|
|
463
|
-
.option("--envelope", "Wrap JSON output in standard envelope")
|
|
464
|
-
.action(async (rawKey, rawOptions, cmd) => {
|
|
465
|
-
try {
|
|
466
|
-
const options = getEffectiveOptions(rawOptions, cmd);
|
|
467
|
-
const isMachine = Boolean(options.json || options.envelope);
|
|
468
|
-
const target = resolveTarget({
|
|
469
|
-
profile: options.profile,
|
|
470
|
-
server: options.server,
|
|
471
|
-
token: options.token,
|
|
472
|
-
});
|
|
473
|
-
|
|
474
|
-
if (target.type === "remote") {
|
|
475
|
-
await deleteRemoteStateKey(target.serverUrl!, rawKey, target.token, {
|
|
476
|
-
package: options.package,
|
|
477
|
-
namespace: options.namespace,
|
|
478
|
-
});
|
|
479
|
-
if (isMachine) {
|
|
480
|
-
renderResult({ ok: true, key: rawKey }, { json: options.json, envelope: options.envelope });
|
|
481
|
-
} else {
|
|
482
|
-
console.log(`[OK] State '${rawKey}' deleted from remote server ${target.serverUrl}`);
|
|
483
|
-
}
|
|
484
|
-
return;
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
const { root, key } = getTargetRoot(options.package, rawKey);
|
|
488
|
-
const projConfig = loadProjectConfig(root);
|
|
489
|
-
const storage = createStorage(projConfig.id, {
|
|
490
|
-
projectRoot: root,
|
|
491
|
-
dataDir: options.dataDir,
|
|
492
|
-
});
|
|
493
|
-
const deleted = await storage.deleteStateSmart(key, options.namespace);
|
|
494
|
-
storage.close();
|
|
495
|
-
|
|
496
|
-
if (deleted) {
|
|
497
|
-
if (isMachine) {
|
|
498
|
-
renderResult({ ok: true, key, deleted: true, packageId: projConfig.id }, { json: options.json, envelope: options.envelope });
|
|
499
|
-
} else {
|
|
500
|
-
console.log(`[OK] State '${key}' deleted from ${projConfig.id}`);
|
|
501
|
-
}
|
|
502
|
-
} else {
|
|
503
|
-
if (!options.silent) {
|
|
504
|
-
throw new ExecutionError(`State key '${key}' not found in ${projConfig.id}`);
|
|
505
|
-
}
|
|
506
|
-
if (isMachine) {
|
|
507
|
-
renderResult({ ok: true, key, deleted: false, packageId: projConfig.id }, { json: options.json, envelope: options.envelope });
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
} catch (err: any) {
|
|
511
|
-
if (err instanceof ArgumentError || err instanceof ExecutionError) throw err;
|
|
512
|
-
throw new ExecutionError(err.message);
|
|
513
|
-
}
|
|
514
|
-
});
|
|
515
|
-
|
|
516
|
-
// state clear
|
|
517
|
-
stateCmd
|
|
518
|
-
.command("clear [prefix]")
|
|
519
|
-
.alias("clean")
|
|
520
|
-
.description("Clear state entries by namespace, prefix, or all")
|
|
521
|
-
.option("-P, --package <id>", "Target package ID or path")
|
|
522
|
-
.option("-n, --namespace <ns>", "Target namespace to clear")
|
|
523
|
-
.option("-p, --profile <name>", "Clear state on a remote target")
|
|
524
|
-
.option("-s, --server <url>", "Remote server URL")
|
|
525
|
-
.option("-t, --token <token>", "Auth token for remote server")
|
|
526
|
-
.option("-a, --all", "Clear all state entries across all namespaces in this package")
|
|
527
|
-
.option("--data-dir <path>", "Custom database storage directory")
|
|
528
|
-
.option("--json", "Output as JSON")
|
|
529
|
-
.option("--envelope", "Wrap JSON output in standard envelope")
|
|
530
|
-
.action(async (prefix = "", rawOptions, cmd) => {
|
|
531
|
-
try {
|
|
532
|
-
const options = getEffectiveOptions(rawOptions, cmd);
|
|
533
|
-
const isMachine = Boolean(options.json || options.envelope);
|
|
534
|
-
const target = resolveTarget({
|
|
535
|
-
profile: options.profile,
|
|
536
|
-
server: options.server,
|
|
537
|
-
token: options.token,
|
|
538
|
-
});
|
|
539
|
-
|
|
540
|
-
if (target.type === "remote") {
|
|
541
|
-
const res = await clearRemoteState(target.serverUrl!, target.token, {
|
|
542
|
-
package: options.package,
|
|
543
|
-
namespace: options.namespace,
|
|
544
|
-
prefix,
|
|
545
|
-
all: Boolean(options.all),
|
|
546
|
-
});
|
|
547
|
-
if (isMachine) {
|
|
548
|
-
renderResult(res, { json: options.json, envelope: options.envelope });
|
|
549
|
-
} else {
|
|
550
|
-
console.log(`[OK] Cleared ${res.clearedCount} state entry(s) on remote server ${target.serverUrl}`);
|
|
551
|
-
}
|
|
552
|
-
return;
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
const { root } = getTargetRoot(options.package);
|
|
556
|
-
const projConfig = loadProjectConfig(root);
|
|
557
|
-
const storage = createStorage(projConfig.id, {
|
|
558
|
-
projectRoot: root,
|
|
559
|
-
dataDir: options.dataDir,
|
|
560
|
-
});
|
|
561
|
-
const count = await storage.clearState({
|
|
562
|
-
namespace: options.namespace,
|
|
563
|
-
all: options.all,
|
|
564
|
-
prefix: prefix || undefined,
|
|
565
|
-
});
|
|
566
|
-
storage.close();
|
|
567
|
-
|
|
568
|
-
const scopeDesc = options.all
|
|
569
|
-
? "all namespaces"
|
|
570
|
-
: options.namespace
|
|
571
|
-
? `namespace '${options.namespace}'`
|
|
572
|
-
: prefix
|
|
573
|
-
? `prefix '${prefix}'`
|
|
574
|
-
: "root namespace";
|
|
575
|
-
|
|
576
|
-
if (isMachine) {
|
|
577
|
-
renderResult(
|
|
578
|
-
{ ok: true, clearedCount: count, scope: scopeDesc, packageId: projConfig.id },
|
|
579
|
-
{ json: options.json, envelope: options.envelope }
|
|
580
|
-
);
|
|
581
|
-
} else {
|
|
582
|
-
console.log(`[OK] Cleared ${count} state entry(s) (${scopeDesc}) from ${projConfig.id}`);
|
|
583
|
-
}
|
|
584
|
-
} catch (err: any) {
|
|
585
|
-
if (err instanceof ArgumentError || err instanceof ExecutionError) throw err;
|
|
586
|
-
throw new ExecutionError(err.message);
|
|
587
|
-
}
|
|
588
|
-
});
|
|
589
|
-
}
|
|
590
|
-
|
package/src/commands/test.ts
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { spawnSync } from "node:child_process";
|
|
2
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import { findProjectRoot } from "@actiondock/core";
|
|
5
|
-
import { Command } from "commander";
|
|
6
|
-
|
|
7
|
-
export function registerTestCommand(program: Command): void {
|
|
8
|
-
program
|
|
9
|
-
.command("test [pattern]")
|
|
10
|
-
.description("Run project tests using configured test runner (node:test or bun test)")
|
|
11
|
-
.action((pattern) => {
|
|
12
|
-
const root = findProjectRoot();
|
|
13
|
-
const cwd = root || process.cwd();
|
|
14
|
-
|
|
15
|
-
const pkgJsonPath = join(cwd, "package.json");
|
|
16
|
-
let testCmd = "npm";
|
|
17
|
-
let testArgs = ["test"];
|
|
18
|
-
|
|
19
|
-
if (existsSync(pkgJsonPath)) {
|
|
20
|
-
try {
|
|
21
|
-
const pkg = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
|
|
22
|
-
if (pkg.scripts?.test?.includes("bun test") && typeof (globalThis as any).Bun !== "undefined") {
|
|
23
|
-
testCmd = "bun";
|
|
24
|
-
testArgs = ["test"];
|
|
25
|
-
}
|
|
26
|
-
} catch {
|
|
27
|
-
// ignore
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if (pattern) {
|
|
32
|
-
if (testCmd === "npm") {
|
|
33
|
-
testArgs.push("--", pattern);
|
|
34
|
-
} else {
|
|
35
|
-
testArgs.push(pattern);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const proc = spawnSync(testCmd, testArgs, {
|
|
40
|
-
cwd,
|
|
41
|
-
stdio: "inherit",
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
if (proc.status !== 0) {
|
|
45
|
-
process.exitCode = proc.status ?? 1;
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { formatError } from "@actiondock/runtime-cli";
|
|
2
|
-
import { setupNodeRuntime } from "@actiondock/runtime-node";
|
|
3
|
-
import { CommanderError } from "commander";
|
|
4
|
-
import { createCliProgram } from "./commands";
|
|
5
|
-
|
|
6
|
-
export * from "./commands";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* ActionDock CLI 主执行入口函数。
|
|
10
|
-
*
|
|
11
|
-
* @param argv 命令行参数数组(默认使用 process.argv)
|
|
12
|
-
* @returns 进程退出码
|
|
13
|
-
*/
|
|
14
|
-
export async function main(argv: string[] = process.argv): Promise<number> {
|
|
15
|
-
setupNodeRuntime();
|
|
16
|
-
const program = createCliProgram();
|
|
17
|
-
|
|
18
|
-
const isMachine = argv.includes("--json") || argv.includes("--envelope");
|
|
19
|
-
|
|
20
|
-
try {
|
|
21
|
-
await program.parseAsync(argv);
|
|
22
|
-
return typeof process.exitCode === "number" ? process.exitCode : 0;
|
|
23
|
-
} catch (err: unknown) {
|
|
24
|
-
if (err instanceof CommanderError) {
|
|
25
|
-
if (err.exitCode === 0) {
|
|
26
|
-
return 0;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const formatted = formatError(err);
|
|
31
|
-
if (isMachine) {
|
|
32
|
-
const errorEnv = {
|
|
33
|
-
ok: false,
|
|
34
|
-
error: {
|
|
35
|
-
code: formatted.code,
|
|
36
|
-
message: formatted.message,
|
|
37
|
-
...(formatted.details !== undefined ? { details: formatted.details } : {}),
|
|
38
|
-
},
|
|
39
|
-
};
|
|
40
|
-
console.log(JSON.stringify(errorEnv, null, 2));
|
|
41
|
-
} else {
|
|
42
|
-
const msg = formatted.message.startsWith("Error: ")
|
|
43
|
-
? formatted.message
|
|
44
|
-
: `Error: ${formatted.message}`;
|
|
45
|
-
console.error(msg);
|
|
46
|
-
}
|
|
47
|
-
process.exitCode = formatted.exitCode;
|
|
48
|
-
return formatted.exitCode;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if ((import.meta as any).main) {
|
|
53
|
-
main().catch((err) => {
|
|
54
|
-
console.error("Fatal error:", err);
|
|
55
|
-
process.exitCode = 1;
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
|
|
File without changes
|
|
File without changes
|