@boxcrew/cli 0.1.2 → 0.1.4

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 +41 -105
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5,7 +5,6 @@ import { Command } from "commander";
5
5
 
6
6
  // src/commands/login.ts
7
7
  import { createInterface } from "readline";
8
- import { createServer } from "http";
9
8
  import open from "open";
10
9
 
11
10
  // src/config.ts
@@ -17,38 +16,55 @@ var config = new Conf({
17
16
  }
18
17
  });
19
18
 
19
+ // src/auth.ts
20
+ function getAuthToken() {
21
+ if (process.env.BOXCREW_API_KEY) return process.env.BOXCREW_API_KEY;
22
+ return config.get("apiKey") ?? null;
23
+ }
24
+ function requireAuth() {
25
+ const token = getAuthToken();
26
+ if (!token) {
27
+ console.error(
28
+ "Not authenticated. Run `bx login` or set the BOXCREW_API_KEY environment variable."
29
+ );
30
+ process.exit(1);
31
+ }
32
+ return token;
33
+ }
34
+
20
35
  // src/commands/login.ts
21
36
  var DEFAULT_FRONTEND_URL = "https://boxcrew.ai";
22
- var LOGIN_TIMEOUT_MS = 5 * 60 * 1e3;
23
37
  function registerLoginCommand(program2) {
24
38
  program2.command("login").description("Authenticate with BoxCrew").option("--api-url <url>", "BoxCrew API URL").option(
25
39
  "--frontend-url <url>",
26
40
  "BoxCrew frontend URL",
27
41
  process.env.BOXCREW_FRONTEND_URL || DEFAULT_FRONTEND_URL
28
- ).option("--paste", "Use paste-based login (for headless environments)").action(
42
+ ).action(
29
43
  async (options) => {
30
44
  if (options.apiUrl) {
31
45
  config.set("apiUrl", options.apiUrl);
32
46
  }
33
- if (options.paste) {
34
- await pasteLogin();
35
- } else {
36
- await browserLogin(options.frontendUrl);
37
- }
47
+ await ensureLoggedIn(options.frontendUrl);
38
48
  }
39
49
  );
40
50
  }
