@agent-commons/cli 0.2.4 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +206 -0
  3. package/dist/bin.js +1773 -508
  4. package/package.json +25 -4
package/dist/bin.js CHANGED
@@ -24,17 +24,14 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  ));
25
25
 
26
26
  // src/bin.ts
27
- var import_commander16 = require("commander");
28
- var import_path5 = require("path");
29
- var import_os4 = require("os");
27
+ var import_commander22 = require("commander");
30
28
  var import_child_process3 = require("child_process");
31
29
 
32
30
  // src/commands/login.ts
33
31
  var import_commander = require("commander");
34
- var readline = __toESM(require("readline"));
35
32
  var import_fs2 = require("fs");
36
- var import_path2 = require("path");
37
33
  var import_os2 = require("os");
34
+ var import_path2 = require("path");
38
35
 
39
36
  // src/config.ts
40
37
  var import_fs = require("fs");
@@ -44,9 +41,16 @@ var import_sdk = require("@agent-commons/sdk");
44
41
  var CONFIG_DIR = (0, import_path.join)((0, import_os.homedir)(), ".agc");
45
42
  var CONFIG_FILE = (0, import_path.join)(CONFIG_DIR, "config.json");
46
43
  var DEFAULT_API_URL = process.env.AGC_API_URL ?? "https://api.agentcommons.io";
47
- var DEFAULT_APP_URL = "https://www.agentcommons.io";
48
44
  var DEFAULT_IDENTITY_URL = process.env.COMMONS_IDENTITY_URL ?? "https://auth.agentcommons.io";
49
45
  var DEFAULT_IDENTITY_CLIENT_ID = process.env.COMMONS_IDENTITY_CLIENT_ID ?? "commons-cli";
