@boxcrew/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/index.js +123 -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,55 @@ 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 localMeta = /* @__PURE__ */ new Map();
233
+ try {
234
+ const stateDir = getStateDir();
235
+ const metaFiles = readdirSync2(stateDir).filter((f) => f.endsWith(".meta.json"));
236
+ for (const file of metaFiles) {
237
+ const key = file.replace(".meta.json", "");
238
+ const meta = readMeta(key);
239
+ if (meta) {
240
+ localMeta.set(meta.agentId, { pid: readPid(key), cwd: meta.cwd });
241
+ }
242
+ }
243
+ } catch {
244
+ }
245
+ const rows = [];
169
246
  for (const agent of data) {
170
- const model = agent.model ? ` (${agent.model})` : "";
171
- console.log(` ${agent.name} ${agent.runtime}${model}`);
247
+ const shortId = agent.id.slice(0, 8);
248
+ const typeDisplay = agent.agent_type === "remote" ? "BYOA" : "hosted";
249
+ const runtimeDisplay = RUNTIME_NAMES[agent.runtime] || agent.runtime;
250
+ let status;
251
+ let directory = "-";
252
+ if (agent.agent_type !== "remote") {
253
+ status = "-";
254
+ } else {
255
+ const local = localMeta.get(agent.id);
256
+ if (local?.pid) {
257
+ status = "online (here)";
258
+ directory = shortenPath(local.cwd);
259
+ } else if (agent.is_online) {
260
+ status = "online";
261
+ } else {
262
+ status = "offline";
263
+ if (local?.cwd) directory = shortenPath(local.cwd);
264
+ }
265
+ }
266
+ rows.push({ name: agent.name, id: shortId, type: typeDisplay, runtime: runtimeDisplay, status, directory });
267
+ }
268
+ const headers = { name: "Name", id: "ID", type: "Type", runtime: "Runtime", status: "Status", directory: "Directory" };
269
+ const cols = Object.keys(headers);
270
+ const widths = {};
271
+ for (const col of cols) {
272
+ widths[col] = Math.max(headers[col].length, ...rows.map((r) => r[col].length));
273
+ }
274
+ const pad = (s, w) => s + " ".repeat(w - s.length);
275
+ console.log("");
276
+ console.log(` ${cols.map((c) => pad(headers[c], widths[c])).join(" ")}`);
277
+ for (const row of rows) {
278
+ console.log(` ${cols.map((c) => pad(row[c], widths[c])).join(" ")}`);
172
279
  }
173
280
  console.log("");
174
281
  });
@@ -360,65 +467,12 @@ function registerApiCommand(program2) {
360
467
  // src/commands/connect.ts
361
468
  import { spawn } from "child_process";
362
469
  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";
470
+ import { openSync, existsSync as existsSync2, writeFileSync, unlinkSync as unlinkSync2, readdirSync as readdirSync3, renameSync } from "fs";
471
+ import { join as join2 } from "path";
472
+ import { homedir as homedir3, platform, release, hostname } from "os";
366
473
  import WebSocket from "ws";
367
474
  var RECONNECT_BASE_MS = 1e3;
368
475
  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
476
  function parseStreamJsonLine(line) {
423
477
  let obj;
424
478
  try {
@@ -647,7 +701,7 @@ function runDaemon(agentId) {
647
701
  };
648
702
  const cleanup = () => {
649
703
  try {
650
- unlinkSync(getPidFile(agentId));
704
+ unlinkSync2(getPidFile(agentId));
651
705
  } catch {
652
706
  }
653
707
  };
@@ -747,11 +801,11 @@ function registerConnectCommand(program2) {
747
801
  `/agents/${encodeURIComponent(agentName)}/connection-config`
748
802
  );
749
803
  const agentId = config2.agent_id;
750
- if (!existsSync(getMetaFile(agentId)) && existsSync(getMetaFile(agentName))) {
804
+ if (!existsSync2(getMetaFile(agentId)) && existsSync2(getMetaFile(agentName))) {
751
805
  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);
806
+ const oldPath = join2(getStateDir(), `${agentName}${ext}`);
807
+ const newPath = join2(getStateDir(), `${agentId}${ext}`);
808
+ if (existsSync2(oldPath)) renameSync(oldPath, newPath);
755
809
  }
756
810
  }
757
811
  const existingPid = readPid(agentId);
@@ -788,7 +842,7 @@ function registerConnectCommand(program2) {
788
842
  writeFileSync(getMetaFile(agentId), JSON.stringify(meta, null, 2));
789
843
  const runtimeDisplay = RUNTIME_NAMES[config2.runtime] || config2.runtime;
790
844
  const cwd = process.cwd();
791
- const home = homedir();
845
+ const home = homedir3();
792
846
  const shortLog = logFile.startsWith(home) ? "~" + logFile.slice(home.length) : logFile;
793
847
  console.log("");
794
848
  console.log(` Agent "${config2.agent_name}" is online.`);
@@ -814,7 +868,7 @@ function registerConnectCommand(program2) {
814
868
  try {
815
869
  process.kill(pid, "SIGTERM");
816
870
  try {
817
- unlinkSync(getPidFile(key));
871
+ unlinkSync2(getPidFile(key));
818
872
  } catch {
819
873
  }
820
874
  console.log(`Agent "${agentName}" disconnected.`);
@@ -822,50 +876,10 @@ function registerConnectCommand(program2) {
822
876
  console.error(`Failed to stop process ${pid}.`);
823
877
  }
824
878
  });
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
879
  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
880
  if (options.all) {
867
881
  const stateDir = getStateDir();
868
- const metaFiles = readdirSync(stateDir).filter((f) => f.endsWith(".meta.json"));
882
+ const metaFiles = readdirSync3(stateDir).filter((f) => f.endsWith(".meta.json"));
869
883
  if (metaFiles.length === 0) {
870
884
  console.log("No agents found. Use `bx connect <agent-name>` to connect one.");
871
885
  return;
@@ -900,7 +914,7 @@ function registerConnectCommand(program2) {
900
914
  } catch {
901
915
  }
902
916
  try {
903
- unlinkSync(getPidFile(key));
917
+ unlinkSync2(getPidFile(key));
904
918
  } catch {
905
919
  }
906
920
  }
@@ -938,7 +952,7 @@ function registerConnectCommand(program2) {
938
952
  };
939
953
  writeFileSync(getMetaFile(key), JSON.stringify(newMeta, null, 2));
940
954
  const runtimeDisplay = RUNTIME_NAMES[config2.runtime] || config2.runtime;
941
- const home = homedir();
955
+ const home = homedir3();
942
956
  const shortCwd = meta.cwd.startsWith(home) ? "~" + meta.cwd.slice(home.length) : meta.cwd;
943
957
  const shortLog = logFile.startsWith(home) ? "~" + logFile.slice(home.length) : logFile;
944
958
  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.17",
4
4
  "description": "BoxCrew CLI — manage your agents from the terminal",
5
5
  "type": "module",
6
6
  "bin": {