@boxcrew/cli 0.1.6 → 0.1.7

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 +125 -26
  2. 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
- const child = spawn(claudePath, args, {
478
- stdio: ["ignore", "pipe", "pipe"],
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
- const rl = createInterface3({ input: child.stdout });
483
- rl.on("line", (line) => {
484
- const event = parseStreamJsonLine(line);
485
- if (event && sendToServer) {
486
- sendToServer({ type: "event", messageId, event });
487
- }
488
- });
489
- child.on("exit", (code) => {
490
- if (activeProcess === child) activeProcess = null;
491
- if (sendToServer) {
492
- sendToServer({ type: "event", messageId, event: { kind: "done" } });
493
- }
494
- if (code && code !== 0) {
495
- console.error(`Claude Code exited with code ${code}`);
496
- sendToServer?.({ type: "error", messageId, error: `Claude Code exited with code ${code}` });
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 Claude Code: ${err.message}`);
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
  };
@@ -573,7 +655,7 @@ function registerConnectCommand(program2) {
573
655
  program2.command("_daemon <agent-name>", { hidden: true }).action((agentName) => {
574
656
  runDaemon(agentName);
575
657
  });
576
- program2.command("connect <agent-name>").description("Connect a local Claude Code instance to a BoxCrew agent.").option("--claude-path <path>", "Path to claude CLI binary", "claude").action(async (agentName, options) => {
658
+ 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
659
  const existingPid = readPid(agentName);
578
660
  if (existingPid) {
579
661
  console.log(`Agent "${agentName}" is already connected (PID ${existingPid}).`);
@@ -591,12 +673,29 @@ function registerConnectCommand(program2) {
591
673
  ...process.env,
592
674
  _BX_WS_URL: config2.websocket_url,
593
675
  _BX_CLAUDE_PATH: options.claudePath,
594
- _BX_AGENT_NAME: config2.agent_name
676
+ _BX_AGENT_NAME: config2.agent_name,
677
+ _BX_RUNTIME: config2.runtime
595
678
  }
596
679
  });
597
680
  child.unref();
598
- console.log(`Agent "${config2.agent_name}" is online.`);
599
- console.log(`Logs: ${logFile}`);
681
+ const runtimeNames = {
682
+ "claude-code": "Claude Code",
683
+ "opencode": "OpenCode",
684
+ "openclaw": "OpenClaw"
685
+ };
686
+ const runtimeDisplay = runtimeNames[config2.runtime] || config2.runtime;
687
+ const cwd = process.cwd();
688
+ const home = homedir();
689
+ const shortLog = logFile.startsWith(home) ? "~" + logFile.slice(home.length) : logFile;
690
+ console.log("");
691
+ console.log(` Agent "${config2.agent_name}" is online.`);
692
+ console.log("");
693
+ console.log(` Runtime: ${runtimeDisplay}`);
694
+ console.log(` Directory: ${cwd}`);
695
+ console.log(` Logs: ${shortLog}`);
696
+ console.log("");
697
+ console.log(` To disconnect: npx @boxcrew/cli disconnect ${agentName}`);
698
+ console.log("");
600
699
  });
601
700
  program2.command("disconnect <agent-name>").description("Disconnect a local agent.").action((agentName) => {
602
701
  const pid = readPid(agentName);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@boxcrew/cli",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "BoxCrew CLI — manage your agents from the terminal",
5
5
  "type": "module",
6
6
  "bin": {