@shadowob/cli 1.1.7 → 1.1.8
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/dist/index.js +409 -37
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
} from "./chunk-S6XCO6ZW.js";
|
|
10
10
|
|
|
11
11
|
// src/index.ts
|
|
12
|
-
import { Command as
|
|
12
|
+
import { Command as Command29 } from "commander";
|
|
13
13
|
|
|
14
14
|
// src/commands/api-tokens.ts
|
|
15
15
|
import { Command } from "commander";
|
|
@@ -183,6 +183,115 @@ function commandHandlerError(error, json) {
|
|
|
183
183
|
outputError(error instanceof Error ? error.message : String(error), { json });
|
|
184
184
|
process.exit(1);
|
|
185
185
|
}
|
|
186
|
+
function prettyJson(value) {
|
|
187
|
+
return JSON.stringify(value, null, 2);
|
|
188
|
+
}
|
|
189
|
+
function commandSummary(command) {
|
|
190
|
+
return command.help?.summary ?? command.description ?? command.title ?? command.permission;
|
|
191
|
+
}
|
|
192
|
+
function formatAppCommandHelp(input) {
|
|
193
|
+
const { appKey, serverId, manifest, commandName } = input;
|
|
194
|
+
const command = commandName ? manifest.commands.find((item) => item.name === commandName) : void 0;
|
|
195
|
+
if (commandName && !command) throw new Error(`App command not found: ${commandName}`);
|
|
196
|
+
if (!command) {
|
|
197
|
+
const lines2 = [
|
|
198
|
+
`${manifest.name} (${appKey})`,
|
|
199
|
+
manifest.description ?? "",
|
|
200
|
+
manifest.help?.overview ?? "",
|
|
201
|
+
"",
|
|
202
|
+
"Usage:",
|
|
203
|
+
` shadowob app call ${appKey} <command> --server "${serverId}" --json-input '<input-json>' --json`,
|
|
204
|
+
` shadowob app call ${appKey} <command> --server "${serverId}" --help`,
|
|
205
|
+
manifest.binary?.supported ? ` shadowob app call ${appKey} <command> --server "${serverId}" --file ./asset.png --json-input '<input-json>' --json` : "",
|
|
206
|
+
"",
|
|
207
|
+
"Commands:",
|
|
208
|
+
...manifest.commands.map((item) => ` ${item.name.padEnd(24)} ${commandSummary(item)}`),
|
|
209
|
+
manifest.realtime ? [
|
|
210
|
+
"",
|
|
211
|
+
"Realtime:",
|
|
212
|
+
` shadowob app events ${appKey} --server "${serverId}" --json`,
|
|
213
|
+
manifest.realtime.subscribe?.help ?? "",
|
|
214
|
+
manifest.realtime.publish?.help ?? ""
|
|
215
|
+
].join("\n") : ""
|
|
216
|
+
];
|
|
217
|
+
return lines2.filter(Boolean).join("\n");
|
|
218
|
+
}
|
|
219
|
+
const help = command.help;
|
|
220
|
+
const usage = help?.usage ?? `shadowob app call ${appKey} ${command.name} --server "${serverId}" --json-input '<input-json>' --json`;
|
|
221
|
+
const lines = [
|
|
222
|
+
`${manifest.name} ${command.name}`,
|
|
223
|
+
commandSummary(command),
|
|
224
|
+
"",
|
|
225
|
+
"Usage:",
|
|
226
|
+
` ${usage}`,
|
|
227
|
+
command.binary?.supported || command.input === "multipart" ? ` shadowob app call ${appKey} ${command.name} --server "${serverId}" --file ./asset.png --json-input '<input-json>' --json` : "",
|
|
228
|
+
help?.details ? ["", help.details].join("\n") : "",
|
|
229
|
+
help?.examples?.length ? [
|
|
230
|
+
"",
|
|
231
|
+
"Examples:",
|
|
232
|
+
...help.examples.flatMap((example) => {
|
|
233
|
+
const rendered = example.command ? [` ${example.command}`] : example.input !== void 0 ? [` ${prettyJson(example.input).replace(/\n/g, "\n ")}`] : [];
|
|
234
|
+
return example.title ? [` # ${example.title}`, ...rendered] : rendered;
|
|
235
|
+
})
|
|
236
|
+
].join("\n") : "",
|
|
237
|
+
command.inputSchema ? ["", "Input schema:", prettyJson(command.inputSchema)].join("\n") : ""
|
|
238
|
+
];
|
|
239
|
+
return lines.filter(Boolean).join("\n");
|
|
240
|
+
}
|
|
241
|
+
function parseSseEvents(chunk, carry) {
|
|
242
|
+
const frames = `${carry}${chunk}`.split(/\r?\n\r?\n/u);
|
|
243
|
+
return {
|
|
244
|
+
complete: frames.slice(0, -1),
|
|
245
|
+
carry: frames.at(-1) ?? ""
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
function decodeSseFrame(frame) {
|
|
249
|
+
let event = "message";
|
|
250
|
+
const data = [];
|
|
251
|
+
for (const line of frame.split(/\r?\n/u)) {
|
|
252
|
+
if (line.startsWith("event:")) event = line.slice(6).trim();
|
|
253
|
+
if (line.startsWith("data:")) data.push(line.slice(5).trimStart());
|
|
254
|
+
}
|
|
255
|
+
return { event, data: data.join("\n") };
|
|
256
|
+
}
|
|
257
|
+
async function streamServerAppEvents(input) {
|
|
258
|
+
const response = await fetch(input.url, { headers: { Accept: "text/event-stream" } });
|
|
259
|
+
if (!response.ok || !response.body) {
|
|
260
|
+
throw new Error(`Event stream failed (${response.status})`);
|
|
261
|
+
}
|
|
262
|
+
const reader = response.body.getReader();
|
|
263
|
+
const decoder = new TextDecoder();
|
|
264
|
+
let carry = "";
|
|
265
|
+
let count = 0;
|
|
266
|
+
const stop = () => reader.cancel().catch(() => void 0);
|
|
267
|
+
process.once("SIGINT", stop);
|
|
268
|
+
try {
|
|
269
|
+
while (true) {
|
|
270
|
+
const next = await reader.read();
|
|
271
|
+
if (next.done) break;
|
|
272
|
+
const parsed = parseSseEvents(decoder.decode(next.value, { stream: true }), carry);
|
|
273
|
+
carry = parsed.carry;
|
|
274
|
+
for (const frame of parsed.complete) {
|
|
275
|
+
const decoded = decodeSseFrame(frame);
|
|
276
|
+
if (!decoded.data || input.event && decoded.event !== input.event) continue;
|
|
277
|
+
let payload = decoded.data;
|
|
278
|
+
try {
|
|
279
|
+
payload = JSON.parse(decoded.data);
|
|
280
|
+
} catch {
|
|
281
|
+
}
|
|
282
|
+
if (input.json) console.log(JSON.stringify({ event: decoded.event, data: payload }));
|
|
283
|
+
else
|
|
284
|
+
console.log(
|
|
285
|
+
`[${decoded.event}] ${typeof payload === "string" ? payload : prettyJson(payload)}`
|
|
286
|
+
);
|
|
287
|
+
count += 1;
|
|
288
|
+
if (input.limit && count >= input.limit) return;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
} finally {
|
|
292
|
+
process.off("SIGINT", stop);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
186
295
|
function createAppCommand() {
|
|
187
296
|
const app = new Command2("app").description("Server App integration commands");
|
|
188
297
|
app.command("list").description("List apps installed in a server").requiredOption("--server <server>", "Server ID or slug").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (options) => {
|
|
@@ -336,21 +445,80 @@ function createAppCommand() {
|
|
|
336
445
|
}
|
|
337
446
|
}
|
|
338
447
|
);
|
|
339
|
-
app.command("
|
|
448
|
+
app.command("events").description("Subscribe to an installed server App event stream").argument("<app-key>", "App key").requiredOption("--server <server>", "Server ID or slug").option("--event <event>", "Only print one event type").option("--limit <count>", "Stop after this many matching events").option("--profile <name>", "Profile to use").option("--json", "Output as JSON lines").action(
|
|
449
|
+
async (appKey, options) => {
|
|
450
|
+
try {
|
|
451
|
+
const client = await getClient(options.profile);
|
|
452
|
+
const launch = await client.createServerAppLaunch(
|
|
453
|
+
resolveServerFlag(options.server),
|
|
454
|
+
appKey
|
|
455
|
+
);
|
|
456
|
+
const limit = options.limit ? Number.parseInt(options.limit, 10) : void 0;
|
|
457
|
+
if (limit !== void 0 && (!Number.isFinite(limit) || limit < 1)) {
|
|
458
|
+
throw new Error("--limit must be a positive integer");
|
|
459
|
+
}
|
|
460
|
+
await streamServerAppEvents({
|
|
461
|
+
url: client.serverAppEventStreamUrl(launch.eventStreamPath),
|
|
462
|
+
event: options.event,
|
|
463
|
+
limit,
|
|
464
|
+
json: options.json
|
|
465
|
+
});
|
|
466
|
+
} catch (error) {
|
|
467
|
+
commandHandlerError(error, options.json);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
);
|
|
471
|
+
app.command("call").description("Call a server App command").helpOption(false).argument("[app-key]", "App key").argument("[command]", "Command name").option("--server <server>", "Server ID or slug").option("--json-input <json>", "JSON command input").option("--input-file <path>", "Read JSON command input from file").option("--channel-id <id>", "Current Shadow channel ID for approval prompts and app context").option("--task-message-id <id>", "Inbox task message ID to bind this app command to").option("--task-card-id <id>", "Inbox task card ID to bind this app command to").option("--task-claim-id <id>", "Inbox task claim ID to bind this app command to").option("--file <path>", "Attach a binary file").option("--field <field>", "Multipart file field name", "file").option("--output <path>", "Write binary dataBase64 response to this path").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").option("-h, --help", "Show app or command help from the installed manifest").action(
|
|
340
472
|
async (appKey, commandName, options) => {
|
|
341
473
|
try {
|
|
474
|
+
if (options.help) {
|
|
475
|
+
if (!appKey) {
|
|
476
|
+
console.log(
|
|
477
|
+
[
|
|
478
|
+
"Usage:",
|
|
479
|
+
" shadowob app call <app-key> <command> --server <server> --json-input '<input-json>' --json",
|
|
480
|
+
" shadowob app call <app-key> <command> --server <server> --help"
|
|
481
|
+
].join("\n")
|
|
482
|
+
);
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
const client2 = await getClient(options.profile);
|
|
486
|
+
const server2 = resolveServerFlag(options.server);
|
|
487
|
+
const app2 = await client2.getServerApp(server2, appKey);
|
|
488
|
+
console.log(
|
|
489
|
+
formatAppCommandHelp({
|
|
490
|
+
appKey,
|
|
491
|
+
serverId: app2.serverId,
|
|
492
|
+
manifest: app2.manifest,
|
|
493
|
+
commandName
|
|
494
|
+
})
|
|
495
|
+
);
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
if (!appKey) throw new Error("Missing app key");
|
|
499
|
+
if (!commandName) throw new Error("Missing command name");
|
|
342
500
|
const client = await getClient(options.profile);
|
|
343
501
|
const input = options.inputFile ? await readJsonFile(options.inputFile) : parseJsonInput(options.jsonInput);
|
|
344
502
|
const server = resolveServerFlag(options.server);
|
|
503
|
+
if ((options.taskMessageId || options.taskCardId || options.taskClaimId) && !(options.taskMessageId && options.taskCardId)) {
|
|
504
|
+
throw new Error("--task-message-id and --task-card-id are required together");
|
|
505
|
+
}
|
|
506
|
+
const task = options.taskMessageId && options.taskCardId ? {
|
|
507
|
+
messageId: options.taskMessageId,
|
|
508
|
+
cardId: options.taskCardId,
|
|
509
|
+
...options.taskClaimId ? { claimId: options.taskClaimId } : {}
|
|
510
|
+
} : void 0;
|
|
345
511
|
const result = options.file ? await client.callServerAppCommandMultipart(server, appKey, commandName, {
|
|
346
512
|
input,
|
|
347
513
|
channelId: options.channelId,
|
|
514
|
+
task,
|
|
348
515
|
file: new Blob([await readFile(options.file)]),
|
|
349
516
|
filename: basename(options.file),
|
|
350
517
|
field: options.field
|
|
351
518
|
}) : await client.callServerAppCommand(server, appKey, commandName, {
|
|
352
519
|
input,
|
|
353
|
-
channelId: options.channelId
|
|
520
|
+
channelId: options.channelId,
|
|
521
|
+
task
|
|
354
522
|
});
|
|
355
523
|
if (options.output && result && typeof result === "object" && "dataBase64" in result && typeof result.dataBase64 === "string") {
|
|
356
524
|
await writeFile(
|
|
@@ -1320,10 +1488,213 @@ function createFriendsCommand() {
|
|
|
1320
1488
|
return friends;
|
|
1321
1489
|
}
|
|
1322
1490
|
|
|
1323
|
-
// src/commands/
|
|
1491
|
+
// src/commands/inbox.ts
|
|
1324
1492
|
import { Command as Command12 } from "commander";
|
|
1493
|
+
var TASK_STATUSES = /* @__PURE__ */ new Set([
|
|
1494
|
+
"queued",
|
|
1495
|
+
"claimed",
|
|
1496
|
+
"running",
|
|
1497
|
+
"completed",
|
|
1498
|
+
"failed",
|
|
1499
|
+
"canceled",
|
|
1500
|
+
"transferred"
|
|
1501
|
+
]);
|
|
1502
|
+
function parseTaskStatus(value) {
|
|
1503
|
+
if (!TASK_STATUSES.has(value)) {
|
|
1504
|
+
throw new Error(`Invalid task status: ${value}`);
|
|
1505
|
+
}
|
|
1506
|
+
return value;
|
|
1507
|
+
}
|
|
1508
|
+
function parseJsonOption(value, label) {
|
|
1509
|
+
if (!value) return void 0;
|
|
1510
|
+
try {
|
|
1511
|
+
return JSON.parse(value);
|
|
1512
|
+
} catch {
|
|
1513
|
+
throw new Error(`Invalid JSON for ${label}`);
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
function parseTaskSourceOption(value) {
|
|
1517
|
+
const source = parseJsonOption(value, "source-json");
|
|
1518
|
+
if (!source) return void 0;
|
|
1519
|
+
if (typeof source.kind !== "string" || !source.kind.trim()) {
|
|
1520
|
+
throw new Error("Invalid source-json: kind is required");
|
|
1521
|
+
}
|
|
1522
|
+
return source;
|
|
1523
|
+
}
|
|
1524
|
+
function createInboxCommand() {
|
|
1525
|
+
const inbox = new Command12("inbox").description("Buddy Inbox task-card commands");
|
|
1526
|
+
inbox.command("list").description("List Buddy Inbox entries").option("--server <server>", "Server ID or slug").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (options) => {
|
|
1527
|
+
try {
|
|
1528
|
+
const client = await getClient(options.profile);
|
|
1529
|
+
const result = options.server ? await client.listServerBuddyInboxes(resolveServerFlag(options.server)) : await client.listBuddyInboxes();
|
|
1530
|
+
output(result, { json: options.json });
|
|
1531
|
+
} catch (error) {
|
|
1532
|
+
outputError(error instanceof Error ? error.message : String(error), {
|
|
1533
|
+
json: options.json
|
|
1534
|
+
});
|
|
1535
|
+
process.exit(1);
|
|
1536
|
+
}
|
|
1537
|
+
});
|
|
1538
|
+
inbox.command("ensure").description("Create or repair a Buddy Inbox channel").requiredOption("--server <server>", "Server ID or slug").requiredOption("--agent <agent-id>", "Buddy agent ID").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(
|
|
1539
|
+
async (options) => {
|
|
1540
|
+
try {
|
|
1541
|
+
const client = await getClient(options.profile);
|
|
1542
|
+
const result = await client.ensureBuddyInbox(
|
|
1543
|
+
resolveServerFlag(options.server),
|
|
1544
|
+
options.agent
|
|
1545
|
+
);
|
|
1546
|
+
output(result, { json: options.json });
|
|
1547
|
+
} catch (error) {
|
|
1548
|
+
outputError(error instanceof Error ? error.message : String(error), {
|
|
1549
|
+
json: options.json
|
|
1550
|
+
});
|
|
1551
|
+
process.exit(1);
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
);
|
|
1555
|
+
inbox.command("enqueue").description("Enqueue a new task card into a Buddy Inbox").requiredOption("--title <title>", "Task title").option("--body <text>", "Task body").option("--priority <priority>", "low|normal|high|urgent").option("--idempotency-key <key>", "Idempotency key").option("--server <server>", "Server ID or slug; required with --agent").option("--agent <agent-id>", "Buddy agent ID").option("--channel <channel-id>", "Buddy Inbox channel ID").option("--source-json <json>", "Task source JSON").option("--data-json <json>", "Task data JSON").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(
|
|
1556
|
+
async (options) => {
|
|
1557
|
+
try {
|
|
1558
|
+
const client = await getClient(options.profile);
|
|
1559
|
+
const task = {
|
|
1560
|
+
title: options.title
|
|
1561
|
+
};
|
|
1562
|
+
if (options.body) task.body = options.body;
|
|
1563
|
+
if (options.priority) task.priority = options.priority;
|
|
1564
|
+
if (options.idempotencyKey) task.idempotencyKey = options.idempotencyKey;
|
|
1565
|
+
const source = parseTaskSourceOption(options.sourceJson);
|
|
1566
|
+
if (source) task.source = source;
|
|
1567
|
+
const data = parseJsonOption(options.dataJson, "data-json");
|
|
1568
|
+
if (data) task.data = data;
|
|
1569
|
+
const result = options.channel ? await client.enqueueInboxTask(options.channel, task) : options.server && options.agent ? await client.enqueueInboxTaskForAgent(
|
|
1570
|
+
resolveServerFlag(options.server),
|
|
1571
|
+
options.agent,
|
|
1572
|
+
task
|
|
1573
|
+
) : null;
|
|
1574
|
+
if (!result) throw new Error("Provide either --channel or both --server and --agent");
|
|
1575
|
+
output(result, { json: options.json });
|
|
1576
|
+
} catch (error) {
|
|
1577
|
+
outputError(error instanceof Error ? error.message : String(error), {
|
|
1578
|
+
json: options.json
|
|
1579
|
+
});
|
|
1580
|
+
process.exit(1);
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
);
|
|
1584
|
+
inbox.command("policy").description("Read or update a Buddy Inbox admission policy").requiredOption("--server <server>", "Server ID or slug").requiredOption("--agent <agent-id>", "Buddy agent ID").option("--set-json <json>", "Admission policy JSON to write").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(
|
|
1585
|
+
async (options) => {
|
|
1586
|
+
try {
|
|
1587
|
+
const client = await getClient(options.profile);
|
|
1588
|
+
const server = resolveServerFlag(options.server);
|
|
1589
|
+
const policy = options.setJson ? parseJsonOption(options.setJson, "set-json") : null;
|
|
1590
|
+
const result = options.setJson ? await client.updateBuddyInboxAdmissionPolicy(server, options.agent, policy) : await client.getBuddyInboxAdmissionPolicy(server, options.agent);
|
|
1591
|
+
output(result, { json: options.json });
|
|
1592
|
+
} catch (error) {
|
|
1593
|
+
outputError(error instanceof Error ? error.message : String(error), {
|
|
1594
|
+
json: options.json
|
|
1595
|
+
});
|
|
1596
|
+
process.exit(1);
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
);
|
|
1600
|
+
inbox.command("claim").description("Claim an Inbox task card by message/card id").argument("<message-id>", "Message ID containing the task card").argument("<card-id>", "Task card ID").option("--ttl-seconds <n>", "Claim TTL in seconds", "3600").option("--note <text>", "Progress note").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(
|
|
1601
|
+
async (messageId, cardId, options) => {
|
|
1602
|
+
try {
|
|
1603
|
+
const client = await getClient(options.profile);
|
|
1604
|
+
const message = await client.claimTaskCard(messageId, cardId, {
|
|
1605
|
+
ttlSeconds: parsePositiveInt(options.ttlSeconds ?? "3600", "ttl-seconds"),
|
|
1606
|
+
note: options.note
|
|
1607
|
+
});
|
|
1608
|
+
output(message, { json: options.json });
|
|
1609
|
+
} catch (error) {
|
|
1610
|
+
outputError(error instanceof Error ? error.message : String(error), {
|
|
1611
|
+
json: options.json
|
|
1612
|
+
});
|
|
1613
|
+
process.exit(1);
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
);
|
|
1617
|
+
inbox.command("update").description("Update an Inbox task card status").argument("<message-id>", "Message ID containing the task card").argument("<card-id>", "Task card ID").requiredOption(
|
|
1618
|
+
"--status <status>",
|
|
1619
|
+
"queued|claimed|running|completed|failed|canceled|transferred"
|
|
1620
|
+
).option("--note <text>", "Progress note").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(
|
|
1621
|
+
async (messageId, cardId, options) => {
|
|
1622
|
+
try {
|
|
1623
|
+
const client = await getClient(options.profile);
|
|
1624
|
+
const message = await client.updateTaskCard(messageId, cardId, {
|
|
1625
|
+
status: parseTaskStatus(options.status),
|
|
1626
|
+
note: options.note
|
|
1627
|
+
});
|
|
1628
|
+
output(message, { json: options.json });
|
|
1629
|
+
} catch (error) {
|
|
1630
|
+
outputError(error instanceof Error ? error.message : String(error), {
|
|
1631
|
+
json: options.json
|
|
1632
|
+
});
|
|
1633
|
+
process.exit(1);
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
);
|
|
1637
|
+
inbox.command("retry").description("Copy a failed task card into a new queued task and mark the original transferred").argument("<message-id>", "Message ID containing the task card").argument("<card-id>", "Task card ID").option("--note <text>", "Transfer note").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(
|
|
1638
|
+
async (messageId, cardId, options) => {
|
|
1639
|
+
try {
|
|
1640
|
+
const client = await getClient(options.profile);
|
|
1641
|
+
const result = await client.retryTaskCard(messageId, cardId, { note: options.note });
|
|
1642
|
+
output(result, { json: options.json });
|
|
1643
|
+
} catch (error) {
|
|
1644
|
+
outputError(error instanceof Error ? error.message : String(error), {
|
|
1645
|
+
json: options.json
|
|
1646
|
+
});
|
|
1647
|
+
process.exit(1);
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
);
|
|
1651
|
+
inbox.command("claim-next").description("Claim the next available task in a Buddy Inbox").requiredOption("--server <server>", "Server ID or slug").requiredOption("--agent <agent-id>", "Buddy agent ID").option("--ttl-seconds <n>", "Claim TTL in seconds", "3600").option("--note <text>", "Progress note").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(
|
|
1652
|
+
async (options) => {
|
|
1653
|
+
try {
|
|
1654
|
+
const client = await getClient(options.profile);
|
|
1655
|
+
const result = await client.claimNextInboxTask(
|
|
1656
|
+
resolveServerFlag(options.server),
|
|
1657
|
+
options.agent,
|
|
1658
|
+
{
|
|
1659
|
+
ttlSeconds: parsePositiveInt(options.ttlSeconds ?? "3600", "ttl-seconds"),
|
|
1660
|
+
note: options.note
|
|
1661
|
+
}
|
|
1662
|
+
);
|
|
1663
|
+
output(result, { json: options.json });
|
|
1664
|
+
} catch (error) {
|
|
1665
|
+
outputError(error instanceof Error ? error.message : String(error), {
|
|
1666
|
+
json: options.json
|
|
1667
|
+
});
|
|
1668
|
+
process.exit(1);
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
);
|
|
1672
|
+
inbox.command("promote").description("Promote a chat message into a Buddy Inbox task").argument("<message-id>", "Source message ID").requiredOption("--server <server>", "Server ID or slug").requiredOption("--agent <agent-id>", "Buddy agent ID").option("--title <title>", "Task title override").option("--priority <priority>", "low|normal|high|urgent").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(
|
|
1673
|
+
async (messageId, options) => {
|
|
1674
|
+
try {
|
|
1675
|
+
const client = await getClient(options.profile);
|
|
1676
|
+
const message = await client.promoteMessageToInboxTask(messageId, {
|
|
1677
|
+
serverId: resolveServerFlag(options.server),
|
|
1678
|
+
agentId: options.agent,
|
|
1679
|
+
title: options.title,
|
|
1680
|
+
priority: options.priority
|
|
1681
|
+
});
|
|
1682
|
+
output(message, { json: options.json });
|
|
1683
|
+
} catch (error) {
|
|
1684
|
+
outputError(error instanceof Error ? error.message : String(error), {
|
|
1685
|
+
json: options.json
|
|
1686
|
+
});
|
|
1687
|
+
process.exit(1);
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
);
|
|
1691
|
+
return inbox;
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
// src/commands/invites.ts
|
|
1695
|
+
import { Command as Command13 } from "commander";
|
|
1325
1696
|
function createInvitesCommand() {
|
|
1326
|
-
const invites = new
|
|
1697
|
+
const invites = new Command13("invites").description("Invite code management commands");
|
|
1327
1698
|
invites.command("list").description("List your invite codes").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (options) => {
|
|
1328
1699
|
try {
|
|
1329
1700
|
const client = await getClient(options.profile);
|
|
@@ -1374,9 +1745,9 @@ function createInvitesCommand() {
|
|
|
1374
1745
|
}
|
|
1375
1746
|
|
|
1376
1747
|
// src/commands/listen.ts
|
|
1377
|
-
import { Command as
|
|
1748
|
+
import { Command as Command14 } from "commander";
|
|
1378
1749
|
function createListenCommand() {
|
|
1379
|
-
const listen = new
|
|
1750
|
+
const listen = new Command14("listen").description("Listen to real-time events");
|
|
1380
1751
|
listen.command("channel").description("Listen to events in a channel").argument("<channel-id>", "Channel ID").option("--mode <mode>", "Listen mode: stream or poll", "stream").option("--timeout <seconds>", "Timeout in seconds (stream mode)", "60").option("--count <n>", "Stop after N events (stream mode)").option("--since <duration>", "Poll events since duration (e.g., 5m, 1h)", "5m").option("--last <n>", "Poll last N messages", "50").option("--event-type <type>", "Filter by event type (comma-separated)").option("--profile <name>", "Profile to use").option("--json", "Output as JSON (one per line)").action(
|
|
1381
1752
|
async (channelId, options) => {
|
|
1382
1753
|
try {
|
|
@@ -1496,9 +1867,9 @@ function createListenCommand() {
|
|
|
1496
1867
|
}
|
|
1497
1868
|
|
|
1498
1869
|
// src/commands/marketplace.ts
|
|
1499
|
-
import { Command as
|
|
1870
|
+
import { Command as Command15 } from "commander";
|
|
1500
1871
|
function createMarketplaceCommand() {
|
|
1501
|
-
const marketplace = new
|
|
1872
|
+
const marketplace = new Command15("marketplace").description("Marketplace commands");
|
|
1502
1873
|
const listings = marketplace.command("listings").description("Listing commands");
|
|
1503
1874
|
listings.command("list").description("Browse marketplace listings").option("--search <text>", "Search query").option("--tags <tags>", "Comma-separated tags").option("--min-price <n>", "Minimum price per hour").option("--max-price <n>", "Maximum price per hour").option("--limit <n>", "Number of results", "20").option("--offset <n>", "Pagination offset", "0").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(
|
|
1504
1875
|
async (options) => {
|
|
@@ -1643,9 +2014,9 @@ function createMarketplaceCommand() {
|
|
|
1643
2014
|
|
|
1644
2015
|
// src/commands/media.ts
|
|
1645
2016
|
import { readFileSync } from "fs";
|
|
1646
|
-
import { Command as
|
|
2017
|
+
import { Command as Command16 } from "commander";
|
|
1647
2018
|
function createMediaCommand() {
|
|
1648
|
-
const media = new
|
|
2019
|
+
const media = new Command16("media").description("Media management commands");
|
|
1649
2020
|
media.command("upload").description("Upload a file").requiredOption("--file <path>", "File path to upload").option("--message-id <id>", "Associate with message").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(
|
|
1650
2021
|
async (options) => {
|
|
1651
2022
|
try {
|
|
@@ -1711,9 +2082,9 @@ function createMediaCommand() {
|
|
|
1711
2082
|
}
|
|
1712
2083
|
|
|
1713
2084
|
// src/commands/notifications.ts
|
|
1714
|
-
import { Command as
|
|
2085
|
+
import { Command as Command17 } from "commander";
|
|
1715
2086
|
function createNotificationsCommand() {
|
|
1716
|
-
const notifications = new
|
|
2087
|
+
const notifications = new Command17("notifications").description("Notification commands");
|
|
1717
2088
|
notifications.command("list").description("List notifications").option("--unread-only", "Show only unread notifications").option("--limit <n>", "Number of notifications", "20").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(
|
|
1718
2089
|
async (options) => {
|
|
1719
2090
|
try {
|
|
@@ -1792,7 +2163,7 @@ function splitIds(value) {
|
|
|
1792
2163
|
}
|
|
1793
2164
|
|
|
1794
2165
|
// src/commands/oauth.ts
|
|
1795
|
-
import { Command as
|
|
2166
|
+
import { Command as Command18 } from "commander";
|
|
1796
2167
|
function resolveOAuthAccessToken(options) {
|
|
1797
2168
|
const token = options.accessToken || process.env.SHADOWOB_OAUTH_TOKEN;
|
|
1798
2169
|
if (!token) {
|
|
@@ -1809,7 +2180,7 @@ function parseMetadata2(value) {
|
|
|
1809
2180
|
return parsed;
|
|
1810
2181
|
}
|
|
1811
2182
|
function createOAuthCommand() {
|
|
1812
|
-
const oauth = new
|
|
2183
|
+
const oauth = new Command18("oauth").description("OAuth management commands");
|
|
1813
2184
|
oauth.command("list").description("List OAuth apps").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (options) => {
|
|
1814
2185
|
try {
|
|
1815
2186
|
const client = await getClient(options.profile);
|
|
@@ -1942,9 +2313,9 @@ function createOAuthCommand() {
|
|
|
1942
2313
|
}
|
|
1943
2314
|
|
|
1944
2315
|
// src/commands/ping.ts
|
|
1945
|
-
import { Command as
|
|
2316
|
+
import { Command as Command19 } from "commander";
|
|
1946
2317
|
function createPingCommand() {
|
|
1947
|
-
const ping = new
|
|
2318
|
+
const ping = new Command19("ping").description("Test connection to Shadow server");
|
|
1948
2319
|
ping.option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (options) => {
|
|
1949
2320
|
const startTime = Date.now();
|
|
1950
2321
|
const outputOpts = { json: options.json };
|
|
@@ -1993,9 +2364,9 @@ function createPingCommand() {
|
|
|
1993
2364
|
}
|
|
1994
2365
|
|
|
1995
2366
|
// src/commands/profile-comments.ts
|
|
1996
|
-
import { Command as
|
|
2367
|
+
import { Command as Command20 } from "commander";
|
|
1997
2368
|
function createProfileCommentsCommand() {
|
|
1998
|
-
const comments = new
|
|
2369
|
+
const comments = new Command20("profile-comments").description(
|
|
1999
2370
|
"Profile comment management commands"
|
|
2000
2371
|
);
|
|
2001
2372
|
comments.command("get").description("Get comments for a user profile").argument("<user-id>", "Profile user ID").option("--limit <n>", "Number of results", "20").option("--offset <n>", "Offset for pagination", "0").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (userId, options) => {
|
|
@@ -2045,9 +2416,9 @@ function createProfileCommentsCommand() {
|
|
|
2045
2416
|
}
|
|
2046
2417
|
|
|
2047
2418
|
// src/commands/search.ts
|
|
2048
|
-
import { Command as
|
|
2419
|
+
import { Command as Command21 } from "commander";
|
|
2049
2420
|
function createSearchCommand() {
|
|
2050
|
-
const search = new
|
|
2421
|
+
const search = new Command21("search").description("Search commands");
|
|
2051
2422
|
search.command("messages").description("Search messages").requiredOption("--query <text>", "Search query").option("--server <server>", "Limit to server").option("--channel-id <id>", "Limit to channel").option("--limit <n>", "Number of results (1-100)", "20").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(
|
|
2052
2423
|
async (options) => {
|
|
2053
2424
|
try {
|
|
@@ -2072,9 +2443,9 @@ function createSearchCommand() {
|
|
|
2072
2443
|
}
|
|
2073
2444
|
|
|
2074
2445
|
// src/commands/servers.ts
|
|
2075
|
-
import { Command as
|
|
2446
|
+
import { Command as Command22 } from "commander";
|
|
2076
2447
|
function createServersCommand() {
|
|
2077
|
-
const servers = new
|
|
2448
|
+
const servers = new Command22("servers").description("Server management commands");
|
|
2078
2449
|
servers.command("list").description("List all servers you have joined").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (options) => {
|
|
2079
2450
|
try {
|
|
2080
2451
|
const client = await getClient(options.profile);
|
|
@@ -2181,7 +2552,7 @@ function createServersCommand() {
|
|
|
2181
2552
|
}
|
|
2182
2553
|
|
|
2183
2554
|
// src/commands/shop.ts
|
|
2184
|
-
import { Command as
|
|
2555
|
+
import { Command as Command23 } from "commander";
|
|
2185
2556
|
function parseJsonObject(value, optionName) {
|
|
2186
2557
|
if (!value) return {};
|
|
2187
2558
|
const parsed = JSON.parse(value);
|
|
@@ -2199,7 +2570,7 @@ function parseOptionalNumber(value, optionName) {
|
|
|
2199
2570
|
return parsed;
|
|
2200
2571
|
}
|
|
2201
2572
|
function createShopCommand() {
|
|
2202
|
-
const shop = new
|
|
2573
|
+
const shop = new Command23("shop").description("Shop commands");
|
|
2203
2574
|
shop.command("get").description("Get shop info").argument("<server-id>", "Server ID").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (serverId, options) => {
|
|
2204
2575
|
try {
|
|
2205
2576
|
const client = await getClient(options.profile);
|
|
@@ -2541,9 +2912,9 @@ function createShopCommand() {
|
|
|
2541
2912
|
}
|
|
2542
2913
|
|
|
2543
2914
|
// src/commands/status.ts
|
|
2544
|
-
import { Command as
|
|
2915
|
+
import { Command as Command24 } from "commander";
|
|
2545
2916
|
function createStatusCommand() {
|
|
2546
|
-
const status = new
|
|
2917
|
+
const status = new Command24("status").description("Show detailed status information");
|
|
2547
2918
|
status.option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (options) => {
|
|
2548
2919
|
const outputOpts = { json: options.json };
|
|
2549
2920
|
try {
|
|
@@ -2621,9 +2992,9 @@ function createStatusCommand() {
|
|
|
2621
2992
|
}
|
|
2622
2993
|
|
|
2623
2994
|
// src/commands/threads.ts
|
|
2624
|
-
import { Command as
|
|
2995
|
+
import { Command as Command25 } from "commander";
|
|
2625
2996
|
function createThreadsCommand() {
|
|
2626
|
-
const threads = new
|
|
2997
|
+
const threads = new Command25("threads").description("Thread commands");
|
|
2627
2998
|
threads.command("list").description("List threads in a channel").argument("<channel-id>", "Channel ID").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (channelId, options) => {
|
|
2628
2999
|
try {
|
|
2629
3000
|
const client = await getClient(options.profile);
|
|
@@ -2706,7 +3077,7 @@ function createThreadsCommand() {
|
|
|
2706
3077
|
|
|
2707
3078
|
// src/commands/voice.ts
|
|
2708
3079
|
import { join as join2 } from "path";
|
|
2709
|
-
import { Command as
|
|
3080
|
+
import { Command as Command26 } from "commander";
|
|
2710
3081
|
|
|
2711
3082
|
// src/utils/voice-media-bridge.ts
|
|
2712
3083
|
import { execFileSync as execFileSync2, spawn } from "child_process";
|
|
@@ -3841,7 +4212,7 @@ function resolveProfileOption(options, command) {
|
|
|
3841
4212
|
return options.profile ?? command.optsWithGlobals().profile;
|
|
3842
4213
|
}
|
|
3843
4214
|
function createVoiceCommand() {
|
|
3844
|
-
const voice = new
|
|
4215
|
+
const voice = new Command26("voice").description("Voice channel commands");
|
|
3845
4216
|
voice.command("join").description("Join a voice channel and print Agora connection info").argument("<channel-id>", "Voice channel ID").option("--muted", "Join muted").option("--deafened", "Join deafened").option("--watch", "Keep the process attached and print voice events").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(
|
|
3846
4217
|
async (channelId, options, command) => {
|
|
3847
4218
|
try {
|
|
@@ -3981,7 +4352,7 @@ function createVoiceCommand() {
|
|
|
3981
4352
|
}
|
|
3982
4353
|
}
|
|
3983
4354
|
);
|
|
3984
|
-
const browser = new
|
|
4355
|
+
const browser = new Command26("browser").description("Voice bridge browser runtime commands");
|
|
3985
4356
|
browser.command("install").description("Install an isolated Chromium runtime for voice bridge tests").option("--json", "Output as JSON").action(async (options) => {
|
|
3986
4357
|
try {
|
|
3987
4358
|
const executable = await installVoiceTestBrowser({ json: options.json });
|
|
@@ -4012,9 +4383,9 @@ function createVoiceCommand() {
|
|
|
4012
4383
|
}
|
|
4013
4384
|
|
|
4014
4385
|
// src/commands/voice-enhance.ts
|
|
4015
|
-
import { Command as
|
|
4386
|
+
import { Command as Command27 } from "commander";
|
|
4016
4387
|
function createVoiceEnhanceCommand() {
|
|
4017
|
-
const voice = new
|
|
4388
|
+
const voice = new Command27("voice-enhance").description("Voice enhancement commands");
|
|
4018
4389
|
voice.command("enhance").description("Enhance a voice transcript").requiredOption("--transcript <text>", "Transcript text to enhance").option("--language <lang>", "Language code (e.g. zh-CN, en-US)").option("--no-self-correction", "Disable self-correction").option("--no-list-formatting", "Disable list formatting").option("--no-filler-removal", "Disable filler word removal").option("--tone-adjustment", "Enable tone adjustment").option("--target-tone <tone>", "Target tone (formal, casual, professional)").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (options) => {
|
|
4019
4390
|
try {
|
|
4020
4391
|
const client = await getClient(options.profile);
|
|
@@ -4054,9 +4425,9 @@ function createVoiceEnhanceCommand() {
|
|
|
4054
4425
|
|
|
4055
4426
|
// src/commands/workspace.ts
|
|
4056
4427
|
import { readFile as readFile3 } from "fs/promises";
|
|
4057
|
-
import { Command as
|
|
4428
|
+
import { Command as Command28 } from "commander";
|
|
4058
4429
|
function createWorkspaceCommand() {
|
|
4059
|
-
const workspace = new
|
|
4430
|
+
const workspace = new Command28("workspace").description("Workspace file management commands");
|
|
4060
4431
|
workspace.command("get").description("Get workspace info").argument("<server-id>", "Server ID or slug").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (serverId, options) => {
|
|
4061
4432
|
try {
|
|
4062
4433
|
const client = await getClient(options.profile);
|
|
@@ -4238,7 +4609,7 @@ function createWorkspaceCommand() {
|
|
|
4238
4609
|
}
|
|
4239
4610
|
|
|
4240
4611
|
// src/index.ts
|
|
4241
|
-
var program = new
|
|
4612
|
+
var program = new Command29();
|
|
4242
4613
|
program.name("shadowob").description("Shadow CLI \u2014 command-line interface for Shadow servers").version("0.1.0").configureHelp({
|
|
4243
4614
|
sortSubcommands: true
|
|
4244
4615
|
});
|
|
@@ -4249,6 +4620,7 @@ program.addCommand(createServersCommand());
|
|
|
4249
4620
|
program.addCommand(createChannelsCommand());
|
|
4250
4621
|
program.addCommand(createThreadsCommand());
|
|
4251
4622
|
program.addCommand(createBuddiesCommand());
|
|
4623
|
+
program.addCommand(createInboxCommand());
|
|
4252
4624
|
program.addCommand(createListenCommand());
|
|
4253
4625
|
program.addCommand(createDirectMessagesCommand());
|
|
4254
4626
|
program.addCommand(createWorkspaceCommand());
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shadowob/cli",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.8",
|
|
4
4
|
"description": "Shadow CLI — command-line interface for Shadow servers",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"commander": "^13.1.0",
|
|
15
15
|
"chalk": "^5.4.1",
|
|
16
|
-
"@shadowob/sdk": "1.1.
|
|
16
|
+
"@shadowob/sdk": "1.1.8"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"@types/node": "^22.15.21",
|