@boxcrew/cli 0.1.16 → 0.1.18

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 +126 -109
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -104,6 +104,10 @@ function registerLogoutCommand(program2) {
104
104
  });
105
105
  }
106
106
 
107
+ // src/commands/agents.ts
108
+ import { readdirSync as readdirSync2 } from "fs";
109
+ import { homedir as homedir2 } from "os";
110
+
107
111
  // src/client.ts
108
112
  function getBaseUrl() {
109
113
  return process.env.BOXCREW_API_URL || config.get("apiUrl");
@@ -142,6 +146,64 @@ async function apiFetchJson(path, options = {}) {
142
146
  return response.json();
143
147
  }
144
148
 
149
+ // src/lib/state.ts
150
+ import { existsSync, mkdirSync, readFileSync, readdirSync, unlinkSync } from "fs";
151
+ import { join } from "path";
152
+ import { homedir } from "os";
153
+ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
154
+ var RUNTIME_NAMES = {
155
+ "claude-code": "Claude Code",
156
+ "opencode": "OpenCode",
157
+ "openclaw": "OpenClaw"
158
+ };
159
+ function getStateDir() {
160
+ const dir = join(homedir(), ".boxcrew");
161
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
162
+ return dir;
163
+ }
164
+ function getPidFile(key) {
165
+ return join(getStateDir(), `${key}.pid`);
166
+ }
167
+ function getMetaFile(key) {
168
+ return join(getStateDir(), `${key}.meta.json`);
169
+ }
170
+ function getLogFile(key) {
171
+ return join(getStateDir(), `${key}.log`);
172
+ }
173
+ function readMeta(key) {
174
+ const metaFile = getMetaFile(key);
175
+ if (!existsSync(metaFile)) return null;
176
+ try {
177
+ return JSON.parse(readFileSync(metaFile, "utf-8"));
178
+ } catch {
179
+ return null;
180
+ }
181
+ }
182
+ function readPid(key) {
183
+ const pidFile = getPidFile(key);
184
+ if (!existsSync(pidFile)) return null;
185
+ const pid = parseInt(readFileSync(pidFile, "utf-8").trim(), 10);
186
+ if (isNaN(pid)) return null;
187
+ try {
188
+ process.kill(pid, 0);
189
+ return pid;
190
+ } catch {
191
+ unlinkSync(pidFile);
192
+ return null;
193
+ }
194
+ }
195
+ function resolveAgentKey(nameOrId) {
196
+ if (UUID_RE.test(nameOrId)) return nameOrId;
197
+ const stateDir = getStateDir();
198
+ const metaFiles = readdirSync(stateDir).filter((f) => f.endsWith(".meta.json"));
199
+ for (const file of metaFiles) {
200
+ const key = file.replace(".meta.json", "");
201
+ const meta = readMeta(key);
202
+ if (meta && meta.agentName === nameOrId) return key;
203
+ }
204
+ return null;
205
+ }
206
+
145
207
  // src/commands/agents.ts
146
208
  var TRANSIENT_CODES = [
147
209
  "ETIMEDOUT",
@@ -165,10 +227,58 @@ function registerAgentsCommands(program2) {
165
227
  console.log("No agents found.");
166
228
  return;
167
229
  }
168
- console.log("");
230
+ const home = homedir2();
231
+ const shortenPath = (p) => p.startsWith(home) ? "~" + p.slice(home.length) : p;
232
+ const localMetaById = /* @__PURE__ */ new Map();
233
+ const localMetaByName = /* @__PURE__ */ new Map();
234
+ try {
235
+ const stateDir = getStateDir();
236
+ const metaFiles = readdirSync2(stateDir).filter((f) => f.endsWith(".meta.json"));
237
+ for (const file of metaFiles) {
238
+ const key = file.replace(".meta.json", "");
239
+ const meta = readMeta(key);
240
+ if (meta) {
241
+ const entry = { pid: readPid(key), cwd: meta.cwd };
242
+ if (meta.agentId) localMetaById.set(meta.agentId, entry);
243
+ if (meta.agentName) localMetaByName.set(meta.agentName, entry);
244
+ }
245
+ }
246
+ } catch {
247
+ }
248
+ const rows = [];
169
249
  for (const agent of data) {
170
- const model = agent.model ? ` (${agent.model})` : "";
171
- console.log(` ${agent.name} ${agent.runtime}${model}`);
250
+ const shortId = agent.id.slice(0, 8);
251
+ const typeDisplay = agent.agent_type === "remote" ? "BYOA" : "hosted";
252
+ const runtimeDisplay = RUNTIME_NAMES[agent.runtime] || agent.runtime;
253
+ let status;
254
+ let directory = "-";
255
+ if (agent.agent_type !== "remote") {
256
+ status = "-";
257
+ } else {
258
+ const local = localMetaById.get(agent.id) || localMetaByName.get(agent.name);
259
+ if (local?.pid) {
260
+ status = "online (here)";
261
+ directory = shortenPath(local.cwd);
262
+ } else if (agent.is_online) {
263
+ status = "online";
264
+ } else {
265
+ status = "offline";
266
+ if (local?.cwd) directory = shortenPath(local.cwd);
267
+ }
268
+ }
269
+ rows.push({ name: agent.name, id: shortId, type: typeDisplay, runtime: runtimeDisplay, status, directory });
270
+ }
271
+ const headers = { name: "Name", id: "ID", type: "Type", runtime: "Runtime", status: "Status", directory: "Directory" };
272
+ const cols = Object.keys(headers);
273
+ const widths = {};
274
+ for (const col of cols) {
275
+ widths[col] = Math.max(headers[col].length, ...rows.map((r) => r[col].length));
276
+ }
277
+ const pad = (s, w) => s + " ".repeat(w - s.length);
278
+ console.log("");
279
+ console.log(` ${cols.map((c) => pad(headers[c], widths[c])).join(" ")}`);
280
+ for (const row of rows) {
281
+ console.log(` ${cols.map((c) => pad(row[c], widths[c])).join(" ")}`);
172
282
  }
173
283
  console.log("");
174
284
  });
