aicomputer 0.1.5 → 0.1.6

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 (3) hide show
  1. package/README.md +7 -0
  2. package/dist/index.js +175 -64
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -22,6 +22,11 @@ computer open my-box --terminal
22
22
  computer ssh
23
23
  computer ssh my-box
24
24
  computer ssh --setup
25
+ computer agent agents my-box
26
+ computer agent sessions list my-box
27
+ computer agent prompt my-box "inspect /workspace" --agent codex
28
+ computer fleet status
29
+ computer acp serve my-box --agent codex
25
30
  ```
26
31
 
27
32
  Run `computer ssh` without a handle in an interactive terminal to pick from your available machines.
@@ -33,4 +38,6 @@ ssh agentcomputer.ai
33
38
  ssh my-box@agentcomputer.ai
34
39
  ```
35
40
 
41
+ Use `computer agent` to inspect agents on one machine and manage remote sessions. Use `computer fleet status` to see active agent work across every machine in your fleet.
42
+
36
43
  You can also run without a global install via `npx aicomputer <command>`.
package/dist/index.js CHANGED
@@ -857,8 +857,28 @@ function emitError(id, code, message) {
857
857
  }
858
858
  });
859
859
  }
860
- async function forwardRawEventStream(computerID, sessionID, signal) {
861
- const response = await openAgentSessionEventsStream(computerID, sessionID, { signal });
860
+ function forwardBridgePayload(payload, publicSessionID, backendSessionID) {
861
+ let message;
862
+ try {
863
+ message = JSON.parse(payload);
864
+ } catch {
865
+ return;
866
+ }
867
+ const method = typeof message.method === "string" ? message.method : "";
868
+ if (method !== "session/update" && method !== "session/request_permission") {
869
+ return;
870
+ }
871
+ const params = typeof message.params === "object" && message.params !== null ? { ...message.params } : {};
872
+ const payloadSessionID = typeof params.sessionId === "string" ? params.sessionId.trim() : "";
873
+ if (backendSessionID?.trim() && payloadSessionID && payloadSessionID !== backendSessionID.trim()) {
874
+ return;
875
+ }
876
+ params.sessionId = publicSessionID;
877
+ process.stdout.write(`${JSON.stringify({ ...message, params })}
878
+ `);
879
+ }
880
+ async function forwardRawEventStream(computerID, publicSessionID, backendSessionID, signal) {
881
+ const response = await openAgentSessionEventsStream(computerID, publicSessionID, { signal });
862
882
  if (!response.body) {
863
883
  return;
864
884
  }
@@ -882,8 +902,7 @@ async function forwardRawEventStream(computerID, sessionID, signal) {
882
902
  if (!payload || payload === "heartbeat") {
883
903
  continue;
884
904
  }
885
- process.stdout.write(`${payload}
886
- `);
905
+ forwardBridgePayload(payload, publicSessionID, backendSessionID);
887
906
  }
888
907
  }
889
908
  }
