@agent-commons/cli 0.1.16 → 0.1.17

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 +181 -30
  2. package/package.json +7 -5
package/dist/bin.js CHANGED
@@ -106,7 +106,7 @@ var sym = {
106
106
  bullet: import_chalk.default.dim("\u2022"),
107
107
  dot: import_chalk.default.dim("\xB7")
108
108
  };
109
- function banner(version = "0.1.16") {
109
+ function banner(version = "0.1.17") {
110
110
  const line = import_chalk.default.cyan(" \u2500".padEnd(2) + "\u2500".repeat(44));
111
111
  console.log("");
112
112
  console.log(line);
@@ -515,7 +515,7 @@ ${sym.ok} Agent created`);
515
515
  process.exit(1);
516
516
  }
517
517
  });
518
- const autonomy = cmd.command("autonomy").description("Manage agent heartbeat / autonomy");
518
+ const autonomy = cmd.command("autonomy").description("Manage agent heartbeat");
519
519
  autonomy.command("status").description("Show autonomy status for an agent").requiredOption("--agent <agentId>", "Agent ID").option("--json", "Output as JSON").action(async (opts) => {
520
520
  const client = makeClient();
521
521
  const spinner = spin("Fetching autonomy status\u2026");
@@ -525,7 +525,7 @@ ${sym.ok} Agent created`);
525
525
  const s = res.data;
526
526
  if (opts.json) return jsonOut(s);
527
527
  console.log(`
528
- ${c.bold("Autonomy Status")}`);
528
+ ${c.bold("Heartbeat Status")}`);
529
529
  detail([
530
530
  ["Enabled", s.enabled ? c.bold("yes") : "no"],
531
531
  ["Interval", s.intervalSec ? `${s.intervalSec}s` : "n/a"],
@@ -539,7 +539,7 @@ ${c.bold("Autonomy Status")}`);
539
539
  process.exit(1);
540
540
  }
541
541
  });
