@shadowob/cli 1.1.6 → 1.1.7

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.
@@ -8,6 +8,11 @@ import { homedir } from "os";
8
8
  import { dirname, join } from "path";
9
9
  var DEFAULT_CONFIG_DIR = join(homedir(), ".shadowob");
10
10
  var _DEFAULT_CONFIG_FILE = join(DEFAULT_CONFIG_DIR, "shadowob.config.json");
11
+ function expandEnvPlaceholders(value) {
12
+ return value.replace(/\$\{(?:env:)?([A-Za-z_][A-Za-z0-9_]*)\}/g, (_match, key) => {
13
+ return process.env[key] ?? "";
14
+ });
15
+ }
11
16
  var ConfigManager = class {
12
17
  config = null;
13
18
  configFile;
@@ -39,7 +44,12 @@ var ConfigManager = class {
39
44
  const config = await this.load();
40
45
  const profileName = name ?? config.currentProfile;
41
46
  if (!profileName) return null;
42
- return config.profiles[profileName] ?? null;
47
+ const profile = config.profiles[profileName];
48
+ if (!profile) return null;
49
+ return {
50
+ serverUrl: expandEnvPlaceholders(profile.serverUrl),
51
+ token: expandEnvPlaceholders(profile.token)
52
+ };
43
53
  }
44
54
  async getCurrentProfileName() {
45
55
  const config = await this.load();
@@ -110,21 +120,23 @@ var ConfigManager = class {
110
120
  }
111
121
  for (const [name, profile] of Object.entries(config.profiles)) {
112
122
  const profileResult = { valid: true };
113
- if (!profile.serverUrl) {
123
+ const serverUrl = expandEnvPlaceholders(profile.serverUrl);
124
+ const token = expandEnvPlaceholders(profile.token);
125
+ if (!serverUrl) {
114
126
  profileResult.valid = false;
115
127
  result.errors.push(`Profile "${name}" missing serverUrl`);
116
128
  } else {
117
129
  try {
118
- new URL(profile.serverUrl);
130
+ new URL(serverUrl);
119
131
  } catch {
120
132
  profileResult.valid = false;
121
- result.errors.push(`Profile "${name}" has invalid serverUrl: ${profile.serverUrl}`);
133
+ result.errors.push(`Profile "${name}" has invalid serverUrl: ${serverUrl}`);
122
134
  }
123
135
  }
124
- if (!profile.token) {
136
+ if (!token) {
125
137
  profileResult.valid = false;
126
138
  result.errors.push(`Profile "${name}" missing token`);
127
- } else if (!profile.token.includes(".")) {
139
+ } else if (!token.includes(".")) {
128
140
  result.warnings.push(`Profile "${name}" token does not look like a JWT`);
129
141
  }
130
142
  result.profileResults[name] = profileResult;
@@ -164,6 +176,12 @@ var ConfigManager = class {
164
176
  var configManager = new ConfigManager();
165
177
 
166
178
  // src/utils/client.ts
179
+ var DEFAULT_SERVER_URL = "https://shadowob.com";
180
+ function resolveServerFlag(value) {
181
+ const server = value ?? process.env.SHADOWOB_SERVER_ID;
182
+ if (!server) throw new Error("Missing server. Pass --server or set SHADOWOB_SERVER_ID.");
183
+ return server;
184
+ }
167
185
  async function getConfig(profile) {
168
186
  const config = await configManager.getProfile(profile);
169
187
  if (!config) {
@@ -251,6 +269,8 @@ async function handleCommand(fn, options, outputFn, errorFn) {
251
269
 
252
270
  export {
253
271
  configManager,
272
+ DEFAULT_SERVER_URL,
273
+ resolveServerFlag,
254
274
  getClient,
255
275
  getClientWithToken,
256
276
  getSocket,
@@ -1,4 +1,5 @@
1
1
  import {
2
+ DEFAULT_SERVER_URL,
2
3
  formatError,
3
4
  getClient,
4
5
  getClientWithToken,
@@ -10,9 +11,11 @@ import {
10
11
  parseNonNegativeInt,
11
12
  parsePositiveInt,
12
13
  parsePrice,
13
- requireOption
14
- } from "./chunk-E364BDQO.js";
14
+ requireOption,
15
+ resolveServerFlag
16
+ } from "./chunk-S6XCO6ZW.js";
15
17
  export {
18
+ DEFAULT_SERVER_URL,
16
19
  formatError,
17
20
  getClient,
18
21
  getClientWithToken,
@@ -24,5 +27,6 @@ export {
24
27
  parseNonNegativeInt,
25
28
  parsePositiveInt,
26
29
  parsePrice,
27
- requireOption
30
+ requireOption,
31
+ resolveServerFlag
28
32
  };
package/dist/index.js CHANGED
@@ -4,13 +4,14 @@ import {
4
4
  getClient,
5
5
  getClientWithToken,
6
6
  getSocket,
7
- parsePositiveInt
8
- } from "./chunk-E364BDQO.js";
7
+ parsePositiveInt,
8
+ resolveServerFlag
9
+ } from "./chunk-S6XCO6ZW.js";
9
10
 
10
11
  // src/index.ts
11
12
  import { Command as Command28 } from "commander";
12
13
 
13
- // src/commands/agents.ts
14
+ // src/commands/api-tokens.ts
14
15
  import { Command } from "commander";
15
16
 
16
17
  // src/utils/output.ts
@@ -88,139 +89,32 @@ function formatObject(obj) {
88
89
  formattedValue = chalk.gray("null");
89
90
  } else if (typeof value === "boolean") {
90
91
  formattedValue = value ? chalk.green("true") : chalk.red("false");
91
- } else if (typeof value === "object") {
92
+ } else if (Array.isArray(value)) {
93
+ if (value.length > 0 && typeof value[0] === "object" && value[0] !== null) {
94
+ console.log(chalk.gray(formattedKey));
95
+ formatArray(value);
96
+ continue;
97
+ }
92
98
  formattedValue = JSON.stringify(value);
99
+ } else if (typeof value === "object") {
100
+ formattedValue = formatNestedValue(value);
93
101
  } else {
94
102
  formattedValue = String(value);
95
103
  }
96
104
  console.log(`${chalk.gray(formattedKey)} ${formattedValue}`);
97
105
  }
98
106
  }
99
-
100
- // src/commands/agents.ts
101
- function createAgentsCommand() {
102
- const agents = new Command("agents").description("Agent management commands");
103
- agents.command("list").description("List your agents").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (options) => {
104
- try {
105
- const client = await getClient(options.profile);
106
- const agents2 = await client.listAgents();
107
- output(agents2, { json: options.json });
108
- } catch (error) {
109
- outputError(error instanceof Error ? error.message : String(error), { json: options.json });
110
- process.exit(1);
111
- }
112
- });
113
- agents.command("get").description("Get agent details").argument("<agent-id>", "Agent ID").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (agentId, options) => {
114
- try {
115
- const client = await getClient(options.profile);
116
- const agent = await client.getAgent(agentId);
117
- output(agent, { json: options.json });
118
- } catch (error) {
119
- outputError(error instanceof Error ? error.message : String(error), { json: options.json });
120
- process.exit(1);
121
- }
122
- });
123
- agents.command("create").description("Create a new agent").requiredOption("--name <name>", "Agent name").requiredOption("--username <username>", "Agent username").option("--display-name <name>", "Display name").option("--avatar-url <url>", "Avatar URL").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(
124
- async (options) => {
125
- try {
126
- const client = await getClient(options.profile);
127
- const agent = await client.createAgent({
128
- name: options.name,
129
- username: options.username,
130
- displayName: options.displayName,
131
- avatarUrl: options.avatarUrl
132
- });
133
- output(agent, { json: options.json });
134
- } catch (error) {
135
- outputError(error instanceof Error ? error.message : String(error), {
136
- json: options.json
137
- });
138
- process.exit(1);
139
- }
140
- }
141
- );
142
- agents.command("update").description("Update an agent").argument("<agent-id>", "Agent ID").option("--name <name>", "New name").option("--display-name <name>", "New display name").option("--avatar-url <url>", "New avatar URL").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(
143
- async (agentId, options) => {
144
- try {
145
- const client = await getClient(options.profile);
146
- const agent = await client.updateAgent(agentId, {
147
- name: options.name,
148
- displayName: options.displayName,
149
- avatarUrl: options.avatarUrl
150
- });
151
- output(agent, { json: options.json });
152
- } catch (error) {
153
- outputError(error instanceof Error ? error.message : String(error), {
154
- json: options.json
155
- });
156
- process.exit(1);
157
- }
158
- }
159
- );
160
- agents.command("delete").description("Delete an agent").argument("<agent-id>", "Agent ID").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (agentId, options) => {
161
- try {
162
- const client = await getClient(options.profile);
163
- await client.deleteAgent(agentId);
164
- const outputOpts = { json: options.json };
165
- outputSuccess("Agent deleted", outputOpts);
166
- } catch (error) {
167
- outputError(error instanceof Error ? error.message : String(error), { json: options.json });
168
- process.exit(1);
169
- }
170
- });
171
- agents.command("start").description("Start an agent").argument("<agent-id>", "Agent ID").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (agentId, options) => {
172
- try {
173
- const client = await getClient(options.profile);
174
- await client.startAgent(agentId);
175
- const outputOpts = { json: options.json };
176
- outputSuccess("Agent started", outputOpts);
177
- } catch (error) {
178
- outputError(error instanceof Error ? error.message : String(error), { json: options.json });
179
- process.exit(1);
180
- }
181
- });
182
- agents.command("stop").description("Stop an agent").argument("<agent-id>", "Agent ID").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (agentId, options) => {
183
- try {
184
- const client = await getClient(options.profile);
185
- await client.stopAgent(agentId);
186
- const outputOpts = { json: options.json };
187
- outputSuccess("Agent stopped", outputOpts);
188
- } catch (error) {
189
- outputError(error instanceof Error ? error.message : String(error), { json: options.json });
190
- process.exit(1);
191
- }
192
- });
193
- agents.command("token").description("Generate a new token for an agent").argument("<agent-id>", "Agent ID").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (agentId, options) => {
194
- try {
195
- const client = await getClient(options.profile);
196
- const result = await client.generateAgentToken(agentId);
197
- if (options.json) {
198
- output(result, { json: true });
199
- } else {
200
- console.log(result.token);
201
- }
202
- } catch (error) {
203
- outputError(error instanceof Error ? error.message : String(error), { json: options.json });
204
- process.exit(1);
205
- }
206
- });
207
- agents.command("config").description("Get agent remote config").argument("<agent-id>", "Agent ID").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (agentId, options) => {
208
- try {
209
- const client = await getClient(options.profile);
210
- const config = await client.getAgentConfig(agentId);
211
- output(config, { json: options.json });
212
- } catch (error) {
213
- outputError(error instanceof Error ? error.message : String(error), { json: options.json });
214
- process.exit(1);
215
- }
216
- });
217
- return agents;
107
+ function formatNestedValue(obj) {
108
+ const username = obj.username || obj.name || obj.displayName;
109
+ if (username) {
110
+ return username;
111
+ }
112
+ return JSON.stringify(obj);
218
113
  }
219
114
 
220
115
  // src/commands/api-tokens.ts
221
- import { Command as Command2 } from "commander";
222
116
  function createApiTokensCommand() {
223
- const tokens = new Command2("api-tokens").description("API token management commands");
117
+ const tokens = new Command("api-tokens").description("API token management commands");
224
118
  tokens.command("list").description("List all API tokens").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (options) => {
225
119
  try {
226
120
  const client = await getClient(options.profile);
@@ -270,12 +164,7 @@ function createApiTokensCommand() {
270
164
  // src/commands/app.ts
271
165
  import { readFile, writeFile } from "fs/promises";
272
166
  import { basename } from "path";
273
- import { Command as Command3 } from "commander";
274
- function resolveServer(value) {
275
- const server = value ?? process.env.SHADOWOB_SERVER_ID;
276
- if (!server) throw new Error("Missing server. Pass --server or set SHADOWOB_SERVER_ID.");
277
- return server;
278
- }
167
+ import { Command as Command2 } from "commander";
279
168
  function parseJsonInput(value) {
280
169
  if (!value) return {};
281
170
  const parsed = JSON.parse(value);
@@ -287,16 +176,26 @@ function parseJsonInput(value) {
287
176
  async function readJsonFile(path) {
288
177
  return JSON.parse(await readFile(path, "utf8"));
289
178
  }
179
+ function parsePermissions(value) {
180
+ return value.split(",").map((item) => item.trim()).filter(Boolean);
181
+ }
290
182
  function commandHandlerError(error, json) {
291
183
  outputError(error instanceof Error ? error.message : String(error), { json });
292
184
  process.exit(1);
293
185
  }
294
186
  function createAppCommand() {
295
- const app = new Command3("app").description("Server App integration commands");
187
+ const app = new Command2("app").description("Server App integration commands");
296
188
  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) => {
297
189
  try {
298
190
  const client = await getClient(options.profile);
299
- output(await client.listServerApps(resolveServer(options.server)), { json: options.json });
191
+ const apps = await client.listServerApps(resolveServerFlag(options.server));
192
+ output(
193
+ apps.map((entry) => ({
194
+ id: entry.id,
195
+ name: `${entry.appKey} (${entry.name})`
196
+ })),
197
+ { json: options.json }
198
+ );
300
199
  } catch (error) {
301
200
  commandHandlerError(error, options.json);
302
201
  }
@@ -310,7 +209,7 @@ function createAppCommand() {
310
209
  const client = await getClient(options.profile);
311
210
  const manifest = options.manifestFile ? await readJsonFile(options.manifestFile) : void 0;
312
211
  output(
313
- await client.discoverServerApp(resolveServer(options.server), {
212
+ await client.discoverServerApp(resolveServerFlag(options.server), {
314
213
  manifestUrl: options.manifestUrl,
315
214
  manifest
316
215
  }),
@@ -329,7 +228,7 @@ function createAppCommand() {
329
228
  }
330
229
  const client = await getClient(options.profile);
331
230
  const manifest = options.manifestFile ? await readJsonFile(options.manifestFile) : void 0;
332
- const result = await client.installServerApp(resolveServer(options.server), {
231
+ const result = await client.installServerApp(resolveServerFlag(options.server), {
333
232
  manifestUrl: options.manifestUrl,
334
233
  manifest
335
234
  });
@@ -343,7 +242,7 @@ function createAppCommand() {
343
242
  async (appKey, options) => {
344
243
  try {
345
244
  const client = await getClient(options.profile);
346
- output(await client.getServerApp(resolveServer(options.server), appKey), {
245
+ output(await client.getServerApp(resolveServerFlag(options.server), appKey), {
347
246
  json: options.json
348
247
  });
349
248
  } catch (error) {
@@ -351,16 +250,57 @@ function createAppCommand() {
351
250
  }
352
251
  }
353
252
  );
354
- 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(
253
+ 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 <buddy-id>", "Buddy 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(
355
254
  async (appKey, options) => {
356
255
  try {
357
256
  const client = await getClient(options.profile);
358
- const permissions = options.permissions.split(",").map((item) => item.trim()).filter(Boolean);
359
- const result = await client.grantServerAppToBuddy(resolveServer(options.server), appKey, {
360
- buddyAgentId: options.buddy,
361
- permissions,
362
- approvalMode: options.approvalMode
363
- });
257
+ const permissions = parsePermissions(options.permissions);
258
+ const result = await client.grantServerAppToBuddy(
259
+ resolveServerFlag(options.server),
260
+ appKey,
261
+ {
262
+ buddyAgentId: options.buddy,
263
+ permissions,
264
+ approvalMode: options.approvalMode
265
+ }
266
+ );
267
+ output(result, { json: options.json });
268
+ } catch (error) {
269
+ commandHandlerError(error, options.json);
270
+ }
271
+ }
272
+ );
273
+ app.command("defaults").description("Set default app permissions that members and Buddies can use without prompting").argument("<app-key>", "App key").requiredOption("--server <server>", "Server ID or slug").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(
274
+ async (appKey, options) => {
275
+ try {
276
+ const client = await getClient(options.profile);
277
+ const result = await client.updateServerAppAccessPolicy(
278
+ resolveServerFlag(options.server),
279
+ appKey,
280
+ {
281
+ defaultPermissions: parsePermissions(options.permissions),
282
+ defaultApprovalMode: options.approvalMode
283
+ }
284
+ );
285
+ output(result, { json: options.json });
286
+ } catch (error) {
287
+ commandHandlerError(error, options.json);
288
+ }
289
+ }
290
+ );
291
+ app.command("approve").description("Approve one app command for yourself or a Buddy after a first-use prompt").argument("<app-key>", "App key").argument("<command>", "Command name").requiredOption("--server <server>", "Server ID or slug").option("--buddy <buddy-id>", "Buddy ID to approve for").option("--no-remember", "Approve only the immediate retry window").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(
292
+ async (appKey, commandName, options) => {
293
+ try {
294
+ const client = await getClient(options.profile);
295
+ const result = await client.approveServerAppCommand(
296
+ resolveServerFlag(options.server),
297
+ appKey,
298
+ {
299
+ commandName,
300
+ buddyAgentId: options.buddy,
301
+ remember: options.remember
302
+ }
303
+ );
364
304
  output(result, { json: options.json });
365
305
  } catch (error) {
366
306
  commandHandlerError(error, options.json);
@@ -370,7 +310,7 @@ function createAppCommand() {
370
310
  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) => {
371
311
  try {
372
312
  const client = await getClient(options.profile);
373
- const server = resolveServer(options.server);
313
+ const server = resolveServerFlag(options.server);
374
314
  const apps = await client.listServerApps(server);
375
315
  const docs = await Promise.all(
376
316
  apps.map((entry) => client.getServerAppSkills(server, entry.appKey))
@@ -388,7 +328,7 @@ function createAppCommand() {
388
328
  async (appKey, options) => {
389
329
  try {
390
330
  const client = await getClient(options.profile);
391
- const result = await client.getServerAppSkills(resolveServer(options.server), appKey);
331
+ const result = await client.getServerAppSkills(resolveServerFlag(options.server), appKey);
392
332
  if (options.json) output(result, { json: true });
393
333
  else console.log(result.markdown);
394
334
  } catch (error) {
@@ -396,18 +336,22 @@ function createAppCommand() {
396
336
  }
397
337
  }
398
338
  );
399
- 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(
339
+ 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("--channel-id <id>", "Current Shadow channel ID for approval prompts and app context").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(
400
340
  async (appKey, commandName, options) => {
401
341
  try {
402
342
  const client = await getClient(options.profile);
403
343
  const input = options.inputFile ? await readJsonFile(options.inputFile) : parseJsonInput(options.jsonInput);
404
- const server = resolveServer(options.server);
344
+ const server = resolveServerFlag(options.server);
405
345
  const result = options.file ? await client.callServerAppCommandMultipart(server, appKey, commandName, {
406
346
  input,
347
+ channelId: options.channelId,
407
348
  file: new Blob([await readFile(options.file)]),
408
349
  filename: basename(options.file),
409
350
  field: options.field
410
- }) : await client.callServerAppCommand(server, appKey, commandName, { input });
351
+ }) : await client.callServerAppCommand(server, appKey, commandName, {
352
+ input,
353
+ channelId: options.channelId
354
+ });
411
355
  if (options.output && result && typeof result === "object" && "dataBase64" in result && typeof result.dataBase64 === "string") {
412
356
  await writeFile(
413
357
  options.output,
@@ -422,15 +366,28 @@ function createAppCommand() {
422
366
  }
423
367
  }
424
368
  );
369
+ app.command("uninstall").description("Uninstall a 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(
370
+ async (appKey, options) => {
371
+ try {
372
+ const client = await getClient(options.profile);
373
+ await client.deleteServerApp(resolveServerFlag(options.server), appKey);
374
+ const outputOpts = { json: options.json };
375
+ if (options.json) output({ ok: true }, outputOpts);
376
+ else outputSuccess(`Uninstalled ${appKey}`, outputOpts);
377
+ } catch (error) {
378
+ commandHandlerError(error, options.json);
379
+ }
380
+ }
381
+ );
425
382
  return app;
426
383
  }
427
384
 
428
385
  // src/commands/auth.ts
429
386
  import { ShadowClient } from "@shadowob/sdk";
430
- import { Command as Command4 } from "commander";
387
+ import { Command as Command3 } from "commander";
431
388
  function createAuthCommand() {
432
- const auth = new Command4("auth").description("Authentication commands");
433
- 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(
389
+ const auth = new Command3("auth").description("Authentication commands");
390
+ auth.command("login").description("Authenticate with a Shadow server").option("--server-url <url>", "Shadow server URL", "https://shadowob.com").requiredOption("--token <token>", "JWT token").option("--profile <name>", "Profile name", "default").option("--json", "Output as JSON").action(
434
391
  async (options) => {
435
392
  try {
436
393
  const client = new ShadowClient(options.serverUrl, options.token);
@@ -541,14 +498,135 @@ function createAuthCommand() {
541
498
  return auth;
542
499
  }
543
500
 
501
+ // src/commands/buddies.ts
502
+ import { Command as Command4 } from "commander";
503
+ function createBuddiesCommand() {
504
+ const buddies = new Command4("buddies").description("Buddy management commands");
505
+ buddies.command("list").description("List your buddies").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (options) => {
506
+ try {
507
+ const client = await getClient(options.profile);
508
+ const agents = await client.listAgents();
509
+ output(agents, { json: options.json });
510
+ } catch (error) {
511
+ outputError(error instanceof Error ? error.message : String(error), { json: options.json });
512
+ process.exit(1);
513
+ }
514
+ });
515
+ buddies.command("get").description("Get buddy details").argument("<buddy-id>", "Buddy ID").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (buddyId, options) => {
516
+ try {
517
+ const client = await getClient(options.profile);
518
+ const agent = await client.getAgent(buddyId);
519
+ output(agent, { json: options.json });
520
+ } catch (error) {
521
+ outputError(error instanceof Error ? error.message : String(error), { json: options.json });
522
+ process.exit(1);
523
+ }
524
+ });
525
+ buddies.command("create").description("Create a new buddy").requiredOption("--name <name>", "Buddy name").requiredOption("--username <username>", "Buddy username").option("--display-name <name>", "Display name").option("--avatar-url <url>", "Avatar URL").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(
526
+ async (options) => {
527
+ try {
528
+ const client = await getClient(options.profile);
529
+ const agent = await client.createAgent({
530
+ name: options.name,
531
+ username: options.username,
532
+ displayName: options.displayName,
533
+ avatarUrl: options.avatarUrl
534
+ });
535
+ output(agent, { json: options.json });
536
+ } catch (error) {
537
+ outputError(error instanceof Error ? error.message : String(error), {
538
+ json: options.json
539
+ });
540
+ process.exit(1);
541
+ }
542
+ }
543
+ );
544
+ buddies.command("update").description("Update a buddy").argument("<buddy-id>", "Buddy ID").option("--name <name>", "New name").option("--display-name <name>", "New display name").option("--avatar-url <url>", "New avatar URL").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(
545
+ async (buddyId, options) => {
546
+ try {
547
+ const client = await getClient(options.profile);
548
+ const agent = await client.updateAgent(buddyId, {
549
+ name: options.name,
550
+ displayName: options.displayName,
551
+ avatarUrl: options.avatarUrl
552
+ });
553
+ output(agent, { json: options.json });
554
+ } catch (error) {
555
+ outputError(error instanceof Error ? error.message : String(error), {
556
+ json: options.json
557
+ });
558
+ process.exit(1);
559
+ }
560
+ }
561
+ );
562
+ buddies.command("delete").description("Delete a buddy").argument("<buddy-id>", "Buddy ID").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (buddyId, options) => {
563
+ try {
564
+ const client = await getClient(options.profile);
565
+ await client.deleteAgent(buddyId);
566
+ const outputOpts = { json: options.json };
567
+ outputSuccess("Buddy deleted", outputOpts);
568
+ } catch (error) {
569
+ outputError(error instanceof Error ? error.message : String(error), { json: options.json });
570
+ process.exit(1);
571
+ }
572
+ });
573
+ buddies.command("start").description("Start a buddy").argument("<buddy-id>", "Buddy ID").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (buddyId, options) => {
574
+ try {
575
+ const client = await getClient(options.profile);
576
+ await client.startAgent(buddyId);
577
+ const outputOpts = { json: options.json };
578
+ outputSuccess("Buddy started", outputOpts);
579
+ } catch (error) {
580
+ outputError(error instanceof Error ? error.message : String(error), { json: options.json });
581
+ process.exit(1);
582
+ }
583
+ });
584
+ buddies.command("stop").description("Stop a buddy").argument("<buddy-id>", "Buddy ID").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (buddyId, options) => {
585
+ try {
586
+ const client = await getClient(options.profile);
587
+ await client.stopAgent(buddyId);
588
+ const outputOpts = { json: options.json };
589
+ outputSuccess("Buddy stopped", outputOpts);
590
+ } catch (error) {
591
+ outputError(error instanceof Error ? error.message : String(error), { json: options.json });
592
+ process.exit(1);
593
+ }
594
+ });
595
+ buddies.command("token").description("Generate a new token for a buddy").argument("<buddy-id>", "Buddy ID").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (buddyId, options) => {
596
+ try {
597
+ const client = await getClient(options.profile);
598
+ const result = await client.generateAgentToken(buddyId);
599
+ if (options.json) {
600
+ output(result, { json: true });
601
+ } else {
602
+ console.log(result.token);
603
+ }
604
+ } catch (error) {
605
+ outputError(error instanceof Error ? error.message : String(error), { json: options.json });
606
+ process.exit(1);
607
+ }
608
+ });
609
+ buddies.command("config").description("Get buddy remote config").argument("<buddy-id>", "Buddy ID").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (buddyId, options) => {
610
+ try {
611
+ const client = await getClient(options.profile);
612
+ const config = await client.getAgentConfig(buddyId);
613
+ output(config, { json: options.json });
614
+ } catch (error) {
615
+ outputError(error instanceof Error ? error.message : String(error), { json: options.json });
616
+ process.exit(1);
617
+ }
618
+ });
619
+ return buddies;
620
+ }
621
+
544
622
  // src/commands/channels.ts
545
623
  import { Command as Command5 } from "commander";
546
624
  function createChannelsCommand() {
547
625
  const channels = new Command5("channels").description("Channel commands");
548
- 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) => {
626
+ channels.command("list").description("List channels in a server").requiredOption("--server <server>", "Server ID or slug").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (options) => {
549
627
  try {
550
628
  const client = await getClient(options.profile);
551
- const channels2 = await client.getServerChannels(options.serverId);
629
+ const channels2 = await client.getServerChannels(resolveServerFlag(options.server));
552
630
  output(channels2, { json: options.json });
553
631
  } catch (error) {
554
632
  outputError(error instanceof Error ? error.message : String(error), { json: options.json });
@@ -565,11 +643,11 @@ function createChannelsCommand() {
565
643
  process.exit(1);
566
644
  }
567
645
  });
568
- channels.command("create").description("Create a channel").requiredOption("--server-id <id>", "Server ID").requiredOption("--name <name>", "Channel name").option("--type <type>", "Channel type", "text").option("--description <desc>", "Channel description").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(
646
+ channels.command("create").description("Create a channel").requiredOption("--server <server>", "Server ID or slug").requiredOption("--name <name>", "Channel name").option("--type <type>", "Channel type", "text").option("--description <desc>", "Channel description").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(
569
647
  async (options) => {
570
648
  try {
571
649
  const client = await getClient(options.profile);
572
- const channel = await client.createChannel(options.serverId, {
650
+ const channel = await client.createChannel(resolveServerFlag(options.server), {
573
651
  name: options.name,
574
652
  type: options.type,
575
653
  description: options.description
@@ -840,10 +918,10 @@ function createCommerceCommand() {
840
918
  })
841
919
  );
842
920
  const entitlements = commerce.command("entitlements").description("Purchase entitlement commands");
843
- entitlements.command("list").description("List my purchase entitlements").option("--server-id <id>", "Limit to a server shop entitlement list").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(
921
+ entitlements.command("list").description("List my purchase entitlements").option("--server <server>", "Limit to a server shop entitlement list").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(
844
922
  (options) => runCommand(options, async () => {
845
923
  const client = await getClient(options.profile);
846
- return options.serverId ? client.getEntitlements(options.serverId) : client.getAllEntitlements();
924
+ return options.server ? client.getEntitlements(resolveServerFlag(options.server)) : client.getAllEntitlements();
847
925
  })
848
926
  );
849
927
  entitlements.command("get").description("Get purchase delivery detail").argument("<entitlement-id>", "Entitlement ID").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(
@@ -1304,7 +1382,7 @@ function createListenCommand() {
1304
1382
  try {
1305
1383
  const eventTypes = options.eventType?.split(",").map((t) => t.trim());
1306
1384
  if (options.mode === "poll") {
1307
- const { getClient: getClient2 } = await import("./client-ZIUDIQPZ.js");
1385
+ const { getClient: getClient2 } = await import("./client-VR35GYUZ.js");
1308
1386
  const client = await getClient2(options.profile);
1309
1387
  const limit = parseInt(options.last ?? "50", 10);
1310
1388
  const result = await client.getMessages(channelId, limit);
@@ -1970,14 +2048,14 @@ function createProfileCommentsCommand() {
1970
2048
  import { Command as Command20 } from "commander";
1971
2049
  function createSearchCommand() {
1972
2050
  const search = new Command20("search").description("Search commands");
1973
- 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(
2051
+ 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(
1974
2052
  async (options) => {
1975
2053
  try {
1976
2054
  const client = await getClient(options.profile);
1977
2055
  const limit = Math.min(Math.max(parseInt(options.limit ?? "20", 10), 1), 100);
1978
2056
  const results = await client.searchMessages({
1979
2057
  q: options.query,
1980
- serverId: options.serverId,
2058
+ serverId: options.server ? resolveServerFlag(options.server) : void 0,
1981
2059
  channelId: options.channelId,
1982
2060
  limit
1983
2061
  });
@@ -2001,7 +2079,10 @@ function createServersCommand() {
2001
2079
  try {
2002
2080
  const client = await getClient(options.profile);
2003
2081
  const servers2 = await client.listServers();
2004
- output(servers2, { json: options.json });
2082
+ output(
2083
+ servers2.map((s) => s.server),
2084
+ { json: options.json }
2085
+ );
2005
2086
  } catch (error) {
2006
2087
  outputError(error instanceof Error ? error.message : String(error), { json: options.json });
2007
2088
  process.exit(1);
@@ -2070,7 +2151,17 @@ function createServersCommand() {
2070
2151
  try {
2071
2152
  const client = await getClient(options.profile);
2072
2153
  const members = await client.getMembers(serverId);
2073
- output(members, { json: options.json });
2154
+ output(
2155
+ members.map(
2156
+ (m) => ({
2157
+ id: m.id,
2158
+ userId: m.userId,
2159
+ username: m.user?.username ?? m.nickname ?? m.uid ?? "",
2160
+ role: m.role
2161
+ })
2162
+ ),
2163
+ { json: options.json }
2164
+ );
2074
2165
  } catch (error) {
2075
2166
  outputError(error instanceof Error ? error.message : String(error), { json: options.json });
2076
2167
  process.exit(1);
@@ -2208,17 +2299,6 @@ function createShopCommand() {
2208
2299
  }
2209
2300
  }
2210
2301
  );
2211
- products.command("context").description("Get buyer-facing product context").argument("<product-id>", "Product ID").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (productId, options) => {
2212
- try {
2213
- const client = await getClient(options.profile);
2214
- output(await client.getCommerceProductContext(productId), { json: options.json });
2215
- } catch (error) {
2216
- outputError(error instanceof Error ? error.message : String(error), {
2217
- json: options.json
2218
- });
2219
- process.exit(1);
2220
- }
2221
- });
2222
2302
  products.command("create-by-shop").description("Create a product by shop ID using a JSON payload").argument("<shop-id>", "Shop ID").requiredOption("--data <json>", "Product JSON payload").option("--profile <name>", "Profile to use").option("--json", "Output as JSON").action(async (shopId, options) => {
2223
2303
  try {
2224
2304
  const client = await getClient(options.profile);
@@ -4168,7 +4248,7 @@ program.addCommand(createAppCommand());
4168
4248
  program.addCommand(createServersCommand());
4169
4249
  program.addCommand(createChannelsCommand());
4170
4250
  program.addCommand(createThreadsCommand());
4171
- program.addCommand(createAgentsCommand());
4251
+ program.addCommand(createBuddiesCommand());
4172
4252
  program.addCommand(createListenCommand());
4173
4253
  program.addCommand(createDirectMessagesCommand());
4174
4254
  program.addCommand(createWorkspaceCommand());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shadowob/cli",
3
- "version": "1.1.6",
3
+ "version": "1.1.7",
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.6"
16
+ "@shadowob/sdk": "1.1.7"
17
17
  },
18
18
  "devDependencies": {
19
19
  "@types/node": "^22.15.21",