@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.
- package/dist/index.js +126 -109
- 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
|
-
|
|
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
|
|
171
|
-
|
|
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,
|
|
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
|
-
|
|
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 (!
|
|
807
|
+
if (!existsSync2(getMetaFile(agentId)) && existsSync2(getMetaFile(agentName))) {
|
|
751
808
|
for (const ext of [".meta.json", ".pid", ".log"]) {
|
|
752
|
-
const oldPath =
|
|
753
|
-
const newPath =
|
|
754
|
-
if (
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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("");
|