@shadowob/cli 1.1.3 → 1.1.4

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.
Files changed (2) hide show
  1. package/dist/index.js +246 -90
  2. package/package.json +3 -2
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  } from "./chunk-T3BKMB7N.js";
7
7
 
8
8
  // src/index.ts
9
- import { Command as Command25 } from "commander";
9
+ import { Command as Command26 } from "commander";
10
10
 
11
11
  // src/commands/agents.ts
12
12
  import { Command } from "commander";
@@ -265,11 +265,169 @@ function createApiTokensCommand() {
265
265
  return tokens;
266
266
  }
267
267
 
268
+ // src/commands/app.ts
269
+ import { readFile, writeFile } from "fs/promises";
270
+ import { basename } from "path";
271
+ import { Command as Command3 } from "commander";
272
+ function resolveServer(value) {
273
+ const server = value ?? process.env.SHADOWOB_SERVER_ID;
274
+ if (!server) throw new Error("Missing server. Pass --server or set SHADOWOB_SERVER_ID.");
275
+ return server;
276
+ }
277
+ function parseJsonInput(value) {
278
+ if (!value) return {};
279
+ const parsed = JSON.parse(value);
280
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed) && "input" in parsed && Object.keys(parsed).every((key) => key === "input" || key === "channelId")) {
281
+ return parsed.input ?? {};
282
+ }
283
+ return parsed;
284
+ }
285
+ async function readJsonFile(path) {
286
+ return JSON.parse(await readFile(path, "utf8"));
287
+ }
288
+ function commandHandlerError(error, json) {
289
+ outputError(error instanceof Error ? error.message : String(error), { json });
290
+ process.exit(1);
291
+ }
292
+ function createAppCommand() {
293
+ const app = new Command3("app").description("Server App integration commands");
294
+ 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) => {
295
+ try {
296
+ const client = await getClient(options.profile);
297
+ output(await client.listServerApps(resolveServer(options.server)), { json: options.json });
298
+ } catch (error) {
299
+ commandHandlerError(error, options.json);
300
+ }
301
+ });
302
+ app.command("preview").description("Discover and preview a server App manifest before installing it").requiredOption("--server <server>", "Server ID or slug").option("--manifest-url <url>", "Manifest URL").option("--manifest-file <path>", "Local manifest JSON file").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(
303
+ async (options) => {
304
+ try {
305
+ if (!options.manifestUrl && !options.manifestFile) {
306
+ throw new Error("Pass --manifest-url or --manifest-file");
307
+ }
308
+ const client = await getClient(options.profile);
309
+ const manifest = options.manifestFile ? await readJsonFile(options.manifestFile) : void 0;
310
+ output(
311
+ await client.discoverServerApp(resolveServer(options.server), {
312
+ manifestUrl: options.manifestUrl,
313
+ manifest
314
+ }),
315
+ { json: options.json }
316
+ );
317
+ } catch (error) {
318
+ commandHandlerError(error, options.json);
319
+ }
320
+ }
321
+ );
322
+ app.command("install").description("Install or update a server App from a manifest").requiredOption("--server <server>", "Server ID or slug").option("--manifest-url <url>", "Manifest URL").option("--manifest-file <path>", "Local manifest JSON file").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(
323
+ async (options) => {
324
+ try {
325
+ if (!options.manifestUrl && !options.manifestFile) {
326
+ throw new Error("Pass --manifest-url or --manifest-file");
327
+ }
328
+ const client = await getClient(options.profile);
329
+ const manifest = options.manifestFile ? await readJsonFile(options.manifestFile) : void 0;
330
+ const result = await client.installServerApp(resolveServer(options.server), {
331
+ manifestUrl: options.manifestUrl,
332
+ manifest
333
+ });
334
+ output(result, { json: options.json });
335
+ } catch (error) {
336
+ commandHandlerError(error, options.json);
337
+ }
338
+ }
339
+ );
340
+ app.command("inspect").description("Inspect an installed server App").argument("<app-key>", "App key").requiredOption("--server <server>", "Server ID or slug").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(
341
+ async (appKey, options) => {
342
+ try {
343
+ const client = await getClient(options.profile);
344
+ output(await client.getServerApp(resolveServer(options.server), appKey), {
345
+ json: options.json
346
+ });
347
+ } catch (error) {
348
+ commandHandlerError(error, options.json);
349
+ }
350
+ }
351
+ );
352
+ app.command("grant").description("Grant a Buddy access to an installed server App").argument("<app-key>", "App key").requiredOption("--server <server>", "Server ID or slug").requiredOption("--buddy <agent-id>", "Buddy agent ID").requiredOption("--permissions <permissions>", "Comma-separated permissions, or *").option("--approval-mode <mode>", "none, first_time, every_time, or policy", "none").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(
353
+ async (appKey, options) => {
354
+ try {
355
+ const client = await getClient(options.profile);
356
+ const permissions = options.permissions.split(",").map((item) => item.trim()).filter(Boolean);
357
+ const result = await client.grantServerAppToBuddy(resolveServer(options.server), appKey, {
358
+ buddyAgentId: options.buddy,
359
+ permissions,
360
+ approvalMode: options.approvalMode
361
+ });
362
+ output(result, { json: options.json });
363
+ } catch (error) {
364
+ commandHandlerError(error, options.json);
365
+ }
366
+ }
367
+ );
368
+ app.command("discover").description("Emit Skill-style command discovery for server Apps").requiredOption("--server <server>", "Server ID or slug").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (options) => {
369
+ try {
370
+ const client = await getClient(options.profile);
371
+ const server = resolveServer(options.server);
372
+ const apps = await client.listServerApps(server);
373
+ const docs = await Promise.all(
374
+ apps.map((entry) => client.getServerAppSkills(server, entry.appKey))
375
+ );
376
+ if (options.json) {
377
+ output(docs, { json: true });
378
+ } else {
379
+ console.log(docs.map((doc) => doc.markdown).join("\n\n---\n\n"));
380
+ }
381
+ } catch (error) {
382
+ commandHandlerError(error, options.json);
383
+ }
384
+ });
385
+ app.command("skills").description("Emit Skill text for one installed server App").argument("<app-key>", "App key").requiredOption("--server <server>", "Server ID or slug").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(
386
+ async (appKey, options) => {
387
+ try {
388
+ const client = await getClient(options.profile);
389
+ const result = await client.getServerAppSkills(resolveServer(options.server), appKey);
390
+ if (options.json) output(result, { json: true });
391
+ else console.log(result.markdown);
392
+ } catch (error) {
393
+ commandHandlerError(error, options.json);
394
+ }
395
+ }
396
+ );
397
+ app.command("call").description("Call a server App command").argument("<app-key>", "App key").argument("<command>", "Command name").requiredOption("--server <server>", "Server ID or slug").option("--json-input <json>", "JSON command input").option("--input-file <path>", "Read JSON command input from file").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").action(
398
+ async (appKey, commandName, options) => {
399
+ try {
400
+ const client = await getClient(options.profile);
401
+ const input = options.inputFile ? await readJsonFile(options.inputFile) : parseJsonInput(options.jsonInput);
402
+ const server = resolveServer(options.server);
403
+ const result = options.file ? await client.callServerAppCommandMultipart(server, appKey, commandName, {
404
+ input,
405
+ file: new Blob([await readFile(options.file)]),
406
+ filename: basename(options.file),
407
+ field: options.field
408
+ }) : await client.callServerAppCommand(server, appKey, commandName, { input });
409
+ if (options.output && result && typeof result === "object" && "dataBase64" in result && typeof result.dataBase64 === "string") {
410
+ await writeFile(
411
+ options.output,
412
+ Buffer.from(result.dataBase64, "base64")
413
+ );
414
+ outputSuccess(`Wrote ${options.output}`, { json: options.json });
415
+ return;
416
+ }
417
+ output(result, { json: options.json });
418
+ } catch (error) {
419
+ commandHandlerError(error, options.json);
420
+ }
421
+ }
422
+ );
423
+ return app;
424
+ }
425
+
268
426
  // src/commands/auth.ts
