@boxcrew/cli 0.1.6 → 0.1.8
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 +132 -27
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -451,10 +451,55 @@ function parseStreamJsonLine(line) {
|
|
|
451
451
|
}
|
|
452
452
|
return null;
|
|
453
453
|
}
|
|
454
|
+
function parseOpenCodeLine(line) {
|
|
455
|
+
let event;
|
|
456
|
+
try {
|
|
457
|
+
event = JSON.parse(line);
|
|
458
|
+
} catch {
|
|
459
|
+
return null;
|
|
460
|
+
}
|
|
461
|
+
if (event.type === "text" && event.part?.type === "text" && event.part.text) {
|
|
462
|
+
return { kind: "text", text: event.part.text };
|
|
463
|
+
}
|
|
464
|
+
if (event.type) {
|
|
465
|
+
return { kind: "raw", raw: event, rawType: event.type };
|
|
466
|
+
}
|
|
467
|
+
return null;
|
|
468
|
+
}
|
|
469
|
+
function parseOpenClawOutput(stdout) {
|
|
470
|
+
try {
|
|
471
|
+
const firstBrace = stdout.indexOf("{");
|
|
472
|
+
if (firstBrace === -1) {
|
|
473
|
+
return [{ kind: "text", text: stdout.trim() || "No response" }];
|
|
474
|
+
}
|
|
475
|
+
const response = JSON.parse(stdout.slice(firstBrace));
|
|
476
|
+
const events = [];
|
|
477
|
+
events.push({ kind: "raw", raw: response, rawType: "openclaw_response" });
|
|
478
|
+
const sessionId = response.meta?.agentMeta?.sessionId;
|
|
479
|
+
if (sessionId) {
|
|
480
|
+
events.push({ kind: "session_id", sessionId });
|
|
481
|
+
}
|
|
482
|
+
const payloads = response.payloads;
|
|
483
|
+
if (Array.isArray(payloads)) {
|
|
484
|
+
const texts = payloads.map((p) => p.text).filter((t) => typeof t === "string" && t.length > 0);
|
|
485
|
+
if (texts.length > 0) {
|
|
486
|
+
events.push({ kind: "text", text: texts.join("\n\n") });
|
|
487
|
+
} else {
|
|
488
|
+
events.push({ kind: "text", text: "No response" });
|
|
489
|
+
}
|
|
490
|
+
} else {
|
|
491
|
+
events.push({ kind: "text", text: "No response" });
|
|
492
|
+
}
|
|
493
|
+
return events;
|
|
494
|
+
} catch {
|
|
495
|
+
return [{ kind: "text", text: stdout.trim() || "No response" }];
|
|
496
|
+
}
|
|
497
|
+
}
|
|
454
498
|
function runDaemon(agentName) {
|
|
455
499
|
const wsUrl = process.env._BX_WS_URL;
|
|
456
500
|
const claudePath = process.env._BX_CLAUDE_PATH || "claude";
|
|
457
501
|
const agentDisplayName = process.env._BX_AGENT_NAME || agentName;
|
|
502
|
+
const runtime = process.env._BX_RUNTIME || "claude-code";
|
|
458
503
|
if (!wsUrl) {
|
|
459
504
|
console.error("Missing _BX_WS_URL");
|
|
460
505
|
process.exit(1);
|
|
@@ -470,34 +515,71 @@ function runDaemon(agentName) {
|
|
|
470
515
|
activeProcess = null;
|
|
471
516
|
}
|
|
472
517
|
const { messageId, message, sessionId } = msg;
|
|
473
|
-
const args = ["-p", message, "--output-format", "stream-json", "--verbose"];
|
|
474
|
-
if (sessionId) args.push("--resume", sessionId);
|
|
475
518
|
const childEnv = { ...process.env };
|
|
476
519
|
delete childEnv.CLAUDECODE;
|
|
477
|
-
|
|
478
|
-
|
|
520
|
+
let cmd;
|
|
521
|
+
let args;
|
|
522
|
+
if (runtime === "opencode") {
|
|
523
|
+
cmd = "opencode";
|
|
524
|
+
args = ["run", "--format", "json"];
|
|
525
|
+
} else if (runtime === "openclaw") {
|
|
526
|
+
cmd = "openclaw";
|
|
527
|
+
args = ["agent", "--message", message, "--json", "--agent", "main", "--timeout", "300"];
|
|
528
|
+
} else {
|
|
529
|
+
cmd = claudePath;
|
|
530
|
+
args = ["-p", message, "--output-format", "stream-json", "--verbose"];
|
|
531
|
+
if (sessionId) args.push("--resume", sessionId);
|
|
532
|
+
}
|
|
533
|
+
const child = spawn(cmd, args, {
|
|
534
|
+
stdio: [runtime === "opencode" ? "pipe" : "ignore", "pipe", "pipe"],
|
|
479
535
|
env: childEnv
|
|
480
536
|
});
|
|
481
537
|
activeProcess = child;
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
538
|
+
if (runtime === "opencode" && child.stdin) {
|
|
539
|
+
child.stdin.write(message);
|
|
540
|
+
child.stdin.end();
|
|
541
|
+
}
|
|
542
|
+
if (runtime === "openclaw") {
|
|
543
|
+
let stdout = "";
|
|
544
|
+
child.stdout.on("data", (chunk) => {
|
|
545
|
+
stdout += chunk.toString();
|
|
546
|
+
});
|
|
547
|
+
child.on("exit", (code) => {
|
|
548
|
+
if (activeProcess === child) activeProcess = null;
|
|
549
|
+
if (sendToServer) {
|
|
550
|
+
const events = parseOpenClawOutput(stdout);
|
|
551
|
+
for (const event of events) {
|
|
552
|
+
sendToServer({ type: "event", messageId, event });
|
|
553
|
+
}
|
|
554
|
+
sendToServer({ type: "event", messageId, event: { kind: "done" } });
|
|
555
|
+
}
|
|
556
|
+
if (code && code !== 0) {
|
|
557
|
+
console.error(`OpenClaw exited with code ${code}`);
|
|
558
|
+
sendToServer?.({ type: "error", messageId, error: `OpenClaw exited with code ${code}` });
|
|
559
|
+
}
|
|
560
|
+
});
|
|
561
|
+
} else {
|
|
562
|
+
const parseLine = runtime === "opencode" ? parseOpenCodeLine : parseStreamJsonLine;
|
|
563
|
+
const rl = createInterface3({ input: child.stdout });
|
|
564
|
+
rl.on("line", (line) => {
|
|
565
|
+
const event = parseLine(line);
|
|
566
|
+
if (event && sendToServer) {
|
|
567
|
+
sendToServer({ type: "event", messageId, event });
|
|
568
|
+
}
|
|
569
|
+
});
|
|
570
|
+
child.on("exit", (code) => {
|
|
571
|
+
if (activeProcess === child) activeProcess = null;
|
|
572
|
+
if (sendToServer) {
|
|
573
|
+
sendToServer({ type: "event", messageId, event: { kind: "done" } });
|
|
574
|
+
}
|
|
575
|
+
if (code && code !== 0) {
|
|
576
|
+
console.error(`${runtime} exited with code ${code}`);
|
|
577
|
+
sendToServer?.({ type: "error", messageId, error: `${runtime} exited with code ${code}` });
|
|
578
|
+
}
|
|
579
|
+
});
|
|
580
|
+
}
|
|
499
581
|
child.on("error", (err) => {
|
|
500
|
-
console.error(`Failed to spawn
|
|
582
|
+
console.error(`Failed to spawn ${runtime}: ${err.message}`);
|
|
501
583
|
sendToServer?.({ type: "error", messageId, error: `Failed to spawn: ${err.message}` });
|
|
502
584
|
});
|
|
503
585
|
};
|
|
@@ -538,12 +620,18 @@ function runDaemon(agentName) {
|
|
|
538
620
|
handleChat(msg);
|
|
539
621
|
}
|
|
540
622
|
});
|
|
541
|
-
ws.on("close", () => {
|
|
623
|
+
ws.on("close", (code, reason) => {
|
|
542
624
|
sendToServer = null;
|
|
543
625
|
if (activeProcess) {
|
|
544
626
|
activeProcess.kill("SIGTERM");
|
|
545
627
|
activeProcess = null;
|
|
546
628
|
}
|
|
629
|
+
if (code === 4006) {
|
|
630
|
+
console.log(`[${(/* @__PURE__ */ new Date()).toISOString()}] Agent was deleted. Shutting down.`);
|
|
631
|
+
cleanup();
|
|
632
|
+
process.exit(0);
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
547
635
|
if (shouldReconnect) {
|
|
548
636
|
const delay = Math.min(
|
|
549
637
|
RECONNECT_BASE_MS * Math.pow(2, reconnectAttempt),
|
|
@@ -573,7 +661,7 @@ function registerConnectCommand(program2) {
|
|
|
573
661
|
program2.command("_daemon <agent-name>", { hidden: true }).action((agentName) => {
|
|
574
662
|
runDaemon(agentName);
|
|
575
663
|
});
|
|
576
|
-
program2.command("connect <agent-name>").description("Connect a local
|
|
664
|
+
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) => {
|
|
577
665
|
const existingPid = readPid(agentName);
|
|
578
666
|
if (existingPid) {
|
|
579
667
|
console.log(`Agent "${agentName}" is already connected (PID ${existingPid}).`);
|
|
@@ -591,12 +679,29 @@ function registerConnectCommand(program2) {
|
|
|
591
679
|
...process.env,
|
|
592
680
|
_BX_WS_URL: config2.websocket_url,
|
|
593
681
|
_BX_CLAUDE_PATH: options.claudePath,
|
|
594
|
-
_BX_AGENT_NAME: config2.agent_name
|
|
682
|
+
_BX_AGENT_NAME: config2.agent_name,
|
|
683
|
+
_BX_RUNTIME: config2.runtime
|
|
595
684
|
}
|
|
596
685
|
});
|
|
597
686
|
child.unref();
|
|
598
|
-
|
|
599
|
-
|
|
687
|
+
const runtimeNames = {
|
|
688
|
+
"claude-code": "Claude Code",
|
|
689
|
+
"opencode": "OpenCode",
|
|
690
|
+
"openclaw": "OpenClaw"
|
|
691
|
+
};
|
|
692
|
+
const runtimeDisplay = runtimeNames[config2.runtime] || config2.runtime;
|
|
693
|
+
const cwd = process.cwd();
|
|
694
|
+
const home = homedir();
|
|
695
|
+
const shortLog = logFile.startsWith(home) ? "~" + logFile.slice(home.length) : logFile;
|
|
696
|
+
console.log("");
|
|
697
|
+
console.log(` Agent "${config2.agent_name}" is online.`);
|
|
698
|
+
console.log("");
|
|
699
|
+
console.log(` Runtime: ${runtimeDisplay}`);
|
|
700
|
+
console.log(` Directory: ${cwd}`);
|
|
701
|
+
console.log(` Logs: ${shortLog}`);
|
|
702
|
+
console.log("");
|
|
703
|
+
console.log(` To disconnect: npx @boxcrew/cli disconnect ${agentName}`);
|
|
704
|
+
console.log("");
|
|
600
705
|
});
|
|
601
706
|
program2.command("disconnect <agent-name>").description("Disconnect a local agent.").action((agentName) => {
|
|
602
707
|
const pid = readPid(agentName);
|