@boxcrew/cli 0.1.3 → 0.1.5

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 +54 -45
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -4,8 +4,8 @@
4
4
  import { Command } from "commander";
5
5
 
6
6
  // src/commands/login.ts
7
- import { createInterface } from "readline";
8
- import open from "open";
7
+ import { createInterface as createInterface2 } from "readline";
8
+ import open2 from "open";
9
9
 
10
10
  // src/config.ts
11
11
  import Conf from "conf";
@@ -16,25 +16,65 @@ var config = new Conf({
16
16
  }
17
17
  });
18
18
 
19
- // src/commands/login.ts
19
+ // src/auth.ts
20
+ import { createInterface } from "readline";
21
+ import open from "open";
20
22
  var DEFAULT_FRONTEND_URL = "https://boxcrew.ai";
23
+ function getAuthToken() {
24
+ if (process.env.BOXCREW_API_KEY) return process.env.BOXCREW_API_KEY;
25
+ return config.get("apiKey") ?? null;
26
+ }
27
+ async function requireAuth() {
28
+ const token = getAuthToken();
29
+ if (token) return token;
30
+ const frontendUrl = process.env.BOXCREW_FRONTEND_URL || DEFAULT_FRONTEND_URL;
31
+ const authUrl = `${frontendUrl}/cli-auth`;
32
+ console.log("Not authenticated. Opening browser to log in...");
33
+ console.log(`If the browser doesn't open, visit: ${authUrl}
34
+ `);
35
+ open(authUrl).catch(() => {
36
+ });
37
+ const rl = createInterface({
38
+ input: process.stdin,
39
+ output: process.stdout
40
+ });
41
+ const apiKey = await new Promise((resolve) => {
42
+ rl.question("Paste your API key: ", (answer) => {
43
+ rl.close();
44
+ resolve(answer.trim());
45
+ });
46
+ });
47
+ if (!apiKey.startsWith("bxk_")) {
48
+ console.error('Invalid API key. Keys should start with "bxk_".');
49
+ process.exit(1);
50
+ }
51
+ config.set("apiKey", apiKey);
52
+ console.log("Authenticated successfully!\n");
53
+ return apiKey;
54
+ }
55
+
56
+ // src/commands/login.ts
57
+ var DEFAULT_FRONTEND_URL2 = "https://boxcrew.ai";
21
58
  function registerLoginCommand(program2) {
22
- program2.command("login").description("Authenticate with BoxCrew").option("--api-url <url>", "BoxCrew API URL").option(
59
+ program2.command("login").description("Authenticate with BoxCrew (or re-authenticate)").option("--api-url <url>", "BoxCrew API URL").option(
23
60
  "--frontend-url <url>",
24
61
  "BoxCrew frontend URL",
25
- process.env.BOXCREW_FRONTEND_URL || DEFAULT_FRONTEND_URL
62
+ process.env.BOXCREW_FRONTEND_URL || DEFAULT_FRONTEND_URL2
26
63
  ).action(
27
64
  async (options) => {
28
65
  if (options.apiUrl) {
29
66
  config.set("apiUrl", options.apiUrl);
30
67
  }
68
+ if (getAuthToken()) {
69
+ console.log("Already authenticated. To re-authenticate, continue below.\n");
70
+ }
31
71
  const authUrl = `${options.frontendUrl}/cli-auth`;
32
72
  console.log("Opening browser to authenticate...");
33
73
  console.log(`If the browser doesn't open, visit: ${authUrl}
34
74
  `);
35
- open(authUrl).catch(() => {
75
+ open2(authUrl).catch(() => {
36
76
  });
37
- const rl = createInterface({
77
+ const rl = createInterface2({
38
78
  input: process.stdin,
39
79
  output: process.stdout
40
80
  });
@@ -49,7 +89,7 @@ function registerLoginCommand(program2) {
49
89
  process.exit(1);
50
90
  }
51
91
  config.set("apiKey", apiKey);
52
- console.log("\nAuthenticated successfully! Credentials stored.");
92
+ console.log("\nAuthenticated successfully!");
53
93
  console.log(`API URL: ${config.get("apiUrl")}`);
54
94
  }
55
95
  );
@@ -63,28 +103,12 @@ function registerLogoutCommand(program2) {
63
103
  });
64
104
  }
65
105
 
66
- // src/auth.ts
67
- function getAuthToken() {
68
- if (process.env.BOXCREW_API_KEY) return process.env.BOXCREW_API_KEY;
69
- return config.get("apiKey") ?? null;
70
- }
71
- function requireAuth() {
72
- const token = getAuthToken();
73
- if (!token) {
74
- console.error(
75
- "Not authenticated. Run `bx login` or set the BOXCREW_API_KEY environment variable."
76
- );
77
- process.exit(1);
78
- }
79
- return token;
80
- }
81
-
82
106
  // src/client.ts
83
107
  function getBaseUrl() {
84
108
  return process.env.BOXCREW_API_URL || config.get("apiUrl");
85
109
  }
86
110
  async function apiFetch(path, options = {}) {
87
- const token = requireAuth();
111
+ const token = await requireAuth();
88
112
  const baseUrl = getBaseUrl();
89
113
  const url = `${baseUrl}${path}`;
90
114
  const headers = {
@@ -334,7 +358,7 @@ function registerApiCommand(program2) {
334
358
 
335
359
  // src/commands/connect.ts
336
360
  import { spawn } from "child_process";
337
- import { createInterface as createInterface2 } from "readline";
361
+ import { createInterface as createInterface3 } from "readline";
338
362
  import WebSocket from "ws";
339
363
  var RECONNECT_BASE_MS = 1e3;
340
364
  var RECONNECT_MAX_MS = 3e4;
@@ -401,16 +425,11 @@ function parseStreamJsonLine(line) {
401
425
  return null;
402
426
  }
403
427
  function registerConnectCommand(program2) {
404
- program2.command("connect <agent-name>").description(
405
- "Connect a local Claude Code instance to a BoxCrew agent.\nRequires authentication \u2014 run `bx login` first."
406
- ).option("--claude-path <path>", "Path to claude CLI binary", "claude").action(async (agentName, options) => {
407
- console.log(`Fetching connection config for agent "${agentName}"...`);
428
+ 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) => {
408
429
  const config2 = await apiFetchJson(
409
430
  `/agents/${encodeURIComponent(agentName)}/connection-config`
410
431
  );
411
432
  const wsUrl = config2.websocket_url;
412
- console.log(`Agent: ${config2.agent_name} (${config2.agent_id.slice(0, 8)}...)`);
413
- console.log(`Proxy: ${config2.proxy_base_url}`);
414
433
  const claudePath = options.claudePath;
415
434
  let activeProcess = null;
416
435
  let sendToServer = null;
@@ -422,8 +441,6 @@ function registerConnectCommand(program2) {
422
441
  activeProcess = null;
423
442
  }
424
443
  const { messageId, message, sessionId } = msg;
425
- console.log(`
426
- Chat ${messageId.slice(0, 8)}: ${message.slice(0, 120)}${message.length > 120 ? "..." : ""}`);
427
444
  const args = ["-p", message, "--output-format", "stream-json", "--verbose"];
428
445
  if (sessionId) args.push("--resume", sessionId);
429
446
  const childEnv = { ...process.env };
@@ -433,19 +450,13 @@ Chat ${messageId.slice(0, 8)}: ${message.slice(0, 120)}${message.length > 120 ?
433
450
  env: childEnv
434
451
  });
435
452
  activeProcess = child;
436
- const rl = createInterface2({ input: child.stdout });
453
+ const rl = createInterface3({ input: child.stdout });
437
454
  rl.on("line", (line) => {
438
455
  const event = parseStreamJsonLine(line);
439
456
  if (event && sendToServer) {
440
457
  sendToServer({ type: "event", messageId, event });
441
458
  }
442
459
  });
443
- if (child.stderr) {
444
- const stderrRl = createInterface2({ input: child.stderr });
445
- stderrRl.on("line", (line) => {
446
- if (line.trim()) console.error(` [claude] ${line}`);
447
- });
448
- }
449
460
  child.on("exit", (code) => {
450
461
  if (activeProcess === child) activeProcess = null;
451
462
  if (sendToServer) {
@@ -462,7 +473,6 @@ Chat ${messageId.slice(0, 8)}: ${message.slice(0, 120)}${message.length > 120 ?
462
473
  });
463
474
  };
464
475
  const connect = () => {
465
- console.log("Connecting to BoxCrew...");
466
476
  const ws = new WebSocket(wsUrl);
467
477
  const send = (msg) => {
468
478
  if (ws.readyState === WebSocket.OPEN) {
@@ -472,7 +482,7 @@ Chat ${messageId.slice(0, 8)}: ${message.slice(0, 120)}${message.length > 120 ?
472
482
  ws.on("open", () => {
473
483
  reconnectAttempt = 0;
474
484
  sendToServer = send;
475
- console.log("Connected. Waiting for messages...");
485
+ console.log(`Agent "${config2.agent_name}" is online. Press Ctrl+C to disconnect.`);
476
486
  });
477
487
  ws.on("message", (data) => {
478
488
  let msg;
@@ -486,7 +496,6 @@ Chat ${messageId.slice(0, 8)}: ${message.slice(0, 120)}${message.length > 120 ?
486
496
  return;
487
497
  }
488
498
  if (msg.type === "stop" && activeProcess) {
489
- console.log(`Stopping chat ${msg.messageId.slice(0, 8)}...`);
490
499
  activeProcess.kill("SIGINT");
491
500
  return;
492
501
  }
@@ -511,7 +520,7 @@ Chat ${messageId.slice(0, 8)}: ${message.slice(0, 120)}${message.length > 120 ?
511
520
  }
512
521
  });
513
522
  ws.on("error", (err) => {
514
- console.error("WebSocket error:", err.message);
523
+ console.error("Connection error:", err.message);
515
524
  });
516
525
  const shutdown = () => {
517
526
  shouldReconnect = false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@boxcrew/cli",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "BoxCrew CLI — manage your agents from the terminal",
5
5
  "type": "module",
6
6
  "bin": {