269
427
  import { ShadowClient } from "@shadowob/sdk";
270
- import { Command as Command3 } from "commander";
428
+ import { Command as Command4 } from "commander";
271
429
  function createAuthCommand() {
272
- const auth = new Command3("auth").description("Authentication commands");
430
+ const auth = new Command4("auth").description("Authentication commands");
273
431
  auth.command("login").description("Authenticate with a Shadow server").requiredOption("--server-url <url>", "Shadow server URL").requiredOption("--token <token>", "JWT token").option("--profile <name>", "Profile name", "default").option("--json", "Output as JSON").action(
274
432
  async (options) => {
275
433
  try {
@@ -382,9 +540,9 @@ function createAuthCommand() {
382
540
  }
383
541
 
384
542
  // src/commands/channels.ts
385
- import { Command as Command4 } from "commander";
543
+ import { Command as Command5 } from "commander";
386
544
  function createChannelsCommand() {
387
- const channels = new Command4("channels").description("Channel commands");
545
+ const channels = new Command5("channels").description("Channel commands");
388
546
  channels.command("list").description("List channels in a server").requiredOption("--server-id <id>", "Server ID").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (options) => {
389
547
  try {
390
548
  const client = await getClient(options.profile);
@@ -572,9 +730,9 @@ function createChannelsCommand() {
572
730
 
573
731
  // src/commands/cloud.ts
574
732
  import { execFileSync, spawnSync } from "child_process";
575
- import { Command as Command5 } from "commander";
733
+ import { Command as Command6 } from "commander";
576
734
  function createCloudCommand() {
577
- const cloud = new Command5("cloud").description("Shadow Cloud \u2014 deploy AI agent clusters to Kubernetes (via shadowob-cloud)").allowUnknownOption(true).allowExcessArguments(true).action(async (_, cmd) => {
735
+ const cloud = new Command6("cloud").description("Shadow Cloud \u2014 deploy AI agent clusters to Kubernetes (via shadowob-cloud)").allowUnknownOption(true).allowExcessArguments(true).action(async (_, cmd) => {
578
736
  const args = cmd.args ?? [];
579
737
  ensureCloudCliInstalled();
580
738
  spawnCloudCli(args);
@@ -598,9 +756,9 @@ function spawnCloudCli(args) {
598
756
  }
599
757
 
600
758
  // src/commands/config.ts
601
- import { Command as Command6 } from "commander";
759
+ import { Command as Command7 } from "commander";
602
760
  function createConfigCommand() {
603
- const config = new Command6("config").description("Configuration management commands");
761
+ const config = new Command7("config").description("Configuration management commands");
604
762
  config.command("path").description("Show configuration file path").action(() => {
605
763
  console.log(configManager.getConfigPath());
606
764
  });
@@ -671,9 +829,9 @@ function createConfigCommand() {
671
829
  }
672
830
 
673
831
  // src/commands/discover.ts
674
- import { Command as Command7 } from "commander";
832
+ import { Command as Command8 } from "commander";
675
833
  function createDiscoverCommand() {
676
- const discover = new Command7("discover").description("Discover popular servers and channels");
834
+ const discover = new Command8("discover").description("Discover popular servers and channels");
677
835
  discover.command("feed").description("Get the discovery feed").option("--type <type>", "Filter by type (all, servers, channels, rentals)", "all").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 (options) => {
678
836
  try {
679
837
  const client = await getClient(options.profile);
@@ -710,9 +868,9 @@ function createDiscoverCommand() {
710
868
  }
711
869
 
712
870
  // src/commands/dms.ts
713
- import { Command as Command8 } from "commander";
871
+ import { Command as Command9 } from "commander";
714
872
  function createDirectMessagesCommand() {
715
- const dms = new Command8("dms").description("Direct message commands");
873
+ const dms = new Command9("dms").description("Direct message commands");
716
874
  dms.command("list").description("List direct channels").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (options) => {
717
875
  try {
718
876
  const client = await getClient(options.profile);
@@ -790,9 +948,9 @@ function createDirectMessagesCommand() {
790
948
  }
791
949
 
792
950
  // src/commands/friends.ts
793
- import { Command as Command9 } from "commander";
951
+ import { Command as Command10 } from "commander";
794
952
  function createFriendsCommand() {
795
- const friends = new Command9("friends").description("Friendship management commands");
953
+ const friends = new Command10("friends").description("Friendship management commands");
796
954
  friends.command("list").description("List friends").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (options) => {
797
955
  try {
798
956
  const client = await getClient(options.profile);
@@ -876,9 +1034,9 @@ function createFriendsCommand() {
876
1034
  }
877
1035
 
878
1036
  // src/commands/invites.ts
879
- import { Command as Command10 } from "commander";
1037
+ import { Command as Command11 } from "commander";
880
1038
  function createInvitesCommand() {
881
- const invites = new Command10("invites").description("Invite code management commands");
1039
+ const invites = new Command11("invites").description("Invite code management commands");
882
1040
  invites.command("list").description("List your invite codes").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (options) => {
883
1041
  try {
884
1042
  const client = await getClient(options.profile);
@@ -929,9 +1087,9 @@ function createInvitesCommand() {
929
1087
  }
930
1088
 
931
1089
  // src/commands/listen.ts
932
- import { Command as Command11 } from "commander";
1090
+ import { Command as Command12 } from "commander";
933
1091
  function createListenCommand() {
934
- const listen = new Command11("listen").description("Listen to real-time events");
1092
+ const listen = new Command12("listen").description("Listen to real-time events");
935
1093
  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(
936
1094
  async (channelId, options) => {
937
1095
  try {
@@ -1051,9 +1209,9 @@ function createListenCommand() {
1051
1209
  }
1052
1210
 
1053
1211
  // src/commands/marketplace.ts
1054
- import { Command as Command12 } from "commander";
1212
+ import { Command as Command13 } from "commander";
1055
1213
  function createMarketplaceCommand() {
1056
- const marketplace = new Command12("marketplace").description("Marketplace commands");
1214
+ const marketplace = new Command13("marketplace").description("Marketplace commands");
1057
1215
  const listings = marketplace.command("listings").description("Listing commands");
1058
1216
  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(
1059
1217
  async (options) => {
@@ -1198,9 +1356,9 @@ function createMarketplaceCommand() {
1198
1356
 
1199
1357
  // src/commands/media.ts
1200
1358
  import { readFileSync } from "fs";
1201
- import { Command as Command13 } from "commander";
1359
+ import { Command as Command14 } from "commander";
1202
1360
  function createMediaCommand() {
1203
- const media = new Command13("media").description("Media management commands");
1361
+ const media = new Command14("media").description("Media management commands");
1204
1362
  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(
1205
1363
  async (options) => {
1206
1364
  try {
@@ -1266,16 +1424,18 @@ function createMediaCommand() {
1266
1424
  }
1267
1425
 
1268
1426
  // src/commands/notifications.ts
1269
- import { Command as Command14 } from "commander";
1427
+ import { Command as Command15 } from "commander";
1270
1428
  function createNotificationsCommand() {
1271
- const notifications = new Command14("notifications").description("Notification commands");
1429
+ const notifications = new Command15("notifications").description("Notification commands");
1272
1430
  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(
1273
1431
  async (options) => {
1274
1432
  try {
1275
1433
  const client = await getClient(options.profile);
1276
1434
  const limit = parseInt(options.limit ?? "20", 10);
1277
1435
  const result = await client.listNotifications(limit);
1278
- const notifications2 = Array.isArray(result) ? result : [];
1436
+ const notifications2 = (Array.isArray(result) ? result : []).filter(
1437
+ (item) => !options.unreadOnly || item.isRead === false
1438
+ );
1279
1439
  output(notifications2, { json: options.json });
1280
1440
  } catch (error) {
1281
1441
  outputError(error instanceof Error ? error.message : String(error), {
@@ -1307,13 +1467,47 @@ function createNotificationsCommand() {
1307
1467
  process.exit(1);
1308
1468
  }
1309
1469
  });
1470
+ const preferences = notifications.command("preferences").description("Notification preferences");
1471
+ preferences.command("get").description("Get notification preferences").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (options) => {
1472
+ try {
1473
+ const client = await getClient(options.profile);
1474
+ output(await client.getNotificationPreferences(), { json: options.json });
1475
+ } catch (error) {
1476
+ outputError(error instanceof Error ? error.message : String(error), { json: options.json });
1477
+ process.exit(1);
1478
+ }
1479
+ });
1480
+ preferences.command("update").description("Update notification preferences").option("--strategy <strategy>", "all | mention_only | none").option("--muted-server-ids <ids>", "Comma-separated server IDs").option("--muted-channel-ids <ids>", "Comma-separated channel IDs").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(
1481
+ async (options) => {
1482
+ try {
1483
+ if (options.strategy && !["all", "mention_only", "none"].includes(options.strategy)) {
1484
+ throw new Error("Invalid --strategy. Expected all, mention_only, or none");
1485
+ }
1486
+ const client = await getClient(options.profile);
1487
+ const data = {
1488
+ ...options.strategy ? { strategy: options.strategy } : {},
1489
+ ...options.mutedServerIds !== void 0 ? { mutedServerIds: splitIds(options.mutedServerIds) } : {},
1490
+ ...options.mutedChannelIds !== void 0 ? { mutedChannelIds: splitIds(options.mutedChannelIds) } : {}
1491
+ };
1492
+ output(await client.updateNotificationPreferences(data), { json: options.json });
1493
+ } catch (error) {
1494
+ outputError(error instanceof Error ? error.message : String(error), {
1495
+ json: options.json
1496
+ });
1497
+ process.exit(1);
1498
+ }
1499
+ }
1500
+ );
1310
1501
  return notifications;
1311
1502
  }
1503
+ function splitIds(value) {
1504
+ return value.split(",").map((item) => item.trim()).filter(Boolean);
1505
+ }
1312
1506
 
1313
1507
  // src/commands/oauth.ts
1314
- import { Command as Command15 } from "commander";
1508
+ import { Command as Command16 } from "commander";
1315
1509
  function createOAuthCommand() {
1316
- const oauth = new Command15("oauth").description("OAuth management commands");
1510
+ const oauth = new Command16("oauth").description("OAuth management commands");
1317
1511
  oauth.command("list").description("List OAuth apps").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (options) => {
1318
1512
  try {
1319
1513
  const client = await getClient(options.profile);
@@ -1407,9 +1601,9 @@ function createOAuthCommand() {
1407
1601
  }
1408
1602
 
1409
1603
  // src/commands/ping.ts
1410
- import { Command as Command16 } from "commander";
1604
+ import { Command as Command17 } from "commander";
1411
1605
  function createPingCommand() {
1412
- const ping = new Command16("ping").description("Test connection to Shadow server");
1606
+ const ping = new Command17("ping").description("Test connection to Shadow server");
1413
1607
  ping.option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (options) => {
1414
1608
  const startTime = Date.now();
1415
1609
  const outputOpts = { json: options.json };
@@ -1458,9 +1652,9 @@ function createPingCommand() {
1458
1652
  }
1459
1653
 
1460
1654
  // src/commands/profile-comments.ts
1461
- import { Command as Command17 } from "commander";
1655
+ import { Command as Command18 } from "commander";
1462
1656
  function createProfileCommentsCommand() {
1463
- const comments = new Command17("profile-comments").description(
1657
+ const comments = new Command18("profile-comments").description(
1464
1658
  "Profile comment management commands"
1465
1659
  );
1466
1660
  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) => {
@@ -1510,9 +1704,9 @@ function createProfileCommentsCommand() {
1510
1704
  }
1511
1705
 
1512
1706
  // src/commands/search.ts
1513
- import { Command as Command18 } from "commander";
1707
+ import { Command as Command19 } from "commander";
1514
1708
  function createSearchCommand() {
1515
- const search = new Command18("search").description("Search commands");
1709
+ const search = new Command19("search").description("Search commands");
1516
1710
  search.command("messages").description("Search messages").requiredOption("--query <text>", "Search query").option("--server-id <id>", "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(
1517
1711
  async (options) => {
1518
1712
  try {
@@ -1537,9 +1731,9 @@ function createSearchCommand() {
1537
1731
  }
1538
1732
 
1539
1733
  // src/commands/servers.ts
1540
- import { Command as Command19 } from "commander";
1734
+ import { Command as Command20 } from "commander";
1541
1735
  function createServersCommand() {
1542
- const servers = new Command19("servers").description("Server management commands");
1736
+ const servers = new Command20("servers").description("Server management commands");
1543
1737
  servers.command("list").description("List all servers you have joined").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (options) => {
1544
1738
  try {
1545
1739
  const client = await getClient(options.profile);
@@ -1619,45 +1813,6 @@ function createServersCommand() {
1619
1813
  process.exit(1);
1620
1814
  }
1621
1815
  });
1622
- servers.command("homepage").description("Get or set server homepage").argument("<server-id>", "Server ID or slug").option("--set <file>", 'Set homepage from HTML file (use "-" for stdin)').option("--clear", "Clear homepage (reset to default)").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(
1623
- async (serverId, options) => {
1624
- try {
1625
- const client = await getClient(options.profile);
1626
- if (options.clear) {
1627
- const result = await client.updateServerHomepage(serverId, null);
1628
- output(result, { json: options.json });
1629
- return;
1630
- }
1631
- if (options.set) {
1632
- let html;
1633
- if (options.set === "-") {
1634
- const chunks = [];
1635
- for await (const chunk of process.stdin) {
1636
- chunks.push(Buffer.from(chunk));
1637
- }
1638
- html = Buffer.concat(chunks).toString("utf-8");
1639
- } else {
1640
- const { readFile: readFile2 } = await import("fs/promises");
1641
- html = await readFile2(options.set, "utf-8");
1642
- }
1643
- const result = await client.updateServerHomepage(serverId, html);
1644
- output(result, { json: options.json });
1645
- return;
1646
- }
1647
- const server = await client.getServer(serverId);
1648
- if (options.json) {
1649
- output({ homepageHtml: server.homepageHtml }, { json: true });
1650
- } else {
1651
- console.log(server.homepageHtml ?? "(default homepage)");
1652
- }
1653
- } catch (error) {
1654
- outputError(error instanceof Error ? error.message : String(error), {
1655
- json: options.json
1656
- });
1657
- process.exit(1);
1658
- }
1659
- }
1660
- );
1661
1816
  servers.command("discover").description("Discover public servers").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (options) => {
1662
1817
  try {
1663
1818
  const client = await getClient(options.profile);
@@ -1672,9 +1827,9 @@ function createServersCommand() {
1672
1827
  }
1673
1828
 
1674
1829
  // src/commands/shop.ts
1675
- import { Command as Command20 } from "commander";
1830
+ import { Command as Command21 } from "commander";
1676
1831
  function createShopCommand() {
1677
- const shop = new Command20("shop").description("Shop commands");
1832
+ const shop = new Command21("shop").description("Shop commands");
1678
1833
  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) => {
1679
1834
  try {
1680
1835
  const client = await getClient(options.profile);
@@ -1765,9 +1920,9 @@ function createShopCommand() {
1765
1920
  }
1766
1921
 
1767
1922
  // src/commands/status.ts
1768
- import { Command as Command21 } from "commander";
1923
+ import { Command as Command22 } from "commander";
1769
1924
  function createStatusCommand() {
1770
- const status = new Command21("status").description("Show detailed status information");
1925
+ const status = new Command22("status").description("Show detailed status information");
1771
1926
  status.option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (options) => {
1772
1927
  const outputOpts = { json: options.json };
1773
1928
  try {
@@ -1782,7 +1937,7 @@ function createStatusCommand() {
1782
1937
  }
1783
1938
  const client = await getClient(options.profile);
1784
1939
  const user = await client.getMe();
1785
- const notifications = await client.listNotifications(1).catch(() => []);
1940
+ const unread = await client.getUnreadCount().catch(() => ({ count: 0 }));
1786
1941
  const statusInfo = {
1787
1942
  profile: {
1788
1943
  name: profileName,
@@ -1795,7 +1950,7 @@ function createStatusCommand() {
1795
1950
  avatarUrl: user.avatarUrl
1796
1951
  },
1797
1952
  stats: {
1798
- unreadNotifications: Array.isArray(notifications) ? notifications.length : 0
1953
+ unreadNotifications: unread.count
1799
1954
  },
1800
1955
  connection: {
1801
1956
  status: "connected",
@@ -1845,9 +2000,9 @@ function createStatusCommand() {
1845
2000
  }
1846
2001
 
1847
2002
  // src/commands/threads.ts
1848
- import { Command as Command22 } from "commander";
2003
+ import { Command as Command23 } from "commander";
1849
2004
  function createThreadsCommand() {
1850
- const threads = new Command22("threads").description("Thread commands");
2005
+ const threads = new Command23("threads").description("Thread commands");
1851
2006
  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) => {
1852
2007
  try {
1853
2008
  const client = await getClient(options.profile);
@@ -1929,9 +2084,9 @@ function createThreadsCommand() {
1929
2084
  }
1930
2085
 
1931
2086
  // src/commands/voice-enhance.ts
1932
- import { Command as Command23 } from "commander";
2087
+ import { Command as Command24 } from "commander";
1933
2088
  function createVoiceEnhanceCommand() {
1934
- const voice = new Command23("voice-enhance").description("Voice enhancement commands");
2089
+ const voice = new Command24("voice-enhance").description("Voice enhancement commands");
1935
2090
  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) => {
1936
2091
  try {
1937
2092
  const client = await getClient(options.profile);
@@ -1970,10 +2125,10 @@ function createVoiceEnhanceCommand() {
1970
2125
  }
1971
2126
 
1972
2127
  // src/commands/workspace.ts
1973
- import { readFile } from "fs/promises";
1974
- import { Command as Command24 } from "commander";
2128
+ import { readFile as readFile2 } from "fs/promises";
2129
+ import { Command as Command25 } from "commander";
1975
2130
  function createWorkspaceCommand() {
1976
- const workspace = new Command24("workspace").description("Workspace file management commands");
2131
+ const workspace = new Command25("workspace").description("Workspace file management commands");
1977
2132
  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) => {
1978
2133
  try {
1979
2134
  const client = await getClient(options.profile);
@@ -2069,7 +2224,7 @@ function createWorkspaceCommand() {
2069
2224
  async (serverId, options) => {
2070
2225
  try {
2071
2226
  const client = await getClient(options.profile);
2072
- const content = await readFile(options.file);
2227
+ const content = await readFile2(options.file);
2073
2228
  const blob = new Blob([content]);
2074
2229
  const name = options.name ?? options.file.split("/").pop() ?? "upload";
2075
2230
  const result = await client.uploadWorkspaceFile(serverId, blob, name, options.parentId);
@@ -2155,12 +2310,13 @@ function createWorkspaceCommand() {
2155
2310
  }
2156
2311
 
2157
2312
  // src/index.ts
2158
- var program = new Command25();
2313
+ var program = new Command26();
2159
2314
  program.name("shadowob").description("Shadow CLI \u2014 command-line interface for Shadow servers").version("0.1.0").configureHelp({
2160
2315
  sortSubcommands: true
2161
2316
  });
2162
2317
  program.option("--profile <name>", "Profile to use (default: current)");
2163
2318
  program.addCommand(createAuthCommand());
2319
+ program.addCommand(createAppCommand());
2164
2320
  program.addCommand(createServersCommand());
2165
2321
  program.addCommand(createChannelsCommand());
2166
2322
  program.addCommand(createThreadsCommand());
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@shadowob/cli",
3
- "version": "1.1.3",
3
+ "version": "1.1.4",
4
4
  "description": "Shadow CLI — command-line interface for Shadow servers",
5
+ "license": "MIT",
5
6
  "type": "module",
6
7
  "bin": {
7
8
  "shadowob": "./dist/index.js"
@@ -12,7 +13,7 @@
12
13
  "dependencies": {
13
14
  "commander": "^13.1.0",
14
15
  "chalk": "^5.4.1",
15
- "@shadowob/sdk": "1.1.3"
16
+ "@shadowob/sdk": "1.1.4"
16
17
  },
17
18
  "devDependencies": {
18
19
  "@types/node": "^22.15.21",