46
+ function readStoredConfig() {
47
+ if (!(0, import_fs.existsSync)(CONFIG_FILE)) return {};
48
+ try {
49
+ return JSON.parse((0, import_fs.readFileSync)(CONFIG_FILE, "utf8"));
50
+ } catch {
51
+ return {};
52
+ }
53
+ }
50
54
  function loadConfig() {
51
55
  const fromEnv = {
52
56
  ...process.env.AGC_API_URL && { apiUrl: process.env.AGC_API_URL },
@@ -56,13 +60,7 @@ function loadConfig() {
56
60
  ...process.env.AGC_INITIATOR && { initiator: process.env.AGC_INITIATOR },
57
61
  ...process.env.AGC_AGENT_ID && { defaultAgentId: process.env.AGC_AGENT_ID }
58
62
  };
59
- let fromFile = {};
60
- if ((0, import_fs.existsSync)(CONFIG_FILE)) {
61
- try {
62
- fromFile = JSON.parse((0, import_fs.readFileSync)(CONFIG_FILE, "utf8"));
63
- } catch {
64
- }
65
- }
63
+ const fromFile = readStoredConfig();
66
64
  return {
67
65
  apiUrl: DEFAULT_API_URL,
68
66
  identityUrl: DEFAULT_IDENTITY_URL,
@@ -72,32 +70,44 @@ function loadConfig() {
72
70
  };
73
71
  }
74
72
  function saveConfig(updates) {
75
- const current = loadConfig();
76
- const next = { ...current, ...updates };
77
- if (!(0, import_fs.existsSync)(CONFIG_DIR)) (0, import_fs.mkdirSync)(CONFIG_DIR, { recursive: true });
78
- (0, import_fs.writeFileSync)(CONFIG_FILE, JSON.stringify(next, null, 2), { mode: 384 });
73
+ const next = {
74
+ apiUrl: DEFAULT_API_URL,
75
+ identityUrl: DEFAULT_IDENTITY_URL,
76
+ identityClientId: DEFAULT_IDENTITY_CLIENT_ID,
77
+ ...readStoredConfig(),
78
+ ...updates
79
+ };
80
+ if (!(0, import_fs.existsSync)(CONFIG_DIR)) {
81
+ (0, import_fs.mkdirSync)(CONFIG_DIR, { recursive: true, mode: 448 });
82
+ }
83
+ (0, import_fs.chmodSync)(CONFIG_DIR, 448);
84
+ const temporaryFile = (0, import_path.join)(CONFIG_DIR, `.config-${process.pid}.tmp`);
85
+ (0, import_fs.writeFileSync)(temporaryFile, JSON.stringify(next, null, 2) + "\n", {
86
+ mode: 384
87
+ });
88
+ (0, import_fs.renameSync)(temporaryFile, CONFIG_FILE);
89
+ (0, import_fs.chmodSync)(CONFIG_FILE, 384);
79
90
  }
80
91
  function clearConfig() {
81
- if ((0, import_fs.existsSync)(CONFIG_FILE)) {
82
- (0, import_fs.writeFileSync)(
83
- CONFIG_FILE,
84
- JSON.stringify(
85
- {
86
- apiUrl: DEFAULT_API_URL,
87
- identityUrl: DEFAULT_IDENTITY_URL,
88
- identityClientId: DEFAULT_IDENTITY_CLIENT_ID
89
- },
90
- null,
91
- 2
92
- ),
93
- { mode: 384 }
94
- );
95
- }
92
+ saveConfig({
93
+ sessionToken: void 0,
94
+ accessToken: void 0,
95
+ accessTokenExpiresAt: void 0,
96
+ userId: void 0,
97
+ userEmail: void 0,
98
+ userName: void 0,
99
+ workspaceId: void 0,
100
+ apiKey: void 0,
101
+ initiator: void 0,
102
+ defaultAgentId: void 0
103
+ });
96
104
  }
97
105
  function makeClient(overrides) {
98
106
  const cfg = { ...loadConfig(), ...overrides };
99
107
  return new import_sdk.CommonsClient({
100
108
  baseUrl: cfg.apiUrl,
109
+ identityUrl: cfg.identityUrl,
110
+ identityToken: cfg.sessionToken,
101
111
  apiKey: cfg.accessToken ?? cfg.apiKey,
102
112
  initiator: cfg.userId ?? cfg.initiator
103
113
  });
@@ -113,6 +123,7 @@ function decodeJwtPayload(token) {
113
123
  }
114
124
  async function ensureAccessToken() {
115
125
  const cfg = loadConfig();
126
+ if (cfg.accessToken && !cfg.sessionToken) return cfg;
116
127
  if (cfg.apiKey && !cfg.sessionToken) return cfg;
117
128
  if (cfg.accessToken && cfg.accessTokenExpiresAt && cfg.accessTokenExpiresAt > Date.now() + 3e4) {
118
129
  return cfg;
@@ -122,9 +133,14 @@ async function ensureAccessToken() {
122
133
  `${cfg.identityUrl.replace(/\/$/, "")}/api/auth/token`,
123
134
  { headers: { Authorization: `Bearer ${cfg.sessionToken}` } }
124
135
  );
125
- if (!response.ok) {
136
+ if (response.status === 401 || response.status === 403) {
126
137
  throw new Error("Your Commons login has expired. Run `agc login` again.");
127
138
  }
139
+ if (!response.ok) {
140
+ throw new Error(
141
+ `Commons Identity is unavailable (${response.status}). Please try again.`
142
+ );
143
+ }
128
144
  const data = await response.json();
129
145
  if (!data.token) throw new Error("Commons Identity did not return an access token.");
130
146
  const claims = decodeJwtPayload(data.token);
@@ -132,6 +148,8 @@ async function ensureAccessToken() {
132
148
  accessToken: data.token,
133
149
  accessTokenExpiresAt: typeof claims.exp === "number" ? claims.exp * 1e3 : Date.now() + 10 * 60 * 1e3,
134
150
  userId: typeof claims.sub === "string" ? claims.sub : cfg.userId,
151
+ userEmail: typeof claims.email === "string" ? claims.email : cfg.userEmail,
152
+ userName: typeof claims.name === "string" ? claims.name : cfg.userName,
135
153
  workspaceId: typeof claims.workspace_id === "string" ? claims.workspace_id : cfg.workspaceId,
136
154
  initiator: typeof claims.sub === "string" ? claims.sub : cfg.initiator
137
155
  };
@@ -160,15 +178,15 @@ var sym = {
160
178
  bullet: import_chalk.default.dim("\u2022"),
161
179
  dot: import_chalk.default.dim("\xB7")
162
180
  };
163
- function banner(version = "0.2.4") {
164
- const line = import_chalk.default.cyan(" \u2500".padEnd(2) + "\u2500".repeat(44));
181
+ function banner(version = "0.4.0") {
165
182
  console.log("");
166
- console.log(line);
183
+ console.log(import_chalk.default.cyan(" \u25C7"));
184
+ console.log(import_chalk.default.cyan(" \u256D\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E"));
167
185
  console.log(
168
- import_chalk.default.cyan(" \u2502 ") + import_chalk.default.bold.white(" \u25C8 Agent Commons") + import_chalk.default.dim(" \xB7 CLI") + " " + import_chalk.default.cyan(`v${version}`)
186
+ import_chalk.default.cyan(" \u2502 ") + import_chalk.default.bold.white("AGENT COMMONS") + import_chalk.default.dim(" // CLI") + import_chalk.default.cyan(` v${version}`) + import_chalk.default.cyan(" \u2502")
169
187
  );
170
- console.log(import_chalk.default.cyan(" \u2502 ") + import_chalk.default.dim(" The Open AI Agent Network \xB7 agentcommons.io"));
171
- console.log(line);
188
+ console.log(import_chalk.default.cyan(" \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F"));
189
+ console.log(import_chalk.default.dim(" Build \xB7 run \xB7 connect \xB7 collaborate"));
172
190
  console.log("");
173
191
  }
174
192
  function step(n, total, title) {
@@ -177,7 +195,7 @@ function step(n, total, title) {
177
195
  ${import_chalk.default.cyan.bold(" Step " + n)} ${fraction} ${import_chalk.default.bold(title)}`);
178
196
  console.log(import_chalk.default.dim(" " + "\u2500".repeat(38)));
179
197
  }
180
- async function select(prompt2, choices) {
198
+ async function select(prompt, choices) {
181
199
  if (!process.stdin.isTTY) {
182
200
  return choices[0].value;
183
201
  }
@@ -187,7 +205,7 @@ async function select(prompt2, choices) {
187
205
  if (!first) {
188
206
  process.stdout.write(`\x1B[${total + 2}A\x1B[0J`);
189
207
  }
190
- console.log("\n" + import_chalk.default.bold(" " + prompt2));
208
+ console.log("\n" + import_chalk.default.bold(" " + prompt));
191
209
  for (let i = 0; i < total; i++) {
192
210
  const { label, hint } = choices[i];
193
211
  if (i === idx) {
@@ -230,9 +248,14 @@ async function select(prompt2, choices) {
230
248
  });
231
249
  }
232
250
  function openBrowser(url) {
233
- const cmd = process.platform === "darwin" ? `open "${url}"` : process.platform === "win32" ? `start "" "${url}"` : `xdg-open "${url}"`;
234
- (0, import_child_process.exec)(cmd, () => {
251
+ const command = process.platform === "darwin" ? { file: "open", args: [url] } : process.platform === "win32" ? { file: "cmd", args: ["/c", "start", "", url] } : { file: "xdg-open", args: [url] };
252
+ const child = (0, import_child_process.spawn)(command.file, command.args, {
253
+ detached: true,
254
+ stdio: "ignore"
255
+ });
256
+ child.on("error", () => {
235
257
  });
258
+ child.unref();
236
259
  }
237
260
  function spin(text) {
238
261
  return (0, import_ora.default)({ text, color: "cyan" }).start();
@@ -273,11 +296,13 @@ function relativeTime(iso) {
273
296
  }
274
297
  function printError(err) {
275
298
  if (err instanceof Error) {
276
- console.error(c.error(`
277
- Error: ${err.message}`));
299
+ console.error(`
300
+ ${sym.fail} ${c.error(err.message)}
301
+ `);
278
302
  } else {
279
- console.error(c.error(`
280
- Unknown error: ${String(err)}`));
303
+ console.error(`
304
+ ${sym.fail} ${c.error(String(err))}
305
+ `);
281
306
  }
282
307
  }
283
308
  function jsonOut(data) {
@@ -311,252 +336,302 @@ function statusBadge(status) {
311
336
 
312
337
  // src/commands/login.ts
313
338
  var CONFIG_FILE2 = (0, import_path2.join)((0, import_os2.homedir)(), ".agc", "config.json");
314
- function prompt(question, hidden = false) {
315
- return new Promise((resolve2) => {
316
- const rl = readline.createInterface({
317
- input: process.stdin,
318
- output: hidden ? void 0 : process.stdout,
319
- terminal: hidden
339
+ var CLI_SCOPES = [
340
+ "openid",
341
+ "profile",
342
+ "email",
343
+ "offline_access",
344
+ "activity:read",
345
+ "agents:create",
346
+ "agents:read",
347
+ "agents:write",
348
+ "agents:run",
349
+ "compute:read",
350
+ "compute:write",
351
+ "usage:read"
352
+ ].join(" ");
353
+ async function jsonResponse(response) {
354
+ return response.json().catch(() => ({}));
355
+ }
356
+ async function signInWithCommons(options) {
357
+ step(1, 2, "Connect your Commons account");
358
+ const starting = spin("Creating a secure sign-in request\u2026");
359
+ const response = await fetch(`${options.identityUrl}/api/auth/device/code`, {
360
+ method: "POST",
361
+ headers: { "Content-Type": "application/json" },
362
+ body: JSON.stringify({
363
+ client_id: options.clientId,
364
+ scope: CLI_SCOPES
365
+ })
366
+ });
367
+ const device = await jsonResponse(response);
368
+ starting.stop();
369
+ if (!response.ok || !device.device_code || !device.user_code) {
370
+ throw new Error(
371
+ device.error_description ?? device.error ?? `Could not start Commons sign-in (${response.status}).`
372
+ );
373
+ }
374
+ const verificationUrl = device.verification_uri_complete ?? `${device.verification_uri ?? `${options.identityUrl}/device`}?user_code=${encodeURIComponent(device.user_code)}`;
375
+ console.log(`
376
+ ${c.dim("Open this page to approve the CLI:")}`);
377
+ console.log(` ${c.primary(verificationUrl)}`);
378
+ console.log(`
379
+ ${c.dim("One-time code")} ${c.bold(device.user_code)}
380
+ `);
381
+ if (options.openBrowser) {
382
+ openBrowser(verificationUrl);
383
+ console.log(` ${sym.arrow} ${c.dim("Your browser should open automatically.")}`);
384
+ } else {
385
+ console.log(` ${sym.arrow} ${c.dim("Open the URL in any browser.")}`);
386
+ }
387
+ step(2, 2, "Approve in your browser");
388
+ const waiting = spin("Waiting for approval\u2026");
389
+ const deadline = Date.now() + (device.expires_in ?? 600) * 1e3;
390
+ let intervalMs = Math.max(device.interval ?? 5, 1) * 1e3;
391
+ let sessionToken;
392
+ while (Date.now() < deadline) {
393
+ await new Promise((resolve2) => setTimeout(resolve2, intervalMs));
394
+ const tokenResponse = await fetch(
395
+ `${options.identityUrl}/api/auth/device/token`,
396
+ {
397
+ method: "POST",
398
+ headers: { "Content-Type": "application/json" },
399
+ body: JSON.stringify({
400
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
401
+ device_code: device.device_code,
402
+ client_id: options.clientId
403
+ })
404
+ }
405
+ );
406
+ const token = await jsonResponse(tokenResponse);
407
+ if (tokenResponse.ok && token.access_token) {
408
+ sessionToken = token.access_token;
409
+ break;
410
+ }
411
+ if (token.error === "slow_down") {
412
+ intervalMs += 1e3;
413
+ continue;
414
+ }
415
+ if (token.error === "authorization_pending") continue;
416
+ waiting.stop();
417
+ throw new Error(
418
+ token.error_description ?? token.error ?? "Commons sign-in failed."
419
+ );
420
+ }
421
+ waiting.stop();
422
+ if (!sessionToken) {
423
+ throw new Error("The sign-in request expired before it was approved.");
424
+ }
425
+ saveConfig({
426
+ apiUrl: options.apiUrl,
427
+ identityUrl: options.identityUrl,
428
+ identityClientId: options.clientId,
429
+ sessionToken,
430
+ accessToken: void 0,
431
+ accessTokenExpiresAt: void 0,
432
+ apiKey: void 0
433
+ });
434
+ const authenticated = await ensureAccessToken();
435
+ const identity = authenticated.userEmail ?? authenticated.userName ?? authenticated.userId ?? "Commons user";
436
+ console.log(`
437
+ ${sym.ok} ${c.success("Signed in")} ${c.bold(identity)}`);
438
+ if (authenticated.workspaceId) {
439
+ console.log(
440
+ ` ${sym.ok} ${c.dim("Workspace")} ${c.id(authenticated.workspaceId)}`
441
+ );
442
+ }
443
+ }
444
+ async function useApiKey(options) {
445
+ step(1, 1, "Verify API key");
446
+ const checking = spin("Checking credentials\u2026");
447
+ try {
448
+ const { CommonsClient: CommonsClient2 } = await import("@agent-commons/sdk");
449
+ const client = new CommonsClient2({
450
+ baseUrl: options.apiUrl,
451
+ apiKey: options.apiKey
320
452
  });
321
- if (hidden) {
322
- process.stdout.write(question);
323
- process.stdin.once("data", (data) => {
324
- process.stdout.write("\n");
325
- rl.close();
326
- resolve2(data.toString().trim());
327
- });
328
- process.stdin.setRawMode?.(false);
329
- } else {
330
- rl.question(question, (ans) => {
331
- rl.close();
332
- resolve2(ans.trim());
333
- });
453
+ const principal = await client.auth.me();
454
+ const initiator = options.initiator ?? (principal.principalType === "user" ? principal.principalId ?? void 0 : void 0);
455
+ saveConfig({
456
+ apiUrl: options.apiUrl,
457
+ apiKey: options.apiKey,
458
+ initiator,
459
+ sessionToken: void 0,
460
+ accessToken: void 0,
461
+ accessTokenExpiresAt: void 0,
462
+ userId: initiator
463
+ });
464
+ checking.stop();
465
+ console.log(`
466
+ ${sym.ok} ${c.success("API key verified")}`);
467
+ if (principal.principalId) {
468
+ console.log(
469
+ ` ${sym.ok} ${c.dim("Principal")} ${c.id(principal.principalId)}`
470
+ );
334
471
  }
335
- });
472
+ } catch (error) {
473
+ checking.stop();
474
+ throw error;
475
+ }
336
476
  }
337
477
  function loginCommand() {
338
- const cmd = new import_commander.Command("login").description("Configure API credentials");
339
- cmd.option("--api-url <url>", "API base URL", DEFAULT_API_URL).option("--identity-url <url>", "Commons Identity URL", DEFAULT_IDENTITY_URL).option("--api-key <key>", "API key (or set AGC_API_KEY env var)").option("--initiator <id>", "User/initiator ID (advanced \u2014 usually auto-detected)").action(async (opts) => {
478
+ return new import_commander.Command("login").description("Sign in with your Commons account").option("--api-url <url>", "Use a custom Agent Commons API endpoint").option("--identity-url <url>", "Use a custom Commons Identity endpoint").option("--client-id <id>", "Override the public CLI identity client ID").option("--no-browser", "Do not open the authorization page automatically").option("--api-key <key>", "Use a project API key for automation").option("--initiator <id>", "Optional delegated principal for compatible keys").action(async (opts) => {
340
479
  try {
341
480
  const current = loadConfig();
342
- const isFirstRun = !(0, import_fs2.existsSync)(CONFIG_FILE2);
481
+ const firstRun = !(0, import_fs2.existsSync)(CONFIG_FILE2);
482
+ const apiUrl = String(opts.apiUrl ?? current.apiUrl ?? DEFAULT_API_URL).replace(
483
+ /\/$/,
484
+ ""
485
+ );
486
+ const identityUrl = String(
487
+ opts.identityUrl ?? current.identityUrl ?? DEFAULT_IDENTITY_URL
488
+ ).replace(/\/$/, "");
489
+ const clientId = String(
490
+ opts.clientId ?? current.identityClientId ?? DEFAULT_IDENTITY_CLIENT_ID
491
+ );
343
492
  banner();
344
- if (isFirstRun) {
345
- console.log(c.bold(" Welcome to Agent Commons CLI!"));
346
- console.log(c.dim(" Sign in once with your Commons account to get started.\n"));
347
- } else {
348
- console.log(c.bold(" Update your credentials"));
349
- console.log(c.dim(" Press Enter to keep existing values.\n"));
350
- }
351
- let apiUrl;
352
- if (opts.apiUrl !== DEFAULT_API_URL) {
353
- apiUrl = opts.apiUrl;
354
- console.log(` ${c.dim("Using API endpoint:")} ${apiUrl}
355
- `);
356
- } else if (current.apiUrl && current.apiUrl !== DEFAULT_API_URL) {
357
- apiUrl = current.apiUrl;
358
- console.log(` ${c.dim("Using existing endpoint:")} ${apiUrl}
359
- `);
360
- } else {
361
- apiUrl = DEFAULT_API_URL;
362
- }
363
- const appUrl = apiUrl.includes("localhost") ? "http://localhost:3000" : DEFAULT_APP_URL;
364
- const apiKeysUrl = `${appUrl}/settings/api-keys`;
365
- if (!opts.apiKey) {
366
- step(1, 1, "Commons account");
367
- const identityUrl = String(opts.identityUrl).replace(/\/$/, "");
368
- const clientId = DEFAULT_IDENTITY_CLIENT_ID;
369
- const deviceResponse = await fetch(`${identityUrl}/api/auth/device/code`, {
370
- method: "POST",
371
- headers: { "Content-Type": "application/json" },
372
- body: JSON.stringify({
373
- client_id: clientId,
374
- scope: "openid profile email offline_access agents:read agents:write agents:run compute:read compute:write activity:read usage:read"
375
- })
493
+ console.log(
494
+ c.bold(
495
+ firstRun ? " Welcome \u2014 let\u2019s connect your Agent Commons account." : " Sign in to Agent Commons"
496
+ )
497
+ );
498
+ console.log(
499
+ c.dim(
500
+ opts.apiKey ? " API-key mode is intended for automation and CI.\n" : " A browser approval keeps passwords and API keys out of your terminal.\n"
501
+ )
502
+ );
503
+ if (opts.apiKey) {
504
+ await useApiKey({
505
+ apiUrl,
506
+ apiKey: String(opts.apiKey),
507
+ initiator: opts.initiator
376
508
  });
377
- const device = await deviceResponse.json();
378
- if (!deviceResponse.ok || !device.device_code || !device.user_code) {
379
- throw new Error(device.error_description || "Could not start Commons login.");
380
- }
381
- const verificationUrl = device.verification_uri_complete ?? `${identityUrl}/device?user_code=${encodeURIComponent(device.user_code)}`;
382
- console.log(` ${c.dim("Authorize this CLI in your browser:")}`);
383
- console.log(` ${c.primary(verificationUrl)}`);
384
- console.log(` ${c.dim("Code:")} ${c.bold(device.user_code)}
385
- `);
386
- openBrowser(verificationUrl);
387
- const deadline = Date.now() + (device.expires_in ?? 600) * 1e3;
388
- let intervalMs = Math.max(device.interval ?? 5, 1) * 1e3;
389
- let sessionToken;
390
- while (Date.now() < deadline) {
391
- await new Promise((resolve2) => setTimeout(resolve2, intervalMs));
392
- const tokenResponse = await fetch(`${identityUrl}/api/auth/device/token`, {
393
- method: "POST",
394
- headers: { "Content-Type": "application/json" },
395
- body: JSON.stringify({
396
- grant_type: "urn:ietf:params:oauth:grant-type:device_code",
397
- device_code: device.device_code,
398
- client_id: clientId
399
- })
400
- });
401
- const token = await tokenResponse.json();
402
- if (tokenResponse.ok && token.access_token) {
403
- sessionToken = token.access_token;
404
- break;
405
- }
406
- if (token.error === "slow_down") {
407
- intervalMs += 1e3;
408
- continue;
409
- }
410
- if (token.error === "authorization_pending") continue;
411
- throw new Error(token.error_description || token.error || "Commons login failed.");
412
- }
413
- if (!sessionToken) throw new Error("Commons login expired before approval.");
414
- saveConfig({
509
+ } else {
510
+ await signInWithCommons({
415
511
  apiUrl,
416
512
  identityUrl,
417
- identityClientId: clientId,
418
- sessionToken,
419
- accessToken: void 0,
420
- accessTokenExpiresAt: void 0,
421
- apiKey: void 0
513
+ clientId,
514
+ openBrowser: opts.browser !== false
422
515
  });
423
- const authenticated = await ensureAccessToken();
424
- console.log(
425
- ` ${sym.ok} ${c.success("Signed in")} as ${c.id(authenticated.userId ?? "Commons user")}`
426
- );
427
- console.log(`
428
- ${sym.ok} ${c.success("All set!")} Credentials saved to ${c.dim("~/.agc/config.json")}
429
- `);
430
- return;
431
- }
432
- step(1, 1, "Legacy API Key");
433
- let apiKey = opts.apiKey;
434
- if (!apiKey) {
435
- console.log(` ${c.dim("You'll need an API key from your Agent Commons account.")}`);
436
- console.log(` ${c.dim("We'll open the API Keys page in your browser.")}
437
- `);
438
- console.log(` ${c.dim("On that page:")}`);
439
- console.log(` ${sym.bullet} ${c.dim("Click")} ${c.bold('"Generate new key"')}`);
440
- console.log(` ${sym.bullet} ${c.dim("Copy the key (it starts with")} ${c.bold("sk-ac-\u2026")}${c.dim(")")}`);
441
- console.log(` ${sym.bullet} ${c.dim("Paste it here when prompted")}
442
- `);
443
- const openNow = await prompt(` ${c.dim("Open browser now? [Y/n]:")} `);
444
- if (!openNow || openNow.toLowerCase() !== "n") {
445
- openBrowser(apiKeysUrl);
446
- console.log(` ${sym.ok} ${c.dim("Opened:")} ${c.primary(apiKeysUrl)}
447
- `);
448
- } else {
449
- console.log(` ${c.dim("You can open it manually:")} ${c.primary(apiKeysUrl)}
450
- `);
451
- }
452
- console.log(c.dim(" Paste your API key below (input is hidden):"));
453
- apiKey = await prompt(` ${c.dim("API Key:")} `, true);
454
- if (!apiKey) apiKey = current.apiKey;
455
- }
456
- if (!apiKey) {
457
- console.log(`
458
- ${c.warn("\u26A0")} No API key provided \u2014 set one later with ${c.bold("agc config set apiKey <key>")}`);
459
- } else {
460
- console.log(` ${sym.ok} ${c.dim("Key saved:")} ****${apiKey.slice(-4)}`);
461
- }
462
- let initiator = opts.initiator ?? current.initiator;
463
- if (!initiator && apiKey) {
464
- try {
465
- const { CommonsClient: CommonsClient2 } = await import("@agent-commons/sdk");
466
- const client = new CommonsClient2({ baseUrl: apiUrl, apiKey });
467
- const me = await client.auth.me();
468
- if (me?.principalId && me.principalType === "user") {
469
- initiator = me.principalId;
470
- console.log(` ${sym.ok} ${c.dim("Identity detected:")} ${c.id(initiator.slice(0, 10) + "\u2026" + initiator.slice(-6))}`);
471
- }
472
- } catch {
473
- }
474
516
  }
475
- saveConfig({ apiUrl, apiKey, ...initiator ? { initiator } : {} });
476
- console.log(`
477
- ${sym.ok} ${c.success("All set!")} Credentials saved to ${c.dim("~/.agc/config.json")}`);
478
- console.log(`
479
- ${c.dim("Next steps:")}`);
480
- console.log(` ${sym.arrow} ${c.dim("Run")} ${c.bold("agc")} ${c.dim("to open the interactive menu")}`);
481
- console.log(` ${sym.arrow} ${c.dim("Run")} ${c.bold("agc agents list")} ${c.dim("to see your agents")}`);
482
- console.log(` ${sym.arrow} ${c.dim("Run")} ${c.bold("agc chat")} ${c.dim("to start chatting with an agent")}
483
- `);
484
- } catch (err) {
485
- printError(err);
486
- process.exit(1);
517
+ console.log(
518
+ `
519
+ ${sym.ok} ${c.success("Ready.")} ${c.dim("Credentials are stored with user-only permissions.")}`
520
+ );
521
+ console.log(` ${sym.arrow} ${c.bold("agc")} ${c.dim("open the command center")}`);
522
+ console.log(
523
+ ` ${sym.arrow} ${c.bold("agc agents list")} ${c.dim("list your agents")}`
524
+ );
525
+ console.log(
526
+ ` ${sym.arrow} ${c.bold("agc chat")} ${c.dim("start a conversation")}
527
+ `
528
+ );
529
+ } catch (error) {
530
+ printError(error);
531
+ process.exitCode = 1;
487
532
  }
488
533
  });
489
- return cmd;
490
534
  }
491
535
  function logoutCommand() {
492
- return new import_commander.Command("logout").description("Clear stored credentials").action(() => {
536
+ return new import_commander.Command("logout").description("Sign out and clear locally stored credentials").action(() => {
493
537
  clearConfig();
494
- console.log(`${sym.ok} Credentials cleared.`);
538
+ console.log(`
539
+ ${sym.ok} Signed out. Local credentials were cleared.
540
+ `);
495
541
  });
496
542
  }
497
543
  function whoamiCommand() {
498
- return new import_commander.Command("whoami").description("Show current configuration and verify API connectivity").option("--json", "Output as JSON").action(async (opts) => {
499
- const cfg = loadConfig();
500
- if (opts.json) {
501
- console.log(JSON.stringify({
544
+ return new import_commander.Command("whoami").description("Show the active identity and verify API access").option("--json", "Output as JSON").action(async (opts) => {
545
+ try {
546
+ const cfg = await ensureAccessToken();
547
+ const authenticated = Boolean(
548
+ cfg.sessionToken || cfg.accessToken || cfg.apiKey
549
+ );
550
+ let principal;
551
+ if (authenticated) principal = await makeClient().auth.me();
552
+ const output = {
502
553
  apiUrl: cfg.apiUrl,
503
554
  identityUrl: cfg.identityUrl,
504
- userId: cfg.userId ?? cfg.initiator,
555
+ userId: cfg.userId ?? cfg.initiator ?? principal?.principalId,
556
+ email: cfg.userEmail,
557
+ name: cfg.userName,
505
558
  workspaceId: cfg.workspaceId,
506
- authenticated: Boolean(cfg.sessionToken || cfg.apiKey)
507
- }, null, 2));
508
- return;
509
- }
510
- console.log(`
511
- ${c.bold("Current configuration")}`);
512
- detail([
513
- ["API URL", cfg.apiUrl],
514
- ["Identity", cfg.userId ?? cfg.initiator ?? c.dim("(not set)")],
515
- ["Workspace", cfg.workspaceId ?? c.dim("(not set)")],
516
- ["Auth", cfg.sessionToken ? "Commons account" : cfg.apiKey ? "Legacy API key" : c.dim("(not set)")],
517
- ["Agent ID", cfg.defaultAgentId ?? c.dim("(not set)")]
518
- ]);
519
- try {
520
- const client = makeClient();
521
- if (cfg.initiator) {
522
- await client.agents.list(cfg.initiator);
523
- console.log(`
524
- ${sym.ok} ${c.success("Connected")} to ${cfg.apiUrl}`);
525
- } else {
526
- console.log(`
527
- ${c.warn("\u26A0")} Set an initiator to verify connectivity.`);
559
+ principalType: principal?.principalType,
560
+ authMode: cfg.sessionToken ? "commons-account" : cfg.apiKey ? "api-key" : cfg.accessToken ? "access-token" : "none",
561
+ authenticated
562
+ };
563
+ if (opts.json) {
564
+ console.log(JSON.stringify(output, null, 2));
565
+ return;
528
566
  }
529
- } catch (err) {
530
567
  console.log(`
531
- ${sym.fail} ${c.error("Could not reach API")}: ${err.message}`);
568
+ ${c.bold("Identity")}`);
569
+ detail([
570
+ ["Account", cfg.userEmail ?? cfg.userName ?? c.dim("(not available)")],
571
+ ["User ID", output.userId ?? c.dim("(not available)")],
572
+ ["Workspace", cfg.workspaceId ?? c.dim("(not available)")],
573
+ ["Auth", output.authMode],
574
+ ["API", cfg.apiUrl],
575
+ ["Default agent", cfg.defaultAgentId ?? c.dim("(not set)")]
576
+ ]);
577
+ console.log(
578
+ authenticated ? `
579
+ ${sym.ok} ${c.success("Authenticated and connected")}
580
+ ` : `
581
+ ${c.warn("\u25CB")} Not signed in. Run ${c.bold("agc login")}.
582
+ `
583
+ );
584
+ } catch (error) {
585
+ printError(error);
586
+ process.exitCode = 1;
532
587
  }
533
588
  });
534
589
  }
535
590
  function configCommand() {
536
- const cmd = new import_commander.Command("config").description("Get or set configuration values");
537
- cmd.command("set <key> <value>").description("Set a config value (apiUrl, apiKey, initiator, defaultAgentId)").action((key, value) => {
538
- const allowed = ["apiUrl", "apiKey", "initiator", "defaultAgentId"];
591
+ const command = new import_commander.Command("config").description(
592
+ "Inspect or update CLI preferences"
593
+ );
594
+ const allowed = [
595
+ "apiUrl",
596
+ "identityUrl",
597
+ "apiKey",
598
+ "initiator",
599
+ "defaultAgentId"
600
+ ];
601
+ command.command("set <key> <value>").description(`Set a preference (${allowed.join(", ")})`).action((key, value) => {
539
602
  if (!allowed.includes(key)) {
540
- console.error(c.error(`Unknown key "${key}". Allowed: ${allowed.join(", ")}`));
541
- process.exit(1);
603
+ console.error(
604
+ c.error(`Unknown key "${key}". Allowed: ${allowed.join(", ")}`)
605
+ );
606
+ process.exitCode = 1;
607
+ return;
542
608
  }
543
609
  saveConfig({ [key]: value });
544
- console.log(`${sym.ok} ${key} = ${key === "apiKey" ? "****" : value}`);
610
+ console.log(
611
+ `${sym.ok} ${key} = ${key === "apiKey" ? `****${value.slice(-4)}` : value}`
612
+ );
545
613
  });
546
- cmd.command("get [key]").description("Get a config value or show all").action((key) => {
614
+ command.command("get [key]").description("Show one preference or the complete non-secret configuration").action((key) => {
547
615
  const cfg = loadConfig();
548
616
  if (key) {
549
- console.log(cfg[key] ?? c.dim("(not set)"));
550
- } else {
551
- detail([
552
- ["apiUrl", cfg.apiUrl],
553
- ["initiator", cfg.initiator ?? ""],
554
- ["apiKey", cfg.apiKey ? `****${cfg.apiKey.slice(-4)}` : ""],
555
- ["defaultAgentId", cfg.defaultAgentId ?? ""]
556
- ]);
617
+ if (["apiKey", "sessionToken", "accessToken"].includes(key)) {
618
+ const value = cfg[key];
619
+ console.log(value ? `****${value.slice(-4)}` : c.dim("(not set)"));
620
+ return;
621
+ }
622
+ console.log(String(cfg[key] ?? c.dim("(not set)")));
623
+ return;
557
624
  }
625
+ detail([
626
+ ["apiUrl", cfg.apiUrl],
627
+ ["identityUrl", cfg.identityUrl],
628
+ ["user", cfg.userEmail ?? cfg.userId ?? cfg.initiator ?? ""],
629
+ ["workspaceId", cfg.workspaceId ?? ""],
630
+ ["auth", cfg.sessionToken ? "Commons account" : cfg.apiKey ? "API key" : ""],
631
+ ["defaultAgentId", cfg.defaultAgentId ?? ""]
632
+ ]);
558
633
  });
559
- return cmd;
634
+ return command;
560
635
  }
561
636
 
562
637
  // src/commands/agents.ts
@@ -577,10 +652,11 @@ function agentsCommand() {
577
652
  agents.map((a) => ({
578
653
  ID: a.agentId.slice(0, 8) + "\u2026",
579
654
  Name: a.name,
655
+ Runtime: a.runtimeType ?? "native",
580
656
  Model: `${a.modelProvider}/${a.modelId}`,
581
657
  Created: relativeTime(a.createdAt)
582
658
  })),
583
- ["ID", "Name", "Model", "Created"]
659
+ ["ID", "Name", "Runtime", "Model", "Created"]
584
660
  );
585
661
  } catch (err) {
586
662
  spinner.stop();
@@ -588,11 +664,11 @@ function agentsCommand() {
588
664
  process.exit(1);
589
665
  }
590
666
  });
591
- cmd.command("get <agentId>").description("Show details for an agent").option("--json", "Output as JSON").action(async (agentId, opts) => {
667
+ cmd.command("get <agentId>").description("Show details for an agent").option("--json", "Output as JSON").action(async (agentId2, opts) => {
592
668
  const spinner = spin("Fetching agent\u2026");
593
669
  try {
594
670
  const client = makeClient();
595
- const res = await client.agents.get(agentId);
671
+ const res = await client.agents.get(agentId2);
596
672
  const agent = res?.data ?? res;
597
673
  spinner.stop();
598
674
  if (opts.json) return jsonOut(agent);
@@ -600,8 +676,15 @@ function agentsCommand() {
600
676
  detail([
601
677
  ["Agent ID", c.id(agent.agentId)],
602
678
  ["Provider", `${agent.modelProvider} / ${agent.modelId}`],
679
+ ["Runtime", agent.runtimeType ?? "native"],
680
+ ["Runtime status", agent.runtimeStatus ?? "ready"],
603
681
  ["Instructions", agent.instructions?.slice(0, 80) ?? c.dim("(none)")],
604
- ["Tools", [...agent.commonTools ?? [], ...agent.externalTools ?? []].join(", ") || c.dim("(none)")],
682
+ [
683
+ "Tools",
684
+ [...agent.commonTools ?? [], ...agent.externalTools ?? []].join(
685
+ ", "
686
+ ) || c.dim("(none)")
687
+ ],
605
688
  ["Created", relativeTime(agent.createdAt)]
606
689
  ]);
607
690
  } catch (err) {
@@ -610,7 +693,18 @@ function agentsCommand() {
610
693
  process.exit(1);
611
694
  }
612
695
  });
613
- cmd.command("create").description("Create a new agent").requiredOption("--name <name>", "Agent name").option("--instructions <text>", "System instructions").option("--provider <provider>", "Model provider (openai|anthropic|google|groq|openrouter|xai|ollama|custom)", "openai").option("--model <id>", "Model ID", "gpt-5.4-mini").option("--model-api-key <key>", "Provider API key (BYOK)").option("--model-base-url <url>", "Base URL for custom or local OpenAI-compatible providers").option("--json", "Output as JSON").action(async (opts) => {
696
+ cmd.command("create").description("Create a new agent").requiredOption("--name <name>", "Agent name").option("--instructions <text>", "System instructions").option(
697
+ "--provider <provider>",
698
+ "Model provider (openai|anthropic|google|groq|openrouter|xai|ollama|custom)",
699
+ "openai"
700
+ ).option("--model <id>", "Model ID", "gpt-5.4-mini").option("--model-api-key <key>", "Provider API key (BYOK)").option(
701
+ "--model-base-url <url>",
702
+ "Base URL for custom or local OpenAI-compatible providers"
703
+ ).option(
704
+ "--runtime <runtime>",
705
+ "Agent runtime (native|openclaw|hermes|custom)",
706
+ "native"
707
+ ).option("--json", "Output as JSON").action(async (opts) => {
614
708
  const cfg = loadConfig();
615
709
  if (!cfg.initiator) {
616
710
  console.error(c.error("No initiator set. Run `agc login` first."));
@@ -618,6 +712,9 @@ function agentsCommand() {
618
712
  }
619
713
  const spinner = spin("Creating agent\u2026");
620
714
  try {
715
+ if (!["native", "openclaw", "hermes", "custom"].includes(opts.runtime)) {
716
+ throw new Error(`Unsupported runtime "${opts.runtime}"`);
717
+ }
621
718
  const client = makeClient();
622
719
  const res = await client.agents.create({
623
720
  name: opts.name,
@@ -626,7 +723,8 @@ function agentsCommand() {
626
723
  modelProvider: opts.provider,
627
724
  modelId: opts.model,
628
725
  modelApiKey: opts.modelApiKey,
629
- modelBaseUrl: opts.modelBaseUrl
726
+ modelBaseUrl: opts.modelBaseUrl,
727
+ runtimeType: opts.runtime
630
728
  });
631
729
  const agent = res?.data ?? res;
632
730
  spinner.stop();
@@ -636,15 +734,57 @@ ${sym.ok} Agent created`);
636
734
  detail([
637
735
  ["Agent ID", c.id(agent.agentId)],
638
736
  ["Name", agent.name],
639
- ["Model", `${agent.modelProvider}/${agent.modelId}`]
737
+ ["Model", `${agent.modelProvider}/${agent.modelId}`],
738
+ ["Runtime", agent.runtimeType ?? opts.runtime]
739
+ ]);
740
+ console.log(
741
+ c.dim("\n Tip: agc config set defaultAgentId " + agent.agentId)
742
+ );
743
+ } catch (err) {
744
+ spinner.stop();
745
+ printError(err);
746
+ process.exit(1);
747
+ }
748
+ });
749
+ const runtime = cmd.command("runtime").description("Manage an agent runtime");
750
+ runtime.command("status <agentId>").description("Show managed runtime status and capabilities").option("--json", "Output as JSON").action(async (agentId2, opts) => {
751
+ const spinner = spin("Fetching runtime status\u2026");
752
+ try {
753
+ const result = await makeClient().agents.getRuntime(agentId2);
754
+ spinner.stop();
755
+ if (opts.json) return jsonOut(result.data);
756
+ detail([
757
+ ["Runtime", result.data.runtimeType],
758
+ ["Status", result.data.status],
759
+ ["Managed", result.data.managed ? "yes" : "no"],
760
+ ["Computer", result.data.computer?.computerId ?? c.dim("(none)")]
640
761
  ]);
641
- console.log(c.dim("\n Tip: agc config set defaultAgentId " + agent.agentId));
642
762
  } catch (err) {
643
763
  spinner.stop();
644
764
  printError(err);
645
765
  process.exit(1);
646
766
  }
647
767
  });
768
+ for (const action of ["deploy", "restart", "sleep"]) {
769
+ runtime.command(`${action} <agentId>`).description(
770
+ `${action[0].toUpperCase()}${action.slice(1)} the managed agent runtime`
771
+ ).action(async (agentId2) => {
772
+ const spinner = spin(
773
+ `${action[0].toUpperCase()}${action.slice(1)}ing runtime\u2026`
774
+ );
775
+ try {
776
+ const client = makeClient();
777
+ const result = action === "deploy" ? await client.agents.deployRuntime(agentId2) : action === "restart" ? await client.agents.restartRuntime(agentId2) : await client.agents.sleepRuntime(agentId2);
778
+ spinner.stop();
779
+ console.log(`
780
+ ${sym.ok} Runtime ${result.data.status}`);
781
+ } catch (err) {
782
+ spinner.stop();
783
+ printError(err);
784
+ process.exit(1);
785
+ }
786
+ });
787
+ }
648
788
  const autonomy = cmd.command("autonomy").description("Manage agent heartbeat");
649
789
  autonomy.command("status").description("Show autonomy status for an agent").requiredOption("--agent <agentId>", "Agent ID").option("--json", "Output as JSON").action(async (opts) => {
650
790
  const client = makeClient();
@@ -660,8 +800,14 @@ ${c.bold("Heartbeat Status")}`);
660
800
  ["Enabled", s.enabled ? c.bold("yes") : "no"],
661
801
  ["Interval", s.intervalSec ? `${s.intervalSec}s` : "n/a"],
662
802
  ["Armed", s.isArmed ? c.bold("yes") : "no"],
663
- ["Last beat", s.lastBeatAt ? new Date(s.lastBeatAt).toLocaleString() : "never"],
664
- ["Next beat", s.nextBeatAt ? new Date(s.nextBeatAt).toLocaleString() : "n/a"]
803
+ [
804
+ "Last beat",
805
+ s.lastBeatAt ? new Date(s.lastBeatAt).toLocaleString() : "never"
806
+ ],
807
+ [
808
+ "Next beat",
809
+ s.nextBeatAt ? new Date(s.nextBeatAt).toLocaleString() : "n/a"
810
+ ]
665
811
  ]);
666
812
  } catch (err) {
667
813
  spinner.stop();
@@ -669,7 +815,11 @@ ${c.bold("Heartbeat Status")}`);
669
815
  process.exit(1);
670
816
  }
671
817
  });
672
- autonomy.command("enable").description("Enable heartbeat for an agent").requiredOption("--agent <agentId>", "Agent ID").option("--interval <seconds>", "Heartbeat interval in seconds (min 30)", "300").action(async (opts) => {
818
+ autonomy.command("enable").description("Enable heartbeat for an agent").requiredOption("--agent <agentId>", "Agent ID").option(
819
+ "--interval <seconds>",
820
+ "Heartbeat interval in seconds (min 30)",
821
+ "300"
822
+ ).action(async (opts) => {
673
823
  const client = makeClient();
674
824
  const spinner = spin("Enabling autonomy\u2026");
675
825
  try {
@@ -678,8 +828,10 @@ ${c.bold("Heartbeat Status")}`);
678
828
  intervalSec: parseInt(opts.interval, 10)
679
829
  });
680
830
  spinner.stop();
681
- console.log(`
682
- ${sym.ok} Autonomy enabled for agent ${c.id(opts.agent)}`);
831
+ console.log(
832
+ `
833
+ ${sym.ok} Autonomy enabled for agent ${c.id(opts.agent)}`
834
+ );
683
835
  console.log(c.dim(` Heartbeat every ${opts.interval}s`));
684
836
  } catch (err) {
685
837
  spinner.stop();
@@ -693,8 +845,10 @@ ${sym.ok} Autonomy enabled for agent ${c.id(opts.agent)}`);
693
845
  try {
694
846
  await client.agents.setAutonomy(opts.agent, { enabled: false });
695
847
  spinner.stop();
696
- console.log(`
697
- ${sym.ok} Autonomy disabled for agent ${c.id(opts.agent)}`);
848
+ console.log(
849
+ `
850
+ ${sym.ok} Autonomy disabled for agent ${c.id(opts.agent)}`
851
+ );
698
852
  } catch (err) {
699
853
  spinner.stop();
700
854
  printError(err);
@@ -707,8 +861,10 @@ ${sym.ok} Autonomy disabled for agent ${c.id(opts.agent)}`);
707
861
  try {
708
862
  await client.agents.triggerHeartbeat(opts.agent);
709
863
  spinner.stop();
710
- console.log(`
711
- ${sym.ok} Heartbeat triggered for agent ${c.id(opts.agent)}`);
864
+ console.log(
865
+ `
866
+ ${sym.ok} Heartbeat triggered for agent ${c.id(opts.agent)}`
867
+ );
712
868
  } catch (err) {
713
869
  spinner.stop();
714
870
  printError(err);
@@ -731,12 +887,12 @@ function sessionsCommand() {
731
887
  const spinner = spin("Fetching sessions\u2026");
732
888
  try {
733
889
  const client = makeClient();
734
- const agentId = opts.agent ?? cfg.defaultAgentId;
735
- const res = agentId ? await client.sessions.list(agentId, cfg.initiator) : await client.sessions.listByUser(cfg.initiator);
890
+ const agentId2 = opts.agent ?? cfg.defaultAgentId;
891
+ const res = agentId2 ? await client.sessions.list(agentId2, cfg.initiator) : await client.sessions.listByUser(cfg.initiator);
736
892
  const sessions = res?.data ?? res ?? [];
737
893
  spinner.stop();
738
894
  if (opts.json) return jsonOut(sessions);
739
- section(`Sessions (${sessions.length})${agentId ? ` \u2014 agent ${agentId.slice(0, 8)}\u2026` : " \u2014 all agents"}`);
895
+ section(`Sessions (${sessions.length})${agentId2 ? ` \u2014 agent ${agentId2.slice(0, 8)}\u2026` : " \u2014 all agents"}`);
740
896
  table(
741
897
  sessions.map((s) => ({
742
898
  ID: s.sessionId.slice(0, 8) + "\u2026",
@@ -777,8 +933,8 @@ function sessionsCommand() {
777
933
  });
778
934
  cmd.command("create").description("Create a new session").option("--agent <agentId>", "Agent ID").option("--title <title>", "Session title").option("--model <id>", "Model ID (e.g. gpt-5.4-mini, claude-sonnet-4-6)").option("--provider <provider>", "Model provider").option("--json", "Output as JSON").action(async (opts) => {
779
935
  const cfg = loadConfig();
780
- const agentId = opts.agent ?? cfg.defaultAgentId;
781
- if (!agentId) {
936
+ const agentId2 = opts.agent ?? cfg.defaultAgentId;
937
+ if (!agentId2) {
782
938
  console.error(c.error("Specify --agent <agentId> or set defaultAgentId"));
783
939
  process.exit(1);
784
940
  }
@@ -790,7 +946,7 @@ function sessionsCommand() {
790
946
  try {
791
947
  const client = makeClient();
792
948
  const res = await client.sessions.create({
793
- agentId,
949
+ agentId: agentId2,
794
950
  initiator: cfg.initiator,
795
951
  title: opts.title,
796
952
  ...opts.model && { model: { modelId: opts.model, provider: opts.provider } }
@@ -810,6 +966,34 @@ ${sym.ok} Session created`);
810
966
  process.exit(1);
811
967
  }
812
968
  });
969
+ cmd.command("rename <sessionId> <title>").description("Rename a session").option("--json", "Output as JSON").action(async (sessionId, title, opts) => {
970
+ const spinner = spin("Renaming session\u2026");
971
+ try {
972
+ const result = await makeClient().sessions.rename(sessionId, title);
973
+ spinner.stop();
974
+ if (opts.json) return jsonOut(result.data);
975
+ console.log(`
976
+ ${sym.ok} Session renamed to ${c.bold(result.data.title ?? title)}`);
977
+ } catch (err) {
978
+ spinner.stop();
979
+ printError(err);
980
+ process.exit(1);
981
+ }
982
+ });
983
+ cmd.command("delete <sessionId>").description("Delete a session").option("--json", "Output as JSON").action(async (sessionId, opts) => {
984
+ const spinner = spin("Deleting session\u2026");
985
+ try {
986
+ const result = await makeClient().sessions.delete(sessionId);
987
+ spinner.stop();
988
+ if (opts.json) return jsonOut(result.data);
989
+ console.log(`
990
+ ${sym.ok} Session deleted.`);
991
+ } catch (err) {
992
+ spinner.stop();
993
+ printError(err);
994
+ process.exit(1);
995
+ }
996
+ });
813
997
  return cmd;
814
998
  }
815
999
 
@@ -918,8 +1102,8 @@ ${sym.ok} Tool created`);
918
1102
  });
919
1103
  cmd.command("exec <toolName>").description("Execute a tool directly by name").option("--agent <agentId>", "Agent context for tool execution").option("--args <json>", "Tool arguments as JSON object", "{}").option("--json", "Output result as JSON").action(async (toolName, opts) => {
920
1104
  const cfg = loadConfig();
921
- const agentId = opts.agent ?? cfg.defaultAgentId;
922
- if (!agentId) {
1105
+ const agentId2 = opts.agent ?? cfg.defaultAgentId;
1106
+ if (!agentId2) {
923
1107
  console.error(c.error("Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`"));
924
1108
  process.exit(1);
925
1109
  }
@@ -930,13 +1114,13 @@ ${sym.ok} Tool created`);
930
1114
  console.error(c.error("--args must be valid JSON"));
931
1115
  process.exit(1);
932
1116
  }
933
- const prompt2 = `Call the tool "${toolName}" with these arguments: ${JSON.stringify(args)}. Return only the tool result, nothing else.`;
1117
+ const prompt = `Call the tool "${toolName}" with these arguments: ${JSON.stringify(args)}. Return only the tool result, nothing else.`;
934
1118
  const spinner = spin(`Executing ${toolName}\u2026`);
935
1119
  try {
936
1120
  const client = makeClient();
937
1121
  const result = await client.run.once({
938
- agentId,
939
- messages: [{ role: "user", content: prompt2 }],
1122
+ agentId: agentId2,
1123
+ messages: [{ role: "user", content: prompt }],
940
1124
  ...cfg.initiator && { initiatorId: cfg.initiator }
941
1125
  });
942
1126
  spinner.stop();
@@ -954,61 +1138,38 @@ ${sym.ok} ${c.label(toolName)}`);
954
1138
  return cmd;
955
1139
  }
956
1140
 
957
- // src/commands/workflow.ts
1141
+ // src/commands/connections.ts
958
1142
  var import_commander5 = require("commander");
959
- var import_fs4 = require("fs");
960
- var import_sdk2 = require("@agent-commons/sdk");
961
- function workflowCommand() {
962
- const cmd = new import_commander5.Command("workflow").description("Run and monitor workflows").alias("wf");
963
- async function createTemplateWorkflow(params) {
964
- const client = makeClient();
965
- const template = (0, import_sdk2.buildWorkflowTemplate)(params.templateName, params.ctx);
966
- const toolIds = {};
967
- const createdTools = [];
968
- for (const tool of template.tools) {
969
- const created = await client.tools.create({
970
- ...tool.payload,
971
- owner: params.ctx.ownerId,
972
- ownerType: "user"
973
- });
974
- const createdTool = created?.data ?? created;
975
- toolIds[tool.key] = createdTool.toolId;
976
- createdTools.push(createdTool);
977
- }
978
- const workflow = await client.workflows.create({
979
- name: template.name,
980
- description: template.description,
981
- ownerId: params.ctx.ownerId,
982
- ownerType: "user",
983
- isPublic: params.isPublic,
984
- category: template.category,
985
- tags: template.tags,
986
- definition: template.buildDefinition(toolIds, params.ctx)
987
- });
988
- return { template, workflow, createdTools };
989
- }
990
- cmd.command("list").description("List workflows owned by the current initiator").option("--json", "Output as JSON").action(async (opts) => {
1143
+ function connectionsCommand() {
1144
+ const cmd = new import_commander5.Command("connections").description(
1145
+ "Manage OAuth account connections (Google Workspace, GitHub, Slack, \u2026) that agents act with"
1146
+ );
1147
+ cmd.command("list", { isDefault: true }).description("List your connected accounts").option("--json", "Output as JSON").action(async (opts) => {
991
1148
  const cfg = loadConfig();
992
- if (!cfg.initiator) {
993
- console.error(c.error("No initiator set. Run `agc login` first."));
994
- process.exit(1);
995
- }
996
- const spinner = spin("Fetching workflows\u2026");
1149
+ const spinner = spin("Fetching connections\u2026");
997
1150
  try {
998
1151
  const client = makeClient();
999
- const workflows = await client.workflows.list(cfg.initiator, "user");
1152
+ const res = await client.oauth.listConnections(
1153
+ cfg.initiator ? { ownerId: cfg.initiator, ownerType: "user" } : void 0
1154
+ );
1155
+ const connections = res?.connections ?? [];
1000
1156
  spinner.stop();
1001
- if (opts.json) return jsonOut(workflows);
1002
- section(`Workflows (${workflows.length})`);
1157
+ if (opts.json) return jsonOut(connections);
1158
+ section(`Connections (${connections.length})`);
1159
+ if (connections.length === 0) {
1160
+ console.log(c.dim(" No connected accounts. Run `agc connections connect <provider>`."));
1161
+ return;
1162
+ }
1003
1163
  table(
1004
- workflows.map((w) => ({
1005
- ID: w.workflowId.slice(0, 8) + "\u2026",
1006
- Name: w.name,
1007
- Nodes: String((w.definition?.nodes ?? []).length),
1008
- Public: w.isPublic ? "yes" : "no",
1009
- Created: relativeTime(w.createdAt)
1164
+ connections.map((conn) => ({
1165
+ ID: (conn.connectionId ?? "").slice(0, 8) + "\u2026",
1166
+ Provider: conn.providerDisplayName || conn.providerKey || "",
1167
+ Account: conn.providerUserEmail || conn.providerUserName || "",
1168
+ Status: conn.status ?? "",
1169
+ Scopes: String((conn.scopes ?? []).length),
1170
+ Used: conn.lastUsedAt ? relativeTime(conn.lastUsedAt) : c.dim("never")
1010
1171
  })),
1011
- ["ID", "Name", "Nodes", "Public", "Created"]
1172
+ ["ID", "Provider", "Account", "Status", "Scopes", "Used"]
1012
1173
  );
1013
1174
  } catch (err) {
1014
1175
  spinner.stop();
@@ -1016,10 +1177,205 @@ function workflowCommand() {
1016
1177
  process.exit(1);
1017
1178
  }
1018
1179
  });
1019
- cmd.command("create").description("Create a workflow from a JSON file").requiredOption("--file <path>", "Path to a workflow payload or definition JSON file").option("--name <name>", "Workflow name").option("--description <text>", "Workflow description").option("--public", "Make workflow public").option("--json", "Output as JSON").action(async (opts) => {
1020
- const cfg = loadConfig();
1021
- if (!cfg.initiator) {
1022
- console.error(c.error("No initiator set. Run `agc login` first."));
1180
+ cmd.command("providers").description("List OAuth providers available to connect").option("--json", "Output as JSON").action(async (opts) => {
1181
+ const spinner = spin("Fetching providers\u2026");
1182
+ try {
1183
+ const client = makeClient();
1184
+ const res = await client.oauth.listProviders();
1185
+ const providers = res?.providers ?? [];
1186
+ spinner.stop();
1187
+ if (opts.json) return jsonOut(providers);
1188
+ section(`Providers (${providers.length})`);
1189
+ table(
1190
+ providers.map((p) => ({
1191
+ Key: p.providerKey ?? "",
1192
+ Name: p.displayName ?? "",
1193
+ Active: p.isActive ? "yes" : "no"
1194
+ })),
1195
+ ["Key", "Name", "Active"]
1196
+ );
1197
+ } catch (err) {
1198
+ spinner.stop();
1199
+ printError(err);
1200
+ process.exit(1);
1201
+ }
1202
+ });
1203
+ cmd.command("connect <providerKey>").description("Connect an account: prints an authorization URL to open in your browser").option("--scopes <scopes>", "Space-separated OAuth scopes to request").option("--no-browser", "Do not open the authorization URL automatically").option("--json", "Output as JSON").action(async (providerKey, opts) => {
1204
+ const cfg = loadConfig();
1205
+ if (!cfg.initiator) {
1206
+ console.error(c.error("No initiator set. Run `agc login` first."));
1207
+ process.exit(1);
1208
+ }
1209
+ const spinner = spin("Starting OAuth flow\u2026");
1210
+ try {
1211
+ const client = makeClient();
1212
+ const res = await client.oauth.connect({
1213
+ providerKey,
1214
+ ...opts.scopes ? { scopes: String(opts.scopes).split(/\s+/).filter(Boolean) } : {}
1215
+ });
1216
+ spinner.stop();
1217
+ if (opts.json) return jsonOut(res);
1218
+ if (opts.browser !== false) openBrowser(res.authorizationUrl);
1219
+ console.log(`
1220
+ ${sym.ok} Authorize the connection in your browser:`);
1221
+ console.log(`
1222
+ ${c.id(res.authorizationUrl)}
1223
+ `);
1224
+ console.log(c.dim(" After approving, the connection appears in `agc connections list`."));
1225
+ } catch (err) {
1226
+ spinner.stop();
1227
+ printError(err);
1228
+ process.exit(1);
1229
+ }
1230
+ });
1231
+ cmd.command("get <connectionId>").description("Show a connected account").option("--json", "Output as JSON").action(async (connectionId, opts) => {
1232
+ const spinner = spin("Fetching connection\u2026");
1233
+ try {
1234
+ const result = await makeClient().oauth.getConnection(connectionId);
1235
+ spinner.stop();
1236
+ if (opts.json) return jsonOut(result.connection);
1237
+ const connection = result.connection;
1238
+ detail([
1239
+ ["Connection ID", c.id(connection.connectionId)],
1240
+ ["Provider", connection.providerDisplayName ?? connection.providerKey],
1241
+ ["Account", connection.providerUserEmail ?? connection.providerUserName ?? ""],
1242
+ ["Status", connection.status],
1243
+ ["Scopes", connection.scopes.join(", ")],
1244
+ ["Expires", connection.expiresAt ?? c.dim("(not reported)")]
1245
+ ]);
1246
+ } catch (err) {
1247
+ spinner.stop();
1248
+ printError(err);
1249
+ process.exit(1);
1250
+ }
1251
+ });
1252
+ cmd.command("refresh <connectionId>").description("Refresh a connected account token").action(async (connectionId) => {
1253
+ const spinner = spin("Refreshing connection\u2026");
1254
+ try {
1255
+ await makeClient().oauth.refresh(connectionId);
1256
+ spinner.stop();
1257
+ console.log(`${sym.ok} Connection refreshed.`);
1258
+ } catch (err) {
1259
+ spinner.stop();
1260
+ printError(err);
1261
+ process.exit(1);
1262
+ }
1263
+ });
1264
+ cmd.command("rename <connectionId> <name>").description("Set a friendly name for a connected account").action(async (connectionId, name) => {
1265
+ const spinner = spin("Updating connection\u2026");
1266
+ try {
1267
+ await makeClient().oauth.updateConnection(connectionId, {
1268
+ displayName: name
1269
+ });
1270
+ spinner.stop();
1271
+ console.log(`${sym.ok} Connection renamed to ${c.bold(name)}.`);
1272
+ } catch (err) {
1273
+ spinner.stop();
1274
+ printError(err);
1275
+ process.exit(1);
1276
+ }
1277
+ });
1278
+ cmd.command("test <connectionId>").description("Check that a connection is active and its token is valid").option("--json", "Output as JSON").action(async (connectionId, opts) => {
1279
+ const spinner = spin("Testing connection\u2026");
1280
+ try {
1281
+ const client = makeClient();
1282
+ const res = await client.oauth.test(connectionId);
1283
+ spinner.stop();
1284
+ if (opts.json) return jsonOut(res);
1285
+ detail([
1286
+ ["Status", res.status],
1287
+ ["Token valid", res.accessTokenValid ? "yes" : "no"],
1288
+ ["Account", res.providerUserEmail ?? c.dim("(unknown)")],
1289
+ ["Last error", res.error ?? c.dim("(none)")]
1290
+ ]);
1291
+ } catch (err) {
1292
+ spinner.stop();
1293
+ printError(err);
1294
+ process.exit(1);
1295
+ }
1296
+ });
1297
+ cmd.command("revoke <connectionId>").description("Revoke a connection and delete its stored tokens").action(async (connectionId) => {
1298
+ const spinner = spin("Revoking connection\u2026");
1299
+ try {
1300
+ const client = makeClient();
1301
+ await client.oauth.revoke(connectionId);
1302
+ spinner.stop();
1303
+ console.log(`${sym.ok} Connection revoked.`);
1304
+ } catch (err) {
1305
+ spinner.stop();
1306
+ printError(err);
1307
+ process.exit(1);
1308
+ }
1309
+ });
1310
+ return cmd;
1311
+ }
1312
+
1313
+ // src/commands/workflow.ts
1314
+ var import_commander6 = require("commander");
1315
+ var import_fs4 = require("fs");
1316
+ var import_sdk2 = require("@agent-commons/sdk");
1317
+ function workflowCommand() {
1318
+ const cmd = new import_commander6.Command("workflow").description("Run and monitor workflows").alias("wf");
1319
+ async function createTemplateWorkflow(params) {
1320
+ const client = makeClient();
1321
+ const template = (0, import_sdk2.buildWorkflowTemplate)(params.templateName, params.ctx);
1322
+ const toolIds = {};
1323
+ const createdTools = [];
1324
+ for (const tool of template.tools) {
1325
+ const created = await client.tools.create({
1326
+ ...tool.payload,
1327
+ owner: params.ctx.ownerId,
1328
+ ownerType: "user"
1329
+ });
1330
+ const createdTool = created?.data ?? created;
1331
+ toolIds[tool.key] = createdTool.toolId;
1332
+ createdTools.push(createdTool);
1333
+ }
1334
+ const workflow = await client.workflows.create({
1335
+ name: template.name,
1336
+ description: template.description,
1337
+ ownerId: params.ctx.ownerId,
1338
+ ownerType: "user",
1339
+ isPublic: params.isPublic,
1340
+ category: template.category,
1341
+ tags: template.tags,
1342
+ definition: template.buildDefinition(toolIds, params.ctx)
1343
+ });
1344
+ return { template, workflow, createdTools };
1345
+ }
1346
+ cmd.command("list").description("List workflows owned by the current initiator").option("--json", "Output as JSON").action(async (opts) => {
1347
+ const cfg = loadConfig();
1348
+ if (!cfg.initiator) {
1349
+ console.error(c.error("No initiator set. Run `agc login` first."));
1350
+ process.exit(1);
1351
+ }
1352
+ const spinner = spin("Fetching workflows\u2026");
1353
+ try {
1354
+ const client = makeClient();
1355
+ const workflows = await client.workflows.list(cfg.initiator, "user");
1356
+ spinner.stop();
1357
+ if (opts.json) return jsonOut(workflows);
1358
+ section(`Workflows (${workflows.length})`);
1359
+ table(
1360
+ workflows.map((w) => ({
1361
+ ID: w.workflowId.slice(0, 8) + "\u2026",
1362
+ Name: w.name,
1363
+ Nodes: String((w.definition?.nodes ?? []).length),
1364
+ Public: w.isPublic ? "yes" : "no",
1365
+ Created: relativeTime(w.createdAt)
1366
+ })),
1367
+ ["ID", "Name", "Nodes", "Public", "Created"]
1368
+ );
1369
+ } catch (err) {
1370
+ spinner.stop();
1371
+ printError(err);
1372
+ process.exit(1);
1373
+ }
1374
+ });
1375
+ cmd.command("create").description("Create a workflow from a JSON file").requiredOption("--file <path>", "Path to a workflow payload or definition JSON file").option("--name <name>", "Workflow name").option("--description <text>", "Workflow description").option("--public", "Make workflow public").option("--json", "Output as JSON").action(async (opts) => {
1376
+ const cfg = loadConfig();
1377
+ if (!cfg.initiator) {
1378
+ console.error(c.error("No initiator set. Run `agc login` first."));
1023
1379
  process.exit(1);
1024
1380
  }
1025
1381
  let fileJson;
@@ -1089,8 +1445,8 @@ ${sym.ok} Workflow created`);
1089
1445
  }
1090
1446
  const templateName = templateNameRaw;
1091
1447
  const needsAgent = templateName === "agent-research-summary" || templateName === "multi-agent-field-report";
1092
- const agentId = opts.agent ?? cfg.defaultAgentId;
1093
- if (needsAgent && !agentId) {
1448
+ const agentId2 = opts.agent ?? cfg.defaultAgentId;
1449
+ if (needsAgent && !agentId2) {
1094
1450
  console.error(c.error("This template requires --agent <agentId> or a configured defaultAgentId."));
1095
1451
  process.exit(1);
1096
1452
  }
@@ -1114,7 +1470,7 @@ ${sym.ok} Workflow created`);
1114
1470
  const ctx = {
1115
1471
  ownerId: cfg.initiator,
1116
1472
  prefix,
1117
- agentId,
1473
+ agentId: agentId2,
1118
1474
  reviewerAgentId: opts.reviewerAgent,
1119
1475
  childWorkflowId
1120
1476
  };
@@ -1134,7 +1490,7 @@ ${sym.ok} Workflow created`);
1134
1490
  }
1135
1491
  }
1136
1492
  execution = await makeClient().workflows.execute(result.workflow.workflowId, {
1137
- agentId,
1493
+ agentId: agentId2,
1138
1494
  inputData,
1139
1495
  userId: cfg.initiator
1140
1496
  });
@@ -1205,7 +1561,7 @@ ${sym.ok} Execution started: ${c.id(execution.executionId)}`);
1205
1561
  });
1206
1562
  cmd.command("run <workflowId>").description("Execute a workflow").option("--agent <agentId>", "Agent context").option("--session <sessionId>", "Session context").option("--input <json>", "Input data as JSON string", "{}").option("--watch", "Stream execution progress via SSE").option("--json", "Output result as JSON").action(async (workflowId, opts) => {
1207
1563
  const cfg = loadConfig();
1208
- const agentId = opts.agent ?? cfg.defaultAgentId;
1564
+ const agentId2 = opts.agent ?? cfg.defaultAgentId;
1209
1565
  let inputData = {};
1210
1566
  try {
1211
1567
  inputData = JSON.parse(opts.input);
@@ -1217,7 +1573,7 @@ ${sym.ok} Execution started: ${c.id(execution.executionId)}`);
1217
1573
  try {
1218
1574
  const client = makeClient();
1219
1575
  const execution = await client.workflows.execute(workflowId, {
1220
- agentId,
1576
+ agentId: agentId2,
1221
1577
  sessionId: opts.session,
1222
1578
  inputData
1223
1579
  });
@@ -1357,17 +1713,17 @@ ${c.warn("\u23F8 Awaiting approval")} at node ${c.id(e.pausedAtNode ?? "")}`);
1357
1713
  }
1358
1714
 
1359
1715
  // src/commands/task.ts
1360
- var import_commander6 = require("commander");
1716
+ var import_commander7 = require("commander");
1361
1717
  function taskCommand() {
1362
- const cmd = new import_commander6.Command("task").description("Manage and execute tasks").alias("t");
1718
+ const cmd = new import_commander7.Command("task").description("Manage and execute tasks").alias("t");
1363
1719
  cmd.command("list").description("List tasks").option("--agent <agentId>", "Filter by agent ID").option("--session <sessionId>", "Filter by session ID").option("--json", "Output as JSON").action(async (opts) => {
1364
1720
  const cfg = loadConfig();
1365
- const agentId = opts.agent ?? cfg.defaultAgentId;
1721
+ const agentId2 = opts.agent ?? cfg.defaultAgentId;
1366
1722
  const spinner = spin("Fetching tasks\u2026");
1367
1723
  try {
1368
1724
  const client = makeClient();
1369
1725
  const filter = {};
1370
- if (agentId) filter.agentId = agentId;
1726
+ if (agentId2) filter.agentId = agentId2;
1371
1727
  if (opts.session) filter.sessionId = opts.session;
1372
1728
  if (cfg.initiator) {
1373
1729
  filter.ownerId = cfg.initiator;
@@ -1423,8 +1779,8 @@ function taskCommand() {
1423
1779
  });
1424
1780
  cmd.command("create").description("Create a new task").requiredOption("--title <title>", "Task title").option("--agent <agentId>", "Agent ID").option("--session <sessionId>", "Session ID").option("--workflow <workflowId>", "Workflow ID to attach").option("--input <json>", "Input data as JSON", "{}").option("--timeout <ms>", "Execution timeout in milliseconds").option("--execute", "Execute immediately after creation").option("--watch", "Stream execution progress (implies --execute)").option("--json", "Output as JSON").action(async (opts) => {
1425
1781
  const cfg = loadConfig();
1426
- const agentId = opts.agent ?? cfg.defaultAgentId;
1427
- if (!agentId) {
1782
+ const agentId2 = opts.agent ?? cfg.defaultAgentId;
1783
+ if (!agentId2) {
1428
1784
  console.error(c.error("Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`"));
1429
1785
  process.exit(1);
1430
1786
  }
@@ -1440,7 +1796,7 @@ function taskCommand() {
1440
1796
  const client = makeClient();
1441
1797
  const res = await client.tasks.create({
1442
1798
  title: opts.title,
1443
- agentId,
1799
+ agentId: agentId2,
1444
1800
  sessionId: opts.session,
1445
1801
  workflowId: opts.workflow,
1446
1802
  inputData,
@@ -1544,14 +1900,14 @@ ${sym.fail} ${c.error(event.message ?? event.type)}`);
1544
1900
  }
1545
1901
 
1546
1902
  // src/commands/run.ts
1547
- var import_commander7 = require("commander");
1548
- var readline3 = __toESM(require("readline"));
1903
+ var import_commander8 = require("commander");
1904
+ var readline2 = __toESM(require("readline"));
1549
1905
 
1550
1906
  // src/local-tools.ts
1551
1907
  var import_fs5 = require("fs");
1552
1908
  var import_path3 = require("path");
1553
1909
  var import_child_process2 = require("child_process");
1554
- var readline2 = __toESM(require("readline"));
1910
+ var readline = __toESM(require("readline"));
1555
1911
  var pdfParse = require("pdf-parse/lib/pdf-parse.js");
1556
1912
  var managedProcesses = /* @__PURE__ */ new Map();
1557
1913
  function capBuffer(existing, chunk, maxBytes) {
@@ -1701,11 +2057,11 @@ function extractToolCall(text) {
1701
2057
  }
1702
2058
  return null;
1703
2059
  }
1704
- function injectAgcTrailer(command, args, agentId, agentName) {
2060
+ function injectAgcTrailer(command, args, agentId2, agentName) {
1705
2061
  if (command !== "git") return args;
1706
2062
  if (!args.some((a) => a === "commit")) return args;
1707
2063
  if (args.some((a) => a.includes("Co-Authored-By: agc"))) return args;
1708
- const identity = agentName ? `${agentName} (agc)` : agentId ? `agc/${agentId}` : "agc agent";
2064
+ const identity = agentName ? `${agentName} (agc)` : agentId2 ? `agc/${agentId2}` : "agc agent";
1709
2065
  return [...args, "--trailer", `Co-Authored-By: ${identity} <agc-agent@users.noreply.github.com>`];
1710
2066
  }
1711
2067
  var AGC_HOOK_MARKER = "# agc-session:";
@@ -1722,7 +2078,7 @@ function findGitDir(rootDir) {
1722
2078
  }
1723
2079
  return null;
1724
2080
  }
1725
- function installGitHook(rootDir, sessionId, agentId, agentName) {
2081
+ function installGitHook(rootDir, sessionId, agentId2, agentName) {
1726
2082
  const gitDir = findGitDir(rootDir);
1727
2083
  if (!gitDir) return;
1728
2084
  const hooksDir = (0, import_path3.join)(gitDir, "hooks");
@@ -1734,7 +2090,7 @@ function installGitHook(rootDir, sessionId, agentId, agentName) {
1734
2090
  (0, import_fs5.writeFileSync)(hookPath + HOOK_BACKUP_SUFFIX, existing, { mode: 493 });
1735
2091
  }
1736
2092
  }
1737
- const identity = agentName ? `${agentName} (agc)` : agentId ? `agc/${agentId}` : "agc agent";
2093
+ const identity = agentName ? `${agentName} (agc)` : agentId2 ? `agc/${agentId2}` : "agc agent";
1738
2094
  const trailer = `Co-Authored-By: ${identity} <agc-agent@users.noreply.github.com>`;
1739
2095
  const chainLine = (0, import_fs5.existsSync)(hookPath + HOOK_BACKUP_SUFFIX) ? `
1740
2096
  # chain pre-existing hook
@@ -1798,7 +2154,7 @@ async function confirm(message, config, permissionKey) {
1798
2154
  if (cached === "allow") return true;
1799
2155
  if (cached === "deny") return false;
1800
2156
  return new Promise((resolve2) => {
1801
- const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
2157
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1802
2158
  process.stdout.write(
1803
2159
  `
1804
2160
  \x1B[33m\u26A0\x1B[0m ${message}
@@ -2177,10 +2533,10 @@ async function runLocalTool(call, cfg) {
2177
2533
 
2178
2534
  // src/commands/run.ts
2179
2535
  function runCommand() {
2180
- return new import_commander7.Command("run").description("Send a single prompt to an agent and stream the response").argument("<prompt>", "Prompt text to send").option("--agent <agentId>", "Agent ID").option("--session <sessionId>", "Resume an existing session by ID").option("--new-session", "Create a new session and print its ID for future use").option("--local", "Enable local file system access (with permission prompts)").option("-y, --yes", "Enable local file system access and auto-approve all operations").option("--no-stream", "Disable streaming (wait for full response)").option("--json", "Output raw event stream as JSON lines").action(async (prompt2, opts) => {
2536
+ return new import_commander8.Command("run").description("Send a single prompt to an agent and stream the response").argument("<prompt>", "Prompt text to send").option("--agent <agentId>", "Agent ID").option("--session <sessionId>", "Resume an existing session by ID").option("--new-session", "Create a new session and print its ID for future use").option("--computer", "Give the agent access to its persistent cloud computer").option("--local", "Enable local file system access (with permission prompts)").option("-y, --yes", "Enable local file system access and auto-approve all operations").option("--no-stream", "Disable streaming (wait for full response)").option("--json", "Output raw event stream as JSON lines").action(async (prompt, opts) => {
2181
2537
  const cfg = loadConfig();
2182
- const agentId = opts.agent ?? cfg.defaultAgentId;
2183
- if (!agentId) {
2538
+ const agentId2 = opts.agent ?? cfg.defaultAgentId;
2539
+ if (!agentId2) {
2184
2540
  console.error(c.error("Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`"));
2185
2541
  process.exit(1);
2186
2542
  }
@@ -2205,7 +2561,7 @@ function runCommand() {
2205
2561
  const spinner = spin("Creating session\u2026");
2206
2562
  try {
2207
2563
  const res = await client.sessions.create({
2208
- agentId,
2564
+ agentId: agentId2,
2209
2565
  initiator: cfg.initiator ?? "",
2210
2566
  title: `agc run ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16)}`,
2211
2567
  source: "cli"
@@ -2231,7 +2587,7 @@ function runCommand() {
2231
2587
  appendLog: () => {
2232
2588
  },
2233
2589
  permissions: /* @__PURE__ */ new Map(),
2234
- agentId,
2590
+ agentId: agentId2,
2235
2591
  autoApprove
2236
2592
  };
2237
2593
  const snapshot = buildDirSnapshot(rootDir, 2);
@@ -2246,16 +2602,20 @@ function runCommand() {
2246
2602
  if (localEnabled) {
2247
2603
  rows.push(["Local tools", autoApprove ? c.warn("enabled (auto-approve on)") : c.success("enabled")]);
2248
2604
  }
2605
+ if (opts.computer) {
2606
+ rows.push(["Cloud computer", c.success("enabled") + c.dim(" (persistent, remote)")]);
2607
+ }
2249
2608
  if (rows.length) {
2250
2609
  detail(rows);
2251
2610
  console.log();
2252
2611
  }
2253
2612
  }
2254
2613
  const params = {
2255
- agentId,
2614
+ agentId: agentId2,
2256
2615
  sessionId,
2257
- messages: [{ role: "user", content: prompt2 }],
2616
+ messages: [{ role: "user", content: prompt }],
2258
2617
  ...cfg.initiator && { initiatorId: cfg.initiator },
2618
+ ...opts.computer && { computerRequest: { enabled: true } },
2259
2619
  ...cliContext && { cliContext }
2260
2620
  };
2261
2621
  if (opts.noStream && !localEnabled) {
@@ -2305,16 +2665,12 @@ Session: ${sessionId} (resume with: agc run --session ${sessionId} "<prompt>")`
2305
2665
  toolOk = false;
2306
2666
  }
2307
2667
  const elapsed = ((Date.now() - startMs) / 1e3).toFixed(1);
2308
- readline3.cursorTo(process.stdout, 0);
2309
- readline3.clearLine(process.stdout, 0);
2668
+ readline2.cursorTo(process.stdout, 0);
2669
+ readline2.clearLine(process.stdout, 0);
2310
2670
  process.stdout.write(` ${c.dim("\u2500")} ${c.bold(displayName)} ${toolOk ? sym.ok : sym.fail} ${c.dim("(" + elapsed + "s)")}
2311
2671
  `);
2312
2672
  try {
2313
- await fetch(`${cfg.apiUrl}/v1/agents/cli-tool-result`, {
2314
- method: "POST",
2315
- headers: { "Content-Type": "application/json", "Authorization": `Bearer ${cfg.apiKey}` },
2316
- body: JSON.stringify({ requestId, result })
2317
- });
2673
+ await client.agents.submitCliToolResult(requestId, result);
2318
2674
  } catch {
2319
2675
  }
2320
2676
  } else if (event.type === "toolStart") {
@@ -2327,8 +2683,8 @@ Session: ${sessionId} (resume with: agc run --session ${sessionId} "<prompt>")`
2327
2683
  process.stdout.write(` ${c.dim("\u2500")} ${c.bold(lastToolName)}`);
2328
2684
  } else if (event.type === "toolEnd") {
2329
2685
  const elapsed = ((Date.now() - toolStartMs) / 1e3).toFixed(1);
2330
- readline3.cursorTo(process.stdout, 0);
2331
- readline3.clearLine(process.stdout, 0);
2686
+ readline2.cursorTo(process.stdout, 0);
2687
+ readline2.clearLine(process.stdout, 0);
2332
2688
  process.stdout.write(` ${c.dim("\u2500")} ${c.bold(lastToolName)} ${sym.ok} ${c.dim("(" + elapsed + "s)")}
2333
2689
  `);
2334
2690
  } else if (event.type === "final") {
@@ -2355,8 +2711,8 @@ ${sym.fail} ${c.error(event.message ?? "Error")}`);
2355
2711
  }
2356
2712
 
2357
2713
  // src/commands/chat.ts
2358
- var import_commander8 = require("commander");
2359
- var readline4 = __toESM(require("readline"));
2714
+ var import_commander9 = require("commander");
2715
+ var readline3 = __toESM(require("readline"));
2360
2716
  var import_fs6 = require("fs");
2361
2717
  var import_path4 = require("path");
2362
2718
  var import_os3 = require("os");
@@ -2399,12 +2755,20 @@ var LOCAL_TOOLS_DISCLAIMER = `
2399
2755
  ${c.dim("Session activity is logged to")} ${c.primary("~/.agc/sessions/")}
2400
2756
  `;
2401
2757
  function chatCommand() {
2402
- return new import_commander8.Command("chat").description("Start an interactive chat REPL with an agent").option("--agent <agentId>", "Agent ID (or set defaultAgentId in config)").option("--resume <sessionId>", "Resume an existing session by ID").option("--no-stream", "Disable token streaming (wait for full response)").option("--no-local", "Disable local file system access for the agent").action(async (opts) => {
2758
+ return new import_commander9.Command("chat").description("Start an interactive chat REPL with an agent").option("--agent <agentId>", "Agent ID (or set defaultAgentId in config)").option("--resume <sessionId>", "Resume an existing session by ID").option("--computer", "Give the agent access to its persistent cloud computer").option("--no-stream", "Disable token streaming (wait for full response)").option("--no-local", "Disable local file system access for the agent").action(async (opts) => {
2403
2759
  const localEnabled = opts.local !== false;
2404
2760
  const cfg = loadConfig();
2405
- const agentId = opts.agent ?? cfg.defaultAgentId;
2406
- if (!agentId) {
2407
- console.error(c.error("Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`"));
2761
+ let agentId2 = opts.agent ?? cfg.defaultAgentId;
2762
+ if (!agentId2 && cfg.initiator) {
2763
+ try {
2764
+ const listed = await makeClient().agents.list(cfg.initiator);
2765
+ const agents = listed?.data ?? listed ?? [];
2766
+ agentId2 = agents.find((agent) => agent.isDefault)?.agentId ?? agents[0]?.agentId;
2767
+ } catch {
2768
+ }
2769
+ }
2770
+ if (!agentId2) {
2771
+ console.error(c.error("No default agent is available. Specify --agent <agentId> or run `agc agents list`."));
2408
2772
  process.exit(1);
2409
2773
  }
2410
2774
  if (!cfg.initiator) {
@@ -2419,7 +2783,7 @@ function chatCommand() {
2419
2783
  const spinner = spin("Creating session\u2026");
2420
2784
  try {
2421
2785
  const res = await client.sessions.create({
2422
- agentId,
2786
+ agentId: agentId2,
2423
2787
  initiator,
2424
2788
  title: `agc chat ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16)}`,
2425
2789
  source: "cli"
@@ -2430,7 +2794,7 @@ function chatCommand() {
2430
2794
  appendSessionLog(sessionId, {
2431
2795
  type: "session_start",
2432
2796
  sessionId,
2433
- agentId,
2797
+ agentId: agentId2,
2434
2798
  initiator,
2435
2799
  source: "cli",
2436
2800
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -2445,9 +2809,9 @@ function chatCommand() {
2445
2809
  try {
2446
2810
  const res = await client.sessions.get(sessionId);
2447
2811
  const session = res?.data ?? res;
2448
- if (session.agentId && session.agentId !== agentId) {
2812
+ if (session.agentId && session.agentId !== agentId2) {
2449
2813
  spinner.stop();
2450
- console.log(c.warn(` Note: session ${sessionId} was created with agent ${session.agentId}, not ${agentId}`));
2814
+ console.log(c.warn(` Note: session ${sessionId} was created with agent ${session.agentId}, not ${agentId2}`));
2451
2815
  } else {
2452
2816
  spinner.stop();
2453
2817
  }
@@ -2460,10 +2824,10 @@ function chatCommand() {
2460
2824
  let agentName;
2461
2825
  let walletLine = "";
2462
2826
  await Promise.allSettled([
2463
- client.agents.get(agentId).then((res) => {
2827
+ client.agents.get(agentId2).then((res) => {
2464
2828
  agentName = (res?.data ?? res)?.name;
2465
2829
  }),
2466
- client.wallets.primary(agentId).then(async (primary) => {
2830
+ client.wallets.primary(agentId2).then(async (primary) => {
2467
2831
  const w = primary?.data ?? primary;
2468
2832
  if (w?.id) {
2469
2833
  const bal = await client.wallets.balance(w.id).catch(() => null);
@@ -2477,10 +2841,11 @@ function chatCommand() {
2477
2841
  console.log(`
2478
2842
  ${c.bold("Agent Commons Chat")}`);
2479
2843
  const headerRows = [
2480
- ["Agent", agentName ? `${agentName} ${c.dim(agentId)}` : agentId],
2844
+ ["Agent", agentName ? `${agentName} ${c.dim(agentId2)}` : agentId2],
2481
2845
  ["Session", c.id(sessionId) + (isResume ? c.dim(" (resumed)") : c.dim(" (new)"))]
2482
2846
  ];
2483
2847
  if (walletLine) headerRows.push(["Wallet", walletLine]);
2848
+ if (opts.computer) headerRows.push(["Cloud computer", c.success("enabled") + c.dim(" (persistent, remote)")]);
2484
2849
  if (localEnabled) headerRows.push(["Local tools", c.success("enabled") + c.dim(" (read, write, search, run)")]);
2485
2850
  detail(headerRows);
2486
2851
  let localToolsCfg = null;
@@ -2490,12 +2855,12 @@ ${c.bold("Agent Commons Chat")}`);
2490
2855
  localToolsCfg = {
2491
2856
  rootDir,
2492
2857
  sessionId,
2493
- agentId,
2858
+ agentId: agentId2,
2494
2859
  agentName,
2495
2860
  appendLog: (record) => appendSessionLog(sessionId, record),
2496
2861
  permissions: /* @__PURE__ */ new Map()
2497
2862
  };
2498
- installGitHook(rootDir, sessionId, agentId, agentName);
2863
+ installGitHook(rootDir, sessionId, agentId2, agentName);
2499
2864
  appendSessionLog(sessionId, {
2500
2865
  type: "local_tools_enabled",
2501
2866
  rootDir,
@@ -2503,7 +2868,7 @@ ${c.bold("Agent Commons Chat")}`);
2503
2868
  });
2504
2869
  }
2505
2870
  console.log(c.dim("\nType your message and press Enter. Type /help for commands.\n"));
2506
- const rl = readline4.createInterface({
2871
+ const rl = readline3.createInterface({
2507
2872
  input: process.stdin,
2508
2873
  output: process.stdout,
2509
2874
  terminal: true,
@@ -2587,9 +2952,10 @@ ${content}
2587
2952
  cliContext = buildLocalToolsManifest(rootDir, snapshot, fileContextBlocks);
2588
2953
  }
2589
2954
  const params = {
2590
- agentId,
2955
+ agentId: agentId2,
2591
2956
  sessionId,
2592
2957
  messages: [{ role: "user", content: userMessage }],
2958
+ ...opts.computer && { computerRequest: { enabled: true } },
2593
2959
  ...cliContext && { cliContext }
2594
2960
  };
2595
2961
  if (opts.noStream) {
@@ -2646,7 +3012,7 @@ ${sym.fail} ${c.error(err.message ?? String(err))}`);
2646
3012
  if (isWaiting) {
2647
3013
  elapsedInterval = setInterval(() => {
2648
3014
  elapsedSec++;
2649
- readline4.cursorTo(process.stdout, 0);
3015
+ readline3.cursorTo(process.stdout, 0);
2650
3016
  process.stdout.write(` ${c.dim("\u2500")} ${c.bold(displayName)}${argStr ? " " + c.dim(argStr) : ""} ${c.dim(elapsedSec + "s\u2026")}`);
2651
3017
  }, 1e3);
2652
3018
  }
@@ -2661,12 +3027,14 @@ ${sym.fail} ${c.error(err.message ?? String(err))}`);
2661
3027
  if (elapsedInterval) clearInterval(elapsedInterval);
2662
3028
  const elapsed = ((Date.now() - startMs) / 1e3).toFixed(1);
2663
3029
  const preview = toolOk ? toolResultPreview(displayName, result) : "";
2664
- readline4.cursorTo(process.stdout, 0);
2665
- readline4.clearLine(process.stdout, 0);
3030
+ readline3.cursorTo(process.stdout, 0);
3031
+ readline3.clearLine(process.stdout, 0);
2666
3032
  const statusIcon = toolOk ? sym.ok : sym.fail;
2667
3033
  const previewPart = preview ? ` ${c.dim(preview)}` : "";
2668
- process.stdout.write(` ${c.dim("\u2500")} ${c.bold(displayName)}${argStr ? " " + c.dim(argStr) : ""} ${statusIcon}${previewPart} ${c.dim("(" + elapsed + "s)")}
2669
- `);
3034
+ process.stdout.write(
3035
+ ` ${c.dim("\u2500")} ${c.bold(displayName)}${argStr ? " " + c.dim(argStr) : ""} ${statusIcon}${previewPart} ${c.dim("(" + elapsed + "s)")}
3036
+ `
3037
+ );
2670
3038
  appendSessionLog(sessionId, {
2671
3039
  type: "local_tool_result",
2672
3040
  tool: toolName,
@@ -2674,14 +3042,7 @@ ${sym.fail} ${c.error(err.message ?? String(err))}`);
2674
3042
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
2675
3043
  });
2676
3044
  try {
2677
- await fetch(`${cfg.apiUrl}/v1/agents/cli-tool-result`, {
2678
- method: "POST",
2679
- headers: {
2680
- "Content-Type": "application/json",
2681
- "Authorization": `Bearer ${cfg.apiKey}`
2682
- },
2683
- body: JSON.stringify({ requestId, result })
2684
- });
3045
+ await client.agents.submitCliToolResult(requestId, result);
2685
3046
  } catch (postErr) {
2686
3047
  console.error(c.warn(`
2687
3048
  [local] Failed to submit tool result: ${postErr?.message}`));
@@ -2696,8 +3057,8 @@ ${sym.fail} ${c.error(err.message ?? String(err))}`);
2696
3057
  hasOutput = false;
2697
3058
  } else if (event.type === "toolEnd") {
2698
3059
  const elapsed = ((Date.now() - toolStartMs) / 1e3).toFixed(1);
2699
- readline4.cursorTo(process.stdout, 0);
2700
- readline4.clearLine(process.stdout, 0);
3060
+ readline3.cursorTo(process.stdout, 0);
3061
+ readline3.clearLine(process.stdout, 0);
2701
3062
  process.stdout.write(` ${c.dim("\u2500")} ${c.bold(lastToolName)} ${sym.ok} ${c.dim("(" + elapsed + "s)")}
2702
3063
  `);
2703
3064
  process.stdout.write(c.primary("agent") + c.dim(" \u203A "));
@@ -2723,7 +3084,13 @@ ${sym.fail} ${c.error(err.message ?? String(err))}`);
2723
3084
  type: "message",
2724
3085
  role: "assistant",
2725
3086
  content: agentContent,
2726
- usage: { inputTokens: inputTok, outputTokens: outputTok, cachedTokens: cachedTok, totalTokens: total, costUsd: usage.costUsd },
3087
+ usage: {
3088
+ inputTokens: inputTok,
3089
+ outputTokens: outputTok,
3090
+ cachedTokens: cachedTok,
3091
+ totalTokens: total,
3092
+ costUsd: usage.costUsd
3093
+ },
2727
3094
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
2728
3095
  });
2729
3096
  } else {
@@ -2746,7 +3113,7 @@ ${sym.fail} ${c.error(event.message ?? "Stream error")}`);
2746
3113
  if (thinkingSpinner.isSpinning) thinkingSpinner.stop();
2747
3114
  process.stdout.write("\n");
2748
3115
  if (localToolsCfg && agentContent) {
2749
- await handleLocalToolLoop(agentContent, localToolsCfg, client, agentId, sessionId, appendSessionLog);
3116
+ await handleLocalToolLoop(agentContent, localToolsCfg, client, agentId2, sessionId, appendSessionLog, !!opts.computer);
2750
3117
  }
2751
3118
  } catch (err) {
2752
3119
  process.stdout.write("\n");
@@ -2754,8 +3121,8 @@ ${sym.fail} ${c.error(event.message ?? "Stream error")}`);
2754
3121
  }
2755
3122
  }
2756
3123
  console.log();
2757
- readline4.cursorTo(process.stdout, 0);
2758
- readline4.clearLine(process.stdout, 0);
3124
+ readline3.cursorTo(process.stdout, 0);
3125
+ readline3.clearLine(process.stdout, 0);
2759
3126
  rl.resume();
2760
3127
  rl.prompt();
2761
3128
  });
@@ -2775,7 +3142,7 @@ Session preserved. Resume with: agc chat --resume ${sessionId}`));
2775
3142
  });
2776
3143
  }
2777
3144
  var MAX_TOOL_DEPTH = 10;
2778
- async function handleLocalToolLoop(agentText, cfg, client, agentId, sessionId, appendLog, depth = 0) {
3145
+ async function handleLocalToolLoop(agentText, cfg, client, agentId2, sessionId, appendLog, computerEnabled = false, depth = 0) {
2779
3146
  if (depth >= MAX_TOOL_DEPTH) {
2780
3147
  console.log(c.dim(`
2781
3148
  [local] Max tool depth reached (${MAX_TOOL_DEPTH}). Stopping tool loop.
@@ -2798,11 +3165,13 @@ async function handleLocalToolLoop(agentText, cfg, client, agentId, sessionId, a
2798
3165
  }
2799
3166
  const elapsed = ((Date.now() - startMs) / 1e3).toFixed(1);
2800
3167
  const preview = toolOk ? toolResultPreview(toolCall.tool, result) : "";
2801
- readline4.cursorTo(process.stdout, 0);
2802
- readline4.clearLine(process.stdout, 0);
3168
+ readline3.cursorTo(process.stdout, 0);
3169
+ readline3.clearLine(process.stdout, 0);
2803
3170
  const previewPart = preview ? ` ${c.dim(preview)}` : "";
2804
- process.stdout.write(` ${c.dim("\u2500")} ${c.bold(toolCall.tool)}${argStr ? " " + c.dim(argStr) : ""} ${toolOk ? sym.ok : sym.fail}${previewPart} ${c.dim("(" + elapsed + "s)")}
2805
- `);
3171
+ process.stdout.write(
3172
+ ` ${c.dim("\u2500")} ${c.bold(toolCall.tool)}${argStr ? " " + c.dim(argStr) : ""} ${toolOk ? sym.ok : sym.fail}${previewPart} ${c.dim("(" + elapsed + "s)")}
3173
+ `
3174
+ );
2806
3175
  const resultMsg = `[Tool result: ${toolCall.tool}]
2807
3176
  \`\`\`
2808
3177
  ${result}
@@ -2820,9 +3189,10 @@ ${result}
2820
3189
  let loopToolName = "";
2821
3190
  let loopToolStartMs = 0;
2822
3191
  for await (const evt of client.agents.stream({
2823
- agentId,
3192
+ agentId: agentId2,
2824
3193
  sessionId,
2825
- messages: [{ role: "user", content: resultMsg }]
3194
+ messages: [{ role: "user", content: resultMsg }],
3195
+ ...computerEnabled && { computerRequest: { enabled: true } }
2826
3196
  })) {
2827
3197
  if (evt.type === "token") {
2828
3198
  const tok = evt.content ?? "";
@@ -2835,8 +3205,8 @@ ${result}
2835
3205
  process.stdout.write(` ${c.dim("\u2500")} ${c.bold(loopToolName)}`);
2836
3206
  } else if (evt.type === "toolEnd") {
2837
3207
  const elapsed2 = ((Date.now() - loopToolStartMs) / 1e3).toFixed(1);
2838
- readline4.cursorTo(process.stdout, 0);
2839
- readline4.clearLine(process.stdout, 0);
3208
+ readline3.cursorTo(process.stdout, 0);
3209
+ readline3.clearLine(process.stdout, 0);
2840
3210
  process.stdout.write(` ${c.dim("\u2500")} ${c.bold(loopToolName)} ${sym.ok} ${c.dim("(" + elapsed2 + "s)")}
2841
3211
  `);
2842
3212
  process.stdout.write(c.primary("agent") + c.dim(" \u203A "));
@@ -2846,7 +3216,12 @@ ${result}
2846
3216
  process.stdout.write(txt);
2847
3217
  followContent += txt;
2848
3218
  }
2849
- appendLog(sessionId, { type: "message", role: "assistant", content: followContent, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
3219
+ appendLog(sessionId, {
3220
+ type: "message",
3221
+ role: "assistant",
3222
+ content: followContent,
3223
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
3224
+ });
2850
3225
  break;
2851
3226
  } else if (evt.type === "error") {
2852
3227
  console.error(`
@@ -2860,7 +3235,7 @@ ${sym.fail} ${c.error(evt.message ?? "Stream error")}`);
2860
3235
  console.error(`${sym.fail} ${c.error(err?.message ?? String(err))}`);
2861
3236
  return;
2862
3237
  }
2863
- await handleLocalToolLoop(followContent, cfg, client, agentId, sessionId, appendLog, depth + 1);
3238
+ await handleLocalToolLoop(followContent, cfg, client, agentId2, sessionId, appendLog, computerEnabled, depth + 1);
2864
3239
  }
2865
3240
  function truncate(s, max) {
2866
3241
  const str = String(s ?? "");
@@ -2949,9 +3324,9 @@ function extractText(payload) {
2949
3324
  }
2950
3325
 
2951
3326
  // src/commands/mcp.ts
2952
- var import_commander9 = require("commander");
3327
+ var import_commander10 = require("commander");
2953
3328
  function mcpCommand() {
2954
- const cmd = new import_commander9.Command("mcp").description("Manage MCP (Model Context Protocol) servers");
3329
+ const cmd = new import_commander10.Command("mcp").description("Manage MCP (Model Context Protocol) servers");
2955
3330
  cmd.command("list").description("List MCP servers for the current initiator").option("--agent <agentId>", "List servers owned by an agent instead of the user").option("--json", "Output as JSON").action(async (opts) => {
2956
3331
  const cfg = loadConfig();
2957
3332
  if (!cfg.initiator && !opts.agent) {
@@ -3249,9 +3624,9 @@ ${sym.ok} MCP server registered`);
3249
3624
  }
3250
3625
 
3251
3626
  // src/commands/skills.ts
3252
- var import_commander10 = require("commander");
3627
+ var import_commander11 = require("commander");
3253
3628
  function skillsCommand() {
3254
- const cmd = new import_commander10.Command("skills").description("Discover and manage skills");
3629
+ const cmd = new import_commander11.Command("skills").description("Discover and manage skills");
3255
3630
  cmd.command("list").description("List available skills").option("--owner <id>", "Filter by owner ID").option("--platform", "Show platform-only skills").option("--json", "Output as JSON").action(async (opts) => {
3256
3631
  const spinner = spin("Fetching skills\u2026");
3257
3632
  try {
@@ -3474,8 +3849,8 @@ function skillsCommand() {
3474
3849
  });
3475
3850
  cmd.command("delete <slug>").description("Permanently delete a skill").option("--yes", "Skip confirmation prompt").option("--json", "Output result as JSON").action(async (slug, opts) => {
3476
3851
  if (!opts.yes) {
3477
- const readline5 = await import("readline");
3478
- const rl = readline5.createInterface({ input: process.stdin, output: process.stdout });
3852
+ const readline4 = await import("readline");
3853
+ const rl = readline4.createInterface({ input: process.stdin, output: process.stdout });
3479
3854
  const answer = await new Promise(
3480
3855
  (resolve2) => rl.question(c.warn(`Delete skill "${slug}"? This cannot be undone. [y/N] `), resolve2)
3481
3856
  );
@@ -3502,24 +3877,24 @@ function skillsCommand() {
3502
3877
  }
3503
3878
 
3504
3879
  // src/commands/wallet.ts
3505
- var import_commander11 = require("commander");
3880
+ var import_commander12 = require("commander");
3506
3881
  function walletCommand() {
3507
- const cmd = new import_commander11.Command("wallet").description("Manage agent wallets");
3882
+ const cmd = new import_commander12.Command("wallet").description("Manage agent wallets");
3508
3883
  cmd.command("list").description("List all wallets for an agent").option("--agent <agentId>", "Agent ID (or use defaultAgentId from config)").option("--json", "Output as JSON").action(async (opts) => {
3509
3884
  const cfg = loadConfig();
3510
- const agentId = opts.agent ?? cfg.defaultAgentId;
3511
- if (!agentId) {
3885
+ const agentId2 = opts.agent ?? cfg.defaultAgentId;
3886
+ if (!agentId2) {
3512
3887
  console.error(c.error("Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`"));
3513
3888
  process.exit(1);
3514
3889
  }
3515
3890
  const spinner = spin("Fetching wallets\u2026");
3516
3891
  try {
3517
3892
  const client = makeClient();
3518
- const wallets = await client.wallets.list(agentId);
3893
+ const wallets = await client.wallets.list(agentId2);
3519
3894
  spinner.stop();
3520
3895
  if (opts.json) return jsonOut(wallets);
3521
3896
  const list = wallets?.data ?? wallets ?? [];
3522
- section(`Wallets for agent ${agentId.slice(0, 8)}\u2026 (${list.length})`);
3897
+ section(`Wallets for agent ${agentId2.slice(0, 8)}\u2026 (${list.length})`);
3523
3898
  table(
3524
3899
  list.map((w) => ({
3525
3900
  ID: w.id.slice(0, 8) + "\u2026",
@@ -3539,19 +3914,19 @@ function walletCommand() {
3539
3914
  });
3540
3915
  cmd.command("show").description("Show the agent's primary wallet address").option("--agent <agentId>", "Agent ID (or use defaultAgentId from config)").option("--json", "Output as JSON").action(async (opts) => {
3541
3916
  const cfg = loadConfig();
3542
- const agentId = opts.agent ?? cfg.defaultAgentId;
3543
- if (!agentId) {
3917
+ const agentId2 = opts.agent ?? cfg.defaultAgentId;
3918
+ if (!agentId2) {
3544
3919
  console.error(c.error("Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`"));
3545
3920
  process.exit(1);
3546
3921
  }
3547
3922
  const spinner = spin("Fetching primary wallet\u2026");
3548
3923
  try {
3549
3924
  const client = makeClient();
3550
- const wallet = await client.wallets.primary(agentId);
3925
+ const wallet = await client.wallets.primary(agentId2);
3551
3926
  spinner.stop();
3552
3927
  if (!wallet) {
3553
- console.log(c.warn(` No wallet found for agent ${agentId}`));
3554
- console.log(c.dim(` Run: agc wallet create --agent ${agentId}`));
3928
+ console.log(c.warn(` No wallet found for agent ${agentId2}`));
3929
+ console.log(c.dim(` Run: agc wallet create --agent ${agentId2}`));
3555
3930
  return;
3556
3931
  }
3557
3932
  const w = wallet?.data ?? wallet;
@@ -3572,8 +3947,8 @@ function walletCommand() {
3572
3947
  });
3573
3948
  cmd.command("balance").description("Show the agent's wallet USDC and ETH balance").option("--agent <agentId>", "Agent ID (or use defaultAgentId from config)").option("--wallet <walletId>", "Specific wallet ID (defaults to primary)").option("--json", "Output as JSON").action(async (opts) => {
3574
3949
  const cfg = loadConfig();
3575
- const agentId = opts.agent ?? cfg.defaultAgentId;
3576
- if (!agentId) {
3950
+ const agentId2 = opts.agent ?? cfg.defaultAgentId;
3951
+ if (!agentId2) {
3577
3952
  console.error(c.error("Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`"));
3578
3953
  process.exit(1);
3579
3954
  }
@@ -3582,11 +3957,11 @@ function walletCommand() {
3582
3957
  const client = makeClient();
3583
3958
  let walletId = opts.wallet;
3584
3959
  if (!walletId) {
3585
- const primary = await client.wallets.primary(agentId);
3960
+ const primary = await client.wallets.primary(agentId2);
3586
3961
  const w = primary?.data ?? primary;
3587
3962
  if (!w) {
3588
3963
  spinner.stop();
3589
- console.log(c.warn(` No wallet found. Run: agc wallet create --agent ${agentId}`));
3964
+ console.log(c.warn(` No wallet found. Run: agc wallet create --agent ${agentId2}`));
3590
3965
  return;
3591
3966
  }
3592
3967
  walletId = w.id;
@@ -3613,8 +3988,8 @@ function walletCommand() {
3613
3988
  });
3614
3989
  cmd.command("create").description("Create a new wallet for an agent").option("--agent <agentId>", "Agent ID (or use defaultAgentId from config)").option("--type <type>", "Wallet type: eoa | external (default: eoa)", "eoa").option("--label <label>", "Wallet label (default: Primary)", "Primary").option("--address <address>", "For --type external: owner-provided address").option("--json", "Output as JSON").action(async (opts) => {
3615
3990
  const cfg = loadConfig();
3616
- const agentId = opts.agent ?? cfg.defaultAgentId;
3617
- if (!agentId) {
3991
+ const agentId2 = opts.agent ?? cfg.defaultAgentId;
3992
+ if (!agentId2) {
3618
3993
  console.error(c.error("Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`"));
3619
3994
  process.exit(1);
3620
3995
  }
@@ -3626,7 +4001,7 @@ function walletCommand() {
3626
4001
  try {
3627
4002
  const client = makeClient();
3628
4003
  const wallet = await client.wallets.create({
3629
- agentId,
4004
+ agentId: agentId2,
3630
4005
  walletType: opts.type,
3631
4006
  label: opts.label,
3632
4007
  externalAddress: opts.address
@@ -3734,9 +4109,9 @@ function chainName(chainId) {
3734
4109
  }
3735
4110
 
3736
4111
  // src/commands/models.ts
3737
- var import_commander12 = require("commander");
4112
+ var import_commander13 = require("commander");
3738
4113
  function modelsCommand() {
3739
- const cmd = new import_commander12.Command("models").description("List available LLM models");
4114
+ const cmd = new import_commander13.Command("models").description("List available LLM models");
3740
4115
  cmd.command("ls").description("List all available models grouped by provider").option("--provider <name>", "Filter by provider (openai, anthropic, google, mistral, groq, ollama)").option("--json", "Output as JSON").action(async (opts) => {
3741
4116
  const client = makeClient();
3742
4117
  const spinner = spin("Fetching models\u2026");
@@ -3779,27 +4154,27 @@ ${c.bold(provider.toUpperCase())}`);
3779
4154
  }
3780
4155
 
3781
4156
  // src/commands/memory.ts
3782
- var import_commander13 = require("commander");
4157
+ var import_commander14 = require("commander");
3783
4158
  function memoryCommand() {
3784
- const cmd = new import_commander13.Command("memory").description("View and manage agent memories");
4159
+ const cmd = new import_commander14.Command("memory").description("View and manage agent memories");
3785
4160
  cmd.command("list").description("List memories for an agent").option("--agent <agentId>", "Agent ID (defaults to configured agent)").option("--type <type>", "Filter by type: episodic | semantic | procedural").option("--limit <n>", "Max results", "50").option("--json", "Output as JSON").action(async (opts) => {
3786
4161
  const cfg = loadConfig();
3787
- const agentId = opts.agent ?? cfg.defaultAgentId;
3788
- if (!agentId) {
4162
+ const agentId2 = opts.agent ?? cfg.defaultAgentId;
4163
+ if (!agentId2) {
3789
4164
  console.error(c.error("Specify --agent <agentId> or set defaultAgentId"));
3790
4165
  process.exit(1);
3791
4166
  }
3792
4167
  const spinner = spin("Fetching memories\u2026");
3793
4168
  try {
3794
4169
  const client = makeClient();
3795
- const res = await client.memory.list(agentId, {
4170
+ const res = await client.memory.list(agentId2, {
3796
4171
  type: opts.type,
3797
4172
  limit: parseInt(opts.limit, 10)
3798
4173
  });
3799
4174
  const memories = res?.data ?? res ?? [];
3800
4175
  spinner.stop();
3801
4176
  if (opts.json) return jsonOut(memories);
3802
- section(`Memories for ${agentId.slice(0, 12)}\u2026 (${memories.length})`);
4177
+ section(`Memories for ${agentId2.slice(0, 12)}\u2026 (${memories.length})`);
3803
4178
  if (memories.length === 0) {
3804
4179
  console.log(c.dim(" No memories yet"));
3805
4180
  return;
@@ -3821,15 +4196,15 @@ function memoryCommand() {
3821
4196
  });
3822
4197
  cmd.command("stats").description("Show memory statistics for an agent").option("--agent <agentId>", "Agent ID").option("--json", "Output as JSON").action(async (opts) => {
3823
4198
  const cfg = loadConfig();
3824
- const agentId = opts.agent ?? cfg.defaultAgentId;
3825
- if (!agentId) {
4199
+ const agentId2 = opts.agent ?? cfg.defaultAgentId;
4200
+ if (!agentId2) {
3826
4201
  console.error(c.error("Specify --agent <agentId>"));
3827
4202
  process.exit(1);
3828
4203
  }
3829
4204
  const spinner = spin("Fetching stats\u2026");
3830
4205
  try {
3831
4206
  const client = makeClient();
3832
- const res = await client.memory.stats(agentId);
4207
+ const res = await client.memory.stats(agentId2);
3833
4208
  const stats = res?.data ?? res;
3834
4209
  spinner.stop();
3835
4210
  if (opts.json) return jsonOut(stats);
@@ -3889,15 +4264,15 @@ ${sym.ok} Memory ${c.id(memoryId)} deleted`);
3889
4264
  });
3890
4265
  cmd.command("search <query>").description("Semantic search over agent memories").option("--agent <agentId>", "Agent ID").option("--limit <n>", "Max results", "10").option("--json", "Output as JSON").action(async (query, opts) => {
3891
4266
  const cfg = loadConfig();
3892
- const agentId = opts.agent ?? cfg.defaultAgentId;
3893
- if (!agentId) {
4267
+ const agentId2 = opts.agent ?? cfg.defaultAgentId;
4268
+ if (!agentId2) {
3894
4269
  console.error(c.error("Specify --agent <agentId>"));
3895
4270
  process.exit(1);
3896
4271
  }
3897
4272
  const spinner = spin("Searching memories\u2026");
3898
4273
  try {
3899
4274
  const client = makeClient();
3900
- const res = await client.memory.retrieve(agentId, query, parseInt(opts.limit, 10));
4275
+ const res = await client.memory.retrieve(agentId2, query, parseInt(opts.limit, 10));
3901
4276
  const memories = res?.data ?? res ?? [];
3902
4277
  spinner.stop();
3903
4278
  if (opts.json) return jsonOut(memories);
@@ -3921,9 +4296,9 @@ ${sym.ok} Memory ${c.id(memoryId)} deleted`);
3921
4296
  }
3922
4297
 
3923
4298
  // src/commands/usage.ts
3924
- var import_commander14 = require("commander");
4299
+ var import_commander15 = require("commander");
3925
4300
  function usageCommand() {
3926
- const cmd = new import_commander14.Command("usage").description("View token usage and cost by agent");
4301
+ const cmd = new import_commander15.Command("usage").description("View token usage and cost by agent");
3927
4302
  cmd.command("agents").description("Show usage summary for all your agents").option("--owner <address>", "Owner address (defaults to configured initiator)").option("--from <date>", "Start date (ISO, e.g. 2025-01-01)").option("--to <date>", "End date (ISO)").option("--json", "Output as JSON").action(async (opts) => {
3928
4303
  const cfg = loadConfig();
3929
4304
  const owner = opts.owner ?? cfg.initiator;
@@ -3986,18 +4361,18 @@ function usageCommand() {
3986
4361
  process.exit(1);
3987
4362
  }
3988
4363
  });
3989
- cmd.command("agent <agentId>").description("Show detailed usage for a specific agent").option("--from <date>", "Start date (ISO)").option("--to <date>", "End date (ISO)").option("--json", "Output as JSON").action(async (agentId, opts) => {
4364
+ cmd.command("agent <agentId>").description("Show detailed usage for a specific agent").option("--from <date>", "Start date (ISO)").option("--to <date>", "End date (ISO)").option("--json", "Output as JSON").action(async (agentId2, opts) => {
3990
4365
  const spinner = spin("Fetching usage\u2026");
3991
4366
  try {
3992
4367
  const client = makeClient();
3993
- const res = await client.usage.getAgentUsage(agentId, {
4368
+ const res = await client.usage.getAgentUsage(agentId2, {
3994
4369
  from: opts.from,
3995
4370
  to: opts.to
3996
4371
  });
3997
4372
  const data = res?.data ?? res;
3998
4373
  spinner.stop();
3999
4374
  if (opts.json) return jsonOut(data);
4000
- section(`Usage \u2014 ${agentId.slice(0, 12)}\u2026`);
4375
+ section(`Usage \u2014 ${agentId2.slice(0, 12)}\u2026`);
4001
4376
  detail([
4002
4377
  ["Calls", (data.callCount ?? 0).toLocaleString()],
4003
4378
  ["Input tokens", (data.totalInputTokens ?? 0).toLocaleString()],
@@ -4014,8 +4389,114 @@ function usageCommand() {
4014
4389
  return cmd;
4015
4390
  }
4016
4391
 
4392
+ // src/commands/billing.ts
4393
+ var import_commander16 = require("commander");
4394
+ function creditsCommand() {
4395
+ const cmd = new import_commander16.Command("credits").description("View your credit balance and ledger");
4396
+ cmd.command("balance", { isDefault: true }).description("Show your current credit balance").option("--json", "Output as JSON").action(async (opts) => {
4397
+ const spinner = spin("Fetching balance\u2026");
4398
+ try {
4399
+ const client = makeClient();
4400
+ const res = await client.credits.balance();
4401
+ spinner.stop();
4402
+ if (opts.json) return jsonOut(res.data);
4403
+ section("Credits");
4404
+ detail([["Balance", String(res?.data?.balance ?? 0)]]);
4405
+ } catch (e) {
4406
+ spinner.stop();
4407
+ console.error(c.error(e.message));
4408
+ process.exit(1);
4409
+ }
4410
+ });
4411
+ cmd.command("ledger").description("Show recent credit ledger entries").option("--limit <n>", "Max entries", "20").option("--json", "Output as JSON").action(async (opts) => {
4412
+ const spinner = spin("Fetching ledger\u2026");
4413
+ try {
4414
+ const client = makeClient();
4415
+ const res = await client.credits.ledger({ limit: Number(opts.limit) });
4416
+ spinner.stop();
4417
+ const rows = res?.data ?? [];
4418
+ if (opts.json) return jsonOut(rows);
4419
+ section("Credit ledger");
4420
+ for (const e of rows) {
4421
+ const sign = e.amount >= 0 ? "+" : "";
4422
+ console.log(
4423
+ `${c.dim(new Date(e.createdAt).toLocaleString())} ${sign}${e.amount} ${e.description || e.eventType}`
4424
+ );
4425
+ }
4426
+ if (!rows.length) console.log(c.dim("No entries."));
4427
+ } catch (e) {
4428
+ spinner.stop();
4429
+ console.error(c.error(e.message));
4430
+ process.exit(1);
4431
+ }
4432
+ });
4433
+ return cmd;
4434
+ }
4435
+ function billingCommand() {
4436
+ const cmd = new import_commander16.Command("billing").description("Manage your subscription and top-ups");
4437
+ cmd.command("status", { isDefault: true }).description("Show your current plan and entitlements").option("--json", "Output as JSON").action(async (opts) => {
4438
+ const spinner = spin("Fetching plan\u2026");
4439
+ try {
4440
+ const client = makeClient();
4441
+ const res = await client.billing.subscription();
4442
+ spinner.stop();
4443
+ if (opts.json) return jsonOut(res.data);
4444
+ const d = res.data;
4445
+ section("Subscription");
4446
+ detail([
4447
+ ["Plan", `${d.planName} (${d.planKey})`],
4448
+ ["Status", d.status],
4449
+ ["Monthly credits", String(d.monthlyCredits)],
4450
+ ["Computer use", d.entitlements?.computerUse ? "yes" : "no"],
4451
+ [
4452
+ "Renews",
4453
+ d.currentPeriodEnd ? new Date(d.currentPeriodEnd).toLocaleDateString() : void 0
4454
+ ]
4455
+ ]);
4456
+ } catch (e) {
4457
+ spinner.stop();
4458
+ console.error(c.error(e.message));
4459
+ process.exit(1);
4460
+ }
4461
+ });
4462
+ cmd.command("upgrade <plan>").description("Start a checkout to upgrade (plus | pro | max)").action(async (plan) => {
4463
+ try {
4464
+ const client = makeClient();
4465
+ const res = await client.billing.subscribe(plan);
4466
+ const url = res?.data?.url;
4467
+ if (!url) {
4468
+ console.error(c.error("Could not create checkout session"));
4469
+ process.exit(1);
4470
+ }
4471
+ console.log(c.dim("Opening checkout in your browser:"));
4472
+ console.log(url);
4473
+ await openBrowser(url);
4474
+ } catch (e) {
4475
+ console.error(c.error(e.message));
4476
+ process.exit(1);
4477
+ }
4478
+ });
4479
+ cmd.command("topup <pack>").description("Buy a one-time credit pack (small | medium | large)").action(async (pack) => {
4480
+ try {
4481
+ const client = makeClient();
4482
+ const res = await client.billing.topup(pack);
4483
+ const url = res?.data?.url;
4484
+ if (!url) {
4485
+ console.error(c.error("Could not create checkout session"));
4486
+ process.exit(1);
4487
+ }
4488
+ console.log(url);
4489
+ await openBrowser(url);
4490
+ } catch (e) {
4491
+ console.error(c.error(e.message));
4492
+ process.exit(1);
4493
+ }
4494
+ });
4495
+ return cmd;
4496
+ }
4497
+
4017
4498
  // src/commands/logs.ts
4018
- var import_commander15 = require("commander");
4499
+ var import_commander17 = require("commander");
4019
4500
  var STATUS_COLOR = {
4020
4501
  success: (s) => c.bold(s),
4021
4502
  error: (s) => c.error(s),
@@ -4025,25 +4506,26 @@ function colorStatus(status) {
4025
4506
  return (STATUS_COLOR[status] ?? c.dim)(status);
4026
4507
  }
4027
4508
  function logsCommand() {
4028
- const cmd = new import_commander15.Command("logs").description("View agent activity logs");
4509
+ const cmd = new import_commander17.Command("logs").description("View agent activity logs");
4029
4510
  cmd.command("list").alias("ls").description("List recent log entries for an agent").option("--agent <agentId>", "Agent ID (defaults to configured agent)").option("--session <sessionId>", "Filter by session ID").option("--status <status>", "Filter: success | error | warning").option("--limit <n>", "Max entries to show", "50").option("--json", "Output as JSON").action(async (opts) => {
4030
4511
  const cfg = loadConfig();
4031
- const agentId = opts.agent ?? cfg.defaultAgentId;
4032
- if (!agentId) {
4512
+ const agentId2 = opts.agent ?? cfg.defaultAgentId;
4513
+ if (!agentId2) {
4033
4514
  console.error(c.error("Specify --agent <agentId> or set defaultAgentId"));
4034
4515
  process.exit(1);
4035
4516
  }
4036
4517
  const spinner = spin("Fetching logs\u2026");
4037
4518
  try {
4038
4519
  const client = makeClient();
4039
- const qs = new URLSearchParams({ limit: opts.limit });
4040
- if (opts.session) qs.set("sessionId", opts.session);
4041
- const res = await client.request("GET", `/v1/logs/agents/${agentId}?${qs}`);
4520
+ const res = await client.logs.list(agentId2, {
4521
+ limit: Number(opts.limit),
4522
+ sessionId: opts.session
4523
+ });
4042
4524
  let logs = res?.data ?? res ?? [];
4043
4525
  if (opts.status) logs = logs.filter((l) => l.status === opts.status);
4044
4526
  spinner.stop();
4045
4527
  if (opts.json) return jsonOut(logs);
4046
- section(`Logs \u2014 ${agentId.slice(0, 12)}\u2026 (${logs.length})`);
4528
+ section(`Logs \u2014 ${agentId2.slice(0, 12)}\u2026 (${logs.length})`);
4047
4529
  if (logs.length === 0) {
4048
4530
  console.log(c.dim(" No logs yet"));
4049
4531
  return;
@@ -4061,26 +4543,28 @@ function logsCommand() {
4061
4543
  console.log("");
4062
4544
  });
4063
4545
  } catch (err) {
4064
- spin("").stop();
4546
+ spinner.stop();
4065
4547
  printError(err);
4066
4548
  process.exit(1);
4067
4549
  }
4068
4550
  });
4069
4551
  cmd.command("errors").description("Show only error log entries for an agent").option("--agent <agentId>", "Agent ID").option("--limit <n>", "Max entries", "20").option("--json", "Output as JSON").action(async (opts) => {
4070
4552
  const cfg = loadConfig();
4071
- const agentId = opts.agent ?? cfg.defaultAgentId;
4072
- if (!agentId) {
4553
+ const agentId2 = opts.agent ?? cfg.defaultAgentId;
4554
+ if (!agentId2) {
4073
4555
  console.error(c.error("Specify --agent <agentId>"));
4074
4556
  process.exit(1);
4075
4557
  }
4076
4558
  const spinner = spin("Fetching error logs\u2026");
4077
4559
  try {
4078
4560
  const client = makeClient();
4079
- const res = await client.request("GET", `/v1/logs/agents/${agentId}?limit=${opts.limit}`);
4561
+ const res = await client.logs.list(agentId2, {
4562
+ limit: Number(opts.limit)
4563
+ });
4080
4564
  const errors = (res?.data ?? []).filter((l) => l.status === "error");
4081
4565
  spinner.stop();
4082
4566
  if (opts.json) return jsonOut(errors);
4083
- section(`Errors \u2014 ${agentId.slice(0, 12)}\u2026 (${errors.length})`);
4567
+ section(`Errors \u2014 ${agentId2.slice(0, 12)}\u2026 (${errors.length})`);
4084
4568
  if (errors.length === 0) {
4085
4569
  console.log(`${sym.ok} No errors found`);
4086
4570
  return;
@@ -4099,33 +4583,783 @@ function logsCommand() {
4099
4583
  return cmd;
4100
4584
  }
4101
4585
 
4102
- // src/bin.ts
4103
- var CONFIG_FILE3 = (0, import_path5.join)((0, import_os4.homedir)(), ".agc", "config.json");
4104
- async function interactiveMenu() {
4105
- banner();
4106
- const cfg = loadConfig();
4107
- const isSetup = !!((cfg.accessToken || cfg.apiKey || cfg.sessionToken) && (cfg.userId || cfg.initiator));
4108
- if (!isSetup) {
4109
- console.log(c.bold(" Welcome to Agent Commons CLI!"));
4110
- console.log(c.dim(" Looks like this is your first time here \u2014 let's get you set up.\n"));
4111
- console.log(` ${sym.arrow} Running ${c.bold("agc login")} to configure your credentials\u2026
4112
- `);
4113
- runSubcommand(["login"]);
4586
+ // src/commands/computer.ts
4587
+ var import_commander18 = require("commander");
4588
+ var RESOURCE_PROFILES = [
4589
+ "starter",
4590
+ "standard",
4591
+ "performance",
4592
+ "gpu"
4593
+ ];
4594
+ var RESOURCE_MODES = ["fixed", "elastic"];
4595
+ function resolveAgentId(opts) {
4596
+ const agentId2 = opts.agent ?? loadConfig().defaultAgentId;
4597
+ if (!agentId2) {
4598
+ throw new Error(
4599
+ "Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`."
4600
+ );
4601
+ }
4602
+ return agentId2;
4603
+ }
4604
+ function unwrap(response) {
4605
+ return response?.data ?? response;
4606
+ }
4607
+ function displayComputer(computer) {
4608
+ if (!computer) {
4609
+ section("Persistent cloud computer");
4610
+ detail([
4611
+ ["Status", statusBadge("disabled")],
4612
+ ["Persistence", "persistent"],
4613
+ ["Computer ID", c.dim("(not provisioned)")]
4614
+ ]);
4615
+ console.log(c.dim(" Enable it with: agc computer enable --agent <agentId>"));
4114
4616
  return;
4115
4617
  }
4116
- console.log(
4117
- ` ${c.dim("Connected to")} ${c.primary(cfg.apiUrl)} ${c.dim("\xB7")} ${c.dim("Identity")} ${c.id((cfg.userId ?? cfg.initiator).slice(0, 8) + "\u2026" + (cfg.userId ?? cfg.initiator).slice(-4))}
4618
+ const wire = computer;
4619
+ const resources = computer.resources ?? {};
4620
+ const gpu = resources.gpu ?? (wire.gpuCount ? { count: wire.gpuCount, type: wire.gpuType } : null);
4621
+ const cpu = resources.vcpu ?? wire.cpuRequest ?? wire.cpuLimit;
4622
+ const memory = resources.memoryGiB != null ? `${resources.memoryGiB} GiB` : wire.memoryRequest ?? wire.memoryLimit;
4623
+ const storage = resources.storageGiB != null ? `${resources.storageGiB} GiB` : wire.storageLimit;
4624
+ section("Persistent cloud computer");
4625
+ detail([
4626
+ ["Computer ID", computer.computerId ? c.id(computer.computerId) : c.dim("(not provisioned)")],
4627
+ ["Enabled", computer.enabled === false ? "no" : c.success("yes")],
4628
+ ["Status", statusBadge(computer.status ?? "disabled")],
4629
+ ["Desired state", computer.desiredState ?? c.dim("n/a")],
4630
+ ["Persistence", computer.persistence ?? wire.lifecycle ?? "persistent"],
4631
+ ["Profile", computer.resourceProfile ?? c.dim("n/a")],
4632
+ ["Mode", computer.resourceMode ?? c.dim("n/a")],
4633
+ ["CPU", cpu != null ? String(cpu) : c.dim("n/a")],
4634
+ ["Memory", memory != null ? String(memory) : c.dim("n/a")],
4635
+ ["Storage", storage != null ? String(storage) : c.dim("n/a")],
4636
+ ["GPU", gpu?.count ? `${gpu.count} \xD7 ${gpu.type ?? "provider default"}` : "none"],
4637
+ ["Region", computer.region ?? c.dim("automatic")],
4638
+ ["Workspace", computer.workspaceRoot ?? c.dim("not mounted")],
4639
+ ["Last activity", computer.lastActivityAt ? relativeTime(computer.lastActivityAt) : c.dim("never")],
4640
+ ["Error", computer.errorMessage ?? void 0]
4641
+ ]);
4642
+ }
4643
+ function parseNumber(value, name, options) {
4644
+ if (value === void 0) return void 0;
4645
+ const parsed = Number(value);
4646
+ const minimum = options?.allowZero ? 0 : Number.MIN_VALUE;
4647
+ if (!Number.isFinite(parsed) || parsed < minimum || options?.integer && !Number.isInteger(parsed)) {
4648
+ const qualifier = options?.integer ? "whole number" : "number";
4649
+ throw new Error(`${name} must be a ${options?.allowZero ? "non-negative" : "positive"} ${qualifier}.`);
4650
+ }
4651
+ return parsed;
4652
+ }
4653
+ async function changeEnabled(agentId2, enabled, json) {
4654
+ const spinner = spin(`${enabled ? "Enabling" : "Disabling"} persistent cloud computer\u2026`);
4655
+ try {
4656
+ const response = await makeClient().agents.updateComputerConfig(agentId2, { enabled });
4657
+ const config = unwrap(response);
4658
+ spinner.stop();
4659
+ if (json) return jsonOut(config);
4660
+ console.log(`
4661
+ ${sym.ok} Persistent cloud computer ${enabled ? "enabled" : "disabled"} for agent ${c.id(agentId2)}`);
4662
+ if (enabled) {
4663
+ console.log(c.dim(` Wake it now with: agc computer wake --agent ${agentId2}`));
4664
+ }
4665
+ } catch (error) {
4666
+ spinner.stop();
4667
+ printError(error);
4668
+ process.exitCode = 1;
4669
+ }
4670
+ }
4671
+ async function lifecycleAction(action, agentId2, reason, json) {
4672
+ const verb = action === "wake" ? "Waking" : action === "sleep" ? "Sleeping" : "Restarting";
4673
+ const spinner = spin(`${verb} persistent cloud computer\u2026`);
4674
+ try {
4675
+ const client = makeClient();
4676
+ const response = action === "wake" ? await client.agents.wakeComputer(agentId2, reason ? { reason } : void 0) : action === "sleep" ? await client.agents.sleepComputer(agentId2, reason ? { reason } : void 0) : await client.agents.restartComputer(agentId2, reason ? { reason } : void 0);
4677
+ const computer = unwrap(response);
4678
+ spinner.stop();
4679
+ if (json) return jsonOut(computer);
4680
+ console.log(`
4681
+ ${sym.ok} Persistent cloud computer ${action === "sleep" ? "is sleeping" : action === "wake" ? "is awake" : "restarted"}`);
4682
+ displayComputer(computer);
4683
+ } catch (error) {
4684
+ spinner.stop();
4685
+ printError(error);
4686
+ process.exitCode = 1;
4687
+ }
4688
+ }
4689
+ function addAgentOption(command) {
4690
+ return command.option("--agent <agentId>", "Agent ID (defaults to configured agent)");
4691
+ }
4692
+ function computerCommand() {
4693
+ const command = new import_commander18.Command("computer").description("Manage an agent's one persistent cloud computer");
4694
+ addAgentOption(command.command("status").description("Show persistent cloud computer status")).option("--json", "Output as JSON").action(async (opts) => {
4695
+ let agentId2;
4696
+ try {
4697
+ agentId2 = resolveAgentId(opts);
4698
+ } catch (error) {
4699
+ printError(error);
4700
+ process.exitCode = 1;
4701
+ return;
4702
+ }
4703
+ const spinner = spin("Fetching persistent cloud computer\u2026");
4704
+ try {
4705
+ const computer = unwrap(await makeClient().agents.getComputer(agentId2));
4706
+ spinner.stop();
4707
+ if (opts.json) return jsonOut(computer);
4708
+ displayComputer(computer);
4709
+ } catch (error) {
4710
+ spinner.stop();
4711
+ printError(error);
4712
+ process.exitCode = 1;
4713
+ }
4714
+ });
4715
+ addAgentOption(command.command("enable").description("Enable a persistent cloud computer for an agent")).option("--json", "Output as JSON").action(async (opts) => {
4716
+ try {
4717
+ await changeEnabled(resolveAgentId(opts), true, !!opts.json);
4718
+ } catch (error) {
4719
+ printError(error);
4720
+ process.exitCode = 1;
4721
+ }
4722
+ });
4723
+ addAgentOption(command.command("disable").description("Disable the agent cloud computer")).option("--json", "Output as JSON").action(async (opts) => {
4724
+ try {
4725
+ await changeEnabled(resolveAgentId(opts), false, !!opts.json);
4726
+ } catch (error) {
4727
+ printError(error);
4728
+ process.exitCode = 1;
4729
+ }
4730
+ });
4731
+ for (const action of ["wake", "sleep", "restart"]) {
4732
+ const descriptions = {
4733
+ wake: "Wake the persistent cloud computer",
4734
+ sleep: "Sleep compute while preserving the persistent workspace",
4735
+ restart: "Restart the runtime while preserving the persistent workspace"
4736
+ };
4737
+ addAgentOption(command.command(action).description(descriptions[action])).option("--reason <text>", `Reason for the ${action}`).option("--json", "Output as JSON").action(async (opts) => {
4738
+ try {
4739
+ await lifecycleAction(action, resolveAgentId(opts), opts.reason, !!opts.json);
4740
+ } catch (error) {
4741
+ printError(error);
4742
+ process.exitCode = 1;
4743
+ }
4744
+ });
4745
+ }
4746
+ addAgentOption(command.command("resize").description("Resize the persistent cloud computer")).option("--profile <profile>", `Resource profile: ${RESOURCE_PROFILES.join(" | ")}`).option("--mode <mode>", `Resource mode: ${RESOURCE_MODES.join(" | ")}`).option("--vcpu <count>", "Requested virtual CPU count").option("--cpu <count>", "Alias for --vcpu").option("--memory <gib>", "Requested memory in GiB").option("--storage <gib>", "Requested persistent storage in GiB").option("--gpu-type <type>", "GPU type, such as nvidia-h100").option("--gpu-count <count>", "GPU count (0 removes GPU allocation)").option("--json", "Output as JSON").action(async (opts) => {
4747
+ let agentId2;
4748
+ let resize;
4749
+ try {
4750
+ agentId2 = resolveAgentId(opts);
4751
+ if (opts.profile && !RESOURCE_PROFILES.includes(opts.profile)) {
4752
+ throw new Error(`--profile must be one of: ${RESOURCE_PROFILES.join(", ")}.`);
4753
+ }
4754
+ if (opts.mode && !RESOURCE_MODES.includes(opts.mode)) {
4755
+ throw new Error(`--mode must be one of: ${RESOURCE_MODES.join(", ")}.`);
4756
+ }
4757
+ if (opts.vcpu !== void 0 && opts.cpu !== void 0) {
4758
+ throw new Error("Use either --vcpu or --cpu, not both.");
4759
+ }
4760
+ const vcpu = parseNumber(opts.vcpu ?? opts.cpu, "CPU");
4761
+ const memoryGiB = parseNumber(opts.memory, "Memory");
4762
+ const storageGiB = parseNumber(opts.storage, "Storage");
4763
+ const gpuCount = parseNumber(opts.gpuCount, "GPU count", { integer: true, allowZero: true });
4764
+ const resources = {
4765
+ ...vcpu !== void 0 && { vcpu },
4766
+ ...memoryGiB !== void 0 && { memoryGiB },
4767
+ ...storageGiB !== void 0 && { storageGiB },
4768
+ ...(gpuCount !== void 0 || opts.gpuType) && {
4769
+ gpu: { count: gpuCount ?? 1, ...opts.gpuType && { type: opts.gpuType } }
4770
+ }
4771
+ };
4772
+ resize = {
4773
+ ...opts.profile && { resourceProfile: opts.profile },
4774
+ ...opts.mode && { resourceMode: opts.mode },
4775
+ ...Object.keys(resources).length > 0 && { resources }
4776
+ };
4777
+ if (Object.keys(resize).length === 0) {
4778
+ throw new Error("Specify --profile, --mode, or at least one resource value.");
4779
+ }
4780
+ } catch (error) {
4781
+ printError(error);
4782
+ process.exitCode = 1;
4783
+ return;
4784
+ }
4785
+ const spinner = spin("Resizing persistent cloud computer\u2026");
4786
+ try {
4787
+ const computer = unwrap(await makeClient().agents.resizeComputer(agentId2, resize));
4788
+ spinner.stop();
4789
+ if (opts.json) return jsonOut(computer);
4790
+ console.log(`
4791
+ ${sym.ok} Persistent cloud computer resize requested`);
4792
+ displayComputer(computer);
4793
+ } catch (error) {
4794
+ spinner.stop();
4795
+ printError(error);
4796
+ process.exitCode = 1;
4797
+ }
4798
+ });
4799
+ addAgentOption(
4800
+ command.command("exec").description("Run a command in the persistent cloud computer").argument("<command...>", "Command and arguments to run")
4801
+ ).option("--cwd <path>", "Working directory").option("--timeout <seconds>", "Command timeout in seconds", "120").option("--json", "Output as JSON").action(async (commandParts, opts) => {
4802
+ let agentId2;
4803
+ let timeoutSeconds;
4804
+ try {
4805
+ agentId2 = resolveAgentId(opts);
4806
+ timeoutSeconds = parseNumber(opts.timeout, "Timeout");
4807
+ } catch (error) {
4808
+ printError(error);
4809
+ process.exitCode = 1;
4810
+ return;
4811
+ }
4812
+ const spinner = spin("Running command in persistent cloud computer\u2026");
4813
+ try {
4814
+ const result = unwrap(await makeClient().agents.execComputer(agentId2, {
4815
+ command: commandParts.join(" "),
4816
+ ...opts.cwd && { cwd: opts.cwd },
4817
+ ...timeoutSeconds !== void 0 && { timeoutSeconds }
4818
+ }));
4819
+ spinner.stop();
4820
+ if (opts.json) return jsonOut(result);
4821
+ const stdout = result?.stdout ?? result?.output ?? result?.result ?? "";
4822
+ const stderr = result?.stderr ?? "";
4823
+ if (stdout) process.stdout.write(String(stdout).replace(/\n?$/, "\n"));
4824
+ if (stderr) process.stderr.write(c.error(String(stderr).replace(/\n?$/, "\n")));
4825
+ const exitCode = result?.exitCode ?? result?.exit_code;
4826
+ if (exitCode !== void 0 && exitCode !== 0) process.exitCode = Number(exitCode);
4827
+ } catch (error) {
4828
+ spinner.stop();
4829
+ printError(error);
4830
+ process.exitCode = 1;
4831
+ }
4832
+ });
4833
+ addAgentOption(command.command("events").description("List recent persistent cloud computer events")).option("--limit <count>", "Maximum events", "50").option("--json", "Output as JSON").action(async (opts) => {
4834
+ let agentId2;
4835
+ let limit;
4836
+ try {
4837
+ agentId2 = resolveAgentId(opts);
4838
+ limit = parseNumber(opts.limit, "Limit", { integer: true });
4839
+ } catch (error) {
4840
+ printError(error);
4841
+ process.exitCode = 1;
4842
+ return;
4843
+ }
4844
+ const spinner = spin("Fetching persistent cloud computer events\u2026");
4845
+ try {
4846
+ const events = unwrap(await makeClient().agents.listComputerEvents(agentId2, limit));
4847
+ spinner.stop();
4848
+ if (opts.json) return jsonOut(events);
4849
+ section(`Cloud computer events (${events.length})`);
4850
+ table(
4851
+ events.map((event) => ({
4852
+ Event: event.eventType ?? "",
4853
+ Summary: event.summary ?? "",
4854
+ Actor: event.actorType ?? "",
4855
+ When: event.createdAt ? relativeTime(event.createdAt) : ""
4856
+ })),
4857
+ ["Event", "Summary", "Actor", "When"]
4858
+ );
4859
+ } catch (error) {
4860
+ spinner.stop();
4861
+ printError(error);
4862
+ process.exitCode = 1;
4863
+ }
4864
+ });
4865
+ return command;
4866
+ }
4867
+
4868
+ // src/commands/library.ts
4869
+ var import_commander19 = require("commander");
4870
+ var import_fs7 = require("fs");
4871
+ var import_path5 = require("path");
4872
+ function libraryCommand() {
4873
+ const command = new import_commander19.Command("library").alias("files").description("Upload, find, and manage files in your Commons library");
4874
+ command.command("list", { isDefault: true }).alias("ls").description("List library items").option("--query <text>", "Search names and descriptions").option("--source <source>", "Filter by source").option("--session <sessionId>", "Filter by session").option("--favorites", "Show favorites only").option("--limit <n>", "Maximum items", "50").option("--json", "Output as JSON").action(async (opts) => {
4875
+ const spinner = spin("Fetching your library\u2026");
4876
+ try {
4877
+ const result = await makeClient().library.list({
4878
+ query: opts.query,
4879
+ source: opts.source,
4880
+ sessionId: opts.session,
4881
+ favorite: opts.favorites ? true : void 0,
4882
+ limit: Number(opts.limit)
4883
+ });
4884
+ spinner.stop();
4885
+ if (opts.json) return jsonOut(result);
4886
+ section(`Library (${result.data.length})`);
4887
+ table(
4888
+ result.data.map((item) => ({
4889
+ ID: String(item.itemId ?? item.fileId).slice(0, 10) + "\u2026",
4890
+ Name: item.name ?? item.originalName ?? "(untitled)",
4891
+ Type: item.mimeType ?? "",
4892
+ Size: typeof item.size === "number" ? `${Math.ceil(item.size / 1024)} KB` : "",
4893
+ Favorite: item.isFavorite ? "\u2605" : "",
4894
+ Created: item.createdAt ? relativeTime(item.createdAt) : ""
4895
+ })),
4896
+ ["ID", "Name", "Type", "Size", "Favorite", "Created"]
4897
+ );
4898
+ } catch (error) {
4899
+ spinner.stop();
4900
+ printError(error);
4901
+ process.exit(1);
4902
+ }
4903
+ });
4904
+ command.command("get <itemId>").description("Show library item details").option("--json", "Output as JSON").action(async (itemId, opts) => {
4905
+ const spinner = spin("Fetching library item\u2026");
4906
+ try {
4907
+ const result = await makeClient().library.get(itemId);
4908
+ spinner.stop();
4909
+ if (opts.json) return jsonOut(result.data);
4910
+ const item = result.data;
4911
+ detail([
4912
+ ["Item ID", c.id(String(item.itemId ?? item.fileId))],
4913
+ ["Name", item.name ?? item.originalName ?? "(untitled)"],
4914
+ ["Description", item.description ?? ""],
4915
+ ["Type", item.mimeType ?? ""],
4916
+ ["Storage", item.storageProvider ?? ""],
4917
+ ["Favorite", item.isFavorite ? "yes" : "no"],
4918
+ ["Created", item.createdAt ? relativeTime(item.createdAt) : ""]
4919
+ ]);
4920
+ } catch (error) {
4921
+ spinner.stop();
4922
+ printError(error);
4923
+ process.exit(1);
4924
+ }
4925
+ });
4926
+ command.command("upload <paths...>").description("Upload one or more local files").option("--agent <agentId>", "Associate files with an agent").option("--session <sessionId>", "Associate files with a session").option("--storage <provider>", "Storage provider: s3 | ipfs").option("--json", "Output as JSON").action(async (paths, opts) => {
4927
+ const spinner = spin(`Uploading ${paths.length} file${paths.length === 1 ? "" : "s"}\u2026`);
4928
+ try {
4929
+ if (opts.storage && opts.storage !== "s3" && opts.storage !== "ipfs") {
4930
+ throw new Error("--storage must be either s3 or ipfs.");
4931
+ }
4932
+ const files = paths.map((path) => ({
4933
+ data: new Blob([new Uint8Array((0, import_fs7.readFileSync)(path))]),
4934
+ name: (0, import_path5.basename)(path)
4935
+ }));
4936
+ const result = await makeClient().files.upload(files, {
4937
+ agentId: opts.agent,
4938
+ sessionId: opts.session,
4939
+ storageProvider: opts.storage
4940
+ });
4941
+ spinner.stop();
4942
+ if (opts.json) return jsonOut(result.data);
4943
+ console.log(
4944
+ `
4945
+ ${sym.ok} Uploaded ${result.data.length} file${result.data.length === 1 ? "" : "s"}.`
4946
+ );
4947
+ for (const file of result.data) {
4948
+ console.log(
4949
+ ` ${sym.arrow} ${c.bold(file.name ?? file.originalName ?? file.fileId)} ${c.dim(file.fileId)}`
4950
+ );
4951
+ }
4952
+ } catch (error) {
4953
+ spinner.stop();
4954
+ printError(error);
4955
+ process.exit(1);
4956
+ }
4957
+ });
4958
+ for (const favorite of [true, false]) {
4959
+ command.command(`${favorite ? "favorite" : "unfavorite"} <itemId>`).description(`${favorite ? "Add" : "Remove"} a library item ${favorite ? "to" : "from"} favorites`).action(async (itemId) => {
4960
+ const spinner = spin("Updating library item\u2026");
4961
+ try {
4962
+ await makeClient().library.update(itemId, {
4963
+ isFavorite: favorite
4964
+ });
4965
+ spinner.stop();
4966
+ console.log(
4967
+ `${sym.ok} Item ${favorite ? "added to" : "removed from"} favorites.`
4968
+ );
4969
+ } catch (error) {
4970
+ spinner.stop();
4971
+ printError(error);
4972
+ process.exit(1);
4973
+ }
4974
+ });
4975
+ }
4976
+ command.command("delete <itemId>").description("Delete a library item").action(async (itemId) => {
4977
+ const spinner = spin("Deleting library item\u2026");
4978
+ try {
4979
+ await makeClient().library.delete(itemId);
4980
+ spinner.stop();
4981
+ console.log(`${sym.ok} Library item deleted.`);
4982
+ } catch (error) {
4983
+ spinner.stop();
4984
+ printError(error);
4985
+ process.exit(1);
4986
+ }
4987
+ });
4988
+ return command;
4989
+ }
4990
+
4991
+ // src/commands/projects.ts
4992
+ var import_commander20 = require("commander");
4993
+ var import_fs8 = require("fs");
4994
+ function agentId(value) {
4995
+ const resolved = value ?? loadConfig().defaultAgentId;
4996
+ if (!resolved) {
4997
+ throw new Error(
4998
+ "Specify --agent <agentId> or set a default with `agc config set defaultAgentId <id>`."
4999
+ );
5000
+ }
5001
+ return resolved;
5002
+ }
5003
+ function projectFiles(path) {
5004
+ if (!path) return void 0;
5005
+ const parsed = JSON.parse((0, import_fs8.readFileSync)(path, "utf8"));
5006
+ const files = Array.isArray(parsed) ? parsed : parsed.files;
5007
+ if (!Array.isArray(files)) {
5008
+ throw new Error("The files document must be an array or an object with a files array.");
5009
+ }
5010
+ return files;
5011
+ }
5012
+ function projectsCommand() {
5013
+ const command = new import_commander20.Command("projects").alias("project").description("Build, publish, and export agent code projects");
5014
+ command.command("list", { isDefault: true }).alias("ls").description("List projects for an agent").option("--agent <agentId>", "Agent ID").option("--json", "Output as JSON").action(async (opts) => {
5015
+ const spinner = spin("Fetching projects\u2026");
5016
+ try {
5017
+ const result = await makeClient().projects.list(agentId(opts.agent));
5018
+ spinner.stop();
5019
+ if (opts.json) return jsonOut(result.data);
5020
+ section(`Projects (${result.data.length})`);
5021
+ table(
5022
+ result.data.map((project) => ({
5023
+ ID: project.projectId.slice(0, 10) + "\u2026",
5024
+ Name: project.name,
5025
+ Files: String(project.files?.length ?? ""),
5026
+ Preview: project.previewUrl ?? project.previewSlug ?? "",
5027
+ Updated: project.updatedAt ?? ""
5028
+ })),
5029
+ ["ID", "Name", "Files", "Preview", "Updated"]
5030
+ );
5031
+ } catch (error) {
5032
+ spinner.stop();
5033
+ printError(error);
5034
+ process.exit(1);
5035
+ }
5036
+ });
5037
+ command.command("get <projectId>").description("Show a code project").option("--agent <agentId>", "Agent ID").option("--json", "Output as JSON").action(async (projectId, opts) => {
5038
+ const spinner = spin("Fetching project\u2026");
5039
+ try {
5040
+ const result = await makeClient().projects.get(
5041
+ agentId(opts.agent),
5042
+ projectId
5043
+ );
5044
+ spinner.stop();
5045
+ if (opts.json) return jsonOut(result.data);
5046
+ const project = result.data;
5047
+ section(project.name);
5048
+ detail([
5049
+ ["Project ID", c.id(project.projectId)],
5050
+ ["Agent ID", project.agentId],
5051
+ ["Description", project.description ?? ""],
5052
+ ["Files", String(project.files?.length ?? 0)],
5053
+ ["Preview", project.previewUrl ?? project.previewSlug ?? ""],
5054
+ ["Updated", project.updatedAt ?? ""]
5055
+ ]);
5056
+ } catch (error) {
5057
+ spinner.stop();
5058
+ printError(error);
5059
+ process.exit(1);
5060
+ }
5061
+ });
5062
+ command.command("create").description("Create a code project").requiredOption("--name <name>", "Project name").option("--description <text>", "Project description").option("--agent <agentId>", "Agent ID").option("--session <sessionId>", "Associated session").option("--files <json>", "JSON file containing [{ path, content }]").option("--json", "Output as JSON").action(async (opts) => {
5063
+ const spinner = spin("Creating project\u2026");
5064
+ try {
5065
+ const result = await makeClient().projects.create(
5066
+ agentId(opts.agent),
5067
+ {
5068
+ name: opts.name,
5069
+ description: opts.description,
5070
+ sessionId: opts.session,
5071
+ files: projectFiles(opts.files)
5072
+ }
5073
+ );
5074
+ spinner.stop();
5075
+ if (opts.json) return jsonOut(result.data);
5076
+ console.log(`
5077
+ ${sym.ok} Project created.`);
5078
+ detail([
5079
+ ["Project ID", c.id(result.data.projectId)],
5080
+ ["Name", result.data.name]
5081
+ ]);
5082
+ } catch (error) {
5083
+ spinner.stop();
5084
+ printError(error);
5085
+ process.exit(1);
5086
+ }
5087
+ });
5088
+ command.command("write <projectId> <json>").description("Write project files from a JSON document").option("--agent <agentId>", "Agent ID").option("--replace", "Replace all existing files").option("--json", "Output as JSON").action(async (projectId, json, opts) => {
5089
+ const spinner = spin("Writing project files\u2026");
5090
+ try {
5091
+ const result = await makeClient().projects.writeFiles(
5092
+ agentId(opts.agent),
5093
+ projectId,
5094
+ projectFiles(json) ?? [],
5095
+ Boolean(opts.replace)
5096
+ );
5097
+ spinner.stop();
5098
+ if (opts.json) return jsonOut(result.data);
5099
+ console.log(`${sym.ok} Project files updated.`);
5100
+ } catch (error) {
5101
+ spinner.stop();
5102
+ printError(error);
5103
+ process.exit(1);
5104
+ }
5105
+ });
5106
+ command.command("publish <projectId>").description("Build and publish a project preview").option("--agent <agentId>", "Agent ID").option("--json", "Output as JSON").action(async (projectId, opts) => {
5107
+ const spinner = spin("Building and publishing project\u2026");
5108
+ try {
5109
+ const result = await makeClient().projects.publish(
5110
+ agentId(opts.agent),
5111
+ projectId
5112
+ );
5113
+ spinner.stop();
5114
+ if (opts.json) return jsonOut(result.data);
5115
+ console.log(`
5116
+ ${sym.ok} Project published.`);
5117
+ jsonOut(result.data);
5118
+ } catch (error) {
5119
+ spinner.stop();
5120
+ printError(error);
5121
+ process.exit(1);
5122
+ }
5123
+ });
5124
+ command.command("export <projectId>").description("Export a project to the agent computer").option("--agent <agentId>", "Agent ID").option("--directory <path>", "Destination directory").option("--session <sessionId>", "Associated session").option("--json", "Output as JSON").action(async (projectId, opts) => {
5125
+ const spinner = spin("Exporting project\u2026");
5126
+ try {
5127
+ const result = await makeClient().projects.exportToComputer(
5128
+ agentId(opts.agent),
5129
+ projectId,
5130
+ { directory: opts.directory, sessionId: opts.session }
5131
+ );
5132
+ spinner.stop();
5133
+ if (opts.json) return jsonOut(result.data);
5134
+ console.log(`${sym.ok} Project exported to the agent computer.`);
5135
+ jsonOut(result.data);
5136
+ } catch (error) {
5137
+ spinner.stop();
5138
+ printError(error);
5139
+ process.exit(1);
5140
+ }
5141
+ });
5142
+ command.command("github <projectId>").description("Export a project to a GitHub repository").option("--agent <agentId>", "Agent ID").option("--repository <name>", "Repository name").option("--public", "Create a public repository").option("--json", "Output as JSON").action(async (projectId, opts) => {
5143
+ const spinner = spin("Exporting project to GitHub\u2026");
5144
+ try {
5145
+ const result = await makeClient().projects.exportToGitHub(
5146
+ agentId(opts.agent),
5147
+ projectId,
5148
+ {
5149
+ repositoryName: opts.repository,
5150
+ private: !opts.public
5151
+ }
5152
+ );
5153
+ spinner.stop();
5154
+ if (opts.json) return jsonOut(result.data);
5155
+ console.log(`${sym.ok} Project exported to GitHub.`);
5156
+ jsonOut(result.data);
5157
+ } catch (error) {
5158
+ spinner.stop();
5159
+ printError(error);
5160
+ process.exit(1);
5161
+ }
5162
+ });
5163
+ return command;
5164
+ }
5165
+
5166
+ // src/commands/api-keys.ts
5167
+ var import_commander21 = require("commander");
5168
+ async function resolveProject(projectId) {
5169
+ const projects = (await makeClient().developer.listProjects()).data;
5170
+ const project = projectId ? projects.find((candidate) => candidate.id === projectId) : projects[0];
5171
+ if (!project) {
5172
+ throw new Error(
5173
+ projectId ? `Developer project "${projectId}" was not found.` : "No developer project exists. Create one with `agc keys projects create --name <name>`."
5174
+ );
5175
+ }
5176
+ return project;
5177
+ }
5178
+ function apiKeysCommand() {
5179
+ const command = new import_commander21.Command("keys").alias("api-keys").description("Create and manage project-scoped developer API keys");
5180
+ command.command("list", { isDefault: true }).alias("ls").description("List API keys for a developer project").option("--project <projectId>", "Developer project ID (defaults to newest)").option("--json", "Output as JSON").action(async (opts) => {
5181
+ const spinner = spin("Fetching developer keys\u2026");
5182
+ try {
5183
+ const project = await resolveProject(opts.project);
5184
+ const result = await makeClient().developer.listApiKeys(project.id);
5185
+ spinner.stop();
5186
+ if (opts.json) {
5187
+ return jsonOut({ project, keys: result.data });
5188
+ }
5189
+ section(`${project.name} \xB7 API keys (${result.data.length})`);
5190
+ table(
5191
+ result.data.map((key) => ({
5192
+ ID: key.id.slice(0, 10) + "\u2026",
5193
+ Name: key.name,
5194
+ Prefix: key.keyPrefix,
5195
+ Status: key.status,
5196
+ Scopes: String(key.scopes.length),
5197
+ Expires: key.expiresAt ? new Date(key.expiresAt).toLocaleDateString() : "never",
5198
+ Used: key.lastUsedAt ? new Date(key.lastUsedAt).toLocaleDateString() : "never"
5199
+ })),
5200
+ ["ID", "Name", "Prefix", "Status", "Scopes", "Expires", "Used"]
5201
+ );
5202
+ } catch (error) {
5203
+ spinner.stop();
5204
+ printError(error);
5205
+ process.exit(1);
5206
+ }
5207
+ });
5208
+ command.command("create").description("Create a project-scoped API key").requiredOption("--name <name>", "Key name").option("--project <projectId>", "Developer project ID (defaults to newest)").option("--scopes <scopes>", "Comma-separated scopes (defaults to all project scopes)").option("--expires <iso>", "Expiration timestamp in ISO 8601 format").option("--json", "Output as JSON").action(async (opts) => {
5209
+ const spinner = spin("Creating developer key\u2026");
5210
+ try {
5211
+ const project = await resolveProject(opts.project);
5212
+ const scopes = opts.scopes ? String(opts.scopes).split(",").map((scope) => scope.trim()).filter(Boolean) : void 0;
5213
+ const result = await makeClient().developer.createApiKey(project.id, {
5214
+ name: opts.name,
5215
+ scopes,
5216
+ expiresAt: opts.expires
5217
+ });
5218
+ spinner.stop();
5219
+ if (opts.json) return jsonOut(result.data);
5220
+ console.log(`
5221
+ ${sym.ok} ${c.success("Developer API key created")}`);
5222
+ detail([
5223
+ ["Project", project.name],
5224
+ ["Name", result.data.name],
5225
+ ["Scopes", result.data.scopes.join(", ")],
5226
+ ["Expires", result.data.expiresAt ?? "never"]
5227
+ ]);
5228
+ console.log(
5229
+ `
5230
+ ${c.warn("Copy this key now. It will not be shown again.")}`
5231
+ );
5232
+ console.log(`
5233
+ ${c.bold(result.data.key)}
5234
+ `);
5235
+ } catch (error) {
5236
+ spinner.stop();
5237
+ printError(error);
5238
+ process.exit(1);
5239
+ }
5240
+ });
5241
+ command.command("revoke <keyId>").description("Revoke a developer API key").action(async (keyId) => {
5242
+ const spinner = spin("Revoking developer key\u2026");
5243
+ try {
5244
+ await makeClient().developer.revokeApiKey(keyId);
5245
+ spinner.stop();
5246
+ console.log(`${sym.ok} Developer API key revoked.`);
5247
+ } catch (error) {
5248
+ spinner.stop();
5249
+ printError(error);
5250
+ process.exit(1);
5251
+ }
5252
+ });
5253
+ command.command("scopes").description("List supported developer API scopes").option("--json", "Output as JSON").action(async (opts) => {
5254
+ const spinner = spin("Fetching API scopes\u2026");
5255
+ try {
5256
+ const result = await makeClient().developer.scopes();
5257
+ spinner.stop();
5258
+ if (opts.json) return jsonOut(result.data);
5259
+ section("Developer API scopes");
5260
+ for (const scope of result.data) {
5261
+ console.log(` ${sym.bullet} ${scope}`);
5262
+ }
5263
+ } catch (error) {
5264
+ spinner.stop();
5265
+ printError(error);
5266
+ process.exit(1);
5267
+ }
5268
+ });
5269
+ const projects = command.command("projects").description("Manage developer projects");
5270
+ projects.command("list", { isDefault: true }).alias("ls").description("List developer projects").option("--json", "Output as JSON").action(async (opts) => {
5271
+ const spinner = spin("Fetching developer projects\u2026");
5272
+ try {
5273
+ const result = await makeClient().developer.listProjects();
5274
+ spinner.stop();
5275
+ if (opts.json) return jsonOut(result.data);
5276
+ section(`Developer projects (${result.data.length})`);
5277
+ table(
5278
+ result.data.map((project) => ({
5279
+ ID: project.id,
5280
+ Name: project.name,
5281
+ Environment: project.environment,
5282
+ Status: project.status
5283
+ })),
5284
+ ["ID", "Name", "Environment", "Status"]
5285
+ );
5286
+ } catch (error) {
5287
+ spinner.stop();
5288
+ printError(error);
5289
+ process.exit(1);
5290
+ }
5291
+ });
5292
+ projects.command("create").description("Create a developer project").requiredOption("--name <name>", "Project name").option(
5293
+ "--environment <environment>",
5294
+ "production | development | staging",
5295
+ "development"
5296
+ ).option("--workspace <workspaceId>", "Workspace ID (defaults to signed-in workspace)").option("--json", "Output as JSON").action(async (opts) => {
5297
+ const workspaceId = opts.workspace ?? loadConfig().workspaceId;
5298
+ if (!workspaceId) {
5299
+ throw new Error(
5300
+ "No workspace is configured. Pass --workspace or sign in again."
5301
+ );
5302
+ }
5303
+ if (!["production", "development", "staging"].includes(opts.environment)) {
5304
+ throw new Error(
5305
+ "--environment must be production, development, or staging."
5306
+ );
5307
+ }
5308
+ const spinner = spin("Creating developer project\u2026");
5309
+ try {
5310
+ const result = await makeClient().developer.createProject({
5311
+ workspaceId,
5312
+ name: opts.name,
5313
+ environment: opts.environment
5314
+ });
5315
+ spinner.stop();
5316
+ if (opts.json) return jsonOut(result.data);
5317
+ console.log(`
5318
+ ${sym.ok} Developer project created.`);
5319
+ detail([
5320
+ ["Project ID", c.id(result.data.id)],
5321
+ ["Name", result.data.name],
5322
+ ["Environment", result.data.environment]
5323
+ ]);
5324
+ } catch (error) {
5325
+ spinner.stop();
5326
+ printError(error);
5327
+ process.exit(1);
5328
+ }
5329
+ });
5330
+ return command;
5331
+ }
5332
+
5333
+ // src/bin.ts
5334
+ async function interactiveMenu() {
5335
+ banner();
5336
+ const cfg = loadConfig();
5337
+ const isSetup = !!((cfg.accessToken || cfg.apiKey || cfg.sessionToken) && (cfg.userId || cfg.initiator));
5338
+ if (!isSetup) {
5339
+ console.log(c.bold(" Welcome to Agent Commons CLI!"));
5340
+ console.log(c.dim(" Looks like this is your first time here \u2014 let's get you set up.\n"));
5341
+ console.log(` ${sym.arrow} Running ${c.bold("agc login")} to configure your credentials\u2026
5342
+ `);
5343
+ runSubcommand(["login"]);
5344
+ return;
5345
+ }
5346
+ console.log(
5347
+ ` ${c.dim("Connected to")} ${c.primary(cfg.apiUrl)} ${c.dim("\xB7")} ${c.dim("Identity")} ${c.id((cfg.userId ?? cfg.initiator).slice(0, 8) + "\u2026" + (cfg.userId ?? cfg.initiator).slice(-4))}
4118
5348
  `
4119
5349
  );
4120
5350
  const action = await select("What would you like to do?", [
4121
5351
  { label: "Chat with an agent", value: "chat", hint: "agc chat" },
4122
5352
  { label: "Run an agent (one-shot)", value: "run", hint: "agc run" },
5353
+ { label: "Manage an agent cloud computer", value: "computer", hint: "agc computer status" },
4123
5354
  { label: "View sessions", value: "sessions", hint: "agc sessions list" },
4124
5355
  { label: "Manage agents", value: "agents", hint: "agc agents list" },
4125
5356
  { label: "Tasks", value: "tasks", hint: "agc task list" },
4126
5357
  { label: "Workflows", value: "workflows", hint: "agc workflow list" },
4127
5358
  { label: "MCP servers", value: "mcp", hint: "agc mcp list" },
4128
5359
  { label: "Skills", value: "skills", hint: "agc skills list" },
5360
+ { label: "Library & files", value: "library", hint: "agc library list" },
5361
+ { label: "Code projects", value: "projects", hint: "agc projects list" },
5362
+ { label: "Developer API keys", value: "keys", hint: "agc keys list" },
4129
5363
  { label: "Wallet & balance", value: "wallet", hint: "agc wallet balance" },
4130
5364
  { label: "Usage & cost", value: "usage", hint: "agc usage" },
4131
5365
  { label: "Logs", value: "logs", hint: "agc logs" },
@@ -4135,24 +5369,29 @@ async function interactiveMenu() {
4135
5369
  if (action === "exit") {
4136
5370
  process.exit(0);
4137
5371
  }
4138
- const agentId = cfg.defaultAgentId ?? await pickAgentInteractively(action);
4139
- if ((action === "chat" || action === "run") && !agentId) return;
5372
+ const needsAgent = action === "chat" || action === "run" || action === "computer";
5373
+ const agentId2 = needsAgent ? cfg.defaultAgentId ?? await pickAgentInteractively(action) : void 0;
5374
+ if (needsAgent && !agentId2) return;
4140
5375
  if (action === "run") {
4141
- const prompt2 = await askPrompt("Enter your prompt:");
4142
- if (!prompt2) return;
4143
- runSubcommand(["run", "--agent", agentId, prompt2]);
5376
+ const prompt = await askPrompt("Enter your prompt:");
5377
+ if (!prompt) return;
5378
+ runSubcommand(["run", "--agent", agentId2, prompt]);
4144
5379
  return;
4145
5380
  }
4146
5381
  const commandMap = {
4147
- chat: ["chat", "--agent", agentId],
5382
+ chat: ["chat", "--agent", agentId2],
4148
5383
  run: [],
4149
5384
  // handled above
5385
+ computer: ["computer", "status", "--agent", agentId2],
4150
5386
  sessions: ["sessions", "list"],
4151
5387
  agents: ["agents", "list"],
4152
5388
  tasks: ["task", "list"],
4153
5389
  workflows: ["workflow", "list"],
4154
5390
  mcp: ["mcp", "list"],
4155
5391
  skills: ["skills", "list"],
5392
+ library: ["library", "list"],
5393
+ projects: ["projects", "list"],
5394
+ keys: ["keys", "list"],
4156
5395
  wallet: ["wallet", "balance"],
4157
5396
  usage: ["usage"],
4158
5397
  logs: ["logs"],
@@ -4162,9 +5401,9 @@ async function interactiveMenu() {
4162
5401
  runSubcommand(commandMap[action]);
4163
5402
  }
4164
5403
  async function askPrompt(question) {
4165
- const { createInterface: createInterface4 } = await import("readline");
5404
+ const { createInterface: createInterface3 } = await import("readline");
4166
5405
  return new Promise((resolve2) => {
4167
- const rl = createInterface4({ input: process.stdin, output: process.stdout });
5406
+ const rl = createInterface3({ input: process.stdin, output: process.stdout });
4168
5407
  process.stdout.write(`
4169
5408
  ${c.bold(question)}
4170
5409
  ${c.primary("\u203A")} `);
@@ -4193,7 +5432,7 @@ async function pickAgentInteractively(action) {
4193
5432
  } catch {
4194
5433
  spinner.stop();
4195
5434
  console.log(`
4196
- ${c.warn("\u26A0")} Could not fetch agents. Check your API key and connection.
5435
+ ${c.warn("\u26A0")} Could not fetch agents. Check your sign-in and connection.
4197
5436
  `);
4198
5437
  return null;
4199
5438
  }
@@ -4211,8 +5450,8 @@ async function pickAgentInteractively(action) {
4211
5450
  return null;
4212
5451
  }
4213
5452
  console.log();
4214
- const agentId = await select(
4215
- `Choose an agent to ${action} with:`,
5453
+ const agentId2 = await select(
5454
+ action === "computer" ? "Choose the agent whose cloud computer you want to manage:" : `Choose an agent to ${action} with:`,
4216
5455
  agents.map((a) => ({
4217
5456
  label: a.name,
4218
5457
  value: a.agentId,
@@ -4224,15 +5463,29 @@ async function pickAgentInteractively(action) {
4224
5463
  { label: "No \u2014 just this once", value: false }
4225
5464
  ]);
4226
5465
  if (saveDefault) {
4227
- saveConfig({ defaultAgentId: agentId });
4228
- const chosen = agents.find((a) => a.agentId === agentId);
4229
- console.log(` ${sym.ok} ${c.dim("Default agent set to")} ${c.bold(chosen?.name ?? agentId)}
5466
+ saveConfig({ defaultAgentId: agentId2 });
5467
+ const chosen = agents.find((a) => a.agentId === agentId2);
5468
+ console.log(` ${sym.ok} ${c.dim("Default agent set to")} ${c.bold(chosen?.name ?? agentId2)}
4230
5469
  `);
4231
5470
  }
4232
- return agentId;
5471
+ return agentId2;
4233
5472
  }
4234
- var program = new import_commander16.Command();
4235
- program.name("agc").description("Agent Commons CLI \u2014 interact with the Agent Commons platform").version("0.2.4", "-v, --version").action(async () => {
5473
+ var program = new import_commander22.Command();
5474
+ program.name("agc").description("Agent Commons CLI \u2014 interact with the Agent Commons platform").version("0.4.0", "-v, --version").showHelpAfterError("(run `agc --help` for usage)").configureHelp({
5475
+ sortOptions: true,
5476
+ sortSubcommands: true
5477
+ }).addHelpText(
5478
+ "after",
5479
+ `
5480
+ Examples:
5481
+ $ agc login
5482
+ $ agc agents list
5483
+ $ agc run --agent <id> "Summarize this week"
5484
+ $ agc keys create --name "CI" --scopes agents:read,agents:run
5485
+
5486
+ Docs: https://docs.agentcommons.io/docs/cli
5487
+ `
5488
+ ).action(async () => {
4236
5489
  await interactiveMenu();
4237
5490
  });
4238
5491
  program.hook("preAction", async (_thisCommand, actionCommand) => {
@@ -4246,10 +5499,15 @@ program.addCommand(configCommand());
4246
5499
  program.addCommand(agentsCommand());
4247
5500
  program.addCommand(sessionsCommand());
4248
5501
  program.addCommand(toolsCommand());
5502
+ program.addCommand(connectionsCommand());
5503
+ program.addCommand(libraryCommand());
5504
+ program.addCommand(projectsCommand());
5505
+ program.addCommand(apiKeysCommand());
4249
5506
  program.addCommand(workflowCommand());
4250
5507
  program.addCommand(taskCommand());
4251
5508
  program.addCommand(runCommand());
4252
5509
  program.addCommand(chatCommand());
5510
+ program.addCommand(computerCommand());
4253
5511
  program.addCommand(mcpCommand());
4254
5512
  program.addCommand(skillsCommand());
4255
5513
  program.addCommand(walletCommand());
@@ -4257,6 +5515,8 @@ program.addCommand(modelsCommand());
4257
5515
  program.addCommand(memoryCommand());
4258
5516
  program.addCommand(usageCommand());
4259
5517
  program.addCommand(logsCommand());
5518
+ program.addCommand(creditsCommand());
5519
+ program.addCommand(billingCommand());
4260
5520
  program.on("command:*", () => {
4261
5521
  console.error(
4262
5522
  `
@@ -4266,4 +5526,9 @@ program.on("command:*", () => {
4266
5526
  );
4267
5527
  process.exit(1);
4268
5528
  });
4269
- program.parse(process.argv);
5529
+ program.parseAsync(process.argv).catch((error) => {
5530
+ console.error(`
5531
+ ${sym.fail} ${c.error(error instanceof Error ? error.message : String(error))}
5532
+ `);
5533
+ process.exitCode = 1;
5534
+ });