@agent-commons/cli 0.0.0-staging-20260714131205

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/bin.js +4893 -0
  2. package/package.json +37 -0
package/dist/bin.js ADDED
@@ -0,0 +1,4893 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
25
+
26
+ // src/bin.ts
27
+ var import_commander19 = require("commander");
28
+ var import_path5 = require("path");
29
+ var import_os4 = require("os");
30
+ var import_child_process3 = require("child_process");
31
+
32
+ // src/commands/login.ts
33
+ var import_commander = require("commander");
34
+ var readline = __toESM(require("readline"));
35
+ var import_fs2 = require("fs");
36
+ var import_path2 = require("path");
37
+ var import_os2 = require("os");
38
+
39
+ // src/config.ts
40
+ var import_fs = require("fs");
41
+ var import_os = require("os");
42
+ var import_path = require("path");
43
+ var import_sdk = require("@agent-commons/sdk");
44
+ var CONFIG_DIR = (0, import_path.join)((0, import_os.homedir)(), ".agc");
45
+ var CONFIG_FILE = (0, import_path.join)(CONFIG_DIR, "config.json");
46
+ var DEFAULT_API_URL = process.env.AGC_API_URL ?? "https://api.agentcommons.io";
47
+ var DEFAULT_APP_URL = "https://www.agentcommons.io";
48
+ var DEFAULT_IDENTITY_URL = process.env.COMMONS_IDENTITY_URL ?? "https://auth.agentcommons.io";
49
+ var DEFAULT_IDENTITY_CLIENT_ID = process.env.COMMONS_IDENTITY_CLIENT_ID ?? "commons-cli";
50
+ function loadConfig() {
51
+ const fromEnv = {
52
+ ...process.env.AGC_API_URL && { apiUrl: process.env.AGC_API_URL },
53
+ ...process.env.AGC_API_KEY && { apiKey: process.env.AGC_API_KEY },
54
+ ...process.env.COMMONS_ACCESS_TOKEN && { accessToken: process.env.COMMONS_ACCESS_TOKEN },
55
+ ...process.env.COMMONS_IDENTITY_URL && { identityUrl: process.env.COMMONS_IDENTITY_URL },
56
+ ...process.env.AGC_INITIATOR && { initiator: process.env.AGC_INITIATOR },
57
+ ...process.env.AGC_AGENT_ID && { defaultAgentId: process.env.AGC_AGENT_ID }
58
+ };
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
+ }
66
+ return {
67
+ apiUrl: DEFAULT_API_URL,
68
+ identityUrl: DEFAULT_IDENTITY_URL,
69
+ identityClientId: DEFAULT_IDENTITY_CLIENT_ID,
70
+ ...fromFile,
71
+ ...fromEnv
72
+ };
73
+ }
74
+ 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 });
79
+ }
80
+ 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
+ }
96
+ }
97
+ function makeClient(overrides) {
98
+ const cfg = { ...loadConfig(), ...overrides };
99
+ return new import_sdk.CommonsClient({
100
+ baseUrl: cfg.apiUrl,
101
+ apiKey: cfg.accessToken ?? cfg.apiKey,
102
+ initiator: cfg.userId ?? cfg.initiator
103
+ });
104
+ }
105
+ function decodeJwtPayload(token) {
106
+ const [, payload] = token.split(".");
107
+ if (!payload) return {};
108
+ try {
109
+ return JSON.parse(Buffer.from(payload, "base64url").toString("utf8"));
110
+ } catch {
111
+ return {};
112
+ }
113
+ }
114
+ async function ensureAccessToken() {
115
+ const cfg = loadConfig();
116
+ if (cfg.apiKey && !cfg.sessionToken) return cfg;
117
+ if (cfg.accessToken && cfg.accessTokenExpiresAt && cfg.accessTokenExpiresAt > Date.now() + 3e4) {
118
+ return cfg;
119
+ }
120
+ if (!cfg.sessionToken || !cfg.identityUrl) return cfg;
121
+ const response = await fetch(
122
+ `${cfg.identityUrl.replace(/\/$/, "")}/api/auth/token`,
123
+ { headers: { Authorization: `Bearer ${cfg.sessionToken}` } }
124
+ );
125
+ if (!response.ok) {
126
+ throw new Error("Your Commons login has expired. Run `agc login` again.");
127
+ }
128
+ const data = await response.json();
129
+ if (!data.token) throw new Error("Commons Identity did not return an access token.");
130
+ const claims = decodeJwtPayload(data.token);
131
+ const updates = {
132
+ accessToken: data.token,
133
+ accessTokenExpiresAt: typeof claims.exp === "number" ? claims.exp * 1e3 : Date.now() + 10 * 60 * 1e3,
134
+ userId: typeof claims.sub === "string" ? claims.sub : cfg.userId,
135
+ workspaceId: typeof claims.workspace_id === "string" ? claims.workspace_id : cfg.workspaceId,
136
+ initiator: typeof claims.sub === "string" ? claims.sub : cfg.initiator
137
+ };
138
+ saveConfig(updates);
139
+ return { ...cfg, ...updates };
140
+ }
141
+
142
+ // src/ui.ts
143
+ var import_chalk = __toESM(require("chalk"));
144
+ var import_ora = __toESM(require("ora"));
145
+ var import_child_process = require("child_process");
146
+ var c = {
147
+ primary: (s) => import_chalk.default.cyan(s),
148
+ success: (s) => import_chalk.default.green(s),
149
+ warn: (s) => import_chalk.default.yellow(s),
150
+ error: (s) => import_chalk.default.red(s),
151
+ dim: (s) => import_chalk.default.dim(s),
152
+ bold: (s) => import_chalk.default.bold(s),
153
+ id: (s) => import_chalk.default.magenta(s),
154
+ label: (s) => import_chalk.default.cyan.bold(s)
155
+ };
156
+ var sym = {
157
+ ok: import_chalk.default.green("\u2713"),
158
+ fail: import_chalk.default.red("\u2717"),
159
+ arrow: import_chalk.default.cyan("\u2192"),
160
+ bullet: import_chalk.default.dim("\u2022"),
161
+ dot: import_chalk.default.dim("\xB7")
162
+ };
163
+ function banner(version = "0.3.0") {
164
+ const line = import_chalk.default.cyan(" \u2500".padEnd(2) + "\u2500".repeat(44));
165
+ console.log("");
166
+ console.log(line);
167
+ 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}`)
169
+ );
170
+ console.log(import_chalk.default.cyan(" \u2502 ") + import_chalk.default.dim(" The Open AI Agent Network \xB7 agentcommons.io"));
171
+ console.log(line);
172
+ console.log("");
173
+ }
174
+ function step(n, total, title) {
175
+ const fraction = import_chalk.default.dim(`${n}/${total}`);
176
+ console.log(`
177
+ ${import_chalk.default.cyan.bold(" Step " + n)} ${fraction} ${import_chalk.default.bold(title)}`);
178
+ console.log(import_chalk.default.dim(" " + "\u2500".repeat(38)));
179
+ }
180
+ async function select(prompt2, choices) {
181
+ if (!process.stdin.isTTY) {
182
+ return choices[0].value;
183
+ }
184
+ let idx = 0;
185
+ const total = choices.length;
186
+ const render = (first = false) => {
187
+ if (!first) {
188
+ process.stdout.write(`\x1B[${total + 2}A\x1B[0J`);
189
+ }
190
+ console.log("\n" + import_chalk.default.bold(" " + prompt2));
191
+ for (let i = 0; i < total; i++) {
192
+ const { label, hint } = choices[i];
193
+ if (i === idx) {
194
+ const hintStr = hint ? import_chalk.default.dim(" " + hint) : "";
195
+ process.stdout.write(import_chalk.default.cyan(" \u203A ") + import_chalk.default.bold.white(label) + hintStr + "\n");
196
+ } else {
197
+ process.stdout.write(import_chalk.default.dim(" " + label) + "\n");
198
+ }
199
+ }
200
+ };
201
+ render(true);
202
+ return new Promise((resolve2) => {
203
+ process.stdin.setRawMode(true);
204
+ process.stdin.resume();
205
+ process.stdin.setEncoding("utf8");
206
+ const handler = (data) => {
207
+ const key = String(data);
208
+ if (key === "\x1B[A" || key === "k") {
209
+ idx = (idx - 1 + total) % total;
210
+ render();
211
+ } else if (key === "\x1B[B" || key === "j") {
212
+ idx = (idx + 1) % total;
213
+ render();
214
+ } else if (key === "\r" || key === "\n" || key === " ") {
215
+ cleanup();
216
+ process.stdout.write("\n");
217
+ resolve2(choices[idx].value);
218
+ } else if (key === "") {
219
+ cleanup();
220
+ process.stdout.write("\n");
221
+ process.exit(130);
222
+ }
223
+ };
224
+ const cleanup = () => {
225
+ process.stdin.removeListener("data", handler);
226
+ process.stdin.setRawMode(false);
227
+ process.stdin.pause();
228
+ };
229
+ process.stdin.on("data", handler);
230
+ });
231
+ }
232
+ 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, () => {
235
+ });
236
+ }
237
+ function spin(text) {
238
+ return (0, import_ora.default)({ text, color: "cyan" }).start();
239
+ }
240
+ function table(rows, columns) {
241
+ if (rows.length === 0) {
242
+ console.log(c.dim(" (none)"));
243
+ return;
244
+ }
245
+ const widths = columns.map(
246
+ (col) => Math.max(col.length, ...rows.map((r) => (r[col] ?? "").length))
247
+ );
248
+ const header = columns.map((col, i) => c.label(col.toUpperCase().padEnd(widths[i]))).join(" ");
249
+ const divider = widths.map((w) => import_chalk.default.dim("\u2500".repeat(w))).join(" ");
250
+ console.log(" " + header);
251
+ console.log(" " + divider);
252
+ for (const row of rows) {
253
+ const line = columns.map((col, i) => (row[col] ?? "").padEnd(widths[i])).join(" ");
254
+ console.log(" " + line);
255
+ }
256
+ }
257
+ function section(title) {
258
+ console.log("\n" + c.bold(title));
259
+ }
260
+ function detail(pairs) {
261
+ const labelWidth = Math.max(...pairs.map(([k]) => k.length));
262
+ for (const [key, val] of pairs) {
263
+ if (val === void 0 || val === "") continue;
264
+ console.log(` ${c.dim(key.padEnd(labelWidth))} ${val}`);
265
+ }
266
+ }
267
+ function relativeTime(iso) {
268
+ const ms = Date.now() - new Date(iso).getTime();
269
+ if (ms < 6e4) return `${Math.round(ms / 1e3)}s ago`;
270
+ if (ms < 36e5) return `${Math.round(ms / 6e4)}m ago`;
271
+ if (ms < 864e5) return `${Math.round(ms / 36e5)}h ago`;
272
+ return `${Math.round(ms / 864e5)}d ago`;
273
+ }
274
+ function printError(err) {
275
+ if (err instanceof Error) {
276
+ console.error(c.error(`
277
+ Error: ${err.message}`));
278
+ } else {
279
+ console.error(c.error(`
280
+ Unknown error: ${String(err)}`));
281
+ }
282
+ }
283
+ function jsonOut(data) {
284
+ process.stdout.write(JSON.stringify(data, null, 2) + "\n");
285
+ }
286
+ function statusBadge(status) {
287
+ switch (status) {
288
+ case "completed":
289
+ case "connected":
290
+ case "active":
291
+ case "success":
292
+ return import_chalk.default.green(status);
293
+ case "running":
294
+ case "working":
295
+ case "submitted":
296
+ return import_chalk.default.cyan(status);
297
+ case "pending":
298
+ return import_chalk.default.yellow(status);
299
+ case "failed":
300
+ case "error":
301
+ case "canceled":
302
+ return import_chalk.default.red(status);
303
+ case "cancelled":
304
+ return import_chalk.default.gray(status);
305
+ case "awaiting_approval":
306
+ return import_chalk.default.magenta(status);
307
+ default:
308
+ return import_chalk.default.dim(status);
309
+ }
310
+ }
311
+
312
+ // src/commands/login.ts
313
+ 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
320
+ });
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
+ });
334
+ }
335
+ });
336
+ }
337
+ 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) => {
340
+ try {
341
+ const current = loadConfig();
342
+ const isFirstRun = !(0, import_fs2.existsSync)(CONFIG_FILE2);
343
+ 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
+ })
376
+ });
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({
415
+ apiUrl,
416
+ identityUrl,
417
+ identityClientId: clientId,
418
+ sessionToken,
419
+ accessToken: void 0,
420
+ accessTokenExpiresAt: void 0,
421
+ apiKey: void 0
422
+ });
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
+ }
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);
487
+ }
488
+ });
489
+ return cmd;
490
+ }
491
+ function logoutCommand() {
492
+ return new import_commander.Command("logout").description("Clear stored credentials").action(() => {
493
+ clearConfig();
494
+ console.log(`${sym.ok} Credentials cleared.`);
495
+ });
496
+ }
497
+ 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({
502
+ apiUrl: cfg.apiUrl,
503
+ identityUrl: cfg.identityUrl,
504
+ userId: cfg.userId ?? cfg.initiator,
505
+ 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.`);
528
+ }
529
+ } catch (err) {
530
+ console.log(`
531
+ ${sym.fail} ${c.error("Could not reach API")}: ${err.message}`);
532
+ }
533
+ });
534
+ }
535
+ 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"];
539
+ if (!allowed.includes(key)) {
540
+ console.error(c.error(`Unknown key "${key}". Allowed: ${allowed.join(", ")}`));
541
+ process.exit(1);
542
+ }
543
+ saveConfig({ [key]: value });
544
+ console.log(`${sym.ok} ${key} = ${key === "apiKey" ? "****" : value}`);
545
+ });
546
+ cmd.command("get [key]").description("Get a config value or show all").action((key) => {
547
+ const cfg = loadConfig();
548
+ 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
+ ]);
557
+ }
558
+ });
559
+ return cmd;
560
+ }
561
+
562
+ // src/commands/agents.ts
563
+ var import_commander2 = require("commander");
564
+ function agentsCommand() {
565
+ const cmd = new import_commander2.Command("agents").description("Manage agents");
566
+ cmd.command("list").description("List agents owned by the current initiator").option("--json", "Output as JSON").action(async (opts) => {
567
+ const cfg = loadConfig();
568
+ const spinner = spin("Fetching agents\u2026");
569
+ try {
570
+ const client = makeClient();
571
+ const res = await client.agents.list(cfg.initiator);
572
+ const agents = res?.data ?? res ?? [];
573
+ spinner.stop();
574
+ if (opts.json) return jsonOut(agents);
575
+ section(`Agents (${agents.length})`);
576
+ table(
577
+ agents.map((a) => ({
578
+ ID: a.agentId.slice(0, 8) + "\u2026",
579
+ Name: a.name,
580
+ Runtime: a.runtimeType ?? "native",
581
+ Model: `${a.modelProvider}/${a.modelId}`,
582
+ Created: relativeTime(a.createdAt)
583
+ })),
584
+ ["ID", "Name", "Runtime", "Model", "Created"]
585
+ );
586
+ } catch (err) {
587
+ spinner.stop();
588
+ printError(err);
589
+ process.exit(1);
590
+ }
591
+ });
592
+ cmd.command("get <agentId>").description("Show details for an agent").option("--json", "Output as JSON").action(async (agentId, opts) => {
593
+ const spinner = spin("Fetching agent\u2026");
594
+ try {
595
+ const client = makeClient();
596
+ const res = await client.agents.get(agentId);
597
+ const agent = res?.data ?? res;
598
+ spinner.stop();
599
+ if (opts.json) return jsonOut(agent);
600
+ section(agent.name);
601
+ detail([
602
+ ["Agent ID", c.id(agent.agentId)],
603
+ ["Provider", `${agent.modelProvider} / ${agent.modelId}`],
604
+ ["Runtime", agent.runtimeType ?? "native"],
605
+ ["Runtime status", agent.runtimeStatus ?? "ready"],
606
+ ["Instructions", agent.instructions?.slice(0, 80) ?? c.dim("(none)")],
607
+ [
608
+ "Tools",
609
+ [...agent.commonTools ?? [], ...agent.externalTools ?? []].join(
610
+ ", "
611
+ ) || c.dim("(none)")
612
+ ],
613
+ ["Created", relativeTime(agent.createdAt)]
614
+ ]);
615
+ } catch (err) {
616
+ spinner.stop();
617
+ printError(err);
618
+ process.exit(1);
619
+ }
620
+ });
621
+ cmd.command("create").description("Create a new agent").requiredOption("--name <name>", "Agent name").option("--instructions <text>", "System instructions").option(
622
+ "--provider <provider>",
623
+ "Model provider (openai|anthropic|google|groq|openrouter|xai|ollama|custom)",
624
+ "openai"
625
+ ).option("--model <id>", "Model ID", "gpt-5.4-mini").option("--model-api-key <key>", "Provider API key (BYOK)").option(
626
+ "--model-base-url <url>",
627
+ "Base URL for custom or local OpenAI-compatible providers"
628
+ ).option(
629
+ "--runtime <runtime>",
630
+ "Agent runtime (native|openclaw|hermes|custom)",
631
+ "native"
632
+ ).option("--json", "Output as JSON").action(async (opts) => {
633
+ const cfg = loadConfig();
634
+ if (!cfg.initiator) {
635
+ console.error(c.error("No initiator set. Run `agc login` first."));
636
+ process.exit(1);
637
+ }
638
+ const spinner = spin("Creating agent\u2026");
639
+ try {
640
+ if (!["native", "openclaw", "hermes", "custom"].includes(opts.runtime)) {
641
+ throw new Error(`Unsupported runtime "${opts.runtime}"`);
642
+ }
643
+ const client = makeClient();
644
+ const res = await client.agents.create({
645
+ name: opts.name,
646
+ instructions: opts.instructions,
647
+ owner: cfg.initiator,
648
+ modelProvider: opts.provider,
649
+ modelId: opts.model,
650
+ modelApiKey: opts.modelApiKey,
651
+ modelBaseUrl: opts.modelBaseUrl,
652
+ runtimeType: opts.runtime
653
+ });
654
+ const agent = res?.data ?? res;
655
+ spinner.stop();
656
+ if (opts.json) return jsonOut(agent);
657
+ console.log(`
658
+ ${sym.ok} Agent created`);
659
+ detail([
660
+ ["Agent ID", c.id(agent.agentId)],
661
+ ["Name", agent.name],
662
+ ["Model", `${agent.modelProvider}/${agent.modelId}`],
663
+ ["Runtime", agent.runtimeType ?? opts.runtime]
664
+ ]);
665
+ console.log(
666
+ c.dim("\n Tip: agc config set defaultAgentId " + agent.agentId)
667
+ );
668
+ } catch (err) {
669
+ spinner.stop();
670
+ printError(err);
671
+ process.exit(1);
672
+ }
673
+ });
674
+ const runtime = cmd.command("runtime").description("Manage an agent runtime");
675
+ runtime.command("status <agentId>").description("Show managed runtime status and capabilities").option("--json", "Output as JSON").action(async (agentId, opts) => {
676
+ const spinner = spin("Fetching runtime status\u2026");
677
+ try {
678
+ const result = await makeClient().agents.getRuntime(agentId);
679
+ spinner.stop();
680
+ if (opts.json) return jsonOut(result.data);
681
+ detail([
682
+ ["Runtime", result.data.runtimeType],
683
+ ["Status", result.data.status],
684
+ ["Managed", result.data.managed ? "yes" : "no"],
685
+ ["Computer", result.data.computer?.computerId ?? c.dim("(none)")]
686
+ ]);
687
+ } catch (err) {
688
+ spinner.stop();
689
+ printError(err);
690
+ process.exit(1);
691
+ }
692
+ });
693
+ for (const action of ["deploy", "restart", "sleep"]) {
694
+ runtime.command(`${action} <agentId>`).description(
695
+ `${action[0].toUpperCase()}${action.slice(1)} the managed agent runtime`
696
+ ).action(async (agentId) => {
697
+ const spinner = spin(
698
+ `${action[0].toUpperCase()}${action.slice(1)}ing runtime\u2026`
699
+ );
700
+ try {
701
+ const client = makeClient();
702
+ const result = action === "deploy" ? await client.agents.deployRuntime(agentId) : action === "restart" ? await client.agents.restartRuntime(agentId) : await client.agents.sleepRuntime(agentId);
703
+ spinner.stop();
704
+ console.log(`
705
+ ${sym.ok} Runtime ${result.data.status}`);
706
+ } catch (err) {
707
+ spinner.stop();
708
+ printError(err);
709
+ process.exit(1);
710
+ }
711
+ });
712
+ }
713
+ const autonomy = cmd.command("autonomy").description("Manage agent heartbeat");
714
+ autonomy.command("status").description("Show autonomy status for an agent").requiredOption("--agent <agentId>", "Agent ID").option("--json", "Output as JSON").action(async (opts) => {
715
+ const client = makeClient();
716
+ const spinner = spin("Fetching autonomy status\u2026");
717
+ try {
718
+ const res = await client.agents.getAutonomy(opts.agent);
719
+ spinner.stop();
720
+ const s = res.data;
721
+ if (opts.json) return jsonOut(s);
722
+ console.log(`
723
+ ${c.bold("Heartbeat Status")}`);
724
+ detail([
725
+ ["Enabled", s.enabled ? c.bold("yes") : "no"],
726
+ ["Interval", s.intervalSec ? `${s.intervalSec}s` : "n/a"],
727
+ ["Armed", s.isArmed ? c.bold("yes") : "no"],
728
+ [
729
+ "Last beat",
730
+ s.lastBeatAt ? new Date(s.lastBeatAt).toLocaleString() : "never"
731
+ ],
732
+ [
733
+ "Next beat",
734
+ s.nextBeatAt ? new Date(s.nextBeatAt).toLocaleString() : "n/a"
735
+ ]
736
+ ]);
737
+ } catch (err) {
738
+ spinner.stop();
739
+ printError(err);
740
+ process.exit(1);
741
+ }
742
+ });
743
+ autonomy.command("enable").description("Enable heartbeat for an agent").requiredOption("--agent <agentId>", "Agent ID").option(
744
+ "--interval <seconds>",
745
+ "Heartbeat interval in seconds (min 30)",
746
+ "300"
747
+ ).action(async (opts) => {
748
+ const client = makeClient();
749
+ const spinner = spin("Enabling autonomy\u2026");
750
+ try {
751
+ await client.agents.setAutonomy(opts.agent, {
752
+ enabled: true,
753
+ intervalSec: parseInt(opts.interval, 10)
754
+ });
755
+ spinner.stop();
756
+ console.log(
757
+ `
758
+ ${sym.ok} Autonomy enabled for agent ${c.id(opts.agent)}`
759
+ );
760
+ console.log(c.dim(` Heartbeat every ${opts.interval}s`));
761
+ } catch (err) {
762
+ spinner.stop();
763
+ printError(err);
764
+ process.exit(1);
765
+ }
766
+ });
767
+ autonomy.command("disable").description("Disable heartbeat for an agent").requiredOption("--agent <agentId>", "Agent ID").action(async (opts) => {
768
+ const client = makeClient();
769
+ const spinner = spin("Disabling autonomy\u2026");
770
+ try {
771
+ await client.agents.setAutonomy(opts.agent, { enabled: false });
772
+ spinner.stop();
773
+ console.log(
774
+ `
775
+ ${sym.ok} Autonomy disabled for agent ${c.id(opts.agent)}`
776
+ );
777
+ } catch (err) {
778
+ spinner.stop();
779
+ printError(err);
780
+ process.exit(1);
781
+ }
782
+ });
783
+ autonomy.command("trigger").description("Trigger a single heartbeat immediately").requiredOption("--agent <agentId>", "Agent ID").action(async (opts) => {
784
+ const client = makeClient();
785
+ const spinner = spin("Triggering heartbeat\u2026");
786
+ try {
787
+ await client.agents.triggerHeartbeat(opts.agent);
788
+ spinner.stop();
789
+ console.log(
790
+ `
791
+ ${sym.ok} Heartbeat triggered for agent ${c.id(opts.agent)}`
792
+ );
793
+ } catch (err) {
794
+ spinner.stop();
795
+ printError(err);
796
+ process.exit(1);
797
+ }
798
+ });
799
+ return cmd;
800
+ }
801
+
802
+ // src/commands/sessions.ts
803
+ var import_commander3 = require("commander");
804
+ function sessionsCommand() {
805
+ const cmd = new import_commander3.Command("sessions").description("Manage chat sessions");
806
+ cmd.command("list").description("List sessions \u2014 all for the current user, or filtered by agent").option("--agent <agentId>", "Filter by agent ID (default: all agents)").option("--json", "Output as JSON").action(async (opts) => {
807
+ const cfg = loadConfig();
808
+ if (!cfg.initiator) {
809
+ console.error(c.error("No initiator set. Run `agc login` first."));
810
+ process.exit(1);
811
+ }
812
+ const spinner = spin("Fetching sessions\u2026");
813
+ try {
814
+ const client = makeClient();
815
+ const agentId = opts.agent ?? cfg.defaultAgentId;
816
+ const res = agentId ? await client.sessions.list(agentId, cfg.initiator) : await client.sessions.listByUser(cfg.initiator);
817
+ const sessions = res?.data ?? res ?? [];
818
+ spinner.stop();
819
+ if (opts.json) return jsonOut(sessions);
820
+ section(`Sessions (${sessions.length})${agentId ? ` \u2014 agent ${agentId.slice(0, 8)}\u2026` : " \u2014 all agents"}`);
821
+ table(
822
+ sessions.map((s) => ({
823
+ ID: s.sessionId.slice(0, 8) + "\u2026",
824
+ Agent: s.agentId ? s.agentId.slice(0, 8) + "\u2026" : "",
825
+ Title: s.title ?? c.dim("(untitled)"),
826
+ Created: relativeTime(s.createdAt)
827
+ })),
828
+ ["ID", "Agent", "Title", "Created"]
829
+ );
830
+ } catch (err) {
831
+ spinner.stop();
832
+ printError(err);
833
+ process.exit(1);
834
+ }
835
+ });
836
+ cmd.command("get <sessionId>").description("Show session details").option("--json", "Output as JSON").action(async (sessionId, opts) => {
837
+ const spinner = spin("Fetching session\u2026");
838
+ try {
839
+ const client = makeClient();
840
+ const res = await client.sessions.get(sessionId);
841
+ const session = res?.data ?? res;
842
+ spinner.stop();
843
+ if (opts.json) return jsonOut(session);
844
+ section("Session");
845
+ detail([
846
+ ["Session ID", c.id(session.sessionId)],
847
+ ["Title", session.title ?? c.dim("(untitled)")],
848
+ ["Agent ID", session.agentId],
849
+ ["Model", session.model?.modelId ?? session.model?.name ?? ""],
850
+ ["Initiator", session.initiator ?? ""],
851
+ ["Created", relativeTime(session.createdAt)]
852
+ ]);
853
+ } catch (err) {
854
+ spinner.stop();
855
+ printError(err);
856
+ process.exit(1);
857
+ }
858
+ });
859
+ 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) => {
860
+ const cfg = loadConfig();
861
+ const agentId = opts.agent ?? cfg.defaultAgentId;
862
+ if (!agentId) {
863
+ console.error(c.error("Specify --agent <agentId> or set defaultAgentId"));
864
+ process.exit(1);
865
+ }
866
+ if (!cfg.initiator) {
867
+ console.error(c.error("No initiator set. Run `agc login` first."));
868
+ process.exit(1);
869
+ }
870
+ const spinner = spin("Creating session\u2026");
871
+ try {
872
+ const client = makeClient();
873
+ const res = await client.sessions.create({
874
+ agentId,
875
+ initiator: cfg.initiator,
876
+ title: opts.title,
877
+ ...opts.model && { model: { modelId: opts.model, provider: opts.provider } }
878
+ });
879
+ const session = res?.data ?? res;
880
+ spinner.stop();
881
+ if (opts.json) return jsonOut(session);
882
+ console.log(`
883
+ ${sym.ok} Session created`);
884
+ detail([
885
+ ["Session ID", c.id(session.sessionId)],
886
+ ["Title", session.title ?? c.dim("(untitled)")]
887
+ ]);
888
+ } catch (err) {
889
+ spinner.stop();
890
+ printError(err);
891
+ process.exit(1);
892
+ }
893
+ });
894
+ return cmd;
895
+ }
896
+
897
+ // src/commands/tools.ts
898
+ var import_commander4 = require("commander");
899
+ var import_fs3 = require("fs");
900
+ function toolsCommand() {
901
+ const cmd = new import_commander4.Command("tools").description("Discover and manage tools");
902
+ cmd.command("create").description("Create a tool from a JSON file").requiredOption("--file <path>", "Path to a JSON tool definition").option("--json", "Output as JSON").action(async (opts) => {
903
+ const cfg = loadConfig();
904
+ if (!cfg.initiator) {
905
+ console.error(c.error("No initiator set. Run `agc login` first."));
906
+ process.exit(1);
907
+ }
908
+ let payload;
909
+ try {
910
+ payload = JSON.parse((0, import_fs3.readFileSync)(opts.file, "utf8"));
911
+ } catch (error) {
912
+ console.error(c.error(`Could not read tool file: ${error.message}`));
913
+ process.exit(1);
914
+ }
915
+ if (!payload.name || !payload.schema) {
916
+ console.error(c.error('Tool file must include at least "name" and "schema".'));
917
+ process.exit(1);
918
+ }
919
+ const spinner = spin("Creating tool\u2026");
920
+ try {
921
+ const client = makeClient();
922
+ const res = await client.tools.create({
923
+ ...payload,
924
+ owner: cfg.initiator,
925
+ ownerType: payload.ownerType ?? "user"
926
+ });
927
+ const tool = res?.data ?? res;
928
+ spinner.stop();
929
+ if (opts.json) return jsonOut(tool);
930
+ console.log(`
931
+ ${sym.ok} Tool created`);
932
+ detail([
933
+ ["Tool ID", c.id(tool.toolId)],
934
+ ["Name", tool.name],
935
+ ["Visibility", tool.visibility ?? payload.visibility ?? "private"]
936
+ ]);
937
+ } catch (err) {
938
+ spinner.stop();
939
+ printError(err);
940
+ process.exit(1);
941
+ }
942
+ });
943
+ cmd.command("list").description("List available tools").option("--owner <id>", "Filter by owner ID").option("--json", "Output as JSON").action(async (opts) => {
944
+ const cfg = loadConfig();
945
+ const spinner = spin("Fetching tools\u2026");
946
+ try {
947
+ const client = makeClient();
948
+ const filter = opts.owner ? { owner: opts.owner } : {};
949
+ const res = await client.tools.list(filter);
950
+ const tools = res?.data ?? res ?? [];
951
+ spinner.stop();
952
+ if (opts.json) return jsonOut(tools);
953
+ section(`Tools (${tools.length})`);
954
+ table(
955
+ tools.map((t) => ({
956
+ ID: (t.toolId ?? "").slice(0, 8) + "\u2026",
957
+ Name: t.name ?? "",
958
+ Description: (t.description ?? "").slice(0, 50),
959
+ Tags: (t.tags ?? []).join(", ")
960
+ })),
961
+ ["ID", "Name", "Description", "Tags"]
962
+ );
963
+ } catch (err) {
964
+ spinner.stop();
965
+ printError(err);
966
+ process.exit(1);
967
+ }
968
+ });
969
+ cmd.command("get <toolId>").description("Show tool details and schema").option("--json", "Output as JSON").action(async (toolId, opts) => {
970
+ const spinner = spin("Fetching tool\u2026");
971
+ try {
972
+ const client = makeClient();
973
+ const res = await client.tools.list({ toolId });
974
+ const tools = res?.data ?? res ?? [];
975
+ const tool = tools.find((t) => t.toolId === toolId || t.name === toolId);
976
+ spinner.stop();
977
+ if (!tool) {
978
+ console.error(c.error(`Tool "${toolId}" not found.`));
979
+ process.exit(1);
980
+ }
981
+ if (opts.json) return jsonOut(tool);
982
+ section(tool.name);
983
+ detail([
984
+ ["Tool ID", c.id(tool.toolId)],
985
+ ["Description", tool.description ?? c.dim("(none)")],
986
+ ["Tags", (tool.tags ?? []).join(", ") || c.dim("(none)")],
987
+ ["Public", tool.isPublic ? "yes" : "no"],
988
+ ["Created", relativeTime(tool.createdAt)]
989
+ ]);
990
+ if (tool.schema) {
991
+ console.log("\n " + c.label("Schema"));
992
+ console.log(" " + JSON.stringify(tool.schema, null, 2).split("\n").join("\n "));
993
+ }
994
+ } catch (err) {
995
+ spinner.stop();
996
+ printError(err);
997
+ process.exit(1);
998
+ }
999
+ });
1000
+ 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) => {
1001
+ const cfg = loadConfig();
1002
+ const agentId = opts.agent ?? cfg.defaultAgentId;
1003
+ if (!agentId) {
1004
+ console.error(c.error("Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`"));
1005
+ process.exit(1);
1006
+ }
1007
+ let args = {};
1008
+ try {
1009
+ args = JSON.parse(opts.args);
1010
+ } catch {
1011
+ console.error(c.error("--args must be valid JSON"));
1012
+ process.exit(1);
1013
+ }
1014
+ const prompt2 = `Call the tool "${toolName}" with these arguments: ${JSON.stringify(args)}. Return only the tool result, nothing else.`;
1015
+ const spinner = spin(`Executing ${toolName}\u2026`);
1016
+ try {
1017
+ const client = makeClient();
1018
+ const result = await client.run.once({
1019
+ agentId,
1020
+ messages: [{ role: "user", content: prompt2 }],
1021
+ ...cfg.initiator && { initiatorId: cfg.initiator }
1022
+ });
1023
+ spinner.stop();
1024
+ if (opts.json) return jsonOut(result);
1025
+ console.log(`
1026
+ ${sym.ok} ${c.label(toolName)}`);
1027
+ const text = result?.content ?? result?.text ?? result?.message ?? JSON.stringify(result, null, 2);
1028
+ console.log(text);
1029
+ } catch (err) {
1030
+ spinner.stop();
1031
+ printError(err);
1032
+ process.exit(1);
1033
+ }
1034
+ });
1035
+ return cmd;
1036
+ }
1037
+
1038
+ // src/commands/connections.ts
1039
+ var import_commander5 = require("commander");
1040
+ function connectionsCommand() {
1041
+ const cmd = new import_commander5.Command("connections").description(
1042
+ "Manage OAuth account connections (Google Workspace, GitHub, Slack, \u2026) that agents act with"
1043
+ );
1044
+ cmd.command("list", { isDefault: true }).description("List your connected accounts").option("--json", "Output as JSON").action(async (opts) => {
1045
+ const cfg = loadConfig();
1046
+ const spinner = spin("Fetching connections\u2026");
1047
+ try {
1048
+ const client = makeClient();
1049
+ const res = await client.oauth.listConnections(
1050
+ cfg.initiator ? { ownerId: cfg.initiator, ownerType: "user" } : void 0
1051
+ );
1052
+ const connections = res?.connections ?? [];
1053
+ spinner.stop();
1054
+ if (opts.json) return jsonOut(connections);
1055
+ section(`Connections (${connections.length})`);
1056
+ if (connections.length === 0) {
1057
+ console.log(c.dim(" No connected accounts. Run `agc connections connect <provider>`."));
1058
+ return;
1059
+ }
1060
+ table(
1061
+ connections.map((conn) => ({
1062
+ ID: (conn.connectionId ?? "").slice(0, 8) + "\u2026",
1063
+ Provider: conn.providerDisplayName || conn.providerKey || "",
1064
+ Account: conn.providerUserEmail || conn.providerUserName || "",
1065
+ Status: conn.status ?? "",
1066
+ Scopes: String((conn.scopes ?? []).length),
1067
+ Used: conn.lastUsedAt ? relativeTime(conn.lastUsedAt) : c.dim("never")
1068
+ })),
1069
+ ["ID", "Provider", "Account", "Status", "Scopes", "Used"]
1070
+ );
1071
+ } catch (err) {
1072
+ spinner.stop();
1073
+ printError(err);
1074
+ process.exit(1);
1075
+ }
1076
+ });
1077
+ cmd.command("providers").description("List OAuth providers available to connect").option("--json", "Output as JSON").action(async (opts) => {
1078
+ const spinner = spin("Fetching providers\u2026");
1079
+ try {
1080
+ const client = makeClient();
1081
+ const res = await client.oauth.listProviders();
1082
+ const providers = res?.providers ?? [];
1083
+ spinner.stop();
1084
+ if (opts.json) return jsonOut(providers);
1085
+ section(`Providers (${providers.length})`);
1086
+ table(
1087
+ providers.map((p) => ({
1088
+ Key: p.providerKey ?? "",
1089
+ Name: p.displayName ?? "",
1090
+ Active: p.isActive ? "yes" : "no"
1091
+ })),
1092
+ ["Key", "Name", "Active"]
1093
+ );
1094
+ } catch (err) {
1095
+ spinner.stop();
1096
+ printError(err);
1097
+ process.exit(1);
1098
+ }
1099
+ });
1100
+ 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("--json", "Output as JSON").action(async (providerKey, opts) => {
1101
+ const cfg = loadConfig();
1102
+ if (!cfg.initiator) {
1103
+ console.error(c.error("No initiator set. Run `agc login` first."));
1104
+ process.exit(1);
1105
+ }
1106
+ const spinner = spin("Starting OAuth flow\u2026");
1107
+ try {
1108
+ const client = makeClient();
1109
+ const res = await client.oauth.connect({
1110
+ providerKey,
1111
+ ...opts.scopes ? { scopes: String(opts.scopes).split(/\s+/).filter(Boolean) } : {}
1112
+ });
1113
+ spinner.stop();
1114
+ if (opts.json) return jsonOut(res);
1115
+ console.log(`
1116
+ ${sym.ok} Open this URL in your browser to authorize:`);
1117
+ console.log(`
1118
+ ${c.id(res.authorizationUrl)}
1119
+ `);
1120
+ console.log(c.dim(" After approving, the connection appears in `agc connections list`."));
1121
+ } catch (err) {
1122
+ spinner.stop();
1123
+ printError(err);
1124
+ process.exit(1);
1125
+ }
1126
+ });
1127
+ 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) => {
1128
+ const spinner = spin("Testing connection\u2026");
1129
+ try {
1130
+ const client = makeClient();
1131
+ const res = await client.oauth.test(connectionId);
1132
+ spinner.stop();
1133
+ if (opts.json) return jsonOut(res);
1134
+ detail([
1135
+ ["Status", res.status],
1136
+ ["Token valid", res.accessTokenValid ? "yes" : "no"],
1137
+ ["Account", res.providerUserEmail ?? c.dim("(unknown)")],
1138
+ ["Last error", res.error ?? c.dim("(none)")]
1139
+ ]);
1140
+ } catch (err) {
1141
+ spinner.stop();
1142
+ printError(err);
1143
+ process.exit(1);
1144
+ }
1145
+ });
1146
+ cmd.command("revoke <connectionId>").description("Revoke a connection and delete its stored tokens").action(async (connectionId) => {
1147
+ const spinner = spin("Revoking connection\u2026");
1148
+ try {
1149
+ const client = makeClient();
1150
+ await client.oauth.revoke(connectionId);
1151
+ spinner.stop();
1152
+ console.log(`${sym.ok} Connection revoked.`);
1153
+ } catch (err) {
1154
+ spinner.stop();
1155
+ printError(err);
1156
+ process.exit(1);
1157
+ }
1158
+ });
1159
+ return cmd;
1160
+ }
1161
+
1162
+ // src/commands/workflow.ts
1163
+ var import_commander6 = require("commander");
1164
+ var import_fs4 = require("fs");
1165
+ var import_sdk2 = require("@agent-commons/sdk");
1166
+ function workflowCommand() {
1167
+ const cmd = new import_commander6.Command("workflow").description("Run and monitor workflows").alias("wf");
1168
+ async function createTemplateWorkflow(params) {
1169
+ const client = makeClient();
1170
+ const template = (0, import_sdk2.buildWorkflowTemplate)(params.templateName, params.ctx);
1171
+ const toolIds = {};
1172
+ const createdTools = [];
1173
+ for (const tool of template.tools) {
1174
+ const created = await client.tools.create({
1175
+ ...tool.payload,
1176
+ owner: params.ctx.ownerId,
1177
+ ownerType: "user"
1178
+ });
1179
+ const createdTool = created?.data ?? created;
1180
+ toolIds[tool.key] = createdTool.toolId;
1181
+ createdTools.push(createdTool);
1182
+ }
1183
+ const workflow = await client.workflows.create({
1184
+ name: template.name,
1185
+ description: template.description,
1186
+ ownerId: params.ctx.ownerId,
1187
+ ownerType: "user",
1188
+ isPublic: params.isPublic,
1189
+ category: template.category,
1190
+ tags: template.tags,
1191
+ definition: template.buildDefinition(toolIds, params.ctx)
1192
+ });
1193
+ return { template, workflow, createdTools };
1194
+ }
1195
+ cmd.command("list").description("List workflows owned by the current initiator").option("--json", "Output as JSON").action(async (opts) => {
1196
+ const cfg = loadConfig();
1197
+ if (!cfg.initiator) {
1198
+ console.error(c.error("No initiator set. Run `agc login` first."));
1199
+ process.exit(1);
1200
+ }
1201
+ const spinner = spin("Fetching workflows\u2026");
1202
+ try {
1203
+ const client = makeClient();
1204
+ const workflows = await client.workflows.list(cfg.initiator, "user");
1205
+ spinner.stop();
1206
+ if (opts.json) return jsonOut(workflows);
1207
+ section(`Workflows (${workflows.length})`);
1208
+ table(
1209
+ workflows.map((w) => ({
1210
+ ID: w.workflowId.slice(0, 8) + "\u2026",
1211
+ Name: w.name,
1212
+ Nodes: String((w.definition?.nodes ?? []).length),
1213
+ Public: w.isPublic ? "yes" : "no",
1214
+ Created: relativeTime(w.createdAt)
1215
+ })),
1216
+ ["ID", "Name", "Nodes", "Public", "Created"]
1217
+ );
1218
+ } catch (err) {
1219
+ spinner.stop();
1220
+ printError(err);
1221
+ process.exit(1);
1222
+ }
1223
+ });
1224
+ 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) => {
1225
+ const cfg = loadConfig();
1226
+ if (!cfg.initiator) {
1227
+ console.error(c.error("No initiator set. Run `agc login` first."));
1228
+ process.exit(1);
1229
+ }
1230
+ let fileJson;
1231
+ try {
1232
+ fileJson = JSON.parse((0, import_fs4.readFileSync)(opts.file, "utf8"));
1233
+ } catch (error) {
1234
+ console.error(c.error(`Could not read workflow file: ${error.message}`));
1235
+ process.exit(1);
1236
+ }
1237
+ const definition = fileJson.definition ?? fileJson;
1238
+ if (!Array.isArray(definition.nodes) || !Array.isArray(definition.edges)) {
1239
+ console.error(c.error('Workflow file must include a definition with "nodes" and "edges".'));
1240
+ process.exit(1);
1241
+ }
1242
+ const spinner = spin("Creating workflow\u2026");
1243
+ try {
1244
+ const client = makeClient();
1245
+ const workflow = await client.workflows.create({
1246
+ name: opts.name ?? fileJson.name ?? "CLI Workflow",
1247
+ description: opts.description ?? fileJson.description,
1248
+ definition,
1249
+ ownerId: cfg.initiator,
1250
+ ownerType: "user",
1251
+ isPublic: opts.public ?? fileJson.isPublic,
1252
+ category: fileJson.category,
1253
+ tags: fileJson.tags
1254
+ });
1255
+ spinner.stop();
1256
+ if (opts.json) return jsonOut(workflow);
1257
+ console.log(`
1258
+ ${sym.ok} Workflow created`);
1259
+ detail([
1260
+ ["Workflow ID", c.id(workflow.workflowId)],
1261
+ ["Name", workflow.name],
1262
+ ["Nodes", String((workflow.definition?.nodes ?? []).length)]
1263
+ ]);
1264
+ } catch (err) {
1265
+ spinner.stop();
1266
+ printError(err);
1267
+ process.exit(1);
1268
+ }
1269
+ });
1270
+ const templates = cmd.command("templates").description("Create workflows from built-in templates");
1271
+ templates.command("list").description("List built-in workflow templates").option("--json", "Output as JSON").action((opts) => {
1272
+ const rows = (0, import_sdk2.listWorkflowTemplates)();
1273
+ if (opts.json) return jsonOut(rows);
1274
+ section(`Workflow Templates (${rows.length})`);
1275
+ table(
1276
+ rows.map((template) => ({
1277
+ Name: template.name,
1278
+ Description: template.description
1279
+ })),
1280
+ ["Name", "Description"]
1281
+ );
1282
+ });
1283
+ templates.command("create <templateName>").description("Create a workflow template and its required API tools").option("--prefix <prefix>", "Stable prefix for generated tool/workflow names").option("--agent <agentId>", "Agent ID for agent_processor nodes").option("--reviewer-agent <agentId>", "Second agent ID for multi-agent templates").option("--child-workflow <workflowId>", "Child workflow ID for workflow-invocation-smoke").option("--public", "Make workflow public").option("--run", "Run the workflow after creating it").option("--input <json>", "Run input JSON; defaults to template sample input").option("--json", "Output as JSON").action(async (templateNameRaw, opts) => {
1284
+ const cfg = loadConfig();
1285
+ if (!cfg.initiator) {
1286
+ console.error(c.error("No initiator set. Run `agc login` first."));
1287
+ process.exit(1);
1288
+ }
1289
+ const templateNames = (0, import_sdk2.listWorkflowTemplates)().map((item) => item.name);
1290
+ if (!templateNames.includes(templateNameRaw)) {
1291
+ console.error(c.error(`Unknown template "${templateNameRaw}".`));
1292
+ console.error(c.dim(`Available: ${templateNames.join(", ")}`));
1293
+ process.exit(1);
1294
+ }
1295
+ const templateName = templateNameRaw;
1296
+ const needsAgent = templateName === "agent-research-summary" || templateName === "multi-agent-field-report";
1297
+ const agentId = opts.agent ?? cfg.defaultAgentId;
1298
+ if (needsAgent && !agentId) {
1299
+ console.error(c.error("This template requires --agent <agentId> or a configured defaultAgentId."));
1300
+ process.exit(1);
1301
+ }
1302
+ const prefix = opts.prefix ?? `cli_${templateName.replace(/[^a-z0-9]+/gi, "_")}_${Date.now().toString(36)}`;
1303
+ const spinner = spin("Creating workflow template\u2026");
1304
+ try {
1305
+ let childWorkflowId = opts.childWorkflow;
1306
+ let childResult;
1307
+ if (templateName === "workflow-invocation-smoke" && !childWorkflowId) {
1308
+ const childCtx = {
1309
+ ownerId: cfg.initiator,
1310
+ prefix: `${prefix}_child`
1311
+ };
1312
+ childResult = await createTemplateWorkflow({
1313
+ templateName: "country-weather-brief",
1314
+ ctx: childCtx,
1315
+ isPublic: opts.public
1316
+ });
1317
+ childWorkflowId = childResult.workflow.workflowId;
1318
+ }
1319
+ const ctx = {
1320
+ ownerId: cfg.initiator,
1321
+ prefix,
1322
+ agentId,
1323
+ reviewerAgentId: opts.reviewerAgent,
1324
+ childWorkflowId
1325
+ };
1326
+ const result = await createTemplateWorkflow({
1327
+ templateName,
1328
+ ctx,
1329
+ isPublic: opts.public
1330
+ });
1331
+ let execution;
1332
+ if (opts.run) {
1333
+ let inputData = result.template.sampleInput;
1334
+ if (opts.input) {
1335
+ try {
1336
+ inputData = JSON.parse(opts.input);
1337
+ } catch {
1338
+ throw new Error("--input must be valid JSON");
1339
+ }
1340
+ }
1341
+ execution = await makeClient().workflows.execute(result.workflow.workflowId, {
1342
+ agentId,
1343
+ inputData,
1344
+ userId: cfg.initiator
1345
+ });
1346
+ }
1347
+ spinner.stop();
1348
+ const output = { ...result, child: childResult, execution };
1349
+ if (opts.json) return jsonOut(output);
1350
+ console.log(`
1351
+ ${sym.ok} Workflow template created`);
1352
+ if (childResult) {
1353
+ detail([
1354
+ ["Child workflow", c.id(childResult.workflow.workflowId)],
1355
+ ["Parent workflow", c.id(result.workflow.workflowId)],
1356
+ ["Template", templateName]
1357
+ ]);
1358
+ } else {
1359
+ detail([
1360
+ ["Workflow ID", c.id(result.workflow.workflowId)],
1361
+ ["Template", templateName],
1362
+ ["Tools created", String(result.createdTools.length)]
1363
+ ]);
1364
+ }
1365
+ if (execution) {
1366
+ console.log(`
1367
+ ${sym.ok} Execution started: ${c.id(execution.executionId)}`);
1368
+ console.log(` Status: ${statusBadge(execution.status)}`);
1369
+ const resultData = execution.result ?? execution.outputData;
1370
+ if (resultData !== void 0) {
1371
+ console.log("\n" + c.label("Result"));
1372
+ console.log(" " + JSON.stringify(resultData, null, 2).replace(/\n/g, "\n "));
1373
+ }
1374
+ } else {
1375
+ console.log(c.dim(`
1376
+ Run it with: agc workflow run ${result.workflow.workflowId} --input '${JSON.stringify(result.template.sampleInput)}'`));
1377
+ }
1378
+ } catch (err) {
1379
+ spinner.stop();
1380
+ printError(err);
1381
+ process.exit(1);
1382
+ }
1383
+ });
1384
+ cmd.command("get <workflowId>").description("Show workflow details").option("--json", "Output as JSON").action(async (workflowId, opts) => {
1385
+ const spinner = spin("Fetching workflow\u2026");
1386
+ try {
1387
+ const client = makeClient();
1388
+ const wf = await client.workflows.get(workflowId);
1389
+ spinner.stop();
1390
+ if (opts.json) return jsonOut(wf);
1391
+ section(wf.name);
1392
+ detail([
1393
+ ["Workflow ID", c.id(wf.workflowId)],
1394
+ ["Description", wf.description ?? c.dim("(none)")],
1395
+ ["Nodes", String((wf.definition?.nodes ?? []).length)],
1396
+ ["Public", wf.isPublic ? "yes" : "no"],
1397
+ ["Created", relativeTime(wf.createdAt)]
1398
+ ]);
1399
+ if (wf.definition?.nodes?.length) {
1400
+ console.log("\n " + c.label("Nodes"));
1401
+ for (const node of wf.definition.nodes) {
1402
+ console.log(` ${c.dim("\xB7")} ${node.id} ${c.dim("(" + (node.type ?? "tool") + ")")}`);
1403
+ }
1404
+ }
1405
+ } catch (err) {
1406
+ spinner.stop();
1407
+ printError(err);
1408
+ process.exit(1);
1409
+ }
1410
+ });
1411
+ 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) => {
1412
+ const cfg = loadConfig();
1413
+ const agentId = opts.agent ?? cfg.defaultAgentId;
1414
+ let inputData = {};
1415
+ try {
1416
+ inputData = JSON.parse(opts.input);
1417
+ } catch {
1418
+ console.error(c.error("--input must be valid JSON"));
1419
+ process.exit(1);
1420
+ }
1421
+ const spinner = spin("Executing workflow\u2026");
1422
+ try {
1423
+ const client = makeClient();
1424
+ const execution = await client.workflows.execute(workflowId, {
1425
+ agentId,
1426
+ sessionId: opts.session,
1427
+ inputData
1428
+ });
1429
+ spinner.stop();
1430
+ if (opts.json && !opts.watch) return jsonOut(execution);
1431
+ console.log(`
1432
+ ${sym.ok} Execution started: ${c.id(execution.executionId)}`);
1433
+ console.log(` Status: ${statusBadge(execution.status)}`);
1434
+ if (!opts.watch) {
1435
+ const result = execution.result ?? execution.outputData;
1436
+ if (execution.status === "completed") {
1437
+ console.log("\n" + c.label("Result"));
1438
+ console.log(" " + JSON.stringify(result, null, 2));
1439
+ const steps = execution.stepResults ?? execution.nodeResults;
1440
+ if (steps && Object.keys(steps).length > 0) {
1441
+ console.log("\n" + c.label("Step Results"));
1442
+ for (const [nodeId, step2] of Object.entries(steps)) {
1443
+ const icon = step2.status === "success" ? sym.ok : step2.status === "error" ? sym.fail : "\xB7";
1444
+ const dur = step2.duration != null ? c.dim(` (${(step2.duration / 1e3).toFixed(2)}s)`) : "";
1445
+ console.log(` ${icon} ${c.id(nodeId)}${dur}`);
1446
+ if (step2.error) console.log(` ${c.error(step2.error)}`);
1447
+ else if (step2.output !== void 0) console.log(` ${JSON.stringify(step2.output, null, 2).replace(/\n/g, "\n ")}`);
1448
+ }
1449
+ }
1450
+ } else {
1451
+ console.log(c.dim(`
1452
+ Workflow is ${execution.status}. Use --watch to stream progress.`));
1453
+ }
1454
+ return;
1455
+ }
1456
+ console.log(c.dim("\nStreaming execution progress...\n"));
1457
+ for await (const event of client.workflows.stream(workflowId, execution.executionId)) {
1458
+ if (event.type === "status") {
1459
+ const e = event;
1460
+ process.stdout.write(`\r ${statusBadge(e.status ?? "")} node: ${c.dim(e.currentNode ?? "\u2026")} `);
1461
+ } else if (event.type === "completed") {
1462
+ process.stdout.write("\n");
1463
+ console.log(`
1464
+ ${sym.ok} ${c.success("Completed")}`);
1465
+ const e = event;
1466
+ if (e.outputData != null) {
1467
+ console.log("\n" + c.label("Output"));
1468
+ console.log(" " + JSON.stringify(e.outputData, null, 2));
1469
+ }
1470
+ if (e.nodeResults && Object.keys(e.nodeResults).length > 0) {
1471
+ console.log("\n" + c.label("Step Results"));
1472
+ for (const [nodeId, step2] of Object.entries(e.nodeResults)) {
1473
+ const icon = step2.status === "success" ? sym.ok : step2.status === "error" ? sym.fail : "\xB7";
1474
+ const dur = step2.duration != null ? c.dim(` (${(step2.duration / 1e3).toFixed(2)}s)`) : "";
1475
+ console.log(` ${icon} ${c.id(nodeId)}${dur}`);
1476
+ if (step2.error) console.log(` ${c.error(step2.error)}`);
1477
+ else if (step2.output !== void 0) console.log(` ${JSON.stringify(step2.output, null, 2).replace(/\n/g, "\n ")}`);
1478
+ }
1479
+ }
1480
+ break;
1481
+ } else if (event.type === "failed" || event.type === "cancelled") {
1482
+ process.stdout.write("\n");
1483
+ console.error(`
1484
+ ${sym.fail} ${c.error(event.errorMessage ?? event.type)}`);
1485
+ break;
1486
+ } else if (event.type === "awaiting_approval") {
1487
+ process.stdout.write("\n");
1488
+ const e = event;
1489
+ console.log(`
1490
+ ${c.warn("\u23F8 Awaiting approval")} at node ${c.id(e.pausedAtNode ?? "")}`);
1491
+ console.log(c.dim(` Token: ${e.approvalToken}`));
1492
+ console.log(c.dim(` Use: agc workflow approve ${workflowId} ${execution.executionId} <token>`));
1493
+ }
1494
+ }
1495
+ } catch (err) {
1496
+ spinner.stop();
1497
+ printError(err);
1498
+ process.exit(1);
1499
+ }
1500
+ });
1501
+ cmd.command("executions <workflowId>").description("List recent executions for a workflow").option("--limit <n>", "Max results", "20").option("--json", "Output as JSON").action(async (workflowId, opts) => {
1502
+ const spinner = spin("Fetching executions\u2026");
1503
+ try {
1504
+ const client = makeClient();
1505
+ const executions = await client.workflows.listExecutions(workflowId, Number(opts.limit));
1506
+ spinner.stop();
1507
+ if (opts.json) return jsonOut(executions);
1508
+ section(`Executions (${executions.length})`);
1509
+ table(
1510
+ executions.map((e) => ({
1511
+ ID: e.executionId.slice(0, 8) + "\u2026",
1512
+ Status: statusBadge(e.status),
1513
+ Node: e.currentNode ?? "",
1514
+ Started: e.startedAt ? relativeTime(e.startedAt) : ""
1515
+ })),
1516
+ ["ID", "Status", "Node", "Started"]
1517
+ );
1518
+ } catch (err) {
1519
+ spinner.stop();
1520
+ printError(err);
1521
+ process.exit(1);
1522
+ }
1523
+ });
1524
+ cmd.command("approve <workflowId> <executionId> <token>").description("Approve a paused human_approval step").option("--data <json>", "Approval data as JSON", "{}").action(async (workflowId, executionId, token, opts) => {
1525
+ let approvalData = {};
1526
+ try {
1527
+ approvalData = JSON.parse(opts.data);
1528
+ } catch {
1529
+ }
1530
+ const spinner = spin("Approving\u2026");
1531
+ try {
1532
+ const client = makeClient();
1533
+ await client.workflows.approveExecution(workflowId, executionId, {
1534
+ approvalToken: token,
1535
+ approvalData
1536
+ });
1537
+ spinner.stop();
1538
+ console.log(`${sym.ok} Execution ${c.id(executionId)} approved \u2014 workflow resuming.`);
1539
+ } catch (err) {
1540
+ spinner.stop();
1541
+ printError(err);
1542
+ process.exit(1);
1543
+ }
1544
+ });
1545
+ cmd.command("reject <workflowId> <executionId> <token>").description("Reject a paused human_approval step").option("--reason <text>", "Rejection reason").action(async (workflowId, executionId, token, opts) => {
1546
+ const spinner = spin("Rejecting\u2026");
1547
+ try {
1548
+ const client = makeClient();
1549
+ await client.workflows.rejectExecution(workflowId, executionId, {
1550
+ approvalToken: token,
1551
+ reason: opts.reason
1552
+ });
1553
+ spinner.stop();
1554
+ console.log(`${sym.ok} Execution ${c.id(executionId)} rejected.`);
1555
+ } catch (err) {
1556
+ spinner.stop();
1557
+ printError(err);
1558
+ process.exit(1);
1559
+ }
1560
+ });
1561
+ return cmd;
1562
+ }
1563
+
1564
+ // src/commands/task.ts
1565
+ var import_commander7 = require("commander");
1566
+ function taskCommand() {
1567
+ const cmd = new import_commander7.Command("task").description("Manage and execute tasks").alias("t");
1568
+ 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) => {
1569
+ const cfg = loadConfig();
1570
+ const agentId = opts.agent ?? cfg.defaultAgentId;
1571
+ const spinner = spin("Fetching tasks\u2026");
1572
+ try {
1573
+ const client = makeClient();
1574
+ const filter = {};
1575
+ if (agentId) filter.agentId = agentId;
1576
+ if (opts.session) filter.sessionId = opts.session;
1577
+ if (cfg.initiator) {
1578
+ filter.ownerId = cfg.initiator;
1579
+ filter.ownerType = "user";
1580
+ }
1581
+ const res = await client.tasks.list(filter);
1582
+ const tasks = res?.data ?? res ?? [];
1583
+ spinner.stop();
1584
+ if (opts.json) return jsonOut(tasks);
1585
+ section(`Tasks (${tasks.length})`);
1586
+ table(
1587
+ tasks.map((t) => ({
1588
+ ID: t.taskId.slice(0, 8) + "\u2026",
1589
+ Title: (t.title ?? t.description ?? "").slice(0, 40),
1590
+ Status: statusBadge(t.status ?? ""),
1591
+ Agent: (t.agentId ?? "").slice(0, 8) + "\u2026",
1592
+ Created: relativeTime(t.createdAt)
1593
+ })),
1594
+ ["ID", "Title", "Status", "Agent", "Created"]
1595
+ );
1596
+ } catch (err) {
1597
+ spinner.stop();
1598
+ printError(err);
1599
+ process.exit(1);
1600
+ }
1601
+ });
1602
+ cmd.command("get <taskId>").description("Show task details").option("--json", "Output as JSON").action(async (taskId, opts) => {
1603
+ const spinner = spin("Fetching task\u2026");
1604
+ try {
1605
+ const client = makeClient();
1606
+ const res = await client.tasks.get(taskId);
1607
+ const task = res?.data ?? res;
1608
+ spinner.stop();
1609
+ if (opts.json) return jsonOut(task);
1610
+ section("Task");
1611
+ detail([
1612
+ ["Task ID", c.id(task.taskId)],
1613
+ ["Title", task.title ?? task.description ?? c.dim("(none)")],
1614
+ ["Status", statusBadge(task.status ?? "")],
1615
+ ["Agent ID", task.agentId ?? c.dim("(none)")],
1616
+ ["Session ID", task.sessionId ?? c.dim("(none)")],
1617
+ ["Created", relativeTime(task.createdAt)]
1618
+ ]);
1619
+ if (task.result) {
1620
+ console.log("\n " + c.label("Result"));
1621
+ console.log(" " + JSON.stringify(task.result, null, 2).split("\n").join("\n "));
1622
+ }
1623
+ } catch (err) {
1624
+ spinner.stop();
1625
+ printError(err);
1626
+ process.exit(1);
1627
+ }
1628
+ });
1629
+ 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) => {
1630
+ const cfg = loadConfig();
1631
+ const agentId = opts.agent ?? cfg.defaultAgentId;
1632
+ if (!agentId) {
1633
+ console.error(c.error("Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`"));
1634
+ process.exit(1);
1635
+ }
1636
+ let inputData = {};
1637
+ try {
1638
+ inputData = JSON.parse(opts.input);
1639
+ } catch {
1640
+ console.error(c.error("--input must be valid JSON"));
1641
+ process.exit(1);
1642
+ }
1643
+ const spinner = spin("Creating task\u2026");
1644
+ try {
1645
+ const client = makeClient();
1646
+ const res = await client.tasks.create({
1647
+ title: opts.title,
1648
+ agentId,
1649
+ sessionId: opts.session,
1650
+ workflowId: opts.workflow,
1651
+ inputData,
1652
+ ...opts.timeout && { timeoutMs: Number(opts.timeout) },
1653
+ ...cfg.initiator && { ownerId: cfg.initiator, ownerType: "user" }
1654
+ });
1655
+ const task = res?.data ?? res;
1656
+ spinner.stop();
1657
+ if (opts.json && !opts.execute && !opts.watch) return jsonOut(task);
1658
+ console.log(`
1659
+ ${sym.ok} Task created: ${c.id(task.taskId)}`);
1660
+ if (!opts.execute && !opts.watch) return;
1661
+ const execSpinner = spin("Executing task\u2026");
1662
+ const execRes = await client.tasks.execute(task.taskId);
1663
+ execSpinner.stop();
1664
+ console.log(` Status: ${statusBadge(execRes?.data?.status ?? "pending")}`);
1665
+ if (!opts.watch) {
1666
+ if (execRes?.data?.result) {
1667
+ console.log("\n" + c.label("Result"));
1668
+ console.log(" " + JSON.stringify(execRes.data.result, null, 2));
1669
+ }
1670
+ return;
1671
+ }
1672
+ console.log(c.dim("\nStreaming task progress...\n"));
1673
+ for await (const event of client.tasks.stream(task.taskId)) {
1674
+ if (event.type === "token") {
1675
+ process.stdout.write(event.content ?? "");
1676
+ } else if (event.type === "status") {
1677
+ const e = event;
1678
+ process.stdout.write(`\r ${statusBadge(e.status ?? "")} `);
1679
+ } else if (event.type === "final" || event.type === "completed") {
1680
+ process.stdout.write("\n");
1681
+ console.log(`
1682
+ ${sym.ok} ${c.success("Completed")}`);
1683
+ const e = event;
1684
+ if (e.result ?? e.outputData) {
1685
+ console.log("\n" + c.label("Output"));
1686
+ console.log(" " + JSON.stringify(e.result ?? e.outputData, null, 2));
1687
+ }
1688
+ break;
1689
+ } else if (event.type === "failed" || event.type === "error") {
1690
+ process.stdout.write("\n");
1691
+ console.error(`
1692
+ ${sym.fail} ${c.error(event.message ?? event.type)}`);
1693
+ break;
1694
+ }
1695
+ }
1696
+ } catch (err) {
1697
+ spinner.stop();
1698
+ printError(err);
1699
+ process.exit(1);
1700
+ }
1701
+ });
1702
+ cmd.command("execute <taskId>").description("Execute an existing task").option("--watch", "Stream execution progress via SSE").option("--json", "Output result as JSON").action(async (taskId, opts) => {
1703
+ const spinner = spin("Executing task\u2026");
1704
+ try {
1705
+ const client = makeClient();
1706
+ const res = await client.tasks.execute(taskId);
1707
+ spinner.stop();
1708
+ if (opts.json && !opts.watch) return jsonOut(res);
1709
+ console.log(`
1710
+ ${sym.ok} Execution started`);
1711
+ console.log(` Status: ${statusBadge(res?.data?.status ?? "pending")}`);
1712
+ if (!opts.watch) return;
1713
+ console.log(c.dim("\nStreaming task progress...\n"));
1714
+ for await (const event of client.tasks.stream(taskId)) {
1715
+ if (event.type === "token") {
1716
+ process.stdout.write(event.content ?? "");
1717
+ } else if (event.type === "final" || event.type === "completed") {
1718
+ process.stdout.write("\n");
1719
+ console.log(`
1720
+ ${sym.ok} ${c.success("Completed")}`);
1721
+ break;
1722
+ } else if (event.type === "failed" || event.type === "error") {
1723
+ process.stdout.write("\n");
1724
+ console.error(`
1725
+ ${sym.fail} ${c.error(event.message ?? event.type)}`);
1726
+ break;
1727
+ }
1728
+ }
1729
+ } catch (err) {
1730
+ spinner.stop();
1731
+ printError(err);
1732
+ process.exit(1);
1733
+ }
1734
+ });
1735
+ cmd.command("cancel <taskId>").description("Cancel a running task").action(async (taskId) => {
1736
+ const spinner = spin("Cancelling task\u2026");
1737
+ try {
1738
+ const client = makeClient();
1739
+ await client.tasks.cancel(taskId);
1740
+ spinner.stop();
1741
+ console.log(`${sym.ok} Task ${c.id(taskId)} cancelled.`);
1742
+ } catch (err) {
1743
+ spinner.stop();
1744
+ printError(err);
1745
+ process.exit(1);
1746
+ }
1747
+ });
1748
+ return cmd;
1749
+ }
1750
+
1751
+ // src/commands/run.ts
1752
+ var import_commander8 = require("commander");
1753
+ var readline3 = __toESM(require("readline"));
1754
+
1755
+ // src/local-tools.ts
1756
+ var import_fs5 = require("fs");
1757
+ var import_path3 = require("path");
1758
+ var import_child_process2 = require("child_process");
1759
+ var readline2 = __toESM(require("readline"));
1760
+ var pdfParse = require("pdf-parse/lib/pdf-parse.js");
1761
+ var managedProcesses = /* @__PURE__ */ new Map();
1762
+ function capBuffer(existing, chunk, maxBytes) {
1763
+ const joined = existing + chunk;
1764
+ if (joined.length <= maxBytes) return joined;
1765
+ return "\u2026(truncated)\n" + joined.slice(-(maxBytes - 20));
1766
+ }
1767
+ var SKIP_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", ".cache", "__pycache__", ".next", "dist", "build", ".DS_Store"]);
1768
+ function buildDirSnapshot(dir, maxDepth = 2) {
1769
+ const lines = [`${dir}/`];
1770
+ function walk(d, depth, prefix) {
1771
+ if (lines.length >= 300) return;
1772
+ let entries;
1773
+ try {
1774
+ entries = (0, import_fs5.readdirSync)(d, { withFileTypes: true });
1775
+ } catch {
1776
+ return;
1777
+ }
1778
+ const sorted = entries.sort((a, b) => {
1779
+ if (a.isDirectory() !== b.isDirectory()) return a.isDirectory() ? -1 : 1;
1780
+ return a.name.localeCompare(b.name);
1781
+ });
1782
+ for (const entry of sorted) {
1783
+ if (lines.length >= 300) {
1784
+ lines.push(`${prefix}... (truncated)`);
1785
+ return;
1786
+ }
1787
+ if (entry.name.startsWith(".") || SKIP_DIRS.has(entry.name)) continue;
1788
+ const isDir = entry.isDirectory();
1789
+ lines.push(`${prefix}${entry.name}${isDir ? "/" : ""}`);
1790
+ if (isDir && depth < maxDepth) walk((0, import_path3.join)(d, entry.name), depth + 1, prefix + " ");
1791
+ }
1792
+ }
1793
+ walk(dir, 1, " ");
1794
+ return lines.join("\n");
1795
+ }
1796
+ function readFileForContext(rootDir, filePath) {
1797
+ try {
1798
+ const abs = (0, import_path3.resolve)(rootDir, filePath);
1799
+ const rel = (0, import_path3.relative)(rootDir, abs);
1800
+ if (rel.startsWith("..") || rel.startsWith("/")) return `[error: path escapes session root]`;
1801
+ for (const pat of [/\/\.ssh\//, /\/\.aws\//, /\/\.env$/, /\/\.env\./, /id_rsa/, /id_ed25519/]) {
1802
+ if (pat.test(abs)) return `[error: sensitive path blocked]`;
1803
+ }
1804
+ if (!(0, import_fs5.existsSync)(abs)) return `[error: file not found: ${filePath}]`;
1805
+ const stat = (0, import_fs5.statSync)(abs);
1806
+ if (stat.isDirectory()) return `[error: "${filePath}" is a directory \u2014 use list_directory]`;
1807
+ if (stat.size > 1e5) return `[truncated \u2014 file too large (${Math.round(stat.size / 1024)} KB). Use cli_read_file for full content]`;
1808
+ return (0, import_fs5.readFileSync)(abs, "utf8");
1809
+ } catch (err) {
1810
+ return `[error reading file: ${err?.message}]`;
1811
+ }
1812
+ }
1813
+ function buildLocalToolsManifest(rootDir, snapshot, fileContextBlocks = [], autoApprove = false) {
1814
+ const fileSection = fileContextBlocks.length ? `
1815
+ ### File contents included in this turn
1816
+
1817
+ ${fileContextBlocks.join("\n\n")}
1818
+ ` : "";
1819
+ return `
1820
+ ## CLI Local File System \u2014 ACTIVE
1821
+
1822
+ You are running inside a CLI session with DIRECT access to the user's local machine. The following tools are in your tool list and execute on the user's machine in real time.
1823
+
1824
+ **Session root:** ${rootDir}
1825
+
1826
+ ### Current file system (live snapshot)
1827
+
1828
+ \`\`\`
1829
+ ${snapshot}
1830
+ \`\`\`
1831
+ ${fileSection}
1832
+
1833
+ ### MANDATORY RULES \u2014 READ CAREFULLY
1834
+
1835
+ 1. **Call cli_* tools immediately and directly.** Do NOT create tasks (createTask) for local file operations. Do NOT delegate to sub-agents. Do NOT ask the user to run commands themselves.
1836
+ 2. **Own the request through completion.** Continue across tool calls, process polling, retries, debugging, and verification. Do not stop after describing a plan or asking whether to proceed when the request is already clear.
1837
+ 3. **Use actual tool output as evidence.** Summarize the important result; do not fabricate success or dump noisy logs unless they help diagnose a failure.
1838
+ 4. **Never fabricate results.** Wait for the real tool output before responding.
1839
+ 5. **Sensitive paths are blocked** (.ssh, .gnupg, .aws, .env, credentials). Attempting to access them will return an error.
1840
+ 6. ${autoApprove ? "**cli_write_file and cli_run_command execute immediately** \u2014 auto-approve is active, no user confirmation is required." : "**cli_write_file and cli_run_command require the user to confirm** before executing \u2014 you will see the result after they approve."}
1841
+ 7. **Git commits must carry the agc co-author trailer.** Always include \`--trailer "Co-Authored-By: <AgentName> (agc) <agc-agent@users.noreply.github.com>"\` when running \`git commit\`. The CLI injects this automatically \u2014 do not omit it or pass \`--no-trailer\`.
1842
+
1843
+ ### Available CLI tools
1844
+
1845
+ | Tool | What it does |
1846
+ |------|-------------|
1847
+ | \`cli_list_directory\` | List files and folders at a path |
1848
+ | \`cli_read_file\` | Read a file (PDF and Word docs are extracted to text) |
1849
+ | \`cli_write_file\` | Write or overwrite a file (user confirmation required) |
1850
+ | \`cli_search_files\` | Find files matching a pattern |
1851
+ | \`cli_run_command\` | Run a short command and return its output (user confirmation required) |
1852
+ | \`cli_start_process\` | Start a long-running command in the background; returns a processId immediately |
1853
+ | \`cli_wait_for_process\` | Block up to N seconds for a background process, then return current output |
1854
+ | \`cli_process_status\` | Instant non-blocking check on a background process |
1855
+ | \`cli_kill_process\` | Kill a running background process |
1856
+ | \`cli_list_processes\` | List all background processes started this session |
1857
+
1858
+ ### Choosing between run_command and start_process
1859
+
1860
+ | Situation | Use |
1861
+ |-----------|-----|
1862
+ | Command finishes in under ~30s | \`cli_run_command\` |
1863
+ | Command may take minutes (npm install, build, scaffold) | \`cli_start_process\` + \`cli_wait_for_process\` |
1864
+ | Command needs live stdin (e.g. a REPL) | \`cli_run_command\` with \`"interactive": true\` |
1865
+
1866
+ ### run_command options
1867
+ - \`timeout_seconds\` (default 120, max 300) \u2014 kill the process after N seconds
1868
+ - \`interactive\` (boolean) \u2014 connects the user's terminal stdin for commands that need input
1869
+
1870
+ ### start_process + wait_for_process pattern
1871
+
1872
+ For long commands like \`npx create-next-app@latest my-app --yes\`:
1873
+
1874
+ 1. Call \`cli_start_process\` \u2014 it returns \`{processId, status: "running"}\` immediately.
1875
+ 2. Call \`cli_wait_for_process\` with \`{"processId": "...", "wait_seconds": 60}\`.
1876
+ 3. Repeat step 2 until \`status\` is \`"done"\` or \`"error"\`; diagnose and repair errors when possible.
1877
+ 4. Continue with the rest of the assignment and verify the final outcome before responding.
1878
+
1879
+ Do not end the turn merely because a process is still running. Keep polling within the same run. Progress events may be streamed by the client, but the final answer comes only after completion or a genuine blocker.
1880
+
1881
+ ### Example \u2014 scaffolding a Next.js project
1882
+
1883
+ \`\`\`
1884
+ cli_start_process: {"command": "npx", "args": ["create-next-app@latest", "my-app", "--yes"], "cwd": "Desktop"}
1885
+ \u2192 {processId: "proc_1a2b", status: "running"}
1886
+
1887
+ cli_wait_for_process: {"processId": "proc_1a2b", "wait_seconds": 60}
1888
+ \u2192 {status: "running", elapsedSec: 60, stdout: "Creating project...
1889
+ Installing packages\u2026"}
1890
+
1891
+ cli_wait_for_process: {"processId": "proc_1a2b", "wait_seconds": 60}
1892
+ \u2192 {status: "done", exitCode: 0, elapsedSec: 93, stdout: "Success! Created my-app"}
1893
+
1894
+ Continue by running the requested checks and opening/inspecting the app when the assignment requires it.
1895
+ \`\`\`
1896
+ `;
1897
+ }
1898
+ var TOOL_CALL_RE = /```tool\s*\n([\s\S]*?)\n```/;
1899
+ function extractToolCall(text) {
1900
+ const match = text.match(TOOL_CALL_RE);
1901
+ if (!match) return null;
1902
+ try {
1903
+ const parsed = JSON.parse(match[1].trim());
1904
+ if (typeof parsed.tool === "string") return parsed;
1905
+ } catch {
1906
+ }
1907
+ return null;
1908
+ }
1909
+ function injectAgcTrailer(command, args, agentId, agentName) {
1910
+ if (command !== "git") return args;
1911
+ if (!args.some((a) => a === "commit")) return args;
1912
+ if (args.some((a) => a.includes("Co-Authored-By: agc"))) return args;
1913
+ const identity = agentName ? `${agentName} (agc)` : agentId ? `agc/${agentId}` : "agc agent";
1914
+ return [...args, "--trailer", `Co-Authored-By: ${identity} <agc-agent@users.noreply.github.com>`];
1915
+ }
1916
+ var AGC_HOOK_MARKER = "# agc-session:";
1917
+ var HOOK_BACKUP_SUFFIX = ".agc-backup";
1918
+ function findGitDir(rootDir) {
1919
+ const gitPath = (0, import_path3.join)(rootDir, ".git");
1920
+ if (!(0, import_fs5.existsSync)(gitPath)) return null;
1921
+ const s = (0, import_fs5.statSync)(gitPath);
1922
+ if (s.isDirectory()) return gitPath;
1923
+ if (s.isFile()) {
1924
+ const content = (0, import_fs5.readFileSync)(gitPath, "utf8");
1925
+ const match = content.match(/^gitdir:\s*(.+)$/m);
1926
+ if (match) return match[1].trim();
1927
+ }
1928
+ return null;
1929
+ }
1930
+ function installGitHook(rootDir, sessionId, agentId, agentName) {
1931
+ const gitDir = findGitDir(rootDir);
1932
+ if (!gitDir) return;
1933
+ const hooksDir = (0, import_path3.join)(gitDir, "hooks");
1934
+ (0, import_fs5.mkdirSync)(hooksDir, { recursive: true });
1935
+ const hookPath = (0, import_path3.join)(hooksDir, "prepare-commit-msg");
1936
+ if ((0, import_fs5.existsSync)(hookPath)) {
1937
+ const existing = (0, import_fs5.readFileSync)(hookPath, "utf8");
1938
+ if (!existing.includes(AGC_HOOK_MARKER)) {
1939
+ (0, import_fs5.writeFileSync)(hookPath + HOOK_BACKUP_SUFFIX, existing, { mode: 493 });
1940
+ }
1941
+ }
1942
+ const identity = agentName ? `${agentName} (agc)` : agentId ? `agc/${agentId}` : "agc agent";
1943
+ const trailer = `Co-Authored-By: ${identity} <agc-agent@users.noreply.github.com>`;
1944
+ const chainLine = (0, import_fs5.existsSync)(hookPath + HOOK_BACKUP_SUFFIX) ? `
1945
+ # chain pre-existing hook
1946
+ "$(dirname "$0")/prepare-commit-msg${HOOK_BACKUP_SUFFIX}" "$@" 2>/dev/null || true
1947
+ ` : "";
1948
+ const hook = `#!/bin/sh
1949
+ ${AGC_HOOK_MARKER}${sessionId}
1950
+ COMMIT_MSG_FILE="$1"
1951
+ COMMIT_SOURCE="$2"
1952
+ ${chainLine}
1953
+ case "$COMMIT_SOURCE" in merge|squash) exit 0 ;; esac
1954
+ TRAILER="${trailer}"
1955
+ grep -qF "$TRAILER" "$COMMIT_MSG_FILE" 2>/dev/null && exit 0
1956
+ printf '\\n%s\\n' "$TRAILER" >> "$COMMIT_MSG_FILE"
1957
+ `;
1958
+ (0, import_fs5.writeFileSync)(hookPath, hook, { mode: 493 });
1959
+ }
1960
+ function removeGitHook(rootDir) {
1961
+ const gitDir = findGitDir(rootDir);
1962
+ if (!gitDir) return;
1963
+ const hookPath = (0, import_path3.join)(gitDir, "hooks", "prepare-commit-msg");
1964
+ if (!(0, import_fs5.existsSync)(hookPath)) return;
1965
+ const content = (0, import_fs5.readFileSync)(hookPath, "utf8");
1966
+ if (!content.includes(AGC_HOOK_MARKER)) return;
1967
+ const backupPath = hookPath + HOOK_BACKUP_SUFFIX;
1968
+ if ((0, import_fs5.existsSync)(backupPath)) {
1969
+ (0, import_fs5.writeFileSync)(hookPath, (0, import_fs5.readFileSync)(backupPath, "utf8"), { mode: 493 });
1970
+ (0, import_fs5.unlinkSync)(backupPath);
1971
+ } else {
1972
+ (0, import_fs5.unlinkSync)(hookPath);
1973
+ }
1974
+ }
1975
+ function safePath(root, userPath) {
1976
+ const abs = (0, import_path3.resolve)(root, userPath);
1977
+ const rel = (0, import_path3.relative)(root, abs);
1978
+ if (rel.startsWith("..") || rel.startsWith("/")) {
1979
+ throw new Error(`Path "${userPath}" escapes the session root. Access denied.`);
1980
+ }
1981
+ return abs;
1982
+ }
1983
+ var BLOCKED_PATTERNS = [
1984
+ /\/\.ssh\//,
1985
+ /\/\.gnupg\//,
1986
+ /\/\.agc\//,
1987
+ /\/\.aws\//,
1988
+ /\/\.env$/,
1989
+ /\/\.env\./,
1990
+ /id_rsa/,
1991
+ /id_ed25519/
1992
+ ];
1993
+ function assertNotSensitive(abs) {
1994
+ for (const pat of BLOCKED_PATTERNS) {
1995
+ if (pat.test(abs)) {
1996
+ throw new Error(`Access to "${abs}" is blocked for security reasons.`);
1997
+ }
1998
+ }
1999
+ }
2000
+ async function confirm(message, config, permissionKey) {
2001
+ if (config.autoApprove) return true;
2002
+ const cached = config.permissions.get(permissionKey);
2003
+ if (cached === "allow") return true;
2004
+ if (cached === "deny") return false;
2005
+ return new Promise((resolve2) => {
2006
+ const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
2007
+ process.stdout.write(
2008
+ `
2009
+ \x1B[33m\u26A0\x1B[0m ${message}
2010
+ \x1B[2m[y] Yes [n] No [A] Always allow this type [N] Never allow this type\x1B[0m
2011
+ \x1B[36m?\x1B[0m `
2012
+ );
2013
+ rl.once("line", (answer) => {
2014
+ rl.close();
2015
+ const a = answer.trim().toLowerCase();
2016
+ if (a === "a") {
2017
+ config.permissions.set(permissionKey, "allow");
2018
+ resolve2(true);
2019
+ } else if (a === "n" || a === "nn") {
2020
+ config.permissions.set(permissionKey, "deny");
2021
+ resolve2(false);
2022
+ } else {
2023
+ resolve2(a === "y" || a === "yes" || a === "");
2024
+ }
2025
+ });
2026
+ });
2027
+ }
2028
+ var OFFICE_EXTS = /* @__PURE__ */ new Set([".docx", ".doc", ".rtf", ".odt", ".pages"]);
2029
+ var PDF_EXTS = /* @__PURE__ */ new Set([".pdf"]);
2030
+ var UNREADABLE_BINARY_EXTS = /* @__PURE__ */ new Set([
2031
+ ".png",
2032
+ ".jpg",
2033
+ ".jpeg",
2034
+ ".gif",
2035
+ ".bmp",
2036
+ ".ico",
2037
+ ".webp",
2038
+ ".tiff",
2039
+ ".mp3",
2040
+ ".mp4",
2041
+ ".wav",
2042
+ ".aac",
2043
+ ".ogg",
2044
+ ".flac",
2045
+ ".zip",
2046
+ ".tar",
2047
+ ".gz",
2048
+ ".bz2",
2049
+ ".7z",
2050
+ ".rar",
2051
+ ".exe",
2052
+ ".dll",
2053
+ ".so",
2054
+ ".dylib",
2055
+ ".bin",
2056
+ ".psd",
2057
+ ".ai",
2058
+ ".sketch",
2059
+ ".figma",
2060
+ ".xlsx",
2061
+ ".xls",
2062
+ ".pptx",
2063
+ ".ppt"
2064
+ ]);
2065
+ function extractViaCommand(cmd, cmdArgs) {
2066
+ return new Promise((res) => {
2067
+ (0, import_child_process2.execFile)(cmd, cmdArgs, { timeout: 3e4, maxBuffer: 2 * 1024 * 1024 }, (err, stdout) => {
2068
+ if (err) res("");
2069
+ else res(stdout.trim());
2070
+ });
2071
+ });
2072
+ }
2073
+ async function extractPdfText(abs) {
2074
+ try {
2075
+ const buffer = (0, import_fs5.readFileSync)(abs);
2076
+ const data = await pdfParse(buffer);
2077
+ const text2 = data.text?.trim();
2078
+ if (text2) {
2079
+ const MAX_CHARS = 15e4;
2080
+ if (text2.length > MAX_CHARS) {
2081
+ return text2.slice(0, MAX_CHARS) + `
2082
+
2083
+ [\u2026truncated \u2014 showing first ${MAX_CHARS.toLocaleString()} characters of ${text2.length.toLocaleString()} total]`;
2084
+ }
2085
+ return text2;
2086
+ }
2087
+ } catch {
2088
+ }
2089
+ const text = await extractViaCommand("pdftotext", [abs, "-"]);
2090
+ if (text) return text;
2091
+ return `[Cannot extract PDF text: the file may be scanned/image-only or password-protected]`;
2092
+ }
2093
+ async function extractOfficeText(abs, ext) {
2094
+ const text = await extractViaCommand("textutil", ["-stdout", "-cat", "txt", abs]);
2095
+ if (text) return text;
2096
+ return `[Cannot extract ${ext} text: textutil failed or is unavailable on this system]`;
2097
+ }
2098
+ async function toolReadFile(args, cfg) {
2099
+ const { path: userPath } = args;
2100
+ if (!userPath) throw new Error('read_file requires a "path" argument');
2101
+ const abs = safePath(cfg.rootDir, userPath);
2102
+ assertNotSensitive(abs);
2103
+ if (!(0, import_fs5.existsSync)(abs)) throw new Error(`File not found: ${userPath}`);
2104
+ const stat = (0, import_fs5.statSync)(abs);
2105
+ if (stat.isDirectory()) throw new Error(`"${userPath}" is a directory, not a file`);
2106
+ const ext = (0, import_path3.extname)(abs).toLowerCase();
2107
+ if (PDF_EXTS.has(ext)) {
2108
+ if (stat.size > 5e7) throw new Error(`PDF too large to read (${Math.round(stat.size / 1e6)} MB). Max 50 MB.`);
2109
+ return extractPdfText(abs);
2110
+ }
2111
+ if (OFFICE_EXTS.has(ext)) {
2112
+ if (stat.size > 2e7) throw new Error(`Document too large to read (${Math.round(stat.size / 1e6)} MB). Max 20 MB.`);
2113
+ return extractOfficeText(abs, ext);
2114
+ }
2115
+ if (UNREADABLE_BINARY_EXTS.has(ext)) {
2116
+ throw new Error(`Cannot read binary file "${userPath}" (${ext} format). Only text, PDF, and Office documents are supported.`);
2117
+ }
2118
+ if (stat.size > 5e5) throw new Error(`File too large to read (${Math.round(stat.size / 1024)} KB). Max 500 KB.`);
2119
+ return (0, import_fs5.readFileSync)(abs, "utf8");
2120
+ }
2121
+ async function toolWriteFile(args, cfg) {
2122
+ const { path: userPath, content } = args;
2123
+ if (!userPath) throw new Error('write_file requires a "path" argument');
2124
+ if (content === void 0) throw new Error('write_file requires a "content" argument');
2125
+ const abs = safePath(cfg.rootDir, userPath);
2126
+ assertNotSensitive(abs);
2127
+ const ok = await confirm(
2128
+ `Agent wants to write file: \x1B[1m${abs}\x1B[0m (${String(content).length} chars)`,
2129
+ cfg,
2130
+ "write_file"
2131
+ );
2132
+ if (!ok) return "User denied write operation.";
2133
+ (0, import_fs5.mkdirSync)((0, import_path3.dirname)(abs), { recursive: true });
2134
+ (0, import_fs5.writeFileSync)(abs, content, "utf8");
2135
+ return `Written ${String(content).length} bytes to ${userPath}`;
2136
+ }
2137
+ async function toolListDirectory(args, cfg) {
2138
+ const userPath = args.path ?? ".";
2139
+ const abs = safePath(cfg.rootDir, userPath);
2140
+ assertNotSensitive(abs);
2141
+ if (!(0, import_fs5.existsSync)(abs)) throw new Error(`Directory not found: ${userPath}`);
2142
+ const entries = (0, import_fs5.readdirSync)(abs, { withFileTypes: true });
2143
+ const lines = entries.map((e) => {
2144
+ const type = e.isDirectory() ? "d" : e.isSymbolicLink() ? "l" : "f";
2145
+ return `[${type}] ${e.name}`;
2146
+ });
2147
+ return lines.join("\n") || "(empty directory)";
2148
+ }
2149
+ async function toolSearchFiles(args, cfg) {
2150
+ const { pattern, directory } = args;
2151
+ if (!pattern) throw new Error('search_files requires a "pattern" argument');
2152
+ const baseDir = safePath(cfg.rootDir, directory ?? ".");
2153
+ assertNotSensitive(baseDir);
2154
+ const results = [];
2155
+ const pat = new RegExp(
2156
+ pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, "."),
2157
+ "i"
2158
+ );
2159
+ function walk(dir, depth = 0) {
2160
+ if (results.length >= 50 || depth > 10) return;
2161
+ try {
2162
+ for (const entry of (0, import_fs5.readdirSync)(dir, { withFileTypes: true })) {
2163
+ if (entry.name.startsWith(".") && depth > 0) continue;
2164
+ const full = (0, import_path3.join)(dir, entry.name);
2165
+ const rel = (0, import_path3.relative)(cfg.rootDir, full);
2166
+ if (pat.test(entry.name) || pat.test(rel)) results.push(rel);
2167
+ if (entry.isDirectory()) walk(full, depth + 1);
2168
+ }
2169
+ } catch {
2170
+ }
2171
+ }
2172
+ walk(baseDir);
2173
+ return results.length ? results.join("\n") : "No files found matching: " + pattern;
2174
+ }
2175
+ async function toolRunCommand(args, cfg) {
2176
+ const { command, args: cmdArgs = [], cwd, timeout_seconds, interactive } = args;
2177
+ if (!command || typeof command !== "string") throw new Error('run_command requires a "command" string');
2178
+ if (!Array.isArray(cmdArgs)) throw new Error('"args" must be an array of strings');
2179
+ const workDir = cwd ? safePath(cfg.rootDir, cwd) : cfg.rootDir;
2180
+ const injectedArgs = injectAgcTrailer(command, cmdArgs, cfg.agentId, cfg.agentName);
2181
+ const preview = [command, ...injectedArgs].join(" ");
2182
+ const timeoutMs = Math.min((typeof timeout_seconds === "number" ? timeout_seconds : 120) * 1e3, 3e5);
2183
+ const ok = await confirm(
2184
+ `Agent wants to run: \x1B[1m${preview}\x1B[0m
2185
+ \x1B[2min: ${workDir}\x1B[0m`,
2186
+ cfg,
2187
+ "run_command"
2188
+ );
2189
+ if (!ok) return "User denied command execution.";
2190
+ if (interactive) {
2191
+ return new Promise((resolve2) => {
2192
+ const child = (0, import_child_process2.spawn)(command, injectedArgs.map(String), { cwd: workDir, stdio: "inherit" });
2193
+ const timer = setTimeout(() => {
2194
+ child.kill();
2195
+ resolve2(`(command timed out after ${timeoutMs / 1e3}s)`);
2196
+ }, timeoutMs);
2197
+ child.on("close", (code) => {
2198
+ clearTimeout(timer);
2199
+ resolve2(`(command exited with code ${code ?? "unknown"})`);
2200
+ });
2201
+ child.on("error", (err) => {
2202
+ clearTimeout(timer);
2203
+ resolve2(`Error: ${err.message}`);
2204
+ });
2205
+ });
2206
+ }
2207
+ return new Promise((resolve2) => {
2208
+ (0, import_child_process2.execFile)(command, injectedArgs.map(String), { cwd: workDir, timeout: timeoutMs, maxBuffer: 1024 * 1024 }, (err, stdout, stderr) => {
2209
+ const out = [stdout, stderr].filter(Boolean).join("\n--- stderr ---\n");
2210
+ if (err && !out) return resolve2(`Error: ${err.message}`);
2211
+ resolve2(out || "(no output)");
2212
+ });
2213
+ });
2214
+ }
2215
+ async function toolStartProcess(args, cfg) {
2216
+ const { command, args: cmdArgs = [], cwd } = args;
2217
+ if (!command || typeof command !== "string") throw new Error('start_process requires a "command" string');
2218
+ if (!Array.isArray(cmdArgs)) throw new Error('"args" must be an array of strings');
2219
+ const workDir = cwd ? safePath(cfg.rootDir, cwd) : cfg.rootDir;
2220
+ const preview = [command, ...cmdArgs].join(" ");
2221
+ const ok = await confirm(
2222
+ `Agent wants to start background process: \x1B[1m${preview}\x1B[0m
2223
+ \x1B[2min: ${workDir}\x1B[0m`,
2224
+ cfg,
2225
+ "start_process"
2226
+ );
2227
+ if (!ok) return JSON.stringify({ error: "User denied process start." });
2228
+ const id = `proc_${Date.now().toString(36)}`;
2229
+ const child = (0, import_child_process2.spawn)(command, cmdArgs.map(String), {
2230
+ cwd: workDir,
2231
+ stdio: ["ignore", "pipe", "pipe"],
2232
+ detached: false
2233
+ });
2234
+ const proc = {
2235
+ id,
2236
+ command: preview,
2237
+ status: "running",
2238
+ exitCode: null,
2239
+ stdout: "",
2240
+ stderr: "",
2241
+ startedAt: /* @__PURE__ */ new Date(),
2242
+ endedAt: null,
2243
+ child
2244
+ };
2245
+ child.stdout?.on("data", (chunk) => {
2246
+ proc.stdout = capBuffer(proc.stdout, chunk.toString(), 2e5);
2247
+ });
2248
+ child.stderr?.on("data", (chunk) => {
2249
+ proc.stderr = capBuffer(proc.stderr, chunk.toString(), 5e4);
2250
+ });
2251
+ child.on("close", (code) => {
2252
+ proc.status = code === 0 ? "done" : "error";
2253
+ proc.exitCode = code;
2254
+ proc.endedAt = /* @__PURE__ */ new Date();
2255
+ });
2256
+ child.on("error", (err) => {
2257
+ proc.status = "error";
2258
+ proc.endedAt = /* @__PURE__ */ new Date();
2259
+ proc.stderr = capBuffer(proc.stderr, `
2260
+ Spawn error: ${err.message}`, 5e4);
2261
+ });
2262
+ managedProcesses.set(id, proc);
2263
+ cfg.appendLog({ type: "process_start", processId: id, command: preview, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
2264
+ return JSON.stringify({ processId: id, status: "running", command: preview });
2265
+ }
2266
+ function processSnapshot(proc) {
2267
+ const elapsedSec = Math.round((Date.now() - proc.startedAt.getTime()) / 1e3);
2268
+ const recentStdout = proc.stdout.length > 4e3 ? "\u2026(earlier output truncated)\n" + proc.stdout.slice(-4e3) : proc.stdout;
2269
+ return JSON.stringify({
2270
+ processId: proc.id,
2271
+ command: proc.command,
2272
+ status: proc.status,
2273
+ exitCode: proc.exitCode,
2274
+ elapsedSec,
2275
+ stdout: recentStdout || "(no output yet)",
2276
+ stderr: proc.stderr.slice(-1e3) || void 0
2277
+ });
2278
+ }
2279
+ async function toolProcessStatus(args, _cfg) {
2280
+ const { processId } = args;
2281
+ if (!processId) throw new Error('process_status requires a "processId" argument');
2282
+ const proc = managedProcesses.get(processId);
2283
+ if (!proc) return JSON.stringify({ error: `No process found with id "${processId}"` });
2284
+ return processSnapshot(proc);
2285
+ }
2286
+ async function toolWaitForProcess(args, _cfg) {
2287
+ const { processId, wait_seconds = 60 } = args;
2288
+ if (!processId) throw new Error('wait_for_process requires a "processId" argument');
2289
+ const proc = managedProcesses.get(processId);
2290
+ if (!proc) return JSON.stringify({ error: `No process found with id "${processId}"` });
2291
+ if (proc.status !== "running") return processSnapshot(proc);
2292
+ const maxWait = Math.min((typeof wait_seconds === "number" ? wait_seconds : 60) * 1e3, 12e4);
2293
+ const deadline = Date.now() + maxWait;
2294
+ await new Promise((resolve2) => {
2295
+ const tick = setInterval(() => {
2296
+ if (proc.status !== "running" || Date.now() >= deadline) {
2297
+ clearInterval(tick);
2298
+ resolve2();
2299
+ }
2300
+ }, 500);
2301
+ });
2302
+ return processSnapshot(proc);
2303
+ }
2304
+ async function toolKillProcess(args, cfg) {
2305
+ const { processId } = args;
2306
+ if (!processId) throw new Error('kill_process requires a "processId" argument');
2307
+ const proc = managedProcesses.get(processId);
2308
+ if (!proc) return JSON.stringify({ error: `No process found with id "${processId}"` });
2309
+ if (proc.status !== "running") return JSON.stringify({ error: `Process "${processId}" is not running (status: ${proc.status})` });
2310
+ proc.child.kill("SIGTERM");
2311
+ proc.status = "killed";
2312
+ proc.endedAt = /* @__PURE__ */ new Date();
2313
+ cfg.appendLog({ type: "process_killed", processId, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
2314
+ return JSON.stringify({ processId, status: "killed" });
2315
+ }
2316
+ async function toolListProcesses(_args, _cfg) {
2317
+ if (managedProcesses.size === 0) return JSON.stringify([]);
2318
+ const list = [...managedProcesses.values()].map((p) => ({
2319
+ processId: p.id,
2320
+ command: p.command,
2321
+ status: p.status,
2322
+ elapsedSec: Math.round((Date.now() - p.startedAt.getTime()) / 1e3)
2323
+ }));
2324
+ return JSON.stringify(list);
2325
+ }
2326
+ async function runLocalTool(call, cfg) {
2327
+ const { tool, args } = call;
2328
+ cfg.appendLog({
2329
+ type: "local_tool_call",
2330
+ tool,
2331
+ args,
2332
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
2333
+ });
2334
+ let result;
2335
+ try {
2336
+ switch (tool) {
2337
+ case "read_file":
2338
+ result = await toolReadFile(args, cfg);
2339
+ break;
2340
+ case "write_file":
2341
+ result = await toolWriteFile(args, cfg);
2342
+ break;
2343
+ case "list_directory":
2344
+ result = await toolListDirectory(args, cfg);
2345
+ break;
2346
+ case "search_files":
2347
+ result = await toolSearchFiles(args, cfg);
2348
+ break;
2349
+ case "run_command":
2350
+ result = await toolRunCommand(args, cfg);
2351
+ break;
2352
+ case "start_process":
2353
+ result = await toolStartProcess(args, cfg);
2354
+ break;
2355
+ case "process_status":
2356
+ result = await toolProcessStatus(args, cfg);
2357
+ break;
2358
+ case "wait_for_process":
2359
+ result = await toolWaitForProcess(args, cfg);
2360
+ break;
2361
+ case "kill_process":
2362
+ result = await toolKillProcess(args, cfg);
2363
+ break;
2364
+ case "list_processes":
2365
+ result = await toolListProcesses(args, cfg);
2366
+ break;
2367
+ default:
2368
+ result = `Unknown tool: "${tool}". Available: read_file, write_file, list_directory, search_files, run_command, start_process, wait_for_process, process_status, kill_process, list_processes`;
2369
+ }
2370
+ } catch (err) {
2371
+ result = `Error: ${err?.message ?? String(err)}`;
2372
+ }
2373
+ cfg.appendLog({
2374
+ type: "local_tool_result",
2375
+ tool,
2376
+ result: result.slice(0, 2e3),
2377
+ // cap log size
2378
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
2379
+ });
2380
+ return result;
2381
+ }
2382
+
2383
+ // src/commands/run.ts
2384
+ function runCommand() {
2385
+ 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 (prompt2, opts) => {
2386
+ const cfg = loadConfig();
2387
+ const agentId = opts.agent ?? cfg.defaultAgentId;
2388
+ if (!agentId) {
2389
+ console.error(c.error("Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`"));
2390
+ process.exit(1);
2391
+ }
2392
+ if (opts.session && opts.newSession) {
2393
+ console.error(c.error("Cannot use --session and --new-session together."));
2394
+ process.exit(1);
2395
+ }
2396
+ const client = makeClient();
2397
+ let sessionId = opts.session;
2398
+ if (opts.session) {
2399
+ const spinner = spin("Loading session\u2026");
2400
+ try {
2401
+ await client.sessions.get(opts.session);
2402
+ spinner.stop();
2403
+ } catch {
2404
+ spinner.stop();
2405
+ console.error(c.error(`Session "${opts.session}" not found.`));
2406
+ process.exit(1);
2407
+ }
2408
+ }
2409
+ if (opts.newSession) {
2410
+ const spinner = spin("Creating session\u2026");
2411
+ try {
2412
+ const res = await client.sessions.create({
2413
+ agentId,
2414
+ initiator: cfg.initiator ?? "",
2415
+ title: `agc run ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16)}`,
2416
+ source: "cli"
2417
+ });
2418
+ const session = res?.data ?? res;
2419
+ sessionId = session.sessionId;
2420
+ spinner.stop();
2421
+ } catch (err) {
2422
+ spinner.stop();
2423
+ printError(err);
2424
+ process.exit(1);
2425
+ }
2426
+ }
2427
+ const localEnabled = opts.yes || opts.local;
2428
+ const autoApprove = !!opts.yes;
2429
+ let localToolsCfg = null;
2430
+ let cliContext;
2431
+ if (localEnabled) {
2432
+ const rootDir = process.cwd();
2433
+ localToolsCfg = {
2434
+ rootDir,
2435
+ sessionId: sessionId ?? "run",
2436
+ appendLog: () => {
2437
+ },
2438
+ permissions: /* @__PURE__ */ new Map(),
2439
+ agentId,
2440
+ autoApprove
2441
+ };
2442
+ const snapshot = buildDirSnapshot(rootDir, 2);
2443
+ cliContext = buildLocalToolsManifest(rootDir, snapshot, [], autoApprove);
2444
+ }
2445
+ if (!opts.json) {
2446
+ const rows = [];
2447
+ if (sessionId) {
2448
+ const label = opts.newSession ? `${c.id(sessionId)}${c.dim(" (new)")}` : `${c.id(sessionId)}${c.dim(" (resumed)")}`;
2449
+ rows.push(["Session", label]);
2450
+ }
2451
+ if (localEnabled) {
2452
+ rows.push(["Local tools", autoApprove ? c.warn("enabled (auto-approve on)") : c.success("enabled")]);
2453
+ }
2454
+ if (opts.computer) {
2455
+ rows.push(["Cloud computer", c.success("enabled") + c.dim(" (persistent, remote)")]);
2456
+ }
2457
+ if (rows.length) {
2458
+ detail(rows);
2459
+ console.log();
2460
+ }
2461
+ }
2462
+ const params = {
2463
+ agentId,
2464
+ sessionId,
2465
+ messages: [{ role: "user", content: prompt2 }],
2466
+ ...cfg.initiator && { initiatorId: cfg.initiator },
2467
+ ...opts.computer && { computerRequest: { enabled: true } },
2468
+ ...cliContext && { cliContext }
2469
+ };
2470
+ if (opts.noStream && !localEnabled) {
2471
+ const spinner = spin("Running\u2026");
2472
+ try {
2473
+ const result = await client.run.once(params);
2474
+ spinner.stop();
2475
+ if (opts.json) return jsonOut(result);
2476
+ const text = result?.content ?? result?.text ?? result?.message ?? JSON.stringify(result);
2477
+ console.log(text);
2478
+ if (sessionId) console.log(c.dim(`
2479
+ Session: ${sessionId} (resume with: agc run --session ${sessionId} "<prompt>")`));
2480
+ } catch (err) {
2481
+ spinner.stop();
2482
+ printError(err);
2483
+ process.exit(1);
2484
+ }
2485
+ return;
2486
+ }
2487
+ try {
2488
+ let hasOutput = false;
2489
+ let toolStartMs = 0;
2490
+ let lastToolName = "";
2491
+ for await (const event of client.agents.stream(params)) {
2492
+ if (opts.json) {
2493
+ console.log(JSON.stringify(event));
2494
+ continue;
2495
+ }
2496
+ if (event.type === "token") {
2497
+ process.stdout.write(event.content ?? "");
2498
+ hasOutput = true;
2499
+ } else if (event.type === "cli_tool_request" && localToolsCfg) {
2500
+ const { requestId, tool: toolName, args } = event;
2501
+ const displayName = String(toolName).replace("cli_", "");
2502
+ if (hasOutput) {
2503
+ process.stdout.write("\n");
2504
+ hasOutput = false;
2505
+ }
2506
+ process.stdout.write(` ${c.dim("\u2500")} ${c.bold(displayName)}`);
2507
+ const startMs = Date.now();
2508
+ let result;
2509
+ let toolOk = true;
2510
+ try {
2511
+ result = await runLocalTool({ tool: displayName, args: args ?? {} }, localToolsCfg);
2512
+ } catch (err) {
2513
+ result = `Error: ${err?.message ?? String(err)}`;
2514
+ toolOk = false;
2515
+ }
2516
+ const elapsed = ((Date.now() - startMs) / 1e3).toFixed(1);
2517
+ readline3.cursorTo(process.stdout, 0);
2518
+ readline3.clearLine(process.stdout, 0);
2519
+ process.stdout.write(` ${c.dim("\u2500")} ${c.bold(displayName)} ${toolOk ? sym.ok : sym.fail} ${c.dim("(" + elapsed + "s)")}
2520
+ `);
2521
+ try {
2522
+ await fetch(`${cfg.apiUrl}/v1/agents/cli-tool-result`, {
2523
+ method: "POST",
2524
+ headers: { "Content-Type": "application/json", "Authorization": `Bearer ${cfg.apiKey}` },
2525
+ body: JSON.stringify({ requestId, result })
2526
+ });
2527
+ } catch {
2528
+ }
2529
+ } else if (event.type === "toolStart") {
2530
+ lastToolName = event.toolName ?? "";
2531
+ toolStartMs = Date.now();
2532
+ if (hasOutput) {
2533
+ process.stdout.write("\n");
2534
+ hasOutput = false;
2535
+ }
2536
+ process.stdout.write(` ${c.dim("\u2500")} ${c.bold(lastToolName)}`);
2537
+ } else if (event.type === "toolEnd") {
2538
+ const elapsed = ((Date.now() - toolStartMs) / 1e3).toFixed(1);
2539
+ readline3.cursorTo(process.stdout, 0);
2540
+ readline3.clearLine(process.stdout, 0);
2541
+ process.stdout.write(` ${c.dim("\u2500")} ${c.bold(lastToolName)} ${sym.ok} ${c.dim("(" + elapsed + "s)")}
2542
+ `);
2543
+ } else if (event.type === "final") {
2544
+ if (hasOutput) process.stdout.write("\n");
2545
+ const e = event;
2546
+ const finalText = e.content ?? e.payload?.content ?? e.payload?.text ?? e.payload?.message;
2547
+ if (finalText && !hasOutput) console.log(finalText);
2548
+ if (sessionId) console.log(c.dim(`
2549
+ Session: ${sessionId} (resume with: agc run --session ${sessionId} "<prompt>")`));
2550
+ break;
2551
+ } else if (event.type === "error") {
2552
+ if (hasOutput) process.stdout.write("\n");
2553
+ console.error(`
2554
+ ${sym.fail} ${c.error(event.message ?? "Error")}`);
2555
+ process.exit(1);
2556
+ }
2557
+ }
2558
+ if (hasOutput && !opts.json) process.stdout.write("\n");
2559
+ } catch (err) {
2560
+ printError(err);
2561
+ process.exit(1);
2562
+ }
2563
+ });
2564
+ }
2565
+
2566
+ // src/commands/chat.ts
2567
+ var import_commander9 = require("commander");
2568
+ var readline4 = __toESM(require("readline"));
2569
+ var import_fs6 = require("fs");
2570
+ var import_path4 = require("path");
2571
+ var import_os3 = require("os");
2572
+ var SESSIONS_DIR = (0, import_path4.join)((0, import_os3.homedir)(), ".agc", "sessions");
2573
+ function ensureSessionsDir() {
2574
+ if (!(0, import_fs6.existsSync)(SESSIONS_DIR)) (0, import_fs6.mkdirSync)(SESSIONS_DIR, { recursive: true });
2575
+ }
2576
+ function appendSessionLog(sessionId, record) {
2577
+ try {
2578
+ ensureSessionsDir();
2579
+ const file = (0, import_path4.join)(SESSIONS_DIR, `${sessionId}.jsonl`);
2580
+ (0, import_fs6.appendFileSync)(file, JSON.stringify(record) + "\n", { mode: 384 });
2581
+ } catch {
2582
+ }
2583
+ }
2584
+ var HELP_TEXT = `
2585
+ ${c.label("Slash commands")}
2586
+ /help Show this help
2587
+ /session Print the current session ID (copy it to resume later)
2588
+ /tools Show local tool status and permissions
2589
+ /clear Clear the terminal screen
2590
+ /quit Exit (session is preserved \u2014 resume with --resume <id>)
2591
+
2592
+ ${c.label("File context")}
2593
+ Use @path/to/file in your message to inject that file's contents into context.
2594
+ Example: "review @src/index.ts and suggest improvements"
2595
+ `;
2596
+ var LOCAL_TOOLS_DISCLAIMER = `
2597
+ ${c.warn("\u26A0")} ${c.bold("Local file system access enabled")}
2598
+
2599
+ ${c.dim("The agent can read and write files, list directories, search files,")}
2600
+ ${c.dim("and execute shell commands on your machine.")}
2601
+
2602
+ ${c.dim("Rules:")}
2603
+ ${sym.bullet} ${c.dim("All paths are restricted to:")} ${c.primary(process.cwd())}
2604
+ ${sym.bullet} ${c.dim("Sensitive paths (.ssh, .env, .aws, credentials) are always blocked")}
2605
+ ${sym.bullet} ${c.dim("Write and run_command operations require your confirmation")}
2606
+ ${sym.bullet} ${c.dim("You can deny any individual request")}
2607
+
2608
+ ${c.dim("Session activity is logged to")} ${c.primary("~/.agc/sessions/")}
2609
+ `;
2610
+ function chatCommand() {
2611
+ 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) => {
2612
+ const localEnabled = opts.local !== false;
2613
+ const cfg = loadConfig();
2614
+ const agentId = opts.agent ?? cfg.defaultAgentId;
2615
+ if (!agentId) {
2616
+ console.error(c.error("Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`"));
2617
+ process.exit(1);
2618
+ }
2619
+ if (!cfg.initiator) {
2620
+ console.error(c.error("No initiator set. Run `agc login` first."));
2621
+ process.exit(1);
2622
+ }
2623
+ const client = makeClient();
2624
+ let sessionId = opts.resume ?? "";
2625
+ const isResume = !!opts.resume;
2626
+ const initiator = cfg.initiator ?? "";
2627
+ if (!isResume) {
2628
+ const spinner = spin("Creating session\u2026");
2629
+ try {
2630
+ const res = await client.sessions.create({
2631
+ agentId,
2632
+ initiator,
2633
+ title: `agc chat ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16)}`,
2634
+ source: "cli"
2635
+ });
2636
+ const session = res?.data ?? res;
2637
+ sessionId = session.sessionId;
2638
+ spinner.stop();
2639
+ appendSessionLog(sessionId, {
2640
+ type: "session_start",
2641
+ sessionId,
2642
+ agentId,
2643
+ initiator,
2644
+ source: "cli",
2645
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
2646
+ });
2647
+ } catch (err) {
2648
+ spinner.stop();
2649
+ printError(err);
2650
+ process.exit(1);
2651
+ }
2652
+ } else {
2653
+ const spinner = spin("Loading session\u2026");
2654
+ try {
2655
+ const res = await client.sessions.get(sessionId);
2656
+ const session = res?.data ?? res;
2657
+ if (session.agentId && session.agentId !== agentId) {
2658
+ spinner.stop();
2659
+ console.log(c.warn(` Note: session ${sessionId} was created with agent ${session.agentId}, not ${agentId}`));
2660
+ } else {
2661
+ spinner.stop();
2662
+ }
2663
+ } catch {
2664
+ spinner.stop();
2665
+ console.error(c.error(`Session "${sessionId}" not found.`));
2666
+ process.exit(1);
2667
+ }
2668
+ }
2669
+ let agentName;
2670
+ let walletLine = "";
2671
+ await Promise.allSettled([
2672
+ client.agents.get(agentId).then((res) => {
2673
+ agentName = (res?.data ?? res)?.name;
2674
+ }),
2675
+ client.wallets.primary(agentId).then(async (primary) => {
2676
+ const w = primary?.data ?? primary;
2677
+ if (w?.id) {
2678
+ const bal = await client.wallets.balance(w.id).catch(() => null);
2679
+ const b = bal?.data ?? bal;
2680
+ const addr = `${w.address.slice(0, 6)}\u2026${w.address.slice(-4)}`;
2681
+ const usdc = b?.usdc ?? "0";
2682
+ walletLine = `${addr} ${c.bold(usdc + " USDC")}`;
2683
+ }
2684
+ })
2685
+ ]);
2686
+ console.log(`
2687
+ ${c.bold("Agent Commons Chat")}`);
2688
+ const headerRows = [
2689
+ ["Agent", agentName ? `${agentName} ${c.dim(agentId)}` : agentId],
2690
+ ["Session", c.id(sessionId) + (isResume ? c.dim(" (resumed)") : c.dim(" (new)"))]
2691
+ ];
2692
+ if (walletLine) headerRows.push(["Wallet", walletLine]);
2693
+ if (opts.computer) headerRows.push(["Cloud computer", c.success("enabled") + c.dim(" (persistent, remote)")]);
2694
+ if (localEnabled) headerRows.push(["Local tools", c.success("enabled") + c.dim(" (read, write, search, run)")]);
2695
+ detail(headerRows);
2696
+ let localToolsCfg = null;
2697
+ if (localEnabled) {
2698
+ console.log(LOCAL_TOOLS_DISCLAIMER);
2699
+ const rootDir = process.cwd();
2700
+ localToolsCfg = {
2701
+ rootDir,
2702
+ sessionId,
2703
+ agentId,
2704
+ agentName,
2705
+ appendLog: (record) => appendSessionLog(sessionId, record),
2706
+ permissions: /* @__PURE__ */ new Map()
2707
+ };
2708
+ installGitHook(rootDir, sessionId, agentId, agentName);
2709
+ appendSessionLog(sessionId, {
2710
+ type: "local_tools_enabled",
2711
+ rootDir,
2712
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
2713
+ });
2714
+ }
2715
+ console.log(c.dim("\nType your message and press Enter. Type /help for commands.\n"));
2716
+ const rl = readline4.createInterface({
2717
+ input: process.stdin,
2718
+ output: process.stdout,
2719
+ terminal: true,
2720
+ prompt: c.primary("you") + c.dim(" \u203A ")
2721
+ });
2722
+ rl.prompt();
2723
+ rl.on("line", async (line) => {
2724
+ const input = line.trim();
2725
+ if (!input) {
2726
+ rl.prompt();
2727
+ return;
2728
+ }
2729
+ if (input === "/quit" || input === "/exit" || input === "/q") {
2730
+ console.log(c.dim(`
2731
+ Session saved. Resume with: agc chat --resume ${sessionId}`));
2732
+ rl.close();
2733
+ process.exit(0);
2734
+ }
2735
+ if (input === "/help") {
2736
+ console.log(HELP_TEXT);
2737
+ rl.prompt();
2738
+ return;
2739
+ }
2740
+ if (input === "/tools") {
2741
+ if (!localToolsCfg) {
2742
+ console.log(c.dim(` Local tools are disabled. Remove ${c.bold("--no-local")} flag to re-enable them.`));
2743
+ } else {
2744
+ console.log(`
2745
+ ${c.bold("Local tools")} ${c.success("enabled")}`);
2746
+ console.log(` ${c.dim("Root directory:")} ${c.primary(localToolsCfg.rootDir)}`);
2747
+ const perms = [...localToolsCfg.permissions.entries()];
2748
+ if (perms.length) {
2749
+ console.log(` ${c.dim("Cached permissions:")}`);
2750
+ for (const [k, v] of perms) {
2751
+ const badge = v === "allow" ? c.success("allow") : c.error("deny");
2752
+ console.log(` ${sym.bullet} ${k}: ${badge}`);
2753
+ }
2754
+ }
2755
+ }
2756
+ console.log();
2757
+ rl.prompt();
2758
+ return;
2759
+ }
2760
+ if (input === "/session") {
2761
+ console.log(c.dim(` ${sessionId}`));
2762
+ console.log(c.dim(` Resume with: agc chat --resume ${sessionId}`));
2763
+ rl.prompt();
2764
+ return;
2765
+ }
2766
+ if (input === "/clear") {
2767
+ process.stdout.write("\x1B[2J\x1B[H");
2768
+ rl.prompt();
2769
+ return;
2770
+ }
2771
+ if (input.startsWith("/")) {
2772
+ console.log(c.warn(` Unknown command "${input}". Type /help for available commands.`));
2773
+ rl.prompt();
2774
+ return;
2775
+ }
2776
+ rl.pause();
2777
+ appendSessionLog(sessionId, {
2778
+ type: "message",
2779
+ role: "user",
2780
+ content: input,
2781
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
2782
+ });
2783
+ let userMessage = input;
2784
+ let cliContext;
2785
+ if (localToolsCfg) {
2786
+ const rootDir = localToolsCfg.rootDir;
2787
+ const atRefs = [...input.matchAll(/@([\S]+)/g)].map((m) => m[1]);
2788
+ const fileContextBlocks = [];
2789
+ for (const ref of atRefs) {
2790
+ const content = readFileForContext(rootDir, ref);
2791
+ fileContextBlocks.push(`**${ref}**
2792
+ \`\`\`
2793
+ ${content}
2794
+ \`\`\``);
2795
+ }
2796
+ const snapshot = buildDirSnapshot(rootDir, 2);
2797
+ cliContext = buildLocalToolsManifest(rootDir, snapshot, fileContextBlocks);
2798
+ }
2799
+ const params = {
2800
+ agentId,
2801
+ sessionId,
2802
+ messages: [{ role: "user", content: userMessage }],
2803
+ ...opts.computer && { computerRequest: { enabled: true } },
2804
+ ...cliContext && { cliContext }
2805
+ };
2806
+ if (opts.noStream) {
2807
+ process.stdout.write(c.primary("agent") + c.dim(" \u203A "));
2808
+ const spinner = spin("thinking\u2026");
2809
+ try {
2810
+ const result = await client.run.once(params);
2811
+ spinner.stop();
2812
+ process.stdout.write(c.primary("agent") + c.dim(" \u203A "));
2813
+ const text = extractText(result);
2814
+ console.log(text);
2815
+ appendSessionLog(sessionId, {
2816
+ type: "message",
2817
+ role: "assistant",
2818
+ content: text,
2819
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
2820
+ });
2821
+ } catch (err) {
2822
+ spinner.stop();
2823
+ console.error(`
2824
+ ${sym.fail} ${c.error(err.message ?? String(err))}`);
2825
+ }
2826
+ } else {
2827
+ try {
2828
+ let hasOutput = false;
2829
+ let agentContent = "";
2830
+ let toolStartMs = 0;
2831
+ let lastToolName = "";
2832
+ const thinkingSpinner = spin("thinking\u2026");
2833
+ for await (const event of client.agents.stream(params)) {
2834
+ if (event.type === "token") {
2835
+ if (thinkingSpinner.isSpinning) {
2836
+ thinkingSpinner.stop();
2837
+ process.stdout.write(c.primary("agent") + c.dim(" \u203A "));
2838
+ }
2839
+ const tok = event.content ?? "";
2840
+ process.stdout.write(tok);
2841
+ agentContent += tok;
2842
+ hasOutput = true;
2843
+ } else if (event.type === "cli_tool_request" && localToolsCfg) {
2844
+ if (thinkingSpinner.isSpinning) thinkingSpinner.stop();
2845
+ const { requestId, tool: toolName, args } = event;
2846
+ const displayName = String(toolName).replace("cli_", "");
2847
+ const argStr = toolArgSummary(displayName, args ?? {});
2848
+ const isWaiting = displayName === "wait_for_process";
2849
+ if (hasOutput) {
2850
+ process.stdout.write("\n");
2851
+ hasOutput = false;
2852
+ }
2853
+ process.stdout.write(` ${c.dim("\u2500")} ${c.bold(displayName)}${argStr ? " " + c.dim(argStr) : ""}`);
2854
+ const startMs = Date.now();
2855
+ let elapsedSec = 0;
2856
+ let elapsedInterval = null;
2857
+ if (isWaiting) {
2858
+ elapsedInterval = setInterval(() => {
2859
+ elapsedSec++;
2860
+ readline4.cursorTo(process.stdout, 0);
2861
+ process.stdout.write(` ${c.dim("\u2500")} ${c.bold(displayName)}${argStr ? " " + c.dim(argStr) : ""} ${c.dim(elapsedSec + "s\u2026")}`);
2862
+ }, 1e3);
2863
+ }
2864
+ let result;
2865
+ let toolOk = true;
2866
+ try {
2867
+ result = await runLocalTool({ tool: displayName, args: args ?? {} }, localToolsCfg);
2868
+ } catch (err) {
2869
+ result = `Error: ${err?.message ?? String(err)}`;
2870
+ toolOk = false;
2871
+ }
2872
+ if (elapsedInterval) clearInterval(elapsedInterval);
2873
+ const elapsed = ((Date.now() - startMs) / 1e3).toFixed(1);
2874
+ const preview = toolOk ? toolResultPreview(displayName, result) : "";
2875
+ readline4.cursorTo(process.stdout, 0);
2876
+ readline4.clearLine(process.stdout, 0);
2877
+ const statusIcon = toolOk ? sym.ok : sym.fail;
2878
+ const previewPart = preview ? ` ${c.dim(preview)}` : "";
2879
+ process.stdout.write(` ${c.dim("\u2500")} ${c.bold(displayName)}${argStr ? " " + c.dim(argStr) : ""} ${statusIcon}${previewPart} ${c.dim("(" + elapsed + "s)")}
2880
+ `);
2881
+ appendSessionLog(sessionId, {
2882
+ type: "local_tool_result",
2883
+ tool: toolName,
2884
+ result: result.slice(0, 4e3),
2885
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
2886
+ });
2887
+ try {
2888
+ await fetch(`${cfg.apiUrl}/v1/agents/cli-tool-result`, {
2889
+ method: "POST",
2890
+ headers: {
2891
+ "Content-Type": "application/json",
2892
+ "Authorization": `Bearer ${cfg.apiKey}`
2893
+ },
2894
+ body: JSON.stringify({ requestId, result })
2895
+ });
2896
+ } catch (postErr) {
2897
+ console.error(c.warn(`
2898
+ [local] Failed to submit tool result: ${postErr?.message}`));
2899
+ }
2900
+ } else if (event.type === "keepalive") {
2901
+ } else if (event.type === "toolStart") {
2902
+ if (thinkingSpinner.isSpinning) thinkingSpinner.stop();
2903
+ lastToolName = event.toolName ?? "";
2904
+ toolStartMs = Date.now();
2905
+ if (hasOutput) process.stdout.write("\n");
2906
+ process.stdout.write(` ${c.dim("\u2500")} ${c.bold(lastToolName)}`);
2907
+ hasOutput = false;
2908
+ } else if (event.type === "toolEnd") {
2909
+ const elapsed = ((Date.now() - toolStartMs) / 1e3).toFixed(1);
2910
+ readline4.cursorTo(process.stdout, 0);
2911
+ readline4.clearLine(process.stdout, 0);
2912
+ process.stdout.write(` ${c.dim("\u2500")} ${c.bold(lastToolName)} ${sym.ok} ${c.dim("(" + elapsed + "s)")}
2913
+ `);
2914
+ process.stdout.write(c.primary("agent") + c.dim(" \u203A "));
2915
+ hasOutput = false;
2916
+ } else if (event.type === "final") {
2917
+ const e = event;
2918
+ const text = extractText(e?.payload);
2919
+ if (text && !hasOutput) {
2920
+ process.stdout.write(text);
2921
+ agentContent += text;
2922
+ }
2923
+ const usage = e?.payload?.usage;
2924
+ if (usage) {
2925
+ const inputTok = usage.inputTokens ?? 0;
2926
+ const outputTok = usage.outputTokens ?? 0;
2927
+ const cachedTok = usage.cachedTokens ?? 0;
2928
+ const total = usage.totalTokens ?? inputTok + outputTok;
2929
+ const cost = typeof usage.costUsd === "number" ? `$${usage.costUsd.toFixed(4)}` : "";
2930
+ const parts = [total ? `${total.toLocaleString()} tokens` : "", cost].filter(Boolean);
2931
+ if (parts.length) process.stdout.write("\n" + c.dim(` \u21B3 ${parts.join(" \xB7 ")}`));
2932
+ if (cachedTok > 0) process.stdout.write(c.dim(` (${cachedTok.toLocaleString()} cached)`));
2933
+ appendSessionLog(sessionId, {
2934
+ type: "message",
2935
+ role: "assistant",
2936
+ content: agentContent,
2937
+ usage: { inputTokens: inputTok, outputTokens: outputTok, cachedTokens: cachedTok, totalTokens: total, costUsd: usage.costUsd },
2938
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
2939
+ });
2940
+ } else {
2941
+ appendSessionLog(sessionId, {
2942
+ type: "message",
2943
+ role: "assistant",
2944
+ content: agentContent,
2945
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
2946
+ });
2947
+ }
2948
+ break;
2949
+ } else if (event.type === "error") {
2950
+ if (thinkingSpinner.isSpinning) thinkingSpinner.stop();
2951
+ if (hasOutput) process.stdout.write("\n");
2952
+ console.error(`
2953
+ ${sym.fail} ${c.error(event.message ?? "Stream error")}`);
2954
+ break;
2955
+ }
2956
+ }
2957
+ if (thinkingSpinner.isSpinning) thinkingSpinner.stop();
2958
+ process.stdout.write("\n");
2959
+ if (localToolsCfg && agentContent) {
2960
+ await handleLocalToolLoop(
2961
+ agentContent,
2962
+ localToolsCfg,
2963
+ client,
2964
+ agentId,
2965
+ sessionId,
2966
+ appendSessionLog,
2967
+ !!opts.computer
2968
+ );
2969
+ }
2970
+ } catch (err) {
2971
+ process.stdout.write("\n");
2972
+ console.error(`${sym.fail} ${c.error(err.message ?? String(err))}`);
2973
+ }
2974
+ }
2975
+ console.log();
2976
+ readline4.cursorTo(process.stdout, 0);
2977
+ readline4.clearLine(process.stdout, 0);
2978
+ rl.resume();
2979
+ rl.prompt();
2980
+ });
2981
+ const cleanup = () => {
2982
+ if (localToolsCfg) removeGitHook(localToolsCfg.rootDir);
2983
+ };
2984
+ rl.on("close", () => {
2985
+ cleanup();
2986
+ process.exit(0);
2987
+ });
2988
+ process.on("SIGINT", () => {
2989
+ cleanup();
2990
+ console.log(c.dim(`
2991
+ Session preserved. Resume with: agc chat --resume ${sessionId}`));
2992
+ process.exit(130);
2993
+ });
2994
+ });
2995
+ }
2996
+ var MAX_TOOL_DEPTH = 10;
2997
+ async function handleLocalToolLoop(agentText, cfg, client, agentId, sessionId, appendLog, computerEnabled = false, depth = 0) {
2998
+ if (depth >= MAX_TOOL_DEPTH) {
2999
+ console.log(c.dim(`
3000
+ [local] Max tool depth reached (${MAX_TOOL_DEPTH}). Stopping tool loop.
3001
+ `));
3002
+ return;
3003
+ }
3004
+ const toolCall = extractToolCall(agentText);
3005
+ if (!toolCall) return;
3006
+ const argStr = toolArgSummary(toolCall.tool, toolCall.args ?? {});
3007
+ process.stdout.write(`
3008
+ ${c.dim("\u2500")} ${c.bold(toolCall.tool)}${argStr ? " " + c.dim(argStr) : ""}`);
3009
+ const startMs = Date.now();
3010
+ let result;
3011
+ let toolOk = true;
3012
+ try {
3013
+ result = await runLocalTool(toolCall, cfg);
3014
+ } catch (err) {
3015
+ result = `Error: ${err?.message ?? String(err)}`;
3016
+ toolOk = false;
3017
+ }
3018
+ const elapsed = ((Date.now() - startMs) / 1e3).toFixed(1);
3019
+ const preview = toolOk ? toolResultPreview(toolCall.tool, result) : "";
3020
+ readline4.cursorTo(process.stdout, 0);
3021
+ readline4.clearLine(process.stdout, 0);
3022
+ const previewPart = preview ? ` ${c.dim(preview)}` : "";
3023
+ process.stdout.write(` ${c.dim("\u2500")} ${c.bold(toolCall.tool)}${argStr ? " " + c.dim(argStr) : ""} ${toolOk ? sym.ok : sym.fail}${previewPart} ${c.dim("(" + elapsed + "s)")}
3024
+ `);
3025
+ const resultMsg = `[Tool result: ${toolCall.tool}]
3026
+ \`\`\`
3027
+ ${result}
3028
+ \`\`\``;
3029
+ appendLog(sessionId, {
3030
+ type: "message",
3031
+ role: "tool",
3032
+ tool: toolCall.tool,
3033
+ result: result.slice(0, 4e3),
3034
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
3035
+ });
3036
+ process.stdout.write(c.primary("agent") + c.dim(" \u203A "));
3037
+ let followContent = "";
3038
+ try {
3039
+ let loopToolName = "";
3040
+ let loopToolStartMs = 0;
3041
+ for await (const evt of client.agents.stream({
3042
+ agentId,
3043
+ sessionId,
3044
+ messages: [{ role: "user", content: resultMsg }],
3045
+ ...computerEnabled && { computerRequest: { enabled: true } }
3046
+ })) {
3047
+ if (evt.type === "token") {
3048
+ const tok = evt.content ?? "";
3049
+ process.stdout.write(tok);
3050
+ followContent += tok;
3051
+ } else if (evt.type === "toolStart") {
3052
+ loopToolName = evt.toolName ?? "";
3053
+ loopToolStartMs = Date.now();
3054
+ if (followContent) process.stdout.write("\n");
3055
+ process.stdout.write(` ${c.dim("\u2500")} ${c.bold(loopToolName)}`);
3056
+ } else if (evt.type === "toolEnd") {
3057
+ const elapsed2 = ((Date.now() - loopToolStartMs) / 1e3).toFixed(1);
3058
+ readline4.cursorTo(process.stdout, 0);
3059
+ readline4.clearLine(process.stdout, 0);
3060
+ process.stdout.write(` ${c.dim("\u2500")} ${c.bold(loopToolName)} ${sym.ok} ${c.dim("(" + elapsed2 + "s)")}
3061
+ `);
3062
+ process.stdout.write(c.primary("agent") + c.dim(" \u203A "));
3063
+ } else if (evt.type === "final") {
3064
+ const txt = extractText(evt?.payload);
3065
+ if (txt && !followContent) {
3066
+ process.stdout.write(txt);
3067
+ followContent += txt;
3068
+ }
3069
+ appendLog(sessionId, { type: "message", role: "assistant", content: followContent, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
3070
+ break;
3071
+ } else if (evt.type === "error") {
3072
+ console.error(`
3073
+ ${sym.fail} ${c.error(evt.message ?? "Stream error")}`);
3074
+ break;
3075
+ }
3076
+ }
3077
+ process.stdout.write("\n");
3078
+ } catch (err) {
3079
+ process.stdout.write("\n");
3080
+ console.error(`${sym.fail} ${c.error(err?.message ?? String(err))}`);
3081
+ return;
3082
+ }
3083
+ await handleLocalToolLoop(
3084
+ followContent,
3085
+ cfg,
3086
+ client,
3087
+ agentId,
3088
+ sessionId,
3089
+ appendLog,
3090
+ computerEnabled,
3091
+ depth + 1
3092
+ );
3093
+ }
3094
+ function truncate(s, max) {
3095
+ const str = String(s ?? "");
3096
+ return str.length <= max ? str : str.slice(0, max - 1) + "\u2026";
3097
+ }
3098
+ function toolArgSummary(toolName, args) {
3099
+ switch (toolName) {
3100
+ case "read_file":
3101
+ return truncate(args.path ?? "", 60);
3102
+ case "write_file":
3103
+ return truncate(args.path ?? "", 60);
3104
+ case "delete_file":
3105
+ return truncate(args.path ?? "", 60);
3106
+ case "list_directory":
3107
+ return truncate(args.path ?? ".", 60);
3108
+ case "run_command":
3109
+ return truncate(args.command ?? "", 60);
3110
+ case "start_process":
3111
+ return truncate(args.command ?? "", 60);
3112
+ case "wait_for_process":
3113
+ return truncate(args.process_id ?? "", 20);
3114
+ case "process_status":
3115
+ return truncate(args.process_id ?? "", 20);
3116
+ case "kill_process":
3117
+ return truncate(args.process_id ?? "", 20);
3118
+ case "list_processes":
3119
+ return "";
3120
+ case "search_files": {
3121
+ const parts = [args.pattern, args.query].filter(Boolean);
3122
+ return truncate(parts.join(" "), 60);
3123
+ }
3124
+ default: {
3125
+ const first = args.path ?? args.query ?? args.command ?? args.pattern ?? "";
3126
+ return truncate(String(first), 60);
3127
+ }
3128
+ }
3129
+ }
3130
+ function toolResultPreview(toolName, result) {
3131
+ if (!result || result.startsWith("Error:")) return "";
3132
+ switch (toolName) {
3133
+ case "read_file": {
3134
+ const lines = result.split("\n").length;
3135
+ return `${lines} lines`;
3136
+ }
3137
+ case "write_file":
3138
+ return "written";
3139
+ case "delete_file":
3140
+ return "deleted";
3141
+ case "list_directory": {
3142
+ const count = result.split("\n").filter(Boolean).length;
3143
+ return `${count} entries`;
3144
+ }
3145
+ case "run_command": {
3146
+ const first = result.split("\n").find((l) => l.trim());
3147
+ return first ? truncate(first.trim(), 50) : "done";
3148
+ }
3149
+ case "start_process": {
3150
+ const match = result.match(/process[_\s-]?id[:\s]+([a-zA-Z0-9_-]+)/i) ?? result.match(/"id"[:\s]+"([^"]+)"/);
3151
+ return match ? `pid ${match[1]}` : "started";
3152
+ }
3153
+ case "wait_for_process": {
3154
+ if (/done|complete|exit/i.test(result)) return "done";
3155
+ if (/running/i.test(result)) return "still running";
3156
+ return truncate(result.split("\n")[0]?.trim() ?? "", 40);
3157
+ }
3158
+ case "search_files": {
3159
+ const count = result.split("\n").filter(Boolean).length;
3160
+ return `${count} match${count === 1 ? "" : "es"}`;
3161
+ }
3162
+ default: {
3163
+ const first = result.split("\n").find((l) => l.trim());
3164
+ return first ? truncate(first.trim(), 50) : "";
3165
+ }
3166
+ }
3167
+ }
3168
+ function extractText(payload) {
3169
+ if (!payload) return "";
3170
+ if (typeof payload === "string") return payload;
3171
+ if (typeof payload.content === "string") return payload.content;
3172
+ if (Array.isArray(payload.content)) {
3173
+ return payload.content.filter((b) => b.type === "text").map((b) => b.text).join("\n");
3174
+ }
3175
+ if (payload.text) return payload.text;
3176
+ if (payload.message) return payload.message;
3177
+ return JSON.stringify(payload);
3178
+ }
3179
+
3180
+ // src/commands/mcp.ts
3181
+ var import_commander10 = require("commander");
3182
+ function mcpCommand() {
3183
+ const cmd = new import_commander10.Command("mcp").description("Manage MCP (Model Context Protocol) servers");
3184
+ 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) => {
3185
+ const cfg = loadConfig();
3186
+ if (!cfg.initiator && !opts.agent) {
3187
+ console.error(c.error("No initiator set. Run `agc login` first."));
3188
+ process.exit(1);
3189
+ }
3190
+ const ownerId = opts.agent ?? cfg.initiator;
3191
+ const ownerType = opts.agent ? "agent" : "user";
3192
+ const spinner = spin("Fetching MCP servers\u2026");
3193
+ try {
3194
+ const client = makeClient();
3195
+ const res = await client.mcp.listServers(ownerId, ownerType);
3196
+ const servers = res.servers ?? [];
3197
+ spinner.stop();
3198
+ if (opts.json) return jsonOut(servers);
3199
+ section(`MCP Servers (${servers.length})`);
3200
+ if (!servers.length) {
3201
+ console.log(c.dim(" No MCP servers configured."));
3202
+ console.log(c.dim(' Add one with: agc mcp add --name "filesystem" --type stdio --command "npx @mcp/server-filesystem ~/projects"'));
3203
+ return;
3204
+ }
3205
+ table(
3206
+ servers.map((s) => ({
3207
+ ID: (s.serverId ?? "").slice(0, 8) + "\u2026",
3208
+ Name: s.name ?? "",
3209
+ Type: s.connectionType ?? "",
3210
+ Tools: String(s.toolCount ?? 0),
3211
+ Created: relativeTime(s.createdAt)
3212
+ })),
3213
+ ["ID", "Name", "Type", "Tools", "Created"]
3214
+ );
3215
+ } catch (err) {
3216
+ spinner.stop();
3217
+ printError(err);
3218
+ process.exit(1);
3219
+ }
3220
+ });
3221
+ cmd.command("get <serverId>").description("Show details for an MCP server").option("--json", "Output as JSON").action(async (serverId, opts) => {
3222
+ const spinner = spin("Fetching server\u2026");
3223
+ try {
3224
+ const client = makeClient();
3225
+ const server = await client.mcp.getServer(serverId);
3226
+ spinner.stop();
3227
+ if (opts.json) return jsonOut(server);
3228
+ section(server.name ?? serverId);
3229
+ detail([
3230
+ ["Server ID", c.id(server.serverId)],
3231
+ ["Type", server.connectionType ?? c.dim("(unknown)")],
3232
+ ["Tools", String(server.toolCount ?? 0)],
3233
+ ["Public", server.isPublic ? "yes" : "no"],
3234
+ ["Created", relativeTime(server.createdAt)]
3235
+ ]);
3236
+ const cfg = server.connectionConfig;
3237
+ if (cfg) {
3238
+ console.log("\n " + c.label("Connection Config"));
3239
+ const safe = { ...cfg, apiKey: cfg.apiKey ? "****" : void 0, token: cfg.token ? "****" : void 0 };
3240
+ console.log(" " + JSON.stringify(safe, null, 2).split("\n").join("\n "));
3241
+ }
3242
+ } catch (err) {
3243
+ spinner.stop();
3244
+ printError(err);
3245
+ process.exit(1);
3246
+ }
3247
+ });
3248
+ cmd.command("add").description("Register a new MCP server").requiredOption("--name <name>", "Server name").requiredOption("--type <type>", "Connection type: stdio | sse | http | streamable-http").option("--command <cmd>", 'Command to run (for stdio type, e.g. "npx @mcp/server-filesystem ~/projects")').option("--url <url>", "Server URL (for sse/http types)").option("--agent <agentId>", "Assign to an agent instead of the current user").option("--public", "Make server publicly visible").option("--json", "Output as JSON").action(async (opts) => {
3249
+ const cfg = loadConfig();
3250
+ if (!cfg.initiator && !opts.agent) {
3251
+ console.error(c.error("No initiator set. Run `agc login` first."));
3252
+ process.exit(1);
3253
+ }
3254
+ const validTypes = ["stdio", "sse", "http", "streamable-http"];
3255
+ if (!validTypes.includes(opts.type)) {
3256
+ console.error(c.error(`Invalid type "${opts.type}". Choose from: ${validTypes.join(", ")}`));
3257
+ process.exit(1);
3258
+ }
3259
+ if (opts.type === "stdio" && !opts.command) {
3260
+ console.error(c.error("--command is required for stdio type"));
3261
+ process.exit(1);
3262
+ }
3263
+ if ((opts.type === "sse" || opts.type === "http" || opts.type === "streamable-http") && !opts.url) {
3264
+ console.error(c.error("--url is required for sse/http/streamable-http types"));
3265
+ process.exit(1);
3266
+ }
3267
+ const connectionConfig = {};
3268
+ if (opts.command) connectionConfig.command = opts.command;
3269
+ if (opts.url) connectionConfig.url = opts.url;
3270
+ const ownerId = opts.agent ?? cfg.initiator;
3271
+ const ownerType = opts.agent ? "agent" : "user";
3272
+ const spinner = spin("Registering MCP server\u2026");
3273
+ try {
3274
+ const client = makeClient();
3275
+ const server = await client.mcp.createServer({
3276
+ name: opts.name,
3277
+ connectionType: opts.type,
3278
+ connectionConfig,
3279
+ isPublic: !!opts.public,
3280
+ ownerId,
3281
+ ownerType
3282
+ });
3283
+ spinner.stop();
3284
+ if (opts.json) return jsonOut(server);
3285
+ console.log(`
3286
+ ${sym.ok} MCP server registered`);
3287
+ detail([
3288
+ ["Server ID", c.id(server.serverId)],
3289
+ ["Name", server.name],
3290
+ ["Type", server.connectionType]
3291
+ ]);
3292
+ console.log(c.dim(`
3293
+ Connect and sync tools with: agc mcp sync ${server.serverId}`));
3294
+ } catch (err) {
3295
+ spinner.stop();
3296
+ printError(err);
3297
+ process.exit(1);
3298
+ }
3299
+ });
3300
+ cmd.command("connect <serverId>").description("Connect to an MCP server").action(async (serverId) => {
3301
+ const spinner = spin("Connecting\u2026");
3302
+ try {
3303
+ const client = makeClient();
3304
+ const res = await client.mcp.connect(serverId);
3305
+ spinner.stop();
3306
+ if (res.connected) {
3307
+ console.log(`${sym.ok} Connected to ${c.id(serverId)}`);
3308
+ console.log(c.dim(` Run \`agc mcp sync ${serverId}\` to discover tools.`));
3309
+ } else {
3310
+ console.log(c.warn("Connection returned but reported not connected."));
3311
+ }
3312
+ } catch (err) {
3313
+ spinner.stop();
3314
+ printError(err);
3315
+ process.exit(1);
3316
+ }
3317
+ });
3318
+ cmd.command("disconnect <serverId>").description("Disconnect from an MCP server").action(async (serverId) => {
3319
+ const spinner = spin("Disconnecting\u2026");
3320
+ try {
3321
+ const client = makeClient();
3322
+ await client.mcp.disconnect(serverId);
3323
+ spinner.stop();
3324
+ console.log(`${sym.ok} Disconnected from ${c.id(serverId)}`);
3325
+ } catch (err) {
3326
+ spinner.stop();
3327
+ printError(err);
3328
+ process.exit(1);
3329
+ }
3330
+ });
3331
+ cmd.command("sync <serverId>").description("Sync tools, resources, and prompts from an MCP server").option("--json", "Output as JSON").action(async (serverId, opts) => {
3332
+ const spinner = spin("Syncing\u2026");
3333
+ try {
3334
+ const client = makeClient();
3335
+ const res = await client.mcp.sync(serverId);
3336
+ spinner.stop();
3337
+ if (opts.json) return jsonOut(res);
3338
+ console.log(`${sym.ok} Sync complete`);
3339
+ detail([
3340
+ ["Tools discovered", String(res.toolsDiscovered)],
3341
+ ["Resources discovered", String(res.resourcesDiscovered)],
3342
+ ["Prompts discovered", String(res.promptsDiscovered)]
3343
+ ]);
3344
+ } catch (err) {
3345
+ spinner.stop();
3346
+ printError(err);
3347
+ process.exit(1);
3348
+ }
3349
+ });
3350
+ cmd.command("tools <serverId>").description("List tools discovered from an MCP server").option("--json", "Output as JSON").action(async (serverId, opts) => {
3351
+ const spinner = spin("Fetching tools\u2026");
3352
+ try {
3353
+ const client = makeClient();
3354
+ const res = await client.mcp.listTools(serverId);
3355
+ const tools = res.tools ?? [];
3356
+ spinner.stop();
3357
+ if (opts.json) return jsonOut(tools);
3358
+ section(`MCP Tools (${res.total ?? tools.length})`);
3359
+ table(
3360
+ tools.map((t) => ({
3361
+ Name: t.name ?? "",
3362
+ Description: (t.description ?? "").slice(0, 60)
3363
+ })),
3364
+ ["Name", "Description"]
3365
+ );
3366
+ } catch (err) {
3367
+ spinner.stop();
3368
+ printError(err);
3369
+ process.exit(1);
3370
+ }
3371
+ });
3372
+ cmd.command("resources <serverId>").description("List resources from an MCP server").option("--json", "Output as JSON").action(async (serverId, opts) => {
3373
+ const spinner = spin("Fetching resources\u2026");
3374
+ try {
3375
+ const client = makeClient();
3376
+ const res = await client.mcp.listResources(serverId);
3377
+ const resources = res.resources ?? [];
3378
+ spinner.stop();
3379
+ if (opts.json) return jsonOut(resources);
3380
+ section(`MCP Resources (${res.total ?? resources.length})`);
3381
+ table(
3382
+ resources.map((r) => ({
3383
+ URI: r.uri ?? "",
3384
+ Name: r.name ?? "",
3385
+ MimeType: r.mimeType ?? c.dim("(none)")
3386
+ })),
3387
+ ["URI", "Name", "MimeType"]
3388
+ );
3389
+ } catch (err) {
3390
+ spinner.stop();
3391
+ printError(err);
3392
+ process.exit(1);
3393
+ }
3394
+ });
3395
+ cmd.command("read <serverId> <uri>").description("Read a resource from an MCP server by URI").option("--json", "Output as JSON").action(async (serverId, uri, opts) => {
3396
+ const spinner = spin("Reading resource\u2026");
3397
+ try {
3398
+ const client = makeClient();
3399
+ const res = await client.mcp.readResource(serverId, uri);
3400
+ spinner.stop();
3401
+ if (opts.json) return jsonOut(res);
3402
+ section(`Resource: ${uri}`);
3403
+ const contents = res.contents;
3404
+ if (typeof contents === "string") {
3405
+ console.log(contents);
3406
+ } else {
3407
+ console.log(JSON.stringify(contents, null, 2));
3408
+ }
3409
+ } catch (err) {
3410
+ spinner.stop();
3411
+ printError(err);
3412
+ process.exit(1);
3413
+ }
3414
+ });
3415
+ cmd.command("prompts <serverId>").description("List prompts from an MCP server").option("--json", "Output as JSON").action(async (serverId, opts) => {
3416
+ const spinner = spin("Fetching prompts\u2026");
3417
+ try {
3418
+ const client = makeClient();
3419
+ const res = await client.mcp.listPrompts(serverId);
3420
+ const prompts = res.prompts ?? [];
3421
+ spinner.stop();
3422
+ if (opts.json) return jsonOut(prompts);
3423
+ section(`MCP Prompts (${res.total ?? prompts.length})`);
3424
+ table(
3425
+ prompts.map((p) => ({
3426
+ Name: p.name ?? "",
3427
+ Description: (p.description ?? "").slice(0, 60)
3428
+ })),
3429
+ ["Name", "Description"]
3430
+ );
3431
+ } catch (err) {
3432
+ spinner.stop();
3433
+ printError(err);
3434
+ process.exit(1);
3435
+ }
3436
+ });
3437
+ cmd.command("prompt <serverId> <promptName>").description("Render an MCP prompt with optional arguments").option("--args <json>", "Prompt arguments as JSON object", "{}").option("--json", "Output as JSON").action(async (serverId, promptName, opts) => {
3438
+ let args = {};
3439
+ try {
3440
+ args = JSON.parse(opts.args);
3441
+ } catch {
3442
+ console.error(c.error("--args must be valid JSON"));
3443
+ process.exit(1);
3444
+ }
3445
+ const spinner = spin("Rendering prompt\u2026");
3446
+ try {
3447
+ const client = makeClient();
3448
+ const res = await client.mcp.getPrompt(serverId, promptName, args);
3449
+ spinner.stop();
3450
+ if (opts.json) return jsonOut(res);
3451
+ if (res.description) console.log(c.dim(res.description) + "\n");
3452
+ for (const msg of res.messages ?? []) {
3453
+ const role = c.label(msg.role ?? "unknown");
3454
+ const text = typeof msg.content === "string" ? msg.content : msg.content?.text ?? JSON.stringify(msg.content);
3455
+ console.log(`${role}: ${text}
3456
+ `);
3457
+ }
3458
+ } catch (err) {
3459
+ spinner.stop();
3460
+ printError(err);
3461
+ process.exit(1);
3462
+ }
3463
+ });
3464
+ cmd.command("remove <serverId>").description("Delete an MCP server").action(async (serverId) => {
3465
+ const spinner = spin("Removing server\u2026");
3466
+ try {
3467
+ const client = makeClient();
3468
+ await client.mcp.deleteServer(serverId);
3469
+ spinner.stop();
3470
+ console.log(`${sym.ok} MCP server ${c.id(serverId)} removed.`);
3471
+ } catch (err) {
3472
+ spinner.stop();
3473
+ printError(err);
3474
+ process.exit(1);
3475
+ }
3476
+ });
3477
+ return cmd;
3478
+ }
3479
+
3480
+ // src/commands/skills.ts
3481
+ var import_commander11 = require("commander");
3482
+ function skillsCommand() {
3483
+ const cmd = new import_commander11.Command("skills").description("Discover and manage skills");
3484
+ 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) => {
3485
+ const spinner = spin("Fetching skills\u2026");
3486
+ try {
3487
+ const client = makeClient();
3488
+ const filter = {};
3489
+ if (opts.owner) filter.ownerId = opts.owner;
3490
+ if (opts.platform) filter.ownerType = "platform";
3491
+ const res = await client.skills.list(filter);
3492
+ const skills = res?.data ?? res ?? [];
3493
+ spinner.stop();
3494
+ if (opts.json) return jsonOut(skills);
3495
+ section(`Skills (${skills.length})`);
3496
+ table(
3497
+ skills.map((s) => ({
3498
+ Slug: s.slug ?? "",
3499
+ Name: s.name ?? "",
3500
+ Description: (s.description ?? "").slice(0, 55),
3501
+ Tags: (s.tags ?? []).join(", "),
3502
+ Source: s.source ?? ""
3503
+ })),
3504
+ ["Slug", "Name", "Description", "Tags", "Source"]
3505
+ );
3506
+ } catch (err) {
3507
+ spinner.stop();
3508
+ printError(err);
3509
+ process.exit(1);
3510
+ }
3511
+ });
3512
+ cmd.command("index").description("Show compact skill index (progressive disclosure view)").option("--owner <id>", "Filter by owner ID").option("--json", "Output as JSON").action(async (opts) => {
3513
+ const spinner = spin("Fetching skill index\u2026");
3514
+ try {
3515
+ const client = makeClient();
3516
+ const res = await client.skills.getIndex(opts.owner);
3517
+ const index = res?.data ?? res ?? [];
3518
+ spinner.stop();
3519
+ if (opts.json) return jsonOut(index);
3520
+ section(`Skill Index (${index.length})`);
3521
+ table(
3522
+ index.map((s) => ({
3523
+ "Icon": s.icon ?? " ",
3524
+ "Slug": s.slug ?? "",
3525
+ "Name": s.name ?? "",
3526
+ "Description": (s.description ?? "").slice(0, 55),
3527
+ "Triggers": (s.triggers ?? []).slice(0, 3).join(", ")
3528
+ })),
3529
+ ["Icon", "Slug", "Name", "Description", "Triggers"]
3530
+ );
3531
+ } catch (err) {
3532
+ spinner.stop();
3533
+ printError(err);
3534
+ process.exit(1);
3535
+ }
3536
+ });
3537
+ cmd.command("get <skillId>").description("Show full skill details and instructions").option("--json", "Output as JSON").action(async (skillId, opts) => {
3538
+ const spinner = spin("Fetching skill\u2026");
3539
+ try {
3540
+ const client = makeClient();
3541
+ const res = await client.skills.get(skillId);
3542
+ const skill = res?.data ?? res;
3543
+ spinner.stop();
3544
+ if (!skill) {
3545
+ console.error(c.error(`Skill "${skillId}" not found.`));
3546
+ process.exit(1);
3547
+ }
3548
+ if (opts.json) return jsonOut(skill);
3549
+ section(skill.name);
3550
+ detail([
3551
+ ["Skill ID", c.id(skill.skillId)],
3552
+ ["Slug", skill.slug],
3553
+ ["Description", skill.description ?? c.dim("(none)")],
3554
+ ["Tags", (skill.tags ?? []).join(", ") || c.dim("(none)")],
3555
+ ["Tools", (skill.tools ?? []).join(", ") || c.dim("(none)")],
3556
+ ["Source", skill.source ?? c.dim("(none)")],
3557
+ ["Version", skill.version ?? "1.0.0"],
3558
+ ["Public", skill.isPublic ? "yes" : "no"],
3559
+ ["Usage", String(skill.usageCount ?? 0)]
3560
+ ]);
3561
+ if (skill.instructions) {
3562
+ console.log("\n " + c.label("Instructions"));
3563
+ const lines = skill.instructions.split("\n");
3564
+ for (const line of lines) {
3565
+ console.log(" " + c.dim(line));
3566
+ }
3567
+ }
3568
+ } catch (err) {
3569
+ spinner.stop();
3570
+ printError(err);
3571
+ process.exit(1);
3572
+ }
3573
+ });
3574
+ cmd.command("create").description("Create a new skill").requiredOption("--slug <slug>", "Unique slug identifier").requiredOption("--name <name>", "Display name").requiredOption("--description <desc>", "Short description").requiredOption("--instructions <text>", "Full skill instructions (markdown)").option("--tools <tools>", "Comma-separated tool names").option("--triggers <triggers>", "Comma-separated trigger phrases").option("--tags <tags>", "Comma-separated tags").option("--icon <icon>", "Emoji icon").option("--public", "Make skill publicly discoverable").option("--json", "Output result as JSON").action(async (opts) => {
3575
+ const cfg = loadConfig();
3576
+ const spinner = spin("Creating skill\u2026");
3577
+ try {
3578
+ const client = makeClient();
3579
+ const res = await client.skills.create({
3580
+ slug: opts.slug,
3581
+ name: opts.name,
3582
+ description: opts.description,
3583
+ instructions: opts.instructions,
3584
+ tools: opts.tools ? opts.tools.split(",").map((t) => t.trim()) : [],
3585
+ triggers: opts.triggers ? opts.triggers.split(",").map((t) => t.trim()) : [],
3586
+ tags: opts.tags ? opts.tags.split(",").map((t) => t.trim()) : [],
3587
+ icon: opts.icon,
3588
+ isPublic: !!opts.public,
3589
+ ownerId: cfg.initiator,
3590
+ ownerType: "user",
3591
+ source: "user"
3592
+ });
3593
+ const skill = res?.data ?? res;
3594
+ spinner.stop();
3595
+ if (opts.json) return jsonOut(skill);
3596
+ console.log(`${sym.ok} Skill ${c.id(skill.skillId)} created`);
3597
+ detail([
3598
+ ["Slug", skill.slug],
3599
+ ["Name", skill.name]
3600
+ ]);
3601
+ } catch (err) {
3602
+ spinner.stop();
3603
+ printError(err);
3604
+ process.exit(1);
3605
+ }
3606
+ });
3607
+ cmd.command("install <slug>").description("Install a skill by slug (fetch full instructions and print as SKILL.md)").action(async (slug) => {
3608
+ const spinner = spin("Loading skill\u2026");
3609
+ try {
3610
+ const client = makeClient();
3611
+ const res = await client.skills.get(slug);
3612
+ const skill = res?.data ?? res;
3613
+ spinner.stop();
3614
+ if (!skill) {
3615
+ console.error(c.error(`Skill "${slug}" not found.`));
3616
+ process.exit(1);
3617
+ }
3618
+ const md = [
3619
+ `# SKILL: ${skill.name}`,
3620
+ ``,
3621
+ `**Slug:** ${skill.slug}`,
3622
+ `**Version:** ${skill.version ?? "1.0.0"}`,
3623
+ `**Tags:** ${(skill.tags ?? []).join(", ")}`,
3624
+ `**Tools:** ${(skill.tools ?? []).join(", ") || "none"}`,
3625
+ ``,
3626
+ `## Description`,
3627
+ ``,
3628
+ skill.description,
3629
+ ``,
3630
+ `## Instructions`,
3631
+ ``,
3632
+ skill.instructions
3633
+ ].join("\n");
3634
+ console.log(md);
3635
+ } catch (err) {
3636
+ spinner.stop();
3637
+ printError(err);
3638
+ process.exit(1);
3639
+ }
3640
+ });
3641
+ cmd.command("publish <slug>").description("Make a skill publicly discoverable").option("--json", "Output result as JSON").action(async (slug, opts) => {
3642
+ const spinner = spin(`Publishing skill "${slug}"\u2026`);
3643
+ try {
3644
+ const client = makeClient();
3645
+ const res = await client.skills.update(slug, { isPublic: true });
3646
+ const skill = res?.data ?? res;
3647
+ spinner.stop();
3648
+ if (opts.json) return jsonOut(skill);
3649
+ console.log(`${sym.ok} Skill ${c.id(skill.slug)} is now ${c.success("public")}`);
3650
+ } catch (err) {
3651
+ spinner.stop();
3652
+ printError(err);
3653
+ process.exit(1);
3654
+ }
3655
+ });
3656
+ cmd.command("unpublish <slug>").description("Make a skill private (remove from public marketplace)").option("--json", "Output result as JSON").action(async (slug, opts) => {
3657
+ const spinner = spin(`Unpublishing skill "${slug}"\u2026`);
3658
+ try {
3659
+ const client = makeClient();
3660
+ const res = await client.skills.update(slug, { isPublic: false });
3661
+ const skill = res?.data ?? res;
3662
+ spinner.stop();
3663
+ if (opts.json) return jsonOut(skill);
3664
+ console.log(`${sym.ok} Skill ${c.id(skill.slug)} is now ${c.warn("private")}`);
3665
+ } catch (err) {
3666
+ spinner.stop();
3667
+ printError(err);
3668
+ process.exit(1);
3669
+ }
3670
+ });
3671
+ cmd.command("update <slug>").description("Update skill properties").option("--name <name>", "New display name").option("--description <desc>", "New description").option("--instructions <text>", "New instructions (markdown)").option("--tools <tools>", "Comma-separated tool names (replaces existing)").option("--triggers <triggers>", "Comma-separated trigger phrases (replaces existing)").option("--tags <tags>", "Comma-separated tags (replaces existing)").option("--icon <icon>", "Emoji icon").option("--json", "Output result as JSON").action(async (slug, opts) => {
3672
+ const updates = {};
3673
+ if (opts.name) updates.name = opts.name;
3674
+ if (opts.description) updates.description = opts.description;
3675
+ if (opts.instructions) updates.instructions = opts.instructions;
3676
+ if (opts.tools) updates.tools = opts.tools.split(",").map((t) => t.trim());
3677
+ if (opts.triggers) updates.triggers = opts.triggers.split(",").map((t) => t.trim());
3678
+ if (opts.tags) updates.tags = opts.tags.split(",").map((t) => t.trim());
3679
+ if (opts.icon) updates.icon = opts.icon;
3680
+ if (Object.keys(updates).length === 0) {
3681
+ console.error(c.warn("No fields to update. Use --name, --description, --instructions, etc."));
3682
+ process.exit(1);
3683
+ }
3684
+ const spinner = spin(`Updating skill "${slug}"\u2026`);
3685
+ try {
3686
+ const client = makeClient();
3687
+ const res = await client.skills.update(slug, updates);
3688
+ const skill = res?.data ?? res;
3689
+ spinner.stop();
3690
+ if (opts.json) return jsonOut(skill);
3691
+ console.log(`${sym.ok} Skill ${c.id(skill.slug)} updated`);
3692
+ detail([
3693
+ ["Name", skill.name],
3694
+ ["Description", skill.description ?? c.dim("(none)")],
3695
+ ["Public", skill.isPublic ? c.success("yes") : c.warn("no")],
3696
+ ["Tags", (skill.tags ?? []).join(", ") || c.dim("(none)")]
3697
+ ]);
3698
+ } catch (err) {
3699
+ spinner.stop();
3700
+ printError(err);
3701
+ process.exit(1);
3702
+ }
3703
+ });
3704
+ cmd.command("delete <slug>").description("Permanently delete a skill").option("--yes", "Skip confirmation prompt").option("--json", "Output result as JSON").action(async (slug, opts) => {
3705
+ if (!opts.yes) {
3706
+ const readline5 = await import("readline");
3707
+ const rl = readline5.createInterface({ input: process.stdin, output: process.stdout });
3708
+ const answer = await new Promise(
3709
+ (resolve2) => rl.question(c.warn(`Delete skill "${slug}"? This cannot be undone. [y/N] `), resolve2)
3710
+ );
3711
+ rl.close();
3712
+ if (!["y", "yes"].includes(answer.trim().toLowerCase())) {
3713
+ console.log(c.dim("Aborted."));
3714
+ return;
3715
+ }
3716
+ }
3717
+ const spinner = spin(`Deleting skill "${slug}"\u2026`);
3718
+ try {
3719
+ const client = makeClient();
3720
+ const res = await client.skills.delete(slug);
3721
+ spinner.stop();
3722
+ if (opts.json) return jsonOut(res);
3723
+ console.log(`${sym.ok} Skill ${c.id(slug)} deleted`);
3724
+ } catch (err) {
3725
+ spinner.stop();
3726
+ printError(err);
3727
+ process.exit(1);
3728
+ }
3729
+ });
3730
+ return cmd;
3731
+ }
3732
+
3733
+ // src/commands/wallet.ts
3734
+ var import_commander12 = require("commander");
3735
+ function walletCommand() {
3736
+ const cmd = new import_commander12.Command("wallet").description("Manage agent wallets");
3737
+ 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) => {
3738
+ const cfg = loadConfig();
3739
+ const agentId = opts.agent ?? cfg.defaultAgentId;
3740
+ if (!agentId) {
3741
+ console.error(c.error("Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`"));
3742
+ process.exit(1);
3743
+ }
3744
+ const spinner = spin("Fetching wallets\u2026");
3745
+ try {
3746
+ const client = makeClient();
3747
+ const wallets = await client.wallets.list(agentId);
3748
+ spinner.stop();
3749
+ if (opts.json) return jsonOut(wallets);
3750
+ const list = wallets?.data ?? wallets ?? [];
3751
+ section(`Wallets for agent ${agentId.slice(0, 8)}\u2026 (${list.length})`);
3752
+ table(
3753
+ list.map((w) => ({
3754
+ ID: w.id.slice(0, 8) + "\u2026",
3755
+ Type: w.walletType,
3756
+ Address: w.address,
3757
+ Chain: chainName(w.chainId),
3758
+ Label: w.label ?? "Primary",
3759
+ Active: w.isActive ? sym.ok : sym.fail
3760
+ })),
3761
+ ["ID", "Type", "Address", "Chain", "Label", "Active"]
3762
+ );
3763
+ } catch (err) {
3764
+ spinner.stop();
3765
+ printError(err);
3766
+ process.exit(1);
3767
+ }
3768
+ });
3769
+ 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) => {
3770
+ const cfg = loadConfig();
3771
+ const agentId = opts.agent ?? cfg.defaultAgentId;
3772
+ if (!agentId) {
3773
+ console.error(c.error("Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`"));
3774
+ process.exit(1);
3775
+ }
3776
+ const spinner = spin("Fetching primary wallet\u2026");
3777
+ try {
3778
+ const client = makeClient();
3779
+ const wallet = await client.wallets.primary(agentId);
3780
+ spinner.stop();
3781
+ if (!wallet) {
3782
+ console.log(c.warn(` No wallet found for agent ${agentId}`));
3783
+ console.log(c.dim(` Run: agc wallet create --agent ${agentId}`));
3784
+ return;
3785
+ }
3786
+ const w = wallet?.data ?? wallet;
3787
+ if (opts.json) return jsonOut(w);
3788
+ section("Primary Wallet");
3789
+ detail([
3790
+ ["Address", w.address],
3791
+ ["Type", w.walletType],
3792
+ ["Chain", chainName(w.chainId)],
3793
+ ["Label", w.label ?? "Primary"],
3794
+ ["Wallet ID", w.id]
3795
+ ]);
3796
+ } catch (err) {
3797
+ spinner.stop();
3798
+ printError(err);
3799
+ process.exit(1);
3800
+ }
3801
+ });
3802
+ 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) => {
3803
+ const cfg = loadConfig();
3804
+ const agentId = opts.agent ?? cfg.defaultAgentId;
3805
+ if (!agentId) {
3806
+ console.error(c.error("Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`"));
3807
+ process.exit(1);
3808
+ }
3809
+ const spinner = spin("Fetching balance\u2026");
3810
+ try {
3811
+ const client = makeClient();
3812
+ let walletId = opts.wallet;
3813
+ if (!walletId) {
3814
+ const primary = await client.wallets.primary(agentId);
3815
+ const w = primary?.data ?? primary;
3816
+ if (!w) {
3817
+ spinner.stop();
3818
+ console.log(c.warn(` No wallet found. Run: agc wallet create --agent ${agentId}`));
3819
+ return;
3820
+ }
3821
+ walletId = w.id;
3822
+ }
3823
+ const balance = await client.wallets.balance(walletId);
3824
+ spinner.stop();
3825
+ const b = balance?.data ?? balance;
3826
+ if (opts.json) return jsonOut(b);
3827
+ section("Wallet Balance");
3828
+ detail([
3829
+ ["Address", b.address],
3830
+ ["Chain", chainName(b.chainId)],
3831
+ ["USDC", c.bold(b.usdc + " USDC")],
3832
+ ["ETH", b.native + " ETH"]
3833
+ ]);
3834
+ console.log();
3835
+ console.log(c.dim(" Fund this wallet by sending USDC to the address above."));
3836
+ console.log(c.dim(" Network: Base Sepolia (chain 84532)"));
3837
+ } catch (err) {
3838
+ spinner.stop();
3839
+ printError(err);
3840
+ process.exit(1);
3841
+ }
3842
+ });
3843
+ 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) => {
3844
+ const cfg = loadConfig();
3845
+ const agentId = opts.agent ?? cfg.defaultAgentId;
3846
+ if (!agentId) {
3847
+ console.error(c.error("Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`"));
3848
+ process.exit(1);
3849
+ }
3850
+ if (opts.type === "external" && !opts.address) {
3851
+ console.error(c.error("--address is required for --type external"));
3852
+ process.exit(1);
3853
+ }
3854
+ const spinner = spin("Creating wallet\u2026");
3855
+ try {
3856
+ const client = makeClient();
3857
+ const wallet = await client.wallets.create({
3858
+ agentId,
3859
+ walletType: opts.type,
3860
+ label: opts.label,
3861
+ externalAddress: opts.address
3862
+ });
3863
+ spinner.stop();
3864
+ const w = wallet?.data ?? wallet;
3865
+ if (opts.json) return jsonOut(w);
3866
+ console.log(`
3867
+ ${sym.ok} ${c.bold("Wallet created")}`);
3868
+ detail([
3869
+ ["Address", c.bold(w.address)],
3870
+ ["Type", w.walletType],
3871
+ ["Chain", chainName(w.chainId)],
3872
+ ["Label", w.label],
3873
+ ["Wallet ID", w.id]
3874
+ ]);
3875
+ console.log();
3876
+ console.log(c.dim(" Fund this wallet by sending USDC to the address above."));
3877
+ } catch (err) {
3878
+ spinner.stop();
3879
+ printError(err);
3880
+ process.exit(1);
3881
+ }
3882
+ });
3883
+ cmd.command("send").description("Send USDC (or ETH) from an agent wallet to another address").requiredOption("--agent <agentId>", "Agent ID").requiredOption("--to <address>", "Recipient address (0x\u2026)").requiredOption("--amount <amount>", "Amount to send (e.g. 10.5)").option("--token <symbol>", "Token to send: USDC or ETH (default: USDC)", "USDC").option("--wallet <walletId>", "Specific wallet ID (defaults to primary)").action(async (opts) => {
3884
+ const client = makeClient();
3885
+ const spinner = spin("Preparing transfer\u2026");
3886
+ try {
3887
+ let walletId = opts.wallet;
3888
+ if (!walletId) {
3889
+ const primary = await client.wallets.primary(opts.agent);
3890
+ const w = primary?.data ?? primary;
3891
+ if (!w?.id) {
3892
+ spinner.stop();
3893
+ console.error(c.error(`No wallet found for agent ${opts.agent}. Run: agc wallet create --agent ${opts.agent}`));
3894
+ process.exit(1);
3895
+ }
3896
+ walletId = w.id;
3897
+ }
3898
+ spinner.text = `Sending ${opts.amount} ${opts.token} \u2192 ${opts.to}\u2026`;
3899
+ const result = await client.wallets.transfer(walletId, {
3900
+ toAddress: opts.to,
3901
+ amount: opts.amount,
3902
+ tokenSymbol: opts.token
3903
+ });
3904
+ const tx = result?.txHash ?? result?.data?.txHash ?? result;
3905
+ spinner.stop();
3906
+ console.log(`
3907
+ ${c.bold("Transfer sent")}`);
3908
+ detail([
3909
+ ["Amount", `${opts.amount} ${opts.token}`],
3910
+ ["To", opts.to],
3911
+ ["Tx Hash", c.id(tx)]
3912
+ ]);
3913
+ } catch (err) {
3914
+ spinner.stop();
3915
+ printError(err);
3916
+ process.exit(1);
3917
+ }
3918
+ });
3919
+ cmd.command("x402-fetch").description("Fetch a URL using an agent wallet to pay any x402 (402 Payment Required) challenge").requiredOption("--agent <agentId>", "Agent ID").requiredOption("--url <url>", "Target URL to fetch").option("--method <method>", "HTTP method", "GET").option("--header <header>", "Extra header in Key:Value format (repeatable)", collect, []).option("--body <body>", "Request body string").option("--json", "Output response as JSON").action(async (opts) => {
3920
+ const client = makeClient();
3921
+ const spinner = spin(`Fetching ${opts.url}\u2026`);
3922
+ try {
3923
+ const headers = {};
3924
+ for (const h of opts.header) {
3925
+ const idx = h.indexOf(":");
3926
+ if (idx > 0) headers[h.slice(0, idx).trim()] = h.slice(idx + 1).trim();
3927
+ }
3928
+ const res = await client.wallets.x402Fetch(opts.agent, {
3929
+ url: opts.url,
3930
+ method: opts.method,
3931
+ headers: Object.keys(headers).length ? headers : void 0,
3932
+ body: opts.body
3933
+ });
3934
+ spinner.stop();
3935
+ if (opts.json) return jsonOut(res);
3936
+ console.log(`
3937
+ ${c.bold("Response")} status ${res.status}`);
3938
+ if (res.status === 200) {
3939
+ console.log(c.dim(JSON.stringify(res.body, null, 2).slice(0, 1e3)));
3940
+ } else {
3941
+ console.log(c.warn(JSON.stringify(res.body, null, 2)));
3942
+ }
3943
+ } catch (err) {
3944
+ spinner.stop();
3945
+ printError(err);
3946
+ process.exit(1);
3947
+ }
3948
+ });
3949
+ return cmd;
3950
+ }
3951
+ function collect(val, acc) {
3952
+ acc.push(val);
3953
+ return acc;
3954
+ }
3955
+ function chainName(chainId) {
3956
+ const names = {
3957
+ "84532": "Base Sepolia",
3958
+ "8453": "Base",
3959
+ "1": "Ethereum",
3960
+ "137": "Polygon"
3961
+ };
3962
+ return names[chainId] ?? `chain ${chainId}`;
3963
+ }
3964
+
3965
+ // src/commands/models.ts
3966
+ var import_commander13 = require("commander");
3967
+ function modelsCommand() {
3968
+ const cmd = new import_commander13.Command("models").description("List available LLM models");
3969
+ 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) => {
3970
+ const client = makeClient();
3971
+ const spinner = spin("Fetching models\u2026");
3972
+ try {
3973
+ const res = await client.models.list();
3974
+ spinner.stop();
3975
+ const all = res?.data ?? res ?? [];
3976
+ if (opts.json) return jsonOut(all);
3977
+ const filtered = opts.provider ? all.filter((m) => m.provider === opts.provider) : all;
3978
+ if (filtered.length === 0) {
3979
+ console.log(c.warn(" No models found."));
3980
+ return;
3981
+ }
3982
+ const grouped = {};
3983
+ for (const m of filtered) {
3984
+ if (!grouped[m.provider]) grouped[m.provider] = [];
3985
+ grouped[m.provider].push(m);
3986
+ }
3987
+ for (const [provider, models] of Object.entries(grouped)) {
3988
+ console.log(`
3989
+ ${c.bold(provider.toUpperCase())}`);
3990
+ for (const m of models) {
3991
+ const tags = [
3992
+ m.tier,
3993
+ m.supportsTools ? "tools" : "",
3994
+ m.supportsVision ? "vision" : ""
3995
+ ].filter(Boolean).join(", ");
3996
+ const price = m.inputPricePer1kTokens > 0 ? c.dim(` ($${m.inputPricePer1kTokens}/$${m.outputPricePer1kTokens} /1k)`) : c.dim(" (free/local)");
3997
+ console.log(` ${c.id(m.modelId.padEnd(36))} ${m.displayName.padEnd(24)} ${c.dim(tags)}${price}`);
3998
+ }
3999
+ }
4000
+ console.log();
4001
+ } catch (err) {
4002
+ spinner.stop();
4003
+ printError(err);
4004
+ process.exit(1);
4005
+ }
4006
+ });
4007
+ return cmd;
4008
+ }
4009
+
4010
+ // src/commands/memory.ts
4011
+ var import_commander14 = require("commander");
4012
+ function memoryCommand() {
4013
+ const cmd = new import_commander14.Command("memory").description("View and manage agent memories");
4014
+ 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) => {
4015
+ const cfg = loadConfig();
4016
+ const agentId = opts.agent ?? cfg.defaultAgentId;
4017
+ if (!agentId) {
4018
+ console.error(c.error("Specify --agent <agentId> or set defaultAgentId"));
4019
+ process.exit(1);
4020
+ }
4021
+ const spinner = spin("Fetching memories\u2026");
4022
+ try {
4023
+ const client = makeClient();
4024
+ const res = await client.memory.list(agentId, {
4025
+ type: opts.type,
4026
+ limit: parseInt(opts.limit, 10)
4027
+ });
4028
+ const memories = res?.data ?? res ?? [];
4029
+ spinner.stop();
4030
+ if (opts.json) return jsonOut(memories);
4031
+ section(`Memories for ${agentId.slice(0, 12)}\u2026 (${memories.length})`);
4032
+ if (memories.length === 0) {
4033
+ console.log(c.dim(" No memories yet"));
4034
+ return;
4035
+ }
4036
+ table(
4037
+ memories.map((m) => ({
4038
+ ID: m.memoryId?.slice(0, 8) + "\u2026",
4039
+ Type: m.memoryType ?? "",
4040
+ Content: (m.content ?? "").slice(0, 60),
4041
+ Created: relativeTime(m.createdAt)
4042
+ })),
4043
+ ["ID", "Type", "Content", "Created"]
4044
+ );
4045
+ } catch (err) {
4046
+ spinner.stop();
4047
+ printError(err);
4048
+ process.exit(1);
4049
+ }
4050
+ });
4051
+ cmd.command("stats").description("Show memory statistics for an agent").option("--agent <agentId>", "Agent ID").option("--json", "Output as JSON").action(async (opts) => {
4052
+ const cfg = loadConfig();
4053
+ const agentId = opts.agent ?? cfg.defaultAgentId;
4054
+ if (!agentId) {
4055
+ console.error(c.error("Specify --agent <agentId>"));
4056
+ process.exit(1);
4057
+ }
4058
+ const spinner = spin("Fetching stats\u2026");
4059
+ try {
4060
+ const client = makeClient();
4061
+ const res = await client.memory.stats(agentId);
4062
+ const stats = res?.data ?? res;
4063
+ spinner.stop();
4064
+ if (opts.json) return jsonOut(stats);
4065
+ section("Memory Stats");
4066
+ detail([
4067
+ ["Total", String(stats.totalCount ?? 0)],
4068
+ ["Episodic", String(stats.episodicCount ?? 0)],
4069
+ ["Semantic", String(stats.semanticCount ?? 0)],
4070
+ ["Procedural", String(stats.proceduralCount ?? 0)]
4071
+ ]);
4072
+ } catch (err) {
4073
+ spinner.stop();
4074
+ printError(err);
4075
+ process.exit(1);
4076
+ }
4077
+ });
4078
+ cmd.command("create").description("Manually add a memory for an agent").requiredOption("--agent <agentId>", "Agent ID").requiredOption("--content <text>", "Memory content").option("--type <type>", "Memory type: episodic | semantic | procedural", "semantic").option("--json", "Output as JSON").action(async (opts) => {
4079
+ const spinner = spin("Creating memory\u2026");
4080
+ try {
4081
+ const client = makeClient();
4082
+ const res = await client.memory.create({
4083
+ agentId: opts.agent,
4084
+ content: opts.content,
4085
+ summary: String(opts.content).slice(0, 200),
4086
+ memoryType: opts.type
4087
+ });
4088
+ const memory = res?.data ?? res;
4089
+ spinner.stop();
4090
+ if (opts.json) return jsonOut(memory);
4091
+ console.log(`
4092
+ ${sym.ok} Memory created`);
4093
+ detail([
4094
+ ["ID", c.id(memory.memoryId)],
4095
+ ["Type", memory.memoryType ?? ""],
4096
+ ["Content", memory.content ?? ""]
4097
+ ]);
4098
+ } catch (err) {
4099
+ spinner.stop();
4100
+ printError(err);
4101
+ process.exit(1);
4102
+ }
4103
+ });
4104
+ cmd.command("delete <memoryId>").description("Delete a memory by ID").option("--json", "Output as JSON").action(async (memoryId, opts) => {
4105
+ const spinner = spin("Deleting memory\u2026");
4106
+ try {
4107
+ const client = makeClient();
4108
+ await client.memory.delete(memoryId);
4109
+ spinner.stop();
4110
+ if (opts.json) return jsonOut({ deleted: true, memoryId });
4111
+ console.log(`
4112
+ ${sym.ok} Memory ${c.id(memoryId)} deleted`);
4113
+ } catch (err) {
4114
+ spinner.stop();
4115
+ printError(err);
4116
+ process.exit(1);
4117
+ }
4118
+ });
4119
+ 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) => {
4120
+ const cfg = loadConfig();
4121
+ const agentId = opts.agent ?? cfg.defaultAgentId;
4122
+ if (!agentId) {
4123
+ console.error(c.error("Specify --agent <agentId>"));
4124
+ process.exit(1);
4125
+ }
4126
+ const spinner = spin("Searching memories\u2026");
4127
+ try {
4128
+ const client = makeClient();
4129
+ const res = await client.memory.retrieve(agentId, query, parseInt(opts.limit, 10));
4130
+ const memories = res?.data ?? res ?? [];
4131
+ spinner.stop();
4132
+ if (opts.json) return jsonOut(memories);
4133
+ section(`Search results (${memories.length})`);
4134
+ if (memories.length === 0) {
4135
+ console.log(c.dim(" No relevant memories found"));
4136
+ return;
4137
+ }
4138
+ memories.forEach((m, i) => {
4139
+ console.log(`
4140
+ ${c.dim(`${i + 1}.`)} ${m.content ?? ""}`);
4141
+ console.log(` ${c.dim(`type: ${m.memoryType ?? ""} \xB7 ${relativeTime(m.createdAt)}`)}`);
4142
+ });
4143
+ } catch (err) {
4144
+ spinner.stop();
4145
+ printError(err);
4146
+ process.exit(1);
4147
+ }
4148
+ });
4149
+ return cmd;
4150
+ }
4151
+
4152
+ // src/commands/usage.ts
4153
+ var import_commander15 = require("commander");
4154
+ function usageCommand() {
4155
+ const cmd = new import_commander15.Command("usage").description("View token usage and cost by agent");
4156
+ 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) => {
4157
+ const cfg = loadConfig();
4158
+ const owner = opts.owner ?? cfg.initiator;
4159
+ if (!owner) {
4160
+ console.error(c.error("Specify --owner or run `agc login` first"));
4161
+ process.exit(1);
4162
+ }
4163
+ const spinner = spin("Fetching agents\u2026");
4164
+ try {
4165
+ const client = makeClient();
4166
+ const agentsRes = await client.agents.list(owner);
4167
+ const agents = agentsRes?.data ?? [];
4168
+ spinner.stop();
4169
+ if (agents.length === 0) {
4170
+ console.log(c.dim("No agents found"));
4171
+ return;
4172
+ }
4173
+ spin("Fetching usage\u2026");
4174
+ const rows = await Promise.allSettled(
4175
+ agents.map(
4176
+ (a) => client.usage.getAgentUsage(a.agentId, {
4177
+ from: opts.from,
4178
+ to: opts.to
4179
+ }).then((r) => ({
4180
+ agentId: a.agentId,
4181
+ name: a.name || a.agentId.slice(0, 12),
4182
+ ...r?.data ?? r ?? {}
4183
+ }))
4184
+ )
4185
+ );
4186
+ const data = rows.filter((r) => r.status === "fulfilled").map((r) => r.value);
4187
+ if (opts.json) return jsonOut(data);
4188
+ let totalTokens = 0, totalCost = 0, totalCalls = 0;
4189
+ data.forEach((r) => {
4190
+ totalTokens += r.totalTokens ?? 0;
4191
+ totalCost += r.totalCostUsd ?? 0;
4192
+ totalCalls += r.callCount ?? 0;
4193
+ });
4194
+ section("Usage Summary");
4195
+ detail([
4196
+ ["Total tokens", totalTokens.toLocaleString()],
4197
+ ["Total cost", `$${totalCost.toFixed(4)} USD`],
4198
+ ["LLM calls", totalCalls.toLocaleString()]
4199
+ ]);
4200
+ const active = data.filter((r) => (r.totalTokens ?? 0) > 0);
4201
+ if (active.length) {
4202
+ console.log("");
4203
+ table(
4204
+ active.sort((a, b) => (b.totalCostUsd ?? 0) - (a.totalCostUsd ?? 0)).map((r) => ({
4205
+ Agent: r.name,
4206
+ Calls: (r.callCount ?? 0).toLocaleString(),
4207
+ Tokens: (r.totalTokens ?? 0).toLocaleString(),
4208
+ "Cost $": (r.totalCostUsd ?? 0).toFixed(4)
4209
+ })),
4210
+ ["Agent", "Calls", "Tokens", "Cost $"]
4211
+ );
4212
+ }
4213
+ } catch (err) {
4214
+ printError(err);
4215
+ process.exit(1);
4216
+ }
4217
+ });
4218
+ 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) => {
4219
+ const spinner = spin("Fetching usage\u2026");
4220
+ try {
4221
+ const client = makeClient();
4222
+ const res = await client.usage.getAgentUsage(agentId, {
4223
+ from: opts.from,
4224
+ to: opts.to
4225
+ });
4226
+ const data = res?.data ?? res;
4227
+ spinner.stop();
4228
+ if (opts.json) return jsonOut(data);
4229
+ section(`Usage \u2014 ${agentId.slice(0, 12)}\u2026`);
4230
+ detail([
4231
+ ["Calls", (data.callCount ?? 0).toLocaleString()],
4232
+ ["Input tokens", (data.totalInputTokens ?? 0).toLocaleString()],
4233
+ ["Output tokens", (data.totalOutputTokens ?? 0).toLocaleString()],
4234
+ ["Total tokens", (data.totalTokens ?? 0).toLocaleString()],
4235
+ ["Cost", `$${(data.totalCostUsd ?? 0).toFixed(6)} USD`]
4236
+ ]);
4237
+ } catch (err) {
4238
+ spinner.stop();
4239
+ printError(err);
4240
+ process.exit(1);
4241
+ }
4242
+ });
4243
+ return cmd;
4244
+ }
4245
+
4246
+ // src/commands/billing.ts
4247
+ var import_commander16 = require("commander");
4248
+ function creditsCommand() {
4249
+ const cmd = new import_commander16.Command("credits").description("View your credit balance and ledger");
4250
+ cmd.command("balance", { isDefault: true }).description("Show your current credit balance").option("--json", "Output as JSON").action(async (opts) => {
4251
+ const spinner = spin("Fetching balance\u2026");
4252
+ try {
4253
+ const client = makeClient();
4254
+ const res = await client.credits.balance();
4255
+ spinner.stop();
4256
+ if (opts.json) return jsonOut(res.data);
4257
+ section("Credits");
4258
+ detail([["Balance", String(res?.data?.balance ?? 0)]]);
4259
+ } catch (e) {
4260
+ spinner.stop();
4261
+ console.error(c.error(e.message));
4262
+ process.exit(1);
4263
+ }
4264
+ });
4265
+ cmd.command("ledger").description("Show recent credit ledger entries").option("--limit <n>", "Max entries", "20").option("--json", "Output as JSON").action(async (opts) => {
4266
+ const spinner = spin("Fetching ledger\u2026");
4267
+ try {
4268
+ const client = makeClient();
4269
+ const res = await client.credits.ledger({ limit: Number(opts.limit) });
4270
+ spinner.stop();
4271
+ const rows = res?.data ?? [];
4272
+ if (opts.json) return jsonOut(rows);
4273
+ section("Credit ledger");
4274
+ for (const e of rows) {
4275
+ const sign = e.amount >= 0 ? "+" : "";
4276
+ console.log(
4277
+ `${c.dim(new Date(e.createdAt).toLocaleString())} ${sign}${e.amount} ${e.description || e.eventType}`
4278
+ );
4279
+ }
4280
+ if (!rows.length) console.log(c.dim("No entries."));
4281
+ } catch (e) {
4282
+ spinner.stop();
4283
+ console.error(c.error(e.message));
4284
+ process.exit(1);
4285
+ }
4286
+ });
4287
+ return cmd;
4288
+ }
4289
+ function billingCommand() {
4290
+ const cmd = new import_commander16.Command("billing").description("Manage your subscription and top-ups");
4291
+ cmd.command("status", { isDefault: true }).description("Show your current plan and entitlements").option("--json", "Output as JSON").action(async (opts) => {
4292
+ const spinner = spin("Fetching plan\u2026");
4293
+ try {
4294
+ const client = makeClient();
4295
+ const res = await client.billing.subscription();
4296
+ spinner.stop();
4297
+ if (opts.json) return jsonOut(res.data);
4298
+ const d = res.data;
4299
+ section("Subscription");
4300
+ detail([
4301
+ ["Plan", `${d.planName} (${d.planKey})`],
4302
+ ["Status", d.status],
4303
+ ["Monthly credits", String(d.monthlyCredits)],
4304
+ ["Computer use", d.entitlements?.computerUse ? "yes" : "no"],
4305
+ [
4306
+ "Renews",
4307
+ d.currentPeriodEnd ? new Date(d.currentPeriodEnd).toLocaleDateString() : void 0
4308
+ ]
4309
+ ]);
4310
+ } catch (e) {
4311
+ spinner.stop();
4312
+ console.error(c.error(e.message));
4313
+ process.exit(1);
4314
+ }
4315
+ });
4316
+ cmd.command("upgrade <plan>").description("Start a checkout to upgrade (plus | pro | max)").action(async (plan) => {
4317
+ try {
4318
+ const client = makeClient();
4319
+ const res = await client.billing.subscribe(plan);
4320
+ const url = res?.data?.url;
4321
+ if (!url) {
4322
+ console.error(c.error("Could not create checkout session"));
4323
+ process.exit(1);
4324
+ }
4325
+ console.log(c.dim("Opening checkout in your browser:"));
4326
+ console.log(url);
4327
+ await openBrowser(url);
4328
+ } catch (e) {
4329
+ console.error(c.error(e.message));
4330
+ process.exit(1);
4331
+ }
4332
+ });
4333
+ cmd.command("topup <pack>").description("Buy a one-time credit pack (small | medium | large)").action(async (pack) => {
4334
+ try {
4335
+ const client = makeClient();
4336
+ const res = await client.billing.topup(pack);
4337
+ const url = res?.data?.url;
4338
+ if (!url) {
4339
+ console.error(c.error("Could not create checkout session"));
4340
+ process.exit(1);
4341
+ }
4342
+ console.log(url);
4343
+ await openBrowser(url);
4344
+ } catch (e) {
4345
+ console.error(c.error(e.message));
4346
+ process.exit(1);
4347
+ }
4348
+ });
4349
+ return cmd;
4350
+ }
4351
+
4352
+ // src/commands/logs.ts
4353
+ var import_commander17 = require("commander");
4354
+ var STATUS_COLOR = {
4355
+ success: (s) => c.bold(s),
4356
+ error: (s) => c.error(s),
4357
+ warning: (s) => c.warn(s)
4358
+ };
4359
+ function colorStatus(status) {
4360
+ return (STATUS_COLOR[status] ?? c.dim)(status);
4361
+ }
4362
+ function logsCommand() {
4363
+ const cmd = new import_commander17.Command("logs").description("View agent activity logs");
4364
+ 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) => {
4365
+ const cfg = loadConfig();
4366
+ const agentId = opts.agent ?? cfg.defaultAgentId;
4367
+ if (!agentId) {
4368
+ console.error(c.error("Specify --agent <agentId> or set defaultAgentId"));
4369
+ process.exit(1);
4370
+ }
4371
+ const spinner = spin("Fetching logs\u2026");
4372
+ try {
4373
+ const client = makeClient();
4374
+ const qs = new URLSearchParams({ limit: opts.limit });
4375
+ if (opts.session) qs.set("sessionId", opts.session);
4376
+ const res = await client.request("GET", `/v1/logs/agents/${agentId}?${qs}`);
4377
+ let logs = res?.data ?? res ?? [];
4378
+ if (opts.status) logs = logs.filter((l) => l.status === opts.status);
4379
+ spinner.stop();
4380
+ if (opts.json) return jsonOut(logs);
4381
+ section(`Logs \u2014 ${agentId.slice(0, 12)}\u2026 (${logs.length})`);
4382
+ if (logs.length === 0) {
4383
+ console.log(c.dim(" No logs yet"));
4384
+ return;
4385
+ }
4386
+ logs.forEach((l) => {
4387
+ const tools = (l.tools ?? []).length > 0 ? ` ${c.dim(`[${l.tools.length} tools]`)}` : "";
4388
+ const rt = l.responseTime > 0 ? c.dim(` ${l.responseTime}ms`) : "";
4389
+ console.log(
4390
+ ` ${colorStatus((l.status ?? "info").padEnd(7))} ${c.bold(l.action ?? "")}${rt}${tools}`
4391
+ );
4392
+ if (l.message) {
4393
+ console.log(` ${" ".repeat(10)}${c.dim(l.message.slice(0, 80))}`);
4394
+ }
4395
+ console.log(` ${" ".repeat(10)}${c.dim(relativeTime(l.timestamp))}`);
4396
+ console.log("");
4397
+ });
4398
+ } catch (err) {
4399
+ spin("").stop();
4400
+ printError(err);
4401
+ process.exit(1);
4402
+ }
4403
+ });
4404
+ 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) => {
4405
+ const cfg = loadConfig();
4406
+ const agentId = opts.agent ?? cfg.defaultAgentId;
4407
+ if (!agentId) {
4408
+ console.error(c.error("Specify --agent <agentId>"));
4409
+ process.exit(1);
4410
+ }
4411
+ const spinner = spin("Fetching error logs\u2026");
4412
+ try {
4413
+ const client = makeClient();
4414
+ const res = await client.request("GET", `/v1/logs/agents/${agentId}?limit=${opts.limit}`);
4415
+ const errors = (res?.data ?? []).filter((l) => l.status === "error");
4416
+ spinner.stop();
4417
+ if (opts.json) return jsonOut(errors);
4418
+ section(`Errors \u2014 ${agentId.slice(0, 12)}\u2026 (${errors.length})`);
4419
+ if (errors.length === 0) {
4420
+ console.log(`${sym.ok} No errors found`);
4421
+ return;
4422
+ }
4423
+ errors.forEach((l) => {
4424
+ console.log(` ${c.error("\u2716")} ${c.bold(l.action ?? "")} ${c.dim(relativeTime(l.timestamp))}`);
4425
+ if (l.message) console.log(` ${c.dim(l.message)}`);
4426
+ console.log("");
4427
+ });
4428
+ } catch (err) {
4429
+ spinner.stop();
4430
+ printError(err);
4431
+ process.exit(1);
4432
+ }
4433
+ });
4434
+ return cmd;
4435
+ }
4436
+
4437
+ // src/commands/computer.ts
4438
+ var import_commander18 = require("commander");
4439
+ var RESOURCE_PROFILES = [
4440
+ "starter",
4441
+ "standard",
4442
+ "performance",
4443
+ "gpu"
4444
+ ];
4445
+ var RESOURCE_MODES = ["fixed", "elastic"];
4446
+ function resolveAgentId(opts) {
4447
+ const agentId = opts.agent ?? loadConfig().defaultAgentId;
4448
+ if (!agentId) {
4449
+ throw new Error(
4450
+ "Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`."
4451
+ );
4452
+ }
4453
+ return agentId;
4454
+ }
4455
+ function unwrap(response) {
4456
+ return response?.data ?? response;
4457
+ }
4458
+ function displayComputer(computer) {
4459
+ if (!computer) {
4460
+ section("Persistent cloud computer");
4461
+ detail([
4462
+ ["Status", statusBadge("disabled")],
4463
+ ["Persistence", "persistent"],
4464
+ ["Computer ID", c.dim("(not provisioned)")]
4465
+ ]);
4466
+ console.log(c.dim(" Enable it with: agc computer enable --agent <agentId>"));
4467
+ return;
4468
+ }
4469
+ const wire = computer;
4470
+ const resources = computer.resources ?? {};
4471
+ const gpu = resources.gpu ?? (wire.gpuCount ? { count: wire.gpuCount, type: wire.gpuType } : null);
4472
+ const cpu = resources.vcpu ?? wire.cpuRequest ?? wire.cpuLimit;
4473
+ const memory = resources.memoryGiB != null ? `${resources.memoryGiB} GiB` : wire.memoryRequest ?? wire.memoryLimit;
4474
+ const storage = resources.storageGiB != null ? `${resources.storageGiB} GiB` : wire.storageLimit;
4475
+ section("Persistent cloud computer");
4476
+ detail([
4477
+ ["Computer ID", computer.computerId ? c.id(computer.computerId) : c.dim("(not provisioned)")],
4478
+ ["Enabled", computer.enabled === false ? "no" : c.success("yes")],
4479
+ ["Status", statusBadge(computer.status ?? "disabled")],
4480
+ ["Desired state", computer.desiredState ?? c.dim("n/a")],
4481
+ ["Persistence", computer.persistence ?? wire.lifecycle ?? "persistent"],
4482
+ ["Profile", computer.resourceProfile ?? c.dim("n/a")],
4483
+ ["Mode", computer.resourceMode ?? c.dim("n/a")],
4484
+ ["CPU", cpu != null ? String(cpu) : c.dim("n/a")],
4485
+ ["Memory", memory != null ? String(memory) : c.dim("n/a")],
4486
+ ["Storage", storage != null ? String(storage) : c.dim("n/a")],
4487
+ ["GPU", gpu?.count ? `${gpu.count} \xD7 ${gpu.type ?? "provider default"}` : "none"],
4488
+ ["Region", computer.region ?? c.dim("automatic")],
4489
+ ["Workspace", computer.workspaceRoot ?? c.dim("not mounted")],
4490
+ ["Last activity", computer.lastActivityAt ? relativeTime(computer.lastActivityAt) : c.dim("never")],
4491
+ ["Error", computer.errorMessage ?? void 0]
4492
+ ]);
4493
+ }
4494
+ function parseNumber(value, name, options) {
4495
+ if (value === void 0) return void 0;
4496
+ const parsed = Number(value);
4497
+ const minimum = options?.allowZero ? 0 : Number.MIN_VALUE;
4498
+ if (!Number.isFinite(parsed) || parsed < minimum || options?.integer && !Number.isInteger(parsed)) {
4499
+ const qualifier = options?.integer ? "whole number" : "number";
4500
+ throw new Error(`${name} must be a ${options?.allowZero ? "non-negative" : "positive"} ${qualifier}.`);
4501
+ }
4502
+ return parsed;
4503
+ }
4504
+ async function changeEnabled(agentId, enabled, json) {
4505
+ const spinner = spin(`${enabled ? "Enabling" : "Disabling"} persistent cloud computer\u2026`);
4506
+ try {
4507
+ const response = await makeClient().agents.updateComputerConfig(agentId, { enabled });
4508
+ const config = unwrap(response);
4509
+ spinner.stop();
4510
+ if (json) return jsonOut(config);
4511
+ console.log(`
4512
+ ${sym.ok} Persistent cloud computer ${enabled ? "enabled" : "disabled"} for agent ${c.id(agentId)}`);
4513
+ if (enabled) {
4514
+ console.log(c.dim(` Wake it now with: agc computer wake --agent ${agentId}`));
4515
+ }
4516
+ } catch (error) {
4517
+ spinner.stop();
4518
+ printError(error);
4519
+ process.exitCode = 1;
4520
+ }
4521
+ }
4522
+ async function lifecycleAction(action, agentId, reason, json) {
4523
+ const verb = action === "wake" ? "Waking" : action === "sleep" ? "Sleeping" : "Restarting";
4524
+ const spinner = spin(`${verb} persistent cloud computer\u2026`);
4525
+ try {
4526
+ const client = makeClient();
4527
+ const response = action === "wake" ? await client.agents.wakeComputer(agentId, reason ? { reason } : void 0) : action === "sleep" ? await client.agents.sleepComputer(agentId, reason ? { reason } : void 0) : await client.agents.restartComputer(agentId, reason ? { reason } : void 0);
4528
+ const computer = unwrap(response);
4529
+ spinner.stop();
4530
+ if (json) return jsonOut(computer);
4531
+ console.log(`
4532
+ ${sym.ok} Persistent cloud computer ${action === "sleep" ? "is sleeping" : action === "wake" ? "is awake" : "restarted"}`);
4533
+ displayComputer(computer);
4534
+ } catch (error) {
4535
+ spinner.stop();
4536
+ printError(error);
4537
+ process.exitCode = 1;
4538
+ }
4539
+ }
4540
+ function addAgentOption(command) {
4541
+ return command.option("--agent <agentId>", "Agent ID (defaults to configured agent)");
4542
+ }
4543
+ function computerCommand() {
4544
+ const command = new import_commander18.Command("computer").description("Manage an agent's one persistent cloud computer");
4545
+ addAgentOption(command.command("status").description("Show persistent cloud computer status")).option("--json", "Output as JSON").action(async (opts) => {
4546
+ let agentId;
4547
+ try {
4548
+ agentId = resolveAgentId(opts);
4549
+ } catch (error) {
4550
+ printError(error);
4551
+ process.exitCode = 1;
4552
+ return;
4553
+ }
4554
+ const spinner = spin("Fetching persistent cloud computer\u2026");
4555
+ try {
4556
+ const computer = unwrap(await makeClient().agents.getComputer(agentId));
4557
+ spinner.stop();
4558
+ if (opts.json) return jsonOut(computer);
4559
+ displayComputer(computer);
4560
+ } catch (error) {
4561
+ spinner.stop();
4562
+ printError(error);
4563
+ process.exitCode = 1;
4564
+ }
4565
+ });
4566
+ addAgentOption(command.command("enable").description("Enable a persistent cloud computer for an agent")).option("--json", "Output as JSON").action(async (opts) => {
4567
+ try {
4568
+ await changeEnabled(resolveAgentId(opts), true, !!opts.json);
4569
+ } catch (error) {
4570
+ printError(error);
4571
+ process.exitCode = 1;
4572
+ }
4573
+ });
4574
+ addAgentOption(command.command("disable").description("Disable the agent cloud computer")).option("--json", "Output as JSON").action(async (opts) => {
4575
+ try {
4576
+ await changeEnabled(resolveAgentId(opts), false, !!opts.json);
4577
+ } catch (error) {
4578
+ printError(error);
4579
+ process.exitCode = 1;
4580
+ }
4581
+ });
4582
+ for (const action of ["wake", "sleep", "restart"]) {
4583
+ const descriptions = {
4584
+ wake: "Wake the persistent cloud computer",
4585
+ sleep: "Sleep compute while preserving the persistent workspace",
4586
+ restart: "Restart the runtime while preserving the persistent workspace"
4587
+ };
4588
+ addAgentOption(command.command(action).description(descriptions[action])).option("--reason <text>", `Reason for the ${action}`).option("--json", "Output as JSON").action(async (opts) => {
4589
+ try {
4590
+ await lifecycleAction(action, resolveAgentId(opts), opts.reason, !!opts.json);
4591
+ } catch (error) {
4592
+ printError(error);
4593
+ process.exitCode = 1;
4594
+ }
4595
+ });
4596
+ }
4597
+ 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) => {
4598
+ let agentId;
4599
+ let resize;
4600
+ try {
4601
+ agentId = resolveAgentId(opts);
4602
+ if (opts.profile && !RESOURCE_PROFILES.includes(opts.profile)) {
4603
+ throw new Error(`--profile must be one of: ${RESOURCE_PROFILES.join(", ")}.`);
4604
+ }
4605
+ if (opts.mode && !RESOURCE_MODES.includes(opts.mode)) {
4606
+ throw new Error(`--mode must be one of: ${RESOURCE_MODES.join(", ")}.`);
4607
+ }
4608
+ if (opts.vcpu !== void 0 && opts.cpu !== void 0) {
4609
+ throw new Error("Use either --vcpu or --cpu, not both.");
4610
+ }
4611
+ const vcpu = parseNumber(opts.vcpu ?? opts.cpu, "CPU");
4612
+ const memoryGiB = parseNumber(opts.memory, "Memory");
4613
+ const storageGiB = parseNumber(opts.storage, "Storage");
4614
+ const gpuCount = parseNumber(opts.gpuCount, "GPU count", { integer: true, allowZero: true });
4615
+ const resources = {
4616
+ ...vcpu !== void 0 && { vcpu },
4617
+ ...memoryGiB !== void 0 && { memoryGiB },
4618
+ ...storageGiB !== void 0 && { storageGiB },
4619
+ ...(gpuCount !== void 0 || opts.gpuType) && {
4620
+ gpu: { count: gpuCount ?? 1, ...opts.gpuType && { type: opts.gpuType } }
4621
+ }
4622
+ };
4623
+ resize = {
4624
+ ...opts.profile && { resourceProfile: opts.profile },
4625
+ ...opts.mode && { resourceMode: opts.mode },
4626
+ ...Object.keys(resources).length > 0 && { resources }
4627
+ };
4628
+ if (Object.keys(resize).length === 0) {
4629
+ throw new Error("Specify --profile, --mode, or at least one resource value.");
4630
+ }
4631
+ } catch (error) {
4632
+ printError(error);
4633
+ process.exitCode = 1;
4634
+ return;
4635
+ }
4636
+ const spinner = spin("Resizing persistent cloud computer\u2026");
4637
+ try {
4638
+ const computer = unwrap(await makeClient().agents.resizeComputer(agentId, resize));
4639
+ spinner.stop();
4640
+ if (opts.json) return jsonOut(computer);
4641
+ console.log(`
4642
+ ${sym.ok} Persistent cloud computer resize requested`);
4643
+ displayComputer(computer);
4644
+ } catch (error) {
4645
+ spinner.stop();
4646
+ printError(error);
4647
+ process.exitCode = 1;
4648
+ }
4649
+ });
4650
+ addAgentOption(
4651
+ command.command("exec").description("Run a command in the persistent cloud computer").argument("<command...>", "Command and arguments to run")
4652
+ ).option("--cwd <path>", "Working directory").option("--timeout <seconds>", "Command timeout in seconds", "120").option("--json", "Output as JSON").action(async (commandParts, opts) => {
4653
+ let agentId;
4654
+ let timeoutSeconds;
4655
+ try {
4656
+ agentId = resolveAgentId(opts);
4657
+ timeoutSeconds = parseNumber(opts.timeout, "Timeout");
4658
+ } catch (error) {
4659
+ printError(error);
4660
+ process.exitCode = 1;
4661
+ return;
4662
+ }
4663
+ const spinner = spin("Running command in persistent cloud computer\u2026");
4664
+ try {
4665
+ const result = unwrap(await makeClient().agents.execComputer(agentId, {
4666
+ command: commandParts.join(" "),
4667
+ ...opts.cwd && { cwd: opts.cwd },
4668
+ ...timeoutSeconds !== void 0 && { timeoutSeconds }
4669
+ }));
4670
+ spinner.stop();
4671
+ if (opts.json) return jsonOut(result);
4672
+ const stdout = result?.stdout ?? result?.output ?? result?.result ?? "";
4673
+ const stderr = result?.stderr ?? "";
4674
+ if (stdout) process.stdout.write(String(stdout).replace(/\n?$/, "\n"));
4675
+ if (stderr) process.stderr.write(c.error(String(stderr).replace(/\n?$/, "\n")));
4676
+ const exitCode = result?.exitCode ?? result?.exit_code;
4677
+ if (exitCode !== void 0 && exitCode !== 0) process.exitCode = Number(exitCode);
4678
+ } catch (error) {
4679
+ spinner.stop();
4680
+ printError(error);
4681
+ process.exitCode = 1;
4682
+ }
4683
+ });
4684
+ 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) => {
4685
+ let agentId;
4686
+ let limit;
4687
+ try {
4688
+ agentId = resolveAgentId(opts);
4689
+ limit = parseNumber(opts.limit, "Limit", { integer: true });
4690
+ } catch (error) {
4691
+ printError(error);
4692
+ process.exitCode = 1;
4693
+ return;
4694
+ }
4695
+ const spinner = spin("Fetching persistent cloud computer events\u2026");
4696
+ try {
4697
+ const events = unwrap(await makeClient().agents.listComputerEvents(agentId, limit));
4698
+ spinner.stop();
4699
+ if (opts.json) return jsonOut(events);
4700
+ section(`Cloud computer events (${events.length})`);
4701
+ table(
4702
+ events.map((event) => ({
4703
+ Event: event.eventType ?? "",
4704
+ Summary: event.summary ?? "",
4705
+ Actor: event.actorType ?? "",
4706
+ When: event.createdAt ? relativeTime(event.createdAt) : ""
4707
+ })),
4708
+ ["Event", "Summary", "Actor", "When"]
4709
+ );
4710
+ } catch (error) {
4711
+ spinner.stop();
4712
+ printError(error);
4713
+ process.exitCode = 1;
4714
+ }
4715
+ });
4716
+ return command;
4717
+ }
4718
+
4719
+ // src/bin.ts
4720
+ var CONFIG_FILE3 = (0, import_path5.join)((0, import_os4.homedir)(), ".agc", "config.json");
4721
+ async function interactiveMenu() {
4722
+ banner();
4723
+ const cfg = loadConfig();
4724
+ const isSetup = !!((cfg.accessToken || cfg.apiKey || cfg.sessionToken) && (cfg.userId || cfg.initiator));
4725
+ if (!isSetup) {
4726
+ console.log(c.bold(" Welcome to Agent Commons CLI!"));
4727
+ console.log(c.dim(" Looks like this is your first time here \u2014 let's get you set up.\n"));
4728
+ console.log(` ${sym.arrow} Running ${c.bold("agc login")} to configure your credentials\u2026
4729
+ `);
4730
+ runSubcommand(["login"]);
4731
+ return;
4732
+ }
4733
+ console.log(
4734
+ ` ${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))}
4735
+ `
4736
+ );
4737
+ const action = await select("What would you like to do?", [
4738
+ { label: "Chat with an agent", value: "chat", hint: "agc chat" },
4739
+ { label: "Run an agent (one-shot)", value: "run", hint: "agc run" },
4740
+ { label: "Manage an agent cloud computer", value: "computer", hint: "agc computer status" },
4741
+ { label: "View sessions", value: "sessions", hint: "agc sessions list" },
4742
+ { label: "Manage agents", value: "agents", hint: "agc agents list" },
4743
+ { label: "Tasks", value: "tasks", hint: "agc task list" },
4744
+ { label: "Workflows", value: "workflows", hint: "agc workflow list" },
4745
+ { label: "MCP servers", value: "mcp", hint: "agc mcp list" },
4746
+ { label: "Skills", value: "skills", hint: "agc skills list" },
4747
+ { label: "Wallet & balance", value: "wallet", hint: "agc wallet balance" },
4748
+ { label: "Usage & cost", value: "usage", hint: "agc usage" },
4749
+ { label: "Logs", value: "logs", hint: "agc logs" },
4750
+ { label: "Config & credentials", value: "config", hint: "agc config get" },
4751
+ { label: "Exit", value: "exit" }
4752
+ ]);
4753
+ if (action === "exit") {
4754
+ process.exit(0);
4755
+ }
4756
+ const needsAgent = action === "chat" || action === "run" || action === "computer";
4757
+ const agentId = needsAgent ? cfg.defaultAgentId ?? await pickAgentInteractively(action) : void 0;
4758
+ if (needsAgent && !agentId) return;
4759
+ if (action === "run") {
4760
+ const prompt2 = await askPrompt("Enter your prompt:");
4761
+ if (!prompt2) return;
4762
+ runSubcommand(["run", "--agent", agentId, prompt2]);
4763
+ return;
4764
+ }
4765
+ const commandMap = {
4766
+ chat: ["chat", "--agent", agentId],
4767
+ run: [],
4768
+ // handled above
4769
+ computer: ["computer", "status", "--agent", agentId],
4770
+ sessions: ["sessions", "list"],
4771
+ agents: ["agents", "list"],
4772
+ tasks: ["task", "list"],
4773
+ workflows: ["workflow", "list"],
4774
+ mcp: ["mcp", "list"],
4775
+ skills: ["skills", "list"],
4776
+ wallet: ["wallet", "balance"],
4777
+ usage: ["usage"],
4778
+ logs: ["logs"],
4779
+ config: ["config", "get"],
4780
+ exit: []
4781
+ };
4782
+ runSubcommand(commandMap[action]);
4783
+ }
4784
+ async function askPrompt(question) {
4785
+ const { createInterface: createInterface4 } = await import("readline");
4786
+ return new Promise((resolve2) => {
4787
+ const rl = createInterface4({ input: process.stdin, output: process.stdout });
4788
+ process.stdout.write(`
4789
+ ${c.bold(question)}
4790
+ ${c.primary("\u203A")} `);
4791
+ rl.once("line", (line) => {
4792
+ rl.close();
4793
+ const trimmed = line.trim();
4794
+ resolve2(trimmed || null);
4795
+ });
4796
+ });
4797
+ }
4798
+ function runSubcommand(args) {
4799
+ const child = (0, import_child_process3.spawn)(process.argv[0], [process.argv[1], ...args], {
4800
+ stdio: "inherit"
4801
+ });
4802
+ child.on("exit", (code) => process.exit(code ?? 0));
4803
+ }
4804
+ async function pickAgentInteractively(action) {
4805
+ const cfg = loadConfig();
4806
+ const spinner = spin("Fetching your agents\u2026");
4807
+ let agents = [];
4808
+ try {
4809
+ const client = makeClient();
4810
+ const res = await client.agents.list(cfg.initiator);
4811
+ agents = res?.data ?? (Array.isArray(res) ? res : []);
4812
+ spinner.stop();
4813
+ } catch {
4814
+ spinner.stop();
4815
+ console.log(`
4816
+ ${c.warn("\u26A0")} Could not fetch agents. Check your API key and connection.
4817
+ `);
4818
+ return null;
4819
+ }
4820
+ if (agents.length === 0) {
4821
+ console.log(`
4822
+ ${c.warn("\u26A0")} You don't have any agents yet.
4823
+ `);
4824
+ const choice = await select("What would you like to do?", [
4825
+ { label: "Create a new agent now", value: "create", hint: "agc agents create" },
4826
+ { label: "Go back", value: "cancel" }
4827
+ ]);
4828
+ if (choice === "create") {
4829
+ runSubcommand(["agents", "create"]);
4830
+ }
4831
+ return null;
4832
+ }
4833
+ console.log();
4834
+ const agentId = await select(
4835
+ action === "computer" ? "Choose the agent whose cloud computer you want to manage:" : `Choose an agent to ${action} with:`,
4836
+ agents.map((a) => ({
4837
+ label: a.name,
4838
+ value: a.agentId,
4839
+ hint: `${a.modelProvider}/${a.modelId}`
4840
+ }))
4841
+ );
4842
+ const saveDefault = await select("Set as your default agent?", [
4843
+ { label: "Yes \u2014 remember this agent for next time", value: true },
4844
+ { label: "No \u2014 just this once", value: false }
4845
+ ]);
4846
+ if (saveDefault) {
4847
+ saveConfig({ defaultAgentId: agentId });
4848
+ const chosen = agents.find((a) => a.agentId === agentId);
4849
+ console.log(` ${sym.ok} ${c.dim("Default agent set to")} ${c.bold(chosen?.name ?? agentId)}
4850
+ `);
4851
+ }
4852
+ return agentId;
4853
+ }
4854
+ var program = new import_commander19.Command();
4855
+ program.name("agc").description("Agent Commons CLI \u2014 interact with the Agent Commons platform").version("0.3.0", "-v, --version").action(async () => {
4856
+ await interactiveMenu();
4857
+ });
4858
+ program.hook("preAction", async (_thisCommand, actionCommand) => {
4859
+ if (actionCommand.name() === "login" || actionCommand.name() === "logout") return;
4860
+ await ensureAccessToken();
4861
+ });
4862
+ program.addCommand(loginCommand());
4863
+ program.addCommand(logoutCommand());
4864
+ program.addCommand(whoamiCommand());
4865
+ program.addCommand(configCommand());
4866
+ program.addCommand(agentsCommand());
4867
+ program.addCommand(sessionsCommand());
4868
+ program.addCommand(toolsCommand());
4869
+ program.addCommand(connectionsCommand());
4870
+ program.addCommand(workflowCommand());
4871
+ program.addCommand(taskCommand());
4872
+ program.addCommand(runCommand());
4873
+ program.addCommand(chatCommand());
4874
+ program.addCommand(computerCommand());
4875
+ program.addCommand(mcpCommand());
4876
+ program.addCommand(skillsCommand());
4877
+ program.addCommand(walletCommand());
4878
+ program.addCommand(modelsCommand());
4879
+ program.addCommand(memoryCommand());
4880
+ program.addCommand(usageCommand());
4881
+ program.addCommand(logsCommand());
4882
+ program.addCommand(creditsCommand());
4883
+ program.addCommand(billingCommand());
4884
+ program.on("command:*", () => {
4885
+ console.error(
4886
+ `
4887
+ ${c.error("Unknown command:")} ${program.args.join(" ")}
4888
+ Run ${c.bold("agc --help")} to see available commands, or just ${c.bold("agc")} for the interactive menu.
4889
+ `
4890
+ );
4891
+ process.exit(1);
4892
+ });
4893
+ program.parse(process.argv);