@actiondock/cli 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +85 -0
- package/bin/ac.js +4 -0
- package/dist/index.js +44620 -0
- package/package.json +51 -0
- package/src/commands/action.ts +602 -0
- package/src/commands/build.ts +54 -0
- package/src/commands/config.ts +398 -0
- package/src/commands/doctor.ts +64 -0
- package/src/commands/export.ts +70 -0
- package/src/commands/index.ts +43 -0
- package/src/commands/info.ts +539 -0
- package/src/commands/init.ts +24 -0
- package/src/commands/link.ts +74 -0
- package/src/commands/mcp.ts +199 -0
- package/src/commands/playbook.ts +359 -0
- package/src/commands/profile.ts +263 -0
- package/src/commands/runs.ts +319 -0
- package/src/commands/serve.ts +101 -0
- package/src/commands/state.ts +396 -0
- package/src/commands/test.ts +26 -0
- package/src/index.ts +20 -0
- package/src/utils/bytes.ts +36 -0
- package/src/utils/duration.ts +1 -0
- package/src/utils/filter.ts +24 -0
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import {
|
|
3
|
+
createStorage,
|
|
4
|
+
filterWithFallbackInfo,
|
|
5
|
+
findProjectRoot,
|
|
6
|
+
listLinkedPackages,
|
|
7
|
+
loadProjectConfig,
|
|
8
|
+
resolvePackageRoot,
|
|
9
|
+
} from "@actiondock/core";
|
|
10
|
+
import { Command } from "commander";
|
|
11
|
+
import { resolveIntent } from "../utils/filter";
|
|
12
|
+
|
|
13
|
+
function getTargetRoot(packageOption?: string, keyHint?: string): { root: string; key: string } {
|
|
14
|
+
let targetPackage = packageOption;
|
|
15
|
+
let effectiveKey = keyHint || "";
|
|
16
|
+
|
|
17
|
+
if (!targetPackage && keyHint) {
|
|
18
|
+
if (keyHint.includes("/")) {
|
|
19
|
+
const slashIdx = keyHint.indexOf("/");
|
|
20
|
+
targetPackage = keyHint.slice(0, slashIdx);
|
|
21
|
+
effectiveKey = keyHint.slice(slashIdx + 1);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const root = resolvePackageRoot(targetPackage);
|
|
26
|
+
if (!root) {
|
|
27
|
+
if (targetPackage) {
|
|
28
|
+
console.error(`Error: Package '${targetPackage}' not found in linked packages or path`);
|
|
29
|
+
} else {
|
|
30
|
+
console.error(
|
|
31
|
+
"Error: Not in an ActionDock project (actiondock.json not found).\nPlease specify -P, --package <id> or cd into a project directory."
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
return { root, key: effectiveKey };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function registerStateCommands(program: Command): void {
|
|
40
|
+
const stateCmd = program
|
|
41
|
+
.command("state")
|
|
42
|
+
.description("Inspect and manage Shared State store");
|
|
43
|
+
|
|
44
|
+
// state list
|
|
45
|
+
stateCmd
|
|
46
|
+
.command("list [prefix]")
|
|
47
|
+
.description("List state keys matching prefix, namespace, or intent pattern (in current project or linked packages)")
|
|
48
|
+
.option("-P, --package <id>", "Target package ID or path")
|
|
49
|
+
.option("-n, --namespace <ns>", "Filter by specific namespace")
|
|
50
|
+
.option("-i, --intent <pattern>", "Regex or fuzzy intent filter; falls back to full list when no match")
|
|
51
|
+
.option("--no-fallback", "Disable fallback to full list when no items match intent")
|
|
52
|
+
.option("-d, --detail", "Show detailed state entry objects (including namespace, expiration, update time)")
|
|
53
|
+
.option("--json", "Output as JSON")
|
|
54
|
+
.action(async (prefix = "", options) => {
|
|
55
|
+
try {
|
|
56
|
+
const effectiveIntent = resolveIntent(options.intent, prefix ? [prefix] : []);
|
|
57
|
+
const shouldFallback = options.fallback !== false;
|
|
58
|
+
|
|
59
|
+
const targetRoot = options.package
|
|
60
|
+
? resolvePackageRoot(options.package)
|
|
61
|
+
: findProjectRoot();
|
|
62
|
+
|
|
63
|
+
if (options.package && !targetRoot) {
|
|
64
|
+
console.error(`Error: Package '${options.package}' not found in linked packages or path`);
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (targetRoot) {
|
|
69
|
+
const projConfig = loadProjectConfig(targetRoot);
|
|
70
|
+
const storage = createStorage(projConfig.id, { projectRoot: targetRoot });
|
|
71
|
+
|
|
72
|
+
if (options.detail && options.json) {
|
|
73
|
+
const entries = await storage.listStateEntries({
|
|
74
|
+
namespace: options.namespace,
|
|
75
|
+
prefix: prefix || undefined,
|
|
76
|
+
});
|
|
77
|
+
storage.close();
|
|
78
|
+
|
|
79
|
+
const filterRes = filterWithFallbackInfo(
|
|
80
|
+
entries,
|
|
81
|
+
effectiveIntent,
|
|
82
|
+
[(e) => e.fullKey, (e) => e.key, (e) => e.namespace],
|
|
83
|
+
shouldFallback
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
console.log(JSON.stringify(filterRes.items, null, 2));
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const allKeys = await storage.listStateKeys(
|
|
91
|
+
options.namespace !== undefined ? options.namespace : null,
|
|
92
|
+
prefix
|
|
93
|
+
);
|
|
94
|
+
storage.close();
|
|
95
|
+
|
|
96
|
+
const filterRes = filterWithFallbackInfo(
|
|
97
|
+
allKeys,
|
|
98
|
+
effectiveIntent,
|
|
99
|
+
[(k) => k],
|
|
100
|
+
shouldFallback
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
if (options.json) {
|
|
104
|
+
console.log(JSON.stringify(filterRes.items, null, 2));
|
|
105
|
+
} else {
|
|
106
|
+
const nsDesc = options.namespace ? ` [namespace: ${options.namespace}]` : "";
|
|
107
|
+
const filterDesc = prefix ? ` (filter: ${prefix})` : "";
|
|
108
|
+
console.log(`State keys for ${projConfig.id} (${targetRoot})${nsDesc}${filterDesc}:\n`);
|
|
109
|
+
if (filterRes.isFallback && effectiveIntent) {
|
|
110
|
+
console.log(`(No state keys matched intent '${effectiveIntent}', showing all keys)\n`);
|
|
111
|
+
}
|
|
112
|
+
if (filterRes.items.length === 0) {
|
|
113
|
+
console.log(" (no state keys found)\n");
|
|
114
|
+
} else {
|
|
115
|
+
for (const k of filterRes.items) {
|
|
116
|
+
console.log(` - ${k}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Outside project: list state keys across all linked packages
|
|
124
|
+
const linkedList = listLinkedPackages();
|
|
125
|
+
if (linkedList.length === 0) {
|
|
126
|
+
console.log("No ActionDock project in current directory, and no packages linked.");
|
|
127
|
+
console.log("Run 'ac link' inside an Action package to register it.");
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const aggregated: Array<{
|
|
132
|
+
packageId: string;
|
|
133
|
+
packageName: string;
|
|
134
|
+
path: string;
|
|
135
|
+
keys: string[];
|
|
136
|
+
entries?: any[];
|
|
137
|
+
}> = [];
|
|
138
|
+
|
|
139
|
+
for (const pkg of linkedList) {
|
|
140
|
+
if (!existsSync(pkg.path)) continue;
|
|
141
|
+
try {
|
|
142
|
+
const projConfig = loadProjectConfig(pkg.path);
|
|
143
|
+
const storage = createStorage(projConfig.id, { projectRoot: pkg.path });
|
|
144
|
+
|
|
145
|
+
if (options.detail && options.json) {
|
|
146
|
+
const entries = await storage.listStateEntries({
|
|
147
|
+
namespace: options.namespace,
|
|
148
|
+
prefix: prefix || undefined,
|
|
149
|
+
});
|
|
150
|
+
storage.close();
|
|
151
|
+
aggregated.push({
|
|
152
|
+
packageId: pkg.id,
|
|
153
|
+
packageName: pkg.name,
|
|
154
|
+
path: pkg.path,
|
|
155
|
+
keys: entries.map((e) => e.fullKey),
|
|
156
|
+
entries,
|
|
157
|
+
});
|
|
158
|
+
} else {
|
|
159
|
+
const keys = await storage.listStateKeys(
|
|
160
|
+
options.namespace !== undefined ? options.namespace : null,
|
|
161
|
+
prefix
|
|
162
|
+
);
|
|
163
|
+
storage.close();
|
|
164
|
+
aggregated.push({
|
|
165
|
+
packageId: pkg.id,
|
|
166
|
+
packageName: pkg.name,
|
|
167
|
+
path: pkg.path,
|
|
168
|
+
keys,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
} catch {
|
|
172
|
+
// Ignore broken linked package
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
let filteredPackages: typeof aggregated = [];
|
|
177
|
+
if (!effectiveIntent) {
|
|
178
|
+
filteredPackages = aggregated;
|
|
179
|
+
} else {
|
|
180
|
+
for (const pkg of aggregated) {
|
|
181
|
+
const pkgMatches = filterWithFallbackInfo(
|
|
182
|
+
[pkg],
|
|
183
|
+
effectiveIntent,
|
|
184
|
+
[(p) => p.packageId, (p) => p.packageName, (p) => p.path],
|
|
185
|
+
false
|
|
186
|
+
).matchedCount > 0;
|
|
187
|
+
|
|
188
|
+
if (pkgMatches) {
|
|
189
|
+
filteredPackages.push(pkg);
|
|
190
|
+
} else {
|
|
191
|
+
const matchedKeys = filterWithFallbackInfo(
|
|
192
|
+
pkg.keys,
|
|
193
|
+
effectiveIntent,
|
|
194
|
+
[(k) => k],
|
|
195
|
+
false
|
|
196
|
+
).items;
|
|
197
|
+
|
|
198
|
+
if (matchedKeys.length > 0) {
|
|
199
|
+
filteredPackages.push({
|
|
200
|
+
...pkg,
|
|
201
|
+
keys: matchedKeys,
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (filteredPackages.length === 0 && shouldFallback) {
|
|
208
|
+
filteredPackages = aggregated;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (options.json) {
|
|
213
|
+
console.log(JSON.stringify(filteredPackages, null, 2));
|
|
214
|
+
} else {
|
|
215
|
+
console.log("State Keys in Linked Packages:\n");
|
|
216
|
+
for (const pkg of filteredPackages) {
|
|
217
|
+
console.log(`* Package: ${pkg.packageId} (${pkg.path})`);
|
|
218
|
+
if (pkg.keys.length === 0) {
|
|
219
|
+
console.log(" (no state keys found)");
|
|
220
|
+
} else {
|
|
221
|
+
for (const k of pkg.keys) {
|
|
222
|
+
console.log(` - ${k}`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
} catch (err: any) {
|
|
228
|
+
console.error(`Error: ${err.message}`);
|
|
229
|
+
process.exit(1);
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
// state get
|
|
234
|
+
stateCmd
|
|
235
|
+
.command("get <key>")
|
|
236
|
+
.description("Get a state value by key, namespace:key, or package/namespace:key")
|
|
237
|
+
.option("-P, --package <id>", "Target package ID or path")
|
|
238
|
+
.option("-n, --namespace <ns>", "Target namespace")
|
|
239
|
+
.option("--json", "Output as JSON")
|
|
240
|
+
.action(async (rawKey, options) => {
|
|
241
|
+
const { root, key } = getTargetRoot(options.package, rawKey);
|
|
242
|
+
try {
|
|
243
|
+
const projConfig = loadProjectConfig(root);
|
|
244
|
+
const storage = createStorage(projConfig.id, { projectRoot: root });
|
|
245
|
+
|
|
246
|
+
let val: unknown;
|
|
247
|
+
let entryNamespace = options.namespace || "";
|
|
248
|
+
|
|
249
|
+
if (options.namespace !== undefined) {
|
|
250
|
+
val = await storage.getState(options.namespace, key);
|
|
251
|
+
} else {
|
|
252
|
+
const entry = await storage.findState(key);
|
|
253
|
+
if (entry) {
|
|
254
|
+
val = entry.value;
|
|
255
|
+
entryNamespace = entry.namespace;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
storage.close();
|
|
259
|
+
|
|
260
|
+
if (val === undefined) {
|
|
261
|
+
console.error(`Error: State key '${key}' not found in ${projConfig.id}`);
|
|
262
|
+
process.exit(1);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (options.json) {
|
|
266
|
+
console.log(
|
|
267
|
+
JSON.stringify(
|
|
268
|
+
{
|
|
269
|
+
key,
|
|
270
|
+
packageId: projConfig.id,
|
|
271
|
+
namespace: entryNamespace,
|
|
272
|
+
value: val,
|
|
273
|
+
},
|
|
274
|
+
null,
|
|
275
|
+
2
|
|
276
|
+
)
|
|
277
|
+
);
|
|
278
|
+
} else {
|
|
279
|
+
console.log(val !== undefined ? JSON.stringify(val, null, 2) : "undefined");
|
|
280
|
+
}
|
|
281
|
+
} catch (err: any) {
|
|
282
|
+
console.error(`Error: ${err.message}`);
|
|
283
|
+
process.exit(1);
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
// state set
|
|
288
|
+
stateCmd
|
|
289
|
+
.command("set <key> <value>")
|
|
290
|
+
.description("Set a state value by key, namespace:key, or package/namespace:key")
|
|
291
|
+
.option("-P, --package <id>", "Target package ID or path")
|
|
292
|
+
.option("-n, --namespace <ns>", "Target namespace")
|
|
293
|
+
.option("--ttl <seconds>", "Time to live in seconds", (v) => parseInt(v, 10))
|
|
294
|
+
.action(async (rawKey, rawValue, options) => {
|
|
295
|
+
const { root, key } = getTargetRoot(options.package, rawKey);
|
|
296
|
+
try {
|
|
297
|
+
const projConfig = loadProjectConfig(root);
|
|
298
|
+
const storage = createStorage(projConfig.id, { projectRoot: root });
|
|
299
|
+
|
|
300
|
+
let ns = "";
|
|
301
|
+
let actualKey = key;
|
|
302
|
+
|
|
303
|
+
if (options.namespace !== undefined) {
|
|
304
|
+
ns = options.namespace;
|
|
305
|
+
actualKey = key;
|
|
306
|
+
} else if (key.includes(":")) {
|
|
307
|
+
const colonIdx = key.indexOf(":");
|
|
308
|
+
ns = key.slice(0, colonIdx);
|
|
309
|
+
actualKey = key.slice(colonIdx + 1);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
let parsed: unknown = rawValue;
|
|
313
|
+
try {
|
|
314
|
+
parsed = JSON.parse(rawValue);
|
|
315
|
+
} catch {
|
|
316
|
+
parsed = rawValue;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
await storage.setState(ns, actualKey, parsed, options.ttl);
|
|
320
|
+
storage.close();
|
|
321
|
+
|
|
322
|
+
const displayKey = ns ? `${ns}:${actualKey}` : actualKey;
|
|
323
|
+
console.log(
|
|
324
|
+
`[OK] State '${displayKey}' set to ${JSON.stringify(parsed)}${options.ttl ? ` (TTL: ${options.ttl}s)` : ""} in ${projConfig.id}`
|
|
325
|
+
);
|
|
326
|
+
} catch (err: any) {
|
|
327
|
+
console.error(`Error: ${err.message}`);
|
|
328
|
+
process.exit(1);
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
// state delete
|
|
333
|
+
stateCmd
|
|
334
|
+
.command("delete <key>")
|
|
335
|
+
.alias("rm")
|
|
336
|
+
.description("Delete a state value by key, namespace:key, or package/namespace:key")
|
|
337
|
+
.option("-P, --package <id>", "Target package ID or path")
|
|
338
|
+
.option("-n, --namespace <ns>", "Target namespace")
|
|
339
|
+
.option("--silent", "Do not exit with error if key is not found")
|
|
340
|
+
.action(async (rawKey, options) => {
|
|
341
|
+
const { root, key } = getTargetRoot(options.package, rawKey);
|
|
342
|
+
try {
|
|
343
|
+
const projConfig = loadProjectConfig(root);
|
|
344
|
+
const storage = createStorage(projConfig.id, { projectRoot: root });
|
|
345
|
+
const deleted = await storage.deleteStateSmart(key, options.namespace);
|
|
346
|
+
storage.close();
|
|
347
|
+
|
|
348
|
+
if (deleted) {
|
|
349
|
+
console.log(`[OK] State '${key}' deleted from ${projConfig.id}`);
|
|
350
|
+
} else {
|
|
351
|
+
if (!options.silent) {
|
|
352
|
+
console.error(`Error: State key '${key}' not found in ${projConfig.id}`);
|
|
353
|
+
process.exit(1);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
} catch (err: any) {
|
|
357
|
+
console.error(`Error: ${err.message}`);
|
|
358
|
+
process.exit(1);
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
// state clear
|
|
363
|
+
stateCmd
|
|
364
|
+
.command("clear [prefix]")
|
|
365
|
+
.alias("clean")
|
|
366
|
+
.description("Clear state entries by namespace, prefix, or all")
|
|
367
|
+
.option("-P, --package <id>", "Target package ID or path")
|
|
368
|
+
.option("-n, --namespace <ns>", "Target namespace to clear")
|
|
369
|
+
.option("-a, --all", "Clear all state entries across all namespaces in this package")
|
|
370
|
+
.action(async (prefix = "", options) => {
|
|
371
|
+
const { root } = getTargetRoot(options.package);
|
|
372
|
+
try {
|
|
373
|
+
const projConfig = loadProjectConfig(root);
|
|
374
|
+
const storage = createStorage(projConfig.id, { projectRoot: root });
|
|
375
|
+
const count = await storage.clearState({
|
|
376
|
+
namespace: options.namespace,
|
|
377
|
+
all: options.all,
|
|
378
|
+
prefix: prefix || undefined,
|
|
379
|
+
});
|
|
380
|
+
storage.close();
|
|
381
|
+
|
|
382
|
+
const scopeDesc = options.all
|
|
383
|
+
? "all namespaces"
|
|
384
|
+
: options.namespace
|
|
385
|
+
? `namespace '${options.namespace}'`
|
|
386
|
+
: prefix
|
|
387
|
+
? `prefix '${prefix}'`
|
|
388
|
+
: "root namespace";
|
|
389
|
+
console.log(`[OK] Cleared ${count} state entry(s) (${scopeDesc}) from ${projConfig.id}`);
|
|
390
|
+
} catch (err: any) {
|
|
391
|
+
console.error(`Error: ${err.message}`);
|
|
392
|
+
process.exit(1);
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { findProjectRoot } from "@actiondock/core";
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
|
|
4
|
+
export function registerTestCommand(program: Command): void {
|
|
5
|
+
program
|
|
6
|
+
.command("test [pattern]")
|
|
7
|
+
.description("Run project tests using Bun test runner")
|
|
8
|
+
.action((pattern) => {
|
|
9
|
+
const root = findProjectRoot();
|
|
10
|
+
const cwd = root || process.cwd();
|
|
11
|
+
|
|
12
|
+
const args = ["bun", "test"];
|
|
13
|
+
if (pattern) {
|
|
14
|
+
args.push(pattern);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const proc = Bun.spawnSync(args, {
|
|
18
|
+
cwd,
|
|
19
|
+
stdio: ["inherit", "inherit", "inherit"],
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
if (proc.exitCode !== 0) {
|
|
23
|
+
process.exit(proc.exitCode ?? 1);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export * from "./commands";
|
|
2
|
+
|
|
3
|
+
import { createCliProgram } from "./commands";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* ActionDock CLI 主执行入口函数。
|
|
7
|
+
*
|
|
8
|
+
* @param argv 命令行参数数组(默认使用 process.argv)
|
|
9
|
+
*/
|
|
10
|
+
export async function main(argv: string[] = process.argv): Promise<void> {
|
|
11
|
+
const program = createCliProgram();
|
|
12
|
+
await program.parseAsync(argv);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (import.meta.main) {
|
|
16
|
+
main().catch((err) => {
|
|
17
|
+
console.error("Fatal error:", err);
|
|
18
|
+
process.exit(1);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses human-readable byte size string into number of bytes.
|
|
3
|
+
* e.g. "1mb" -> 1048576, "500kb" -> 512000, "1048576" -> 1048576
|
|
4
|
+
*/
|
|
5
|
+
export function parseByteSize(str: string): number {
|
|
6
|
+
const trimmed = str.trim().toLowerCase();
|
|
7
|
+
const match = trimmed.match(/^(\d+(?:\.\d+)?)\s*([a-z]+)?$/);
|
|
8
|
+
if (!match) {
|
|
9
|
+
const num = Number(trimmed);
|
|
10
|
+
if (!isNaN(num) && num > 0) return num;
|
|
11
|
+
throw new Error(`Invalid byte size format: '${str}'. Examples: '1mb', '500kb', '1048576'`);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const value = parseFloat(match[1]);
|
|
15
|
+
const unit = match[2] || "b";
|
|
16
|
+
|
|
17
|
+
switch (unit) {
|
|
18
|
+
case "b":
|
|
19
|
+
case "bytes":
|
|
20
|
+
return Math.floor(value);
|
|
21
|
+
case "k":
|
|
22
|
+
case "kb":
|
|
23
|
+
case "kib":
|
|
24
|
+
return Math.floor(value * 1024);
|
|
25
|
+
case "m":
|
|
26
|
+
case "mb":
|
|
27
|
+
case "mib":
|
|
28
|
+
return Math.floor(value * 1024 * 1024);
|
|
29
|
+
case "g":
|
|
30
|
+
case "gb":
|
|
31
|
+
case "gib":
|
|
32
|
+
return Math.floor(value * 1024 * 1024 * 1024);
|
|
33
|
+
default:
|
|
34
|
+
throw new Error(`Unsupported byte size unit '${unit}' in '${str}'`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { parseDuration } from "@actiondock/core";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves effective intent string from an explicit option (--intent)
|
|
3
|
+
* and optional positional pattern arguments.
|
|
4
|
+
* Multiple terms are combined with '|' (regex OR).
|
|
5
|
+
*/
|
|
6
|
+
export function resolveIntent(
|
|
7
|
+
optionsIntent?: string,
|
|
8
|
+
positionalPatterns?: string[]
|
|
9
|
+
): string | undefined {
|
|
10
|
+
const parts: string[] = [];
|
|
11
|
+
if (optionsIntent && optionsIntent.trim()) {
|
|
12
|
+
parts.push(optionsIntent.trim());
|
|
13
|
+
}
|
|
14
|
+
if (positionalPatterns && positionalPatterns.length > 0) {
|
|
15
|
+
for (const p of positionalPatterns) {
|
|
16
|
+
if (typeof p === "string" && p.trim()) {
|
|
17
|
+
parts.push(p.trim());
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
if (parts.length === 0) return undefined;
|
|
22
|
+
if (parts.length === 1) return parts[0];
|
|
23
|
+
return parts.join("|");
|
|
24
|
+
}
|