41
- async function pasteLogin() {
42
- console.log(
43
- "Create an API key in the BoxCrew dashboard (Settings > API Keys),"
44
- );
45
- console.log("then paste it below.\n");
51
+ async function ensureLoggedIn(frontendUrl = DEFAULT_FRONTEND_URL) {
52
+ if (getAuthToken()) {
53
+ console.log("Already authenticated.");
54
+ return;
55
+ }
56
+ const authUrl = `${frontendUrl}/cli-auth`;
57
+ console.log("Opening browser to authenticate...");
58
+ console.log(`If the browser doesn't open, visit: ${authUrl}
59
+ `);
60
+ open(authUrl).catch(() => {
61
+ });
46
62
  const rl = createInterface({
47
63
  input: process.stdin,
48
64
  output: process.stdout
49
65
  });
50
66
  const apiKey = await new Promise((resolve) => {
51
- rl.question("API key: ", (answer) => {
67
+ rl.question("Paste your API key: ", (answer) => {
52
68
  rl.close();
53
69
  resolve(answer.trim());
54
70
  });
@@ -58,63 +74,7 @@ async function pasteLogin() {
58
74
  process.exit(1);
59
75
  }
60
76
  config.set("apiKey", apiKey);
61
- console.log("\nAuthenticated successfully! Credentials stored.");
62
- console.log(`API URL: ${config.get("apiUrl")}`);
63
- }
64
- async function browserLogin(frontendUrl) {
65
- return new Promise((resolve, reject) => {
66
- const server = createServer((req, res) => {
67
- const url = new URL(req.url || "/", `http://localhost`);
68
- res.setHeader("Access-Control-Allow-Origin", "*");
69
- res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
70
- if (req.method === "OPTIONS") {
71
- res.writeHead(204);
72
- res.end();
73
- return;
74
- }
75
- if (url.pathname === "/callback") {
76
- const key = url.searchParams.get("key");
77
- if (key && key.startsWith("bxk_")) {
78
- config.set("apiKey", key);
79
- res.writeHead(200, { "Content-Type": "text/plain" });
80
- res.end("ok");
81
- console.log("\nAuthenticated successfully! Credentials stored.");
82
- console.log(`API URL: ${config.get("apiUrl")}`);
83
- server.close();
84
- clearTimeout(timeout);
85
- resolve();
86
- } else {
87
- res.writeHead(400, { "Content-Type": "text/plain" });
88
- res.end("Invalid key");
89
- }
90
- } else {
91
- res.writeHead(404, { "Content-Type": "text/plain" });
92
- res.end("Not found");
93
- }
94
- });
95
- server.listen(0, "127.0.0.1", () => {
96
- const addr = server.address();
97
- if (!addr || typeof addr === "string") {
98
- reject(new Error("Failed to start local server"));
99
- return;
100
- }
101
- const port = addr.port;
102
- const authUrl = `${frontendUrl}/cli-auth?port=${port}`;
103
- console.log("Opening browser to authenticate...");
104
- console.log(`If the browser doesn't open, visit: ${authUrl}
105
- `);
106
- console.log("Waiting for authentication...");
107
- open(authUrl).catch(() => {
108
- });
109
- });
110
- const timeout = setTimeout(() => {
111
- console.error(
112
- '\nLogin timed out. Use "bx login --paste" to authenticate manually.'
113
- );
114
- server.close();
115
- process.exit(1);
116
- }, LOGIN_TIMEOUT_MS);
117
- });
77
+ console.log("Authenticated successfully!\n");
118
78
  }
119
79
 
120
80
  // src/commands/logout.ts
@@ -125,22 +85,6 @@ function registerLogoutCommand(program2) {
125
85
  });
126
86
  }
127
87
 
128
- // src/auth.ts
129
- function getAuthToken() {
130
- if (process.env.BOXCREW_API_KEY) return process.env.BOXCREW_API_KEY;
131
- return config.get("apiKey") ?? null;
132
- }
133
- function requireAuth() {
134
- const token = getAuthToken();
135
- if (!token) {
136
- console.error(
137
- "Not authenticated. Run `bx login` or set the BOXCREW_API_KEY environment variable."
138
- );
139
- process.exit(1);
140
- }
141
- return token;
142
- }
143
-
144
88
  // src/client.ts
145
89
  function getBaseUrl() {
146
90
  return process.env.BOXCREW_API_URL || config.get("apiUrl");
@@ -463,16 +407,18 @@ function parseStreamJsonLine(line) {
463
407
  return null;
464
408
  }
465
409
  function registerConnectCommand(program2) {
466
- program2.command("connect <agent-name>").description(
467
- "Connect a local Claude Code instance to a BoxCrew agent.\nRequires authentication \u2014 run `bx login` first."
468
- ).option("--claude-path <path>", "Path to claude CLI binary", "claude").action(async (agentName, options) => {
469
- console.log(`Fetching connection config for agent "${agentName}"...`);
410
+ 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").option(
411
+ "--frontend-url <url>",
412
+ "BoxCrew frontend URL",
413
+ process.env.BOXCREW_FRONTEND_URL || "https://boxcrew.ai"
414
+ ).action(async (agentName, options) => {
415
+ if (!getAuthToken()) {
416
+ await ensureLoggedIn(options.frontendUrl);
417
+ }
470
418
  const config2 = await apiFetchJson(
471
419
  `/agents/${encodeURIComponent(agentName)}/connection-config`
472
420
  );
473
421
  const wsUrl = config2.websocket_url;
474
- console.log(`Agent: ${config2.agent_name} (${config2.agent_id.slice(0, 8)}...)`);
475
- console.log(`Proxy: ${config2.proxy_base_url}`);
476
422
  const claudePath = options.claudePath;
477
423
  let activeProcess = null;
478
424
  let sendToServer = null;
@@ -484,8 +430,6 @@ function registerConnectCommand(program2) {
484
430
  activeProcess = null;
485
431
  }
486
432
  const { messageId, message, sessionId } = msg;
487
- console.log(`
488
- Chat ${messageId.slice(0, 8)}: ${message.slice(0, 120)}${message.length > 120 ? "..." : ""}`);
489
433
  const args = ["-p", message, "--output-format", "stream-json", "--verbose"];
490
434
  if (sessionId) args.push("--resume", sessionId);
491
435
  const childEnv = { ...process.env };
@@ -502,12 +446,6 @@ Chat ${messageId.slice(0, 8)}: ${message.slice(0, 120)}${message.length > 120 ?
502
446
  sendToServer({ type: "event", messageId, event });
503
447
  }
504
448
  });
505
- if (child.stderr) {
506
- const stderrRl = createInterface2({ input: child.stderr });
507
- stderrRl.on("line", (line) => {
508
- if (line.trim()) console.error(` [claude] ${line}`);
509
- });
510
- }
511
449
  child.on("exit", (code) => {
512
450
  if (activeProcess === child) activeProcess = null;
513
451
  if (sendToServer) {
@@ -524,7 +462,6 @@ Chat ${messageId.slice(0, 8)}: ${message.slice(0, 120)}${message.length > 120 ?
524
462
  });
525
463
  };
526
464
  const connect = () => {
527
- console.log("Connecting to BoxCrew...");
528
465
  const ws = new WebSocket(wsUrl);
529
466
  const send = (msg) => {
530
467
  if (ws.readyState === WebSocket.OPEN) {
@@ -534,7 +471,7 @@ Chat ${messageId.slice(0, 8)}: ${message.slice(0, 120)}${message.length > 120 ?
534
471
  ws.on("open", () => {
535
472
  reconnectAttempt = 0;
536
473
  sendToServer = send;
537
- console.log("Connected. Waiting for messages...");
474
+ console.log(`Agent "${config2.agent_name}" is online. Press Ctrl+C to disconnect.`);
538
475
  });
539
476
  ws.on("message", (data) => {
540
477
  let msg;
@@ -548,7 +485,6 @@ Chat ${messageId.slice(0, 8)}: ${message.slice(0, 120)}${message.length > 120 ?
548
485
  return;
549
486
  }
550
487
  if (msg.type === "stop" && activeProcess) {
551
- console.log(`Stopping chat ${msg.messageId.slice(0, 8)}...`);
552
488
  activeProcess.kill("SIGINT");
553
489
  return;
554
490
  }
@@ -573,7 +509,7 @@ Chat ${messageId.slice(0, 8)}: ${message.slice(0, 120)}${message.length > 120 ?
573
509
  }
574
510
  });
575
511
  ws.on("error", (err) => {
576
- console.error("WebSocket error:", err.message);
512
+ console.error("Connection error:", err.message);
577
513
  });
578
514
  const shutdown = () => {
579
515
  shouldReconnect = false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@boxcrew/cli",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "BoxCrew CLI — manage your agents from the terminal",
5
5
  "type": "module",
6
6
  "bin": {