542
- autonomy.command("enable").description("Enable autonomous heartbeat for an agent").requiredOption("--agent <agentId>", "Agent ID").option("--interval <seconds>", "Heartbeat interval in seconds (min 30)", "300").action(async (opts) => {
542
+ autonomy.command("enable").description("Enable heartbeat for an agent").requiredOption("--agent <agentId>", "Agent ID").option("--interval <seconds>", "Heartbeat interval in seconds (min 30)", "300").action(async (opts) => {
543
543
  const client = makeClient();
544
544
  const spinner = spin("Enabling autonomy\u2026");
545
545
  try {
@@ -557,7 +557,7 @@ ${sym.ok} Autonomy enabled for agent ${c.id(opts.agent)}`);
557
557
  process.exit(1);
558
558
  }
559
559
  });
560
- autonomy.command("disable").description("Disable autonomous heartbeat for an agent").requiredOption("--agent <agentId>", "Agent ID").action(async (opts) => {
560
+ autonomy.command("disable").description("Disable heartbeat for an agent").requiredOption("--agent <agentId>", "Agent ID").action(async (opts) => {
561
561
  const client = makeClient();
562
562
  const spinner = spin("Disabling autonomy\u2026");
563
563
  try {
@@ -571,7 +571,7 @@ ${sym.ok} Autonomy disabled for agent ${c.id(opts.agent)}`);
571
571
  process.exit(1);
572
572
  }
573
573
  });
574
- autonomy.command("trigger").description("Trigger a single heartbeat beat immediately").requiredOption("--agent <agentId>", "Agent ID").action(async (opts) => {
574
+ autonomy.command("trigger").description("Trigger a single heartbeat immediately").requiredOption("--agent <agentId>", "Agent ID").action(async (opts) => {
575
575
  const client = makeClient();
576
576
  const spinner = spin("Triggering heartbeat\u2026");
577
577
  try {
@@ -1257,6 +1257,7 @@ var import_fs3 = require("fs");
1257
1257
  var import_path3 = require("path");
1258
1258
  var import_child_process2 = require("child_process");
1259
1259
  var readline2 = __toESM(require("readline"));
1260
+ var pdfParse = require("pdf-parse/lib/pdf-parse.js");
1260
1261
  var managedProcesses = /* @__PURE__ */ new Map();
1261
1262
  function capBuffer(existing, chunk, maxBytes) {
1262
1263
  const joined = existing + chunk;
@@ -1505,9 +1506,24 @@ function extractViaCommand(cmd, cmdArgs) {
1505
1506
  });
1506
1507
  }
1507
1508
  async function extractPdfText(abs) {
1509
+ try {
1510
+ const buffer = (0, import_fs3.readFileSync)(abs);
1511
+ const data = await pdfParse(buffer);
1512
+ const text2 = data.text?.trim();
1513
+ if (text2) {
1514
+ const MAX_CHARS = 15e4;
1515
+ if (text2.length > MAX_CHARS) {
1516
+ return text2.slice(0, MAX_CHARS) + `
1517
+
1518
+ [\u2026truncated \u2014 showing first ${MAX_CHARS.toLocaleString()} characters of ${text2.length.toLocaleString()} total]`;
1519
+ }
1520
+ return text2;
1521
+ }
1522
+ } catch {
1523
+ }
1508
1524
  const text = await extractViaCommand("pdftotext", [abs, "-"]);
1509
1525
  if (text) return text;
1510
- return `[Cannot extract PDF text: pdftotext not found. Install with: brew install poppler]`;
1526
+ return `[Cannot extract PDF text: the file may be scanned/image-only or password-protected]`;
1511
1527
  }
1512
1528
  async function extractOfficeText(abs, ext) {
1513
1529
  const text = await extractViaCommand("textutil", ["-stdout", "-cat", "txt", abs]);
@@ -1522,13 +1538,19 @@ async function toolReadFile(args, cfg) {
1522
1538
  if (!(0, import_fs3.existsSync)(abs)) throw new Error(`File not found: ${userPath}`);
1523
1539
  const stat = (0, import_fs3.statSync)(abs);
1524
1540
  if (stat.isDirectory()) throw new Error(`"${userPath}" is a directory, not a file`);
1525
- if (stat.size > 5e5) throw new Error(`File too large to read (${Math.round(stat.size / 1024)} KB). Max 500 KB.`);
1526
1541
  const ext = (0, import_path3.extname)(abs).toLowerCase();
1527
- if (PDF_EXTS.has(ext)) return extractPdfText(abs);
1528
- if (OFFICE_EXTS.has(ext)) return extractOfficeText(abs, ext);
1542
+ if (PDF_EXTS.has(ext)) {
1543
+ if (stat.size > 5e7) throw new Error(`PDF too large to read (${Math.round(stat.size / 1e6)} MB). Max 50 MB.`);
1544
+ return extractPdfText(abs);
1545
+ }
1546
+ if (OFFICE_EXTS.has(ext)) {
1547
+ if (stat.size > 2e7) throw new Error(`Document too large to read (${Math.round(stat.size / 1e6)} MB). Max 20 MB.`);
1548
+ return extractOfficeText(abs, ext);
1549
+ }
1529
1550
  if (UNREADABLE_BINARY_EXTS.has(ext)) {
1530
- throw new Error(`Cannot read binary file "${userPath}" (${ext} format). Only text, PDF, and Word documents are supported.`);
1551
+ throw new Error(`Cannot read binary file "${userPath}" (${ext} format). Only text, PDF, and Office documents are supported.`);
1531
1552
  }
1553
+ if (stat.size > 5e5) throw new Error(`File too large to read (${Math.round(stat.size / 1024)} KB). Max 500 KB.`);
1532
1554
  return (0, import_fs3.readFileSync)(abs, "utf8");
1533
1555
  }
1534
1556
  async function toolWriteFile(args, cfg) {
@@ -2018,12 +2040,13 @@ ${content}
2018
2040
  messages: [{ role: "user", content: userMessage }],
2019
2041
  ...cliContext && { cliContext }
2020
2042
  };
2021
- process.stdout.write(c.primary("agent") + c.dim(" \u203A "));
2022
2043
  if (opts.noStream) {
2023
- const spinner = spin("");
2044
+ process.stdout.write(c.primary("agent") + c.dim(" \u203A "));
2045
+ const spinner = spin("thinking\u2026");
2024
2046
  try {
2025
2047
  const result = await client.run.once(params);
2026
2048
  spinner.stop();
2049
+ process.stdout.write(c.primary("agent") + c.dim(" \u203A "));
2027
2050
  const text = extractText(result);
2028
2051
  console.log(text);
2029
2052
  appendSessionLog(sessionId, {
@@ -2041,29 +2064,57 @@ ${sym.fail} ${c.error(err.message ?? String(err))}`);
2041
2064
  try {
2042
2065
  let hasOutput = false;
2043
2066
  let agentContent = "";
2067
+ let toolStartMs = 0;
2068
+ let lastToolName = "";
2069
+ const thinkingSpinner = spin("thinking\u2026");
2044
2070
  for await (const event of client.agents.stream(params)) {
2045
2071
  if (event.type === "token") {
2072
+ if (thinkingSpinner.isSpinning) {
2073
+ thinkingSpinner.stop();
2074
+ process.stdout.write(c.primary("agent") + c.dim(" \u203A "));
2075
+ }
2046
2076
  const tok = event.content ?? "";
2047
2077
  process.stdout.write(tok);
2048
2078
  agentContent += tok;
2049
2079
  hasOutput = true;
2050
2080
  } else if (event.type === "cli_tool_request" && localToolsCfg) {
2081
+ if (thinkingSpinner.isSpinning) thinkingSpinner.stop();
2051
2082
  const { requestId, tool: toolName, args } = event;
2052
2083
  const displayName = String(toolName).replace("cli_", "");
2084
+ const argStr = toolArgSummary(displayName, args ?? {});
2085
+ const isWaiting = displayName === "wait_for_process";
2053
2086
  if (hasOutput) {
2054
2087
  process.stdout.write("\n");
2055
2088
  hasOutput = false;
2056
2089
  }
2057
- process.stdout.write(c.dim(` [local] ${displayName}\u2026`));
2090
+ process.stdout.write(` ${c.dim("\u2500")} ${c.bold(displayName)}${argStr ? " " + c.dim(argStr) : ""}`);
2091
+ const startMs = Date.now();
2092
+ let elapsedSec = 0;
2093
+ let elapsedInterval = null;
2094
+ if (isWaiting) {
2095
+ elapsedInterval = setInterval(() => {
2096
+ elapsedSec++;
2097
+ readline3.cursorTo(process.stdout, 0);
2098
+ process.stdout.write(` ${c.dim("\u2500")} ${c.bold(displayName)}${argStr ? " " + c.dim(argStr) : ""} ${c.dim(elapsedSec + "s\u2026")}`);
2099
+ }, 1e3);
2100
+ }
2058
2101
  let result;
2102
+ let toolOk = true;
2059
2103
  try {
2060
- const localToolName = String(toolName).replace("cli_", "");
2061
- result = await runLocalTool({ tool: localToolName, args: args ?? {} }, localToolsCfg);
2062
- process.stdout.write(c.dim(" \u2713\n"));
2104
+ result = await runLocalTool({ tool: displayName, args: args ?? {} }, localToolsCfg);
2063
2105
  } catch (err) {
2064
2106
  result = `Error: ${err?.message ?? String(err)}`;
2065
- process.stdout.write(c.dim(" \u2717\n"));
2107
+ toolOk = false;
2066
2108
  }
2109
+ if (elapsedInterval) clearInterval(elapsedInterval);
2110
+ const elapsed = ((Date.now() - startMs) / 1e3).toFixed(1);
2111
+ const preview = toolOk ? toolResultPreview(displayName, result) : "";
2112
+ readline3.cursorTo(process.stdout, 0);
2113
+ readline3.clearLine(process.stdout, 0);
2114
+ const statusIcon = toolOk ? sym.ok : sym.fail;
2115
+ const previewPart = preview ? ` ${c.dim(preview)}` : "";
2116
+ process.stdout.write(` ${c.dim("\u2500")} ${c.bold(displayName)}${argStr ? " " + c.dim(argStr) : ""} ${statusIcon}${previewPart} ${c.dim("(" + elapsed + "s)")}
2117
+ `);
2067
2118
  appendSessionLog(sessionId, {
2068
2119
  type: "local_tool_result",
2069
2120
  tool: toolName,
@@ -2083,14 +2134,20 @@ ${sym.fail} ${c.error(err.message ?? String(err))}`);
2083
2134
  console.error(c.warn(`
2084
2135
  [local] Failed to submit tool result: ${postErr?.message}`));
2085
2136
  }
2086
- } else if (event.type === "ping") {
2137
+ } else if (event.type === "keepalive") {
2087
2138
  } else if (event.type === "toolStart") {
2088
- const name = event.toolName ?? "";
2139
+ if (thinkingSpinner.isSpinning) thinkingSpinner.stop();
2140
+ lastToolName = event.toolName ?? "";
2141
+ toolStartMs = Date.now();
2089
2142
  if (hasOutput) process.stdout.write("\n");
2090
- process.stdout.write(c.dim(` [tool] ${name}\u2026`));
2143
+ process.stdout.write(` ${c.dim("\u2500")} ${c.bold(lastToolName)}`);
2091
2144
  hasOutput = false;
2092
2145
  } else if (event.type === "toolEnd") {
2093
- process.stdout.write(c.dim(" done\n"));
2146
+ const elapsed = ((Date.now() - toolStartMs) / 1e3).toFixed(1);
2147
+ readline3.cursorTo(process.stdout, 0);
2148
+ readline3.clearLine(process.stdout, 0);
2149
+ process.stdout.write(` ${c.dim("\u2500")} ${c.bold(lastToolName)} ${sym.ok} ${c.dim("(" + elapsed + "s)")}
2150
+ `);
2094
2151
  process.stdout.write(c.primary("agent") + c.dim(" \u203A "));
2095
2152
  hasOutput = false;
2096
2153
  } else if (event.type === "final") {
@@ -2127,12 +2184,14 @@ ${sym.fail} ${c.error(err.message ?? String(err))}`);
2127
2184
  }
2128
2185
  break;
2129
2186
  } else if (event.type === "error") {
2187
+ if (thinkingSpinner.isSpinning) thinkingSpinner.stop();
2130
2188
  if (hasOutput) process.stdout.write("\n");
2131
2189
  console.error(`
2132
2190
  ${sym.fail} ${c.error(event.message ?? "Stream error")}`);
2133
2191
  break;
2134
2192
  }
2135
2193
  }
2194
+ if (thinkingSpinner.isSpinning) thinkingSpinner.stop();
2136
2195
  process.stdout.write("\n");
2137
2196
  if (localToolsCfg && agentContent) {
2138
2197
  await handleLocalToolLoop(agentContent, localToolsCfg, client, agentId, sessionId, appendSessionLog);
@@ -2143,6 +2202,8 @@ ${sym.fail} ${c.error(event.message ?? "Stream error")}`);
2143
2202
  }
2144
2203
  }
2145
2204
  console.log();
2205
+ readline3.cursorTo(process.stdout, 0);
2206
+ readline3.clearLine(process.stdout, 0);
2146
2207
  rl.resume();
2147
2208
  rl.prompt();
2148
2209
  });
@@ -2166,16 +2227,25 @@ async function handleLocalToolLoop(agentText, cfg, client, agentId, sessionId, a
2166
2227
  }
2167
2228
  const toolCall = extractToolCall(agentText);
2168
2229
  if (!toolCall) return;
2169
- process.stdout.write(c.dim(`
2170
- [local] ${toolCall.tool}`));
2230
+ const argStr = toolArgSummary(toolCall.tool, toolCall.args ?? {});
2231
+ process.stdout.write(`
2232
+ ${c.dim("\u2500")} ${c.bold(toolCall.tool)}${argStr ? " " + c.dim(argStr) : ""}`);
2233
+ const startMs = Date.now();
2171
2234
  let result;
2235
+ let toolOk = true;
2172
2236
  try {
2173
2237
  result = await runLocalTool(toolCall, cfg);
2174
- process.stdout.write(c.dim(" \u2713\n"));
2175
2238
  } catch (err) {
2176
2239
  result = `Error: ${err?.message ?? String(err)}`;
2177
- process.stdout.write(c.dim(" \u2717\n"));
2240
+ toolOk = false;
2178
2241
  }
2242
+ const elapsed = ((Date.now() - startMs) / 1e3).toFixed(1);
2243
+ const preview = toolOk ? toolResultPreview(toolCall.tool, result) : "";
2244
+ readline3.cursorTo(process.stdout, 0);
2245
+ readline3.clearLine(process.stdout, 0);
2246
+ const previewPart = preview ? ` ${c.dim(preview)}` : "";
2247
+ process.stdout.write(` ${c.dim("\u2500")} ${c.bold(toolCall.tool)}${argStr ? " " + c.dim(argStr) : ""} ${toolOk ? sym.ok : sym.fail}${previewPart} ${c.dim("(" + elapsed + "s)")}
2248
+ `);
2179
2249
  const resultMsg = `[Tool result: ${toolCall.tool}]
2180
2250
  \`\`\`
2181
2251
  ${result}
@@ -2190,6 +2260,8 @@ ${result}
2190
2260
  process.stdout.write(c.primary("agent") + c.dim(" \u203A "));
2191
2261
  let followContent = "";
2192
2262
  try {
2263
+ let loopToolName = "";
2264
+ let loopToolStartMs = 0;
2193
2265
  for await (const evt of client.agents.stream({
2194
2266
  agentId,
2195
2267
  sessionId,
@@ -2200,11 +2272,16 @@ ${result}
2200
2272
  process.stdout.write(tok);
2201
2273
  followContent += tok;
2202
2274
  } else if (evt.type === "toolStart") {
2203
- const name = evt.toolName ?? "";
2275
+ loopToolName = evt.toolName ?? "";
2276
+ loopToolStartMs = Date.now();
2204
2277
  if (followContent) process.stdout.write("\n");
2205
- process.stdout.write(c.dim(` [tool] ${name}\u2026`));
2278
+ process.stdout.write(` ${c.dim("\u2500")} ${c.bold(loopToolName)}`);
2206
2279
  } else if (evt.type === "toolEnd") {
2207
- process.stdout.write(c.dim(" done\n"));
2280
+ const elapsed2 = ((Date.now() - loopToolStartMs) / 1e3).toFixed(1);
2281
+ readline3.cursorTo(process.stdout, 0);
2282
+ readline3.clearLine(process.stdout, 0);
2283
+ process.stdout.write(` ${c.dim("\u2500")} ${c.bold(loopToolName)} ${sym.ok} ${c.dim("(" + elapsed2 + "s)")}
2284
+ `);
2208
2285
  process.stdout.write(c.primary("agent") + c.dim(" \u203A "));
2209
2286
  } else if (evt.type === "final") {
2210
2287
  const txt = extractText(evt?.payload);
@@ -2228,6 +2305,80 @@ ${sym.fail} ${c.error(evt.message ?? "Stream error")}`);
2228
2305
  }
2229
2306
  await handleLocalToolLoop(followContent, cfg, client, agentId, sessionId, appendLog, depth + 1);
2230
2307
  }