@@ -360,65 +470,12 @@ function registerApiCommand(program2) {
360
470
  // src/commands/connect.ts
361
471
  import { spawn } from "child_process";
362
472
  import { createInterface as createInterface3 } from "readline";
363
- import { openSync, mkdirSync, existsSync, writeFileSync, readFileSync, unlinkSync, readdirSync, renameSync } from "fs";
364
- import { join } from "path";
365
- import { homedir, platform, release, hostname } from "os";
473
+ import { openSync, existsSync as existsSync2, writeFileSync, unlinkSync as unlinkSync2, readdirSync as readdirSync3, renameSync } from "fs";
474
+ import { join as join2 } from "path";
475
+ import { homedir as homedir3, platform, release, hostname } from "os";
366
476
  import WebSocket from "ws";
367
477
  var RECONNECT_BASE_MS = 1e3;
368
478
  var RECONNECT_MAX_MS = 3e4;
369
- var RUNTIME_NAMES = {
370
- "claude-code": "Claude Code",
371
- "opencode": "OpenCode",
372
- "openclaw": "OpenClaw"
373
- };
374
- function getStateDir() {
375
- const dir = join(homedir(), ".boxcrew");
376
- if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
377
- return dir;
378
- }
379
- function getPidFile(key) {
380
- return join(getStateDir(), `${key}.pid`);
381
- }
382
- function getMetaFile(key) {
383
- return join(getStateDir(), `${key}.meta.json`);
384
- }
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);
388
- if (!existsSync(metaFile)) return null;
389
- try {
390
- return JSON.parse(readFileSync(metaFile, "utf-8"));
391
- } catch {
392
- return null;
393
- }
394
- }
395
- function getLogFile(key) {
396
- return join(getStateDir(), `${key}.log`);
397
- }
398
- function readPid(key) {
399
- const pidFile = getPidFile(key);
400
- if (!existsSync(pidFile)) return null;
401
- const pid = parseInt(readFileSync(pidFile, "utf-8").trim(), 10);
402
- if (isNaN(pid)) return null;
403
- try {
404
- process.kill(pid, 0);
405
- return pid;
406
- } catch {
407
- unlinkSync(pidFile);
408
- return null;
409
- }
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
- }
422
479
  function parseStreamJsonLine(line) {
423
480
  let obj;
424
481
  try {
@@ -647,7 +704,7 @@ function runDaemon(agentId) {
647
704
  };
648
705
  const cleanup = () => {
649
706
  try {
650
- unlinkSync(getPidFile(agentId));
707
+ unlinkSync2(getPidFile(agentId));
651
708
  } catch {
652
709
  }
653
710
  };
@@ -747,11 +804,11 @@ function registerConnectCommand(program2) {
747
804
  `/agents/${encodeURIComponent(agentName)}/connection-config`
748
805
  );
749
806
  const agentId = config2.agent_id;
750
- if (!existsSync(getMetaFile(agentId)) && existsSync(getMetaFile(agentName))) {
807
+ if (!existsSync2(getMetaFile(agentId)) && existsSync2(getMetaFile(agentName))) {
751
808
  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);
809
+ const oldPath = join2(getStateDir(), `${agentName}${ext}`);
810
+ const newPath = join2(getStateDir(), `${agentId}${ext}`);
811
+ if (existsSync2(oldPath)) renameSync(oldPath, newPath);
755
812
  }
756
813
  }
757
814
  const existingPid = readPid(agentId);
@@ -788,7 +845,7 @@ function registerConnectCommand(program2) {
788
845
  writeFileSync(getMetaFile(agentId), JSON.stringify(meta, null, 2));
789
846
  const runtimeDisplay = RUNTIME_NAMES[config2.runtime] || config2.runtime;
790
847
  const cwd = process.cwd();
791
- const home = homedir();
848
+ const home = homedir3();
792
849
  const shortLog = logFile.startsWith(home) ? "~" + logFile.slice(home.length) : logFile;
793
850
  console.log("");
794
851
  console.log(` Agent "${config2.agent_name}" is online.`);
@@ -814,7 +871,7 @@ function registerConnectCommand(program2) {
814
871
  try {
815
872
  process.kill(pid, "SIGTERM");
816
873
  try {
817
- unlinkSync(getPidFile(key));
874
+ unlinkSync2(getPidFile(key));
818
875
  } catch {
819
876
  }
820
877
  console.log(`Agent "${agentName}" disconnected.`);
@@ -822,50 +879,10 @@ function registerConnectCommand(program2) {
822
879
  console.error(`Failed to stop process ${pid}.`);
823
880
  }
824
881
  });
825
- program2.command("status").description("Show status of all locally connected agents.").action(() => {
826
- const stateDir = getStateDir();
827
- const metaFiles = readdirSync(stateDir).filter((f) => f.endsWith(".meta.json"));
828
- if (metaFiles.length === 0) {
829
- console.log("No agents found. Use `bx connect <agent-name>` to connect one.");
830
- return;
831
- }
832
- const home = homedir();
833
- const shortenPath = (p) => p.startsWith(home) ? "~" + p.slice(home.length) : p;
834
- const rows = [];
835
- for (const file of metaFiles) {
836
- const key = file.replace(".meta.json", "");
837
- const meta = readMeta(key);
838
- if (!meta) continue;
839
- const pid = readPid(key);
840
- const isOnline = pid !== null;
841
- rows.push({
842
- agent: meta.agentName || key,
843
- status: isOnline ? "online" : "offline",
844
- runtime: RUNTIME_NAMES[meta.runtime] || meta.runtime,
845
- directory: shortenPath(meta.cwd),
846
- pid: isOnline ? String(pid) : "-"
847
- });
848
- }
849
- const headers = { agent: "Agent", status: "Status", runtime: "Runtime", directory: "Directory", pid: "PID" };
850
- const cols = Object.keys(headers);
851
- const widths = {};
852
- for (const col of cols) {
853
- widths[col] = Math.max(headers[col].length, ...rows.map((r) => r[col].length));
854
- }
855
- const pad = (s, w) => s + " ".repeat(w - s.length);
856
- const headerLine = cols.map((c) => pad(headers[c], widths[c])).join(" ");
857
- console.log("");
858
- console.log(` ${headerLine}`);
859
- for (const row of rows) {
860
- const line = cols.map((c) => pad(row[c], widths[c])).join(" ");
861
- console.log(` ${line}`);
862
- }
863
- console.log("");
864
- });
865
882
  program2.command("reconnect [agent-name]").description("Reconnect a previously connected agent.").option("--all", "Reconnect all agents").option("--claude-path <path>", "Path to claude CLI binary").action(async (agentName, options) => {
866
883
  if (options.all) {
867
884
  const stateDir = getStateDir();
868
- const metaFiles = readdirSync(stateDir).filter((f) => f.endsWith(".meta.json"));
885
+ const metaFiles = readdirSync3(stateDir).filter((f) => f.endsWith(".meta.json"));
869
886
  if (metaFiles.length === 0) {
870
887
  console.log("No agents found. Use `bx connect <agent-name>` to connect one.");
871
888
  return;
@@ -900,7 +917,7 @@ function registerConnectCommand(program2) {
900
917
  } catch {
901
918
  }
902
919
  try {
903
- unlinkSync(getPidFile(key));
920
+ unlinkSync2(getPidFile(key));
904
921
  } catch {
905
922
  }
906
923
  }
@@ -938,7 +955,7 @@ function registerConnectCommand(program2) {
938
955
  };
939
956
  writeFileSync(getMetaFile(key), JSON.stringify(newMeta, null, 2));
940
957
  const runtimeDisplay = RUNTIME_NAMES[config2.runtime] || config2.runtime;
941
- const home = homedir();
958
+ const home = homedir3();
942
959
  const shortCwd = meta.cwd.startsWith(home) ? "~" + meta.cwd.slice(home.length) : meta.cwd;
943
960
  const shortLog = logFile.startsWith(home) ? "~" + logFile.slice(home.length) : logFile;
944
961
  console.log("");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@boxcrew/cli",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "description": "BoxCrew CLI — manage your agents from the terminal",
5
5
  "type": "module",
6
6
  "bin": {