@boxcrew/cli 0.1.15 → 0.1.16

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/index.js +87 -43
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
+ import { createRequire } from "module";
4
5
  import { Command } from "commander";
5
6
 
6
7
  // src/commands/login.ts
@@ -359,7 +360,7 @@ function registerApiCommand(program2) {
359
360
  // src/commands/connect.ts
360
361
  import { spawn } from "child_process";
361
362
  import { createInterface as createInterface3 } from "readline";
362
- import { openSync, mkdirSync, existsSync, writeFileSync, readFileSync, unlinkSync, readdirSync } from "fs";
363
+ import { openSync, mkdirSync, existsSync, writeFileSync, readFileSync, unlinkSync, readdirSync, renameSync } from "fs";
363
364
  import { join } from "path";
364
365
  import { homedir, platform, release, hostname } from "os";
365
366
  import WebSocket from "ws";
@@ -375,14 +376,15 @@ function getStateDir() {
375
376
  if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
376
377
  return dir;
377
378
  }
378
- function getPidFile(agentName) {
379
- return join(getStateDir(), `${agentName}.pid`);
379
+ function getPidFile(key) {
380
+ return join(getStateDir(), `${key}.pid`);
380
381
  }
381
- function getMetaFile(agentName) {
382
- return join(getStateDir(), `${agentName}.meta.json`);
382
+ function getMetaFile(key) {
383
+ return join(getStateDir(), `${key}.meta.json`);
383
384
  }
384
- function readMeta(agentName) {
385
- const metaFile = getMetaFile(agentName);
385
+ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
386
+ function readMeta(key) {
387
+ const metaFile = getMetaFile(key);
386
388
  if (!existsSync(metaFile)) return null;
387
389
  try {
388
390
  return JSON.parse(readFileSync(metaFile, "utf-8"));
@@ -390,11 +392,11 @@ function readMeta(agentName) {
390
392
  return null;
391
393
  }
392
394
  }
393
- function getLogFile(agentName) {
394
- return join(getStateDir(), `${agentName}.log`);
395
+ function getLogFile(key) {
396
+ return join(getStateDir(), `${key}.log`);
395
397
  }
396
- function readPid(agentName) {
397
- const pidFile = getPidFile(agentName);
398
+ function readPid(key) {
399
+ const pidFile = getPidFile(key);
398
400
  if (!existsSync(pidFile)) return null;
399
401
  const pid = parseInt(readFileSync(pidFile, "utf-8").trim(), 10);
400
402
  if (isNaN(pid)) return null;
@@ -406,6 +408,17 @@ function readPid(agentName) {
406
408
  return null;
407
409
  }
408
410
  }
411
+ function resolveAgentKey(nameOrId) {
412
+ if (UUID_RE.test(nameOrId)) return nameOrId;
413
+ const stateDir = getStateDir();
414
+ const metaFiles = readdirSync(stateDir).filter((f) => f.endsWith(".meta.json"));
415
+ for (const file of metaFiles) {
416
+ const key = file.replace(".meta.json", "");
417
+ const meta = readMeta(key);
418
+ if (meta && meta.agentName === nameOrId) return key;
419
+ }
420
+ return null;
421
+ }
409
422
  function parseStreamJsonLine(line) {
410
423
  let obj;
411
424
  try {
@@ -512,16 +525,16 @@ function parseOpenClawOutput(stdout) {
512
525
  return [{ kind: "text", text: stdout.trim() || "No response" }];
513
526
  }
514
527
  }
515
- function runDaemon(agentName) {
528
+ function runDaemon(agentId) {
516
529
  const wsUrl = process.env._BX_WS_URL;
517
530
  const claudePath = process.env._BX_CLAUDE_PATH || "claude";
518
- const agentDisplayName = process.env._BX_AGENT_NAME || agentName;
531
+ const agentDisplayName = process.env._BX_AGENT_NAME || agentId;
519
532
  const runtime = process.env._BX_RUNTIME || "claude-code";
520
533
  if (!wsUrl) {
521
534
  console.error("Missing _BX_WS_URL");
522
535
  process.exit(1);
523
536
  }
524
- writeFileSync(getPidFile(agentName), String(process.pid));
537
+ writeFileSync(getPidFile(agentId), String(process.pid));
525
538
  let activeProcess = null;
526
539
  let sendToServer = null;
527
540
  let reconnectAttempt = 0;
@@ -634,7 +647,7 @@ function runDaemon(agentName) {
634
647
  };
635
648
  const cleanup = () => {
636
649
  try {
637
- unlinkSync(getPidFile(agentName));
650
+ unlinkSync(getPidFile(agentId));
638
651
  } catch {
639
652
  }
640
653
  };
@@ -678,6 +691,14 @@ function runDaemon(agentName) {
678
691
  if (msg.type === "chat") {
679
692
  handleChat(msg);
680
693
  }
694
+ if (msg.type === "rename") {
695
+ const meta = readMeta(agentId);
696
+ if (meta) {
697
+ meta.agentName = msg.newName;
698
+ writeFileSync(getMetaFile(agentId), JSON.stringify(meta, null, 2));
699
+ }
700
+ console.log(`[${(/* @__PURE__ */ new Date()).toISOString()}] Agent renamed to "${msg.newName}"`);
701
+ }
681
702
  });
682
703
  ws.on("close", (code, reason) => {
683
704
  sendToServer = null;
@@ -718,19 +739,27 @@ function runDaemon(agentName) {
718
739
  connect();
719
740
  }
720
741
  function registerConnectCommand(program2) {
721
- program2.command("_daemon <agent-name>", { hidden: true }).action((agentName) => {
722
- runDaemon(agentName);
742
+ program2.command("_daemon <agent-id>", { hidden: true }).action((agentId) => {
743
+ runDaemon(agentId);
723
744
  });
724
745
  program2.command("connect <agent-name>").description("Connect a local agent to BoxCrew.").option("--claude-path <path>", "Path to claude CLI binary", "claude").action(async (agentName, options) => {
725
- const existingPid = readPid(agentName);
746
+ const config2 = await apiFetchJson(
747
+ `/agents/${encodeURIComponent(agentName)}/connection-config`
748
+ );
749
+ const agentId = config2.agent_id;
750
+ if (!existsSync(getMetaFile(agentId)) && existsSync(getMetaFile(agentName))) {
751
+ for (const ext of [".meta.json", ".pid", ".log"]) {
752
+ const oldPath = join(getStateDir(), `${agentName}${ext}`);
753
+ const newPath = join(getStateDir(), `${agentId}${ext}`);
754
+ if (existsSync(oldPath)) renameSync(oldPath, newPath);
755
+ }
756
+ }
757
+ const existingPid = readPid(agentId);
726
758
  if (existingPid) {
727
759
  console.log(`Agent "${agentName}" is already connected (PID ${existingPid}).`);
728
760
  return;
729
761
  }
730
- const config2 = await apiFetchJson(
731
- `/agents/${encodeURIComponent(agentName)}/connection-config`
732
- );
733
- const logFile = getLogFile(agentName);
762
+ const logFile = getLogFile(agentId);
734
763
  const logFd = openSync(logFile, "a");
735
764
  const daemonEnv = {
736
765
  ...process.env,
@@ -741,7 +770,7 @@ function registerConnectCommand(program2) {
741
770
  };
742
771
  delete daemonEnv.CLAUDECODE;
743
772
  delete daemonEnv.CLAUDE_CODE_ENTRYPOINT;
744
- const child = spawn(process.argv[0], [process.argv[1], "_daemon", agentName], {
773
+ const child = spawn(process.argv[0], [process.argv[1], "_daemon", agentId], {
745
774
  detached: true,
746
775
  stdio: ["ignore", logFd, logFd],
747
776
  env: daemonEnv
@@ -751,11 +780,12 @@ function registerConnectCommand(program2) {
751
780
  pid: child.pid,
752
781
  cwd: process.cwd(),
753
782
  runtime: config2.runtime,
783
+ agentId,
754
784
  agentName: config2.agent_name,
755
785
  connectedAt: (/* @__PURE__ */ new Date()).toISOString(),
756
786
  claudePath: options.claudePath
757
787
  };
758
- writeFileSync(getMetaFile(agentName), JSON.stringify(meta, null, 2));
788
+ writeFileSync(getMetaFile(agentId), JSON.stringify(meta, null, 2));
759
789
  const runtimeDisplay = RUNTIME_NAMES[config2.runtime] || config2.runtime;
760
790
  const cwd = process.cwd();
761
791
  const home = homedir();
@@ -771,7 +801,12 @@ function registerConnectCommand(program2) {
771
801
  console.log("");
772
802
  });
773
803
  program2.command("disconnect <agent-name>").description("Disconnect a local agent.").action((agentName) => {
774
- const pid = readPid(agentName);
804
+ const key = resolveAgentKey(agentName);
805
+ if (!key) {
806
+ console.log(`Agent "${agentName}" is not connected.`);
807
+ return;
808
+ }
809
+ const pid = readPid(key);
775
810
  if (!pid) {
776
811
  console.log(`Agent "${agentName}" is not connected.`);
777
812
  return;
@@ -779,7 +814,7 @@ function registerConnectCommand(program2) {
779
814
  try {
780
815
  process.kill(pid, "SIGTERM");
781
816
  try {
782
- unlinkSync(getPidFile(agentName));
817
+ unlinkSync(getPidFile(key));
783
818
  } catch {
784
819
  }
785
820
  console.log(`Agent "${agentName}" disconnected.`);
@@ -798,13 +833,13 @@ function registerConnectCommand(program2) {
798
833
  const shortenPath = (p) => p.startsWith(home) ? "~" + p.slice(home.length) : p;
799
834
  const rows = [];
800
835
  for (const file of metaFiles) {
801
- const agentName = file.replace(".meta.json", "");
802
- const meta = readMeta(agentName);
836
+ const key = file.replace(".meta.json", "");
837
+ const meta = readMeta(key);
803
838
  if (!meta) continue;
804
- const pid = readPid(agentName);
839
+ const pid = readPid(key);
805
840
  const isOnline = pid !== null;
806
841
  rows.push({
807
- agent: meta.agentName || agentName,
842
+ agent: meta.agentName || key,
808
843
  status: isOnline ? "online" : "offline",
809
844
  runtime: RUNTIME_NAMES[meta.runtime] || meta.runtime,
810
845
  directory: shortenPath(meta.cwd),
@@ -836,8 +871,8 @@ function registerConnectCommand(program2) {
836
871
  return;
837
872
  }
838
873
  for (const file of metaFiles) {
839
- const name = file.replace(".meta.json", "");
840
- await reconnectAgent(name, options.claudePath);
874
+ const key2 = file.replace(".meta.json", "");
875
+ await reconnectAgent(key2, options.claudePath);
841
876
  }
842
877
  return;
843
878
  }
@@ -845,30 +880,36 @@ function registerConnectCommand(program2) {
845
880
  console.error("Please specify an agent name or use --all.");
846
881
  process.exit(1);
847
882
  }
848
- await reconnectAgent(agentName, options.claudePath);
883
+ const key = resolveAgentKey(agentName);
884
+ if (!key) {
885
+ console.error(`No previous connection found for "${agentName}". Use \`bx connect ${agentName}\` instead.`);
886
+ process.exit(1);
887
+ }
888
+ await reconnectAgent(key, options.claudePath);
849
889
  });
850
- async function reconnectAgent(agentName, claudePathOverride) {
851
- const meta = readMeta(agentName);
890
+ async function reconnectAgent(key, claudePathOverride) {
891
+ const meta = readMeta(key);
852
892
  if (!meta) {
853
- console.error(`No previous connection found for "${agentName}". Use \`bx connect ${agentName}\` instead.`);
893
+ console.error(`No previous connection found for "${key}". Use \`bx connect <agent-name>\` instead.`);
854
894
  return;
855
895
  }
856
- const existingPid = readPid(agentName);
896
+ const existingPid = readPid(key);
857
897
  if (existingPid) {
858
898
  try {
859
899
  process.kill(existingPid, "SIGTERM");
860
900
  } catch {
861
901
  }
862
902
  try {
863
- unlinkSync(getPidFile(agentName));
903
+ unlinkSync(getPidFile(key));
864
904
  } catch {
865
905
  }
866
906
  }
907
+ const agentId = meta.agentId || key;
867
908
  const config2 = await apiFetchJson(
868
- `/agents/${encodeURIComponent(agentName)}/connection-config`
909
+ `/agents/${encodeURIComponent(agentId)}/connection-config`
869
910
  );
870
911
  const claudePath = claudePathOverride || meta.claudePath || "claude";
871
- const logFile = getLogFile(agentName);
912
+ const logFile = getLogFile(key);
872
913
  const logFd = openSync(logFile, "a");
873
914
  const daemonEnv = {
874
915
  ...process.env,
@@ -879,7 +920,7 @@ function registerConnectCommand(program2) {
879
920
  };
880
921
  delete daemonEnv.CLAUDECODE;
881
922
  delete daemonEnv.CLAUDE_CODE_ENTRYPOINT;
882
- const child = spawn(process.argv[0], [process.argv[1], "_daemon", agentName], {
923
+ const child = spawn(process.argv[0], [process.argv[1], "_daemon", key], {
883
924
  detached: true,
884
925
  stdio: ["ignore", logFd, logFd],
885
926
  cwd: meta.cwd,
@@ -890,11 +931,12 @@ function registerConnectCommand(program2) {
890
931
  pid: child.pid,
891
932
  cwd: meta.cwd,
892
933
  runtime: config2.runtime,
934
+ agentId: config2.agent_id,
893
935
  agentName: config2.agent_name,
894
936
  connectedAt: (/* @__PURE__ */ new Date()).toISOString(),
895
937
  claudePath
896
938
  };
897
- writeFileSync(getMetaFile(agentName), JSON.stringify(newMeta, null, 2));
939
+ writeFileSync(getMetaFile(key), JSON.stringify(newMeta, null, 2));
898
940
  const runtimeDisplay = RUNTIME_NAMES[config2.runtime] || config2.runtime;
899
941
  const home = homedir();
900
942
  const shortCwd = meta.cwd.startsWith(home) ? "~" + meta.cwd.slice(home.length) : meta.cwd;
@@ -910,7 +952,9 @@ function registerConnectCommand(program2) {
910
952
  }
911
953
 
912
954
  // src/index.ts
913
- var program = new Command().name("bx").description("BoxCrew CLI \u2014 manage your agents from the terminal").version("0.1.0");
955
+ var require2 = createRequire(import.meta.url);
956
+ var { version } = require2("../package.json");
957
+ var program = new Command().name("bx").description("BoxCrew CLI \u2014 manage your agents from the terminal").version(version);
914
958
  registerLoginCommand(program);
915
959
  registerLogoutCommand(program);
916
960
  registerAgentsCommands(program);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@boxcrew/cli",
3
- "version": "0.1.15",
3
+ "version": "0.1.16",
4
4
  "description": "BoxCrew CLI — manage your agents from the terminal",
5
5
  "type": "module",
6
6
  "bin": {