@@ -904,15 +923,21 @@ async function handlePromptRequest(computerID, sessionID, id, params) {
904
923
  if (prompt.length === 0) {
905
924
  throw new Error("session/prompt requires prompt content");
906
925
  }
926
+ const accepted = await promptAgentSession(computerID, sessionID, { prompt });
927
+ const current = await getAgentSession(computerID, sessionID);
907
928
  const controller = new AbortController();
908
- const streamPromise = forwardRawEventStream(computerID, sessionID, controller.signal).catch((error) => {
929
+ const streamPromise = forwardRawEventStream(
930
+ computerID,
931
+ sessionID,
932
+ current.backend_session_id,
933
+ controller.signal
934
+ ).catch((error) => {
909
935
  if (error instanceof Error && error.name === "AbortError") {
910
936
  return;
911
937
  }
912
- throw error;
938
+ return;
913
939
  });
914
940
  try {
915
- const accepted = await promptAgentSession(computerID, sessionID, { prompt });
916
941
  const settled = await waitForSessionToSettle(computerID, sessionID, accepted.prompt_id);
917
942
  emitResult(id, {
918
943
  stopReason: settled.last_stop_reason || "end_turn"
@@ -1129,7 +1154,7 @@ var StreamPrinter = class {
1129
1154
  };
1130
1155
  async function streamSessionEvents(computerID, sessionID, options = {}) {
1131
1156
  const response = await openAgentSessionEventsStream(computerID, sessionID, {
1132
- lastEventId: options.replay ? "0" : void 0,
1157
+ lastEventId: options.lastEventId?.trim() || void 0,
1133
1158
  signal: options.signal
1134
1159
  });
1135
1160
  if (!response.body) {
@@ -1314,22 +1339,33 @@ agentCommand.command("prompt").description("Send a prompt to a machine agent ses
1314
1339
  const computer = await resolveComputer(identifier);
1315
1340
  const session = await resolvePromptSession(computer.id, options);
1316
1341
  spinner?.stop();
1317
- if (!options.noStream) {
1342
+ const canStreamImmediately = !options.noStream && Boolean(session.backend_session_id?.trim());
1343
+ if (canStreamImmediately) {
1318
1344
  streamPromise = streamSessionEvents(computer.id, session.id, {
1319
- replay: false,
1320
1345
  json: options.json,
1321
1346
  signal: controller.signal
1322
1347
  }).catch((error) => {
1323
1348
  if (error instanceof Error && error.name === "AbortError") {
1324
1349
  return;
1325
1350
  }
1326
- throw error;
1351
+ return;
1327
1352
  });
1328
1353
  await new Promise((resolve) => setTimeout(resolve, 150));
1329
1354
  }
1330
1355
  const promptResponse = await promptAgentSession(computer.id, session.id, {
1331
1356
  prompt: [{ type: "text", text }]
1332
1357
  });
1358
+ if (!options.noStream && !canStreamImmediately) {
1359
+ streamPromise = streamSessionEvents(computer.id, session.id, {
1360
+ json: options.json,
1361
+ signal: controller.signal
1362
+ }).catch((error) => {
1363
+ if (error instanceof Error && error.name === "AbortError") {
1364
+ return;
1365
+ }
1366
+ return;
1367
+ });
1368
+ }
1333
1369
  const settled = await waitForSessionToSettle(computer.id, session.id, promptResponse.prompt_id);
1334
1370
  controller.abort();
1335
1371
  if (streamPromise) {
@@ -1358,11 +1394,14 @@ agentCommand.command("prompt").description("Send a prompt to a machine agent ses
1358
1394
  process.exit(1);
1359
1395
  }
1360
1396
  });
1361
- agentCommand.command("watch").description("Watch a machine agent session event stream").argument("<machine>", "Computer id or handle").requiredOption("--session <id>", "Session id").option("--no-replay", "Do not replay buffered events before following live output").option("--json", "Print raw event envelopes").action(async (identifier, options) => {
1397
+ agentCommand.command("watch").description("Watch a machine agent session event stream").argument("<machine>", "Computer id or handle").requiredOption("--session <id>", "Session id").option("--last-event-id <id>", "Replay buffered events after this event id").option("--json", "Print raw event envelopes").action(async (identifier, options) => {
1362
1398
  try {
1363
1399
  const computer = await resolveComputer(identifier);
1400
+ if (options.lastEventId && !/^[1-9]\d*$/.test(options.lastEventId.trim())) {
1401
+ throw new Error("--last-event-id must be a positive integer");
1402
+ }
1364
1403
  await streamSessionEvents(computer.id, options.session.trim(), {
1365
- replay: options.replay,
1404
+ lastEventId: options.lastEventId,
1366
1405
  json: options.json
1367
1406
  });
1368
1407
  } catch (error) {
@@ -1876,6 +1915,9 @@ _computer() {
1876
1915
  'open:Open in browser'
1877
1916
  'ssh:SSH into a computer'
1878
1917
  'ports:Manage published ports'
1918
+ 'agent:Manage cloud agent sessions'
1919
+ 'fleet:View agent activity across your fleet'
1920
+ 'acp:Run a local ACP bridge for remote agent sessions'
1879
1921
  'rm:Delete a computer'
1880
1922
  'completion:Generate shell completions'
1881
1923
  'help:Display help'
@@ -2002,7 +2044,7 @@ var BASH_SCRIPT = `_computer() {
2002
2044
  local cur prev words cword
2003
2045
  _init_completion || return
2004
2046
 
2005
- local commands="login logout whoami create ls get open ssh ports rm completion help"
2047
+ local commands="login logout whoami create ls get open ssh ports agent fleet acp rm completion help"
2006
2048
  local ports_commands="ls publish rm"
2007
2049
 
2008
2050
  if [[ $cword -eq 1 ]]; then
@@ -2444,60 +2486,128 @@ var pkg2 = JSON.parse(
2444
2486
  );
2445
2487
  var cliName = process.argv[1] ? basename2(process.argv[1]) : "agentcomputer";
2446
2488
  var program = new Command9();
2447
- program.name(cliName).description("Agent Computer CLI").version(pkg2.version ?? "0.0.0").option("-y, --yes", "Skip confirmation prompts").configureHelp({
2448
- formatHelp(cmd, helper) {
2449
- const version = pkg2.version ?? "0.0.0";
2450
- const lines = [];
2451
- lines.push(`${chalk8.bold(cliName)} ${chalk8.dim(`v${version}`)}`);
2489
+ function appendTextSection(lines, title, values) {
2490
+ if (values.length === 0) {
2491
+ return;
2492
+ }
2493
+ lines.push(` ${chalk8.dim(title)}`);
2494
+ lines.push("");
2495
+ for (const value of values) {
2496
+ lines.push(` ${chalk8.white(value)}`);
2497
+ }
2498
+ lines.push("");
2499
+ }
2500
+ function appendTableSection(lines, title, entries) {
2501
+ if (entries.length === 0) {
2502
+ return;
2503
+ }
2504
+ const width = Math.max(...entries.map((entry) => entry.term.length), 0) + 2;
2505
+ lines.push(` ${chalk8.dim(title)}`);
2506
+ lines.push("");
2507
+ for (const entry of entries) {
2508
+ lines.push(` ${chalk8.white(padEnd(entry.term, width))}${chalk8.dim(entry.desc)}`);
2509
+ }
2510
+ lines.push("");
2511
+ }
2512
+ function commandPath(cmd) {
2513
+ const parts = [];
2514
+ let current = cmd;
2515
+ while (current) {
2516
+ parts.unshift(current.name());
2517
+ current = current.parent ?? null;
2518
+ }
2519
+ return parts.join(" ");
2520
+ }
2521
+ function formatRootHelp(cmd) {
2522
+ const version = pkg2.version ?? "0.0.0";
2523
+ const lines = [];
2524
+ const groups = [
2525
+ ["Auth", []],
2526
+ ["Computers", []],
2527
+ ["Access", []],
2528
+ ["Agents", []],
2529
+ ["Other", []]
2530
+ ];
2531
+ const otherGroup = groups.find(([name]) => name === "Other")[1];
2532
+ lines.push(`${chalk8.bold(cliName)} ${chalk8.dim(`v${version}`)}`);
2533
+ lines.push("");
2534
+ if (cmd.description()) {
2535
+ lines.push(` ${chalk8.dim(cmd.description())}`);
2452
2536
  lines.push("");
2453
- if (cmd.commands.length > 0) {
2454
- const groups = {
2455
- Auth: [],
2456
- Computers: [],
2457
- Agents: [],
2458
- Access: [],
2459
- Other: []
2460
- };
2461
- for (const sub of cmd.commands) {
2462
- const name = sub.name();
2463
- const desc = sub.description();
2464
- const entry = { name, desc };
2465
- if (["login", "logout", "whoami"].includes(name)) {
2466
- groups.Auth.push(entry);
2467
- } else if (["create", "ls", "get", "rm"].includes(name)) {
2468
- groups.Computers.push(entry);
2469
- } else if (["agent", "fleet", "acp"].includes(name)) {
2470
- groups.Agents.push(entry);
2471
- } else if (["open", "ssh", "ports"].includes(name)) {
2472
- groups.Access.push(entry);
2473
- } else {
2474
- groups.Other.push(entry);
2475
- }
2476
- }
2477
- for (const [groupName, entries] of Object.entries(groups)) {
2478
- if (entries.length === 0) continue;
2479
- lines.push(` ${chalk8.dim(groupName)}`);
2480
- for (const entry of entries) {
2481
- const padded = entry.name.padEnd(14);
2482
- lines.push(` ${chalk8.white(padded)}${chalk8.dim(entry.desc)}`);
2483
- }
2484
- lines.push("");
2485
- }
2486
- }
2487
- const globalOpts = [
2488
- { flags: "-y, --yes", desc: "Skip confirmation prompts" },
2489
- { flags: "-V, --version", desc: "Show version" },
2490
- { flags: "-h, --help", desc: "Show help" }
2491
- ];
2492
- lines.push(` ${chalk8.dim("Options")}`);
2493
- for (const opt of globalOpts) {
2494
- const padded = opt.flags.padEnd(14);
2495
- lines.push(` ${chalk8.white(padded)}${chalk8.dim(opt.desc)}`);
2537
+ }
2538
+ appendTextSection(lines, "Usage", [`${cliName} <command> [options]`]);
2539
+ for (const sub of cmd.commands) {
2540
+ const name = sub.name();
2541
+ const entry = { term: name, desc: sub.description() };
2542
+ if (["login", "logout", "whoami"].includes(name)) {
2543
+ groups[0][1].push(entry);
2544
+ } else if (["create", "ls", "get", "rm"].includes(name)) {
2545
+ groups[1][1].push(entry);
2546
+ } else if (["open", "ssh", "ports"].includes(name)) {
2547
+ groups[2][1].push(entry);
2548
+ } else if (["agent", "fleet", "acp"].includes(name)) {
2549
+ groups[3][1].push(entry);
2550
+ } else {
2551
+ otherGroup.push(entry);
2496
2552
  }
2553
+ }
2554
+ for (const [groupName, entries] of groups) {
2555
+ appendTableSection(
2556
+ lines,
2557
+ groupName,
2558
+ entries
2559
+ );
2560
+ }
2561
+ appendTableSection(lines, "Options", [
2562
+ { term: "-y, --yes", desc: "Skip confirmation prompts" },
2563
+ { term: "-V, --version", desc: "Show version" },
2564
+ { term: "-h, --help", desc: "Show help" }
2565
+ ]);
2566
+ return `${lines.join("\n").trimEnd()}
2567
+ `;
2568
+ }
2569
+ function formatSubcommandHelp(cmd, helper) {
2570
+ const lines = [];
2571
+ const description = helper.commandDescription(cmd);
2572
+ const argumentsList = helper.visibleArguments(cmd).map((argument) => ({
2573
+ term: helper.argumentTerm(argument),
2574
+ desc: helper.argumentDescription(argument)
2575
+ }));
2576
+ const commandList = helper.visibleCommands(cmd).map((subcommand) => ({
2577
+ term: helper.subcommandTerm(subcommand),
2578
+ desc: helper.subcommandDescription(subcommand)
2579
+ }));
2580
+ const optionList = helper.visibleOptions(cmd).map((option) => ({
2581
+ term: helper.optionTerm(option),
2582
+ desc: helper.optionDescription(option)
2583
+ }));
2584
+ lines.push(chalk8.bold(commandPath(cmd)));
2585
+ lines.push("");
2586
+ if (description) {
2587
+ lines.push(` ${chalk8.dim(description)}`);
2497
2588
  lines.push("");
2498
- return lines.join("\n");
2499
2589
  }
2500
- });
2590
+ appendTextSection(lines, "Usage", [helper.commandUsage(cmd)]);
2591
+ appendTableSection(lines, "Arguments", argumentsList);
2592
+ appendTableSection(lines, "Commands", commandList);
2593
+ appendTableSection(lines, "Options", optionList);
2594
+ return `${lines.join("\n").trimEnd()}
2595
+ `;
2596
+ }
2597
+ function applyHelpFormatting(cmd) {
2598
+ cmd.configureHelp({
2599
+ formatHelp(current, helper) {
2600
+ if (!current.parent) {
2601
+ return formatRootHelp(current);
2602
+ }
2603
+ return formatSubcommandHelp(current, helper);
2604
+ }
2605
+ });
2606
+ for (const subcommand of cmd.commands) {
2607
+ applyHelpFormatting(subcommand);
2608
+ }
2609
+ }
2610
+ program.name(cliName).description("Agent Computer CLI").version(pkg2.version ?? "0.0.0").option("-y, --yes", "Skip confirmation prompts");
2501
2611
  program.addCommand(loginCommand);
2502
2612
  program.addCommand(logoutCommand);
2503
2613
  program.addCommand(whoamiCommand);
@@ -2512,4 +2622,5 @@ program.addCommand(sshCommand);
2512
2622
  program.addCommand(portsCommand);
2513
2623
  program.addCommand(removeCommand);
2514
2624
  program.addCommand(completionCommand);
2625
+ applyHelpFormatting(program);
2515
2626
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aicomputer",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "Computer CLI - manage your Agent Computer fleet from the terminal",
5
5
  "homepage": "https://agentcomputer.ai",
6
6
  "repository": {