2308
+ function truncate(s, max) {
2309
+ const str = String(s ?? "");
2310
+ return str.length <= max ? str : str.slice(0, max - 1) + "\u2026";
2311
+ }
2312
+ function toolArgSummary(toolName, args) {
2313
+ switch (toolName) {
2314
+ case "read_file":
2315
+ return truncate(args.path ?? "", 60);
2316
+ case "write_file":
2317
+ return truncate(args.path ?? "", 60);
2318
+ case "delete_file":
2319
+ return truncate(args.path ?? "", 60);
2320
+ case "list_directory":
2321
+ return truncate(args.path ?? ".", 60);
2322
+ case "run_command":
2323
+ return truncate(args.command ?? "", 60);
2324
+ case "start_process":
2325
+ return truncate(args.command ?? "", 60);
2326
+ case "wait_for_process":
2327
+ return truncate(args.process_id ?? "", 20);
2328
+ case "process_status":
2329
+ return truncate(args.process_id ?? "", 20);
2330
+ case "kill_process":
2331
+ return truncate(args.process_id ?? "", 20);
2332
+ case "list_processes":
2333
+ return "";
2334
+ case "search_files": {
2335
+ const parts = [args.pattern, args.query].filter(Boolean);
2336
+ return truncate(parts.join(" "), 60);
2337
+ }
2338
+ default: {
2339
+ const first = args.path ?? args.query ?? args.command ?? args.pattern ?? "";
2340
+ return truncate(String(first), 60);
2341
+ }
2342
+ }
2343
+ }
2344
+ function toolResultPreview(toolName, result) {
2345
+ if (!result || result.startsWith("Error:")) return "";
2346
+ switch (toolName) {
2347
+ case "read_file": {
2348
+ const lines = result.split("\n").length;
2349
+ return `${lines} lines`;
2350
+ }
2351
+ case "write_file":
2352
+ return "written";
2353
+ case "delete_file":
2354
+ return "deleted";
2355
+ case "list_directory": {
2356
+ const count = result.split("\n").filter(Boolean).length;
2357
+ return `${count} entries`;
2358
+ }
2359
+ case "run_command": {
2360
+ const first = result.split("\n").find((l) => l.trim());
2361
+ return first ? truncate(first.trim(), 50) : "done";
2362
+ }
2363
+ case "start_process": {
2364
+ const match = result.match(/process[_\s-]?id[:\s]+([a-zA-Z0-9_-]+)/i) ?? result.match(/"id"[:\s]+"([^"]+)"/);
2365
+ return match ? `pid ${match[1]}` : "started";
2366
+ }
2367
+ case "wait_for_process": {
2368
+ if (/done|complete|exit/i.test(result)) return "done";
2369
+ if (/running/i.test(result)) return "still running";
2370
+ return truncate(result.split("\n")[0]?.trim() ?? "", 40);
2371
+ }
2372
+ case "search_files": {
2373
+ const count = result.split("\n").filter(Boolean).length;
2374
+ return `${count} match${count === 1 ? "" : "es"}`;
2375
+ }
2376
+ default: {
2377
+ const first = result.split("\n").find((l) => l.trim());
2378
+ return first ? truncate(first.trim(), 50) : "";
2379
+ }
2380
+ }
2381
+ }
2231
2382
  function extractText(payload) {
2232
2383
  if (!payload) return "";
2233
2384
  if (typeof payload === "string") return payload;
@@ -3523,7 +3674,7 @@ async function pickAgentInteractively(action) {
3523
3674
  return agentId;
3524
3675
  }
3525
3676
  var program = new import_commander16.Command();
3526
- program.name("agc").description("Agent Commons CLI \u2014 interact with the Agent Commons platform").version("0.1.16", "-v, --version").action(async () => {
3677
+ program.name("agc").description("Agent Commons CLI \u2014 interact with the Agent Commons platform").version("0.1.17", "-v, --version").action(async () => {
3527
3678
  await interactiveMenu();
3528
3679
  });
3529
3680
  program.addCommand(loginCommand());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-commons/cli",
3
- "version": "0.1.16",
3
+ "version": "0.1.17",
4
4
  "description": "Agent Commons CLI — chat, run, and manage agents from your terminal",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -11,15 +11,17 @@
11
11
  "dist"
12
12
  ],
13
13
  "dependencies": {
14
- "commander": "^12.1.0",
14
+ "@types/pdf-parse": "^1.1.5",
15
15
  "chalk": "^5.3.0",
16
+ "commander": "^12.1.0",
16
17
  "ora": "^8.1.1",
17
- "@agent-commons/sdk": "0.1.12"
18
+ "pdf-parse": "^1.1.1",
19
+ "@agent-commons/sdk": "0.1.13"
18
20
  },
19
21
  "devDependencies": {
22
+ "@types/node": "^22.10.2",
20
23
  "tsup": "^8.3.5",
21
- "typescript": "^5.7.2",
22
- "@types/node": "^22.10.2"
24
+ "typescript": "^5.7.2"
23
25
  },
24
26
  "engines": {
25
27
  "node": ">=18"