@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.
- package/dist/index.js +54 -45
- 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
|
|
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/
|
|
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 ||
|
|
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
|
-
|
|
75
|
+
open2(authUrl).catch(() => {
|
|
36
76
|
});
|
|
37
|
-
const rl =
|
|
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!
|
|
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
|
|
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 =
|
|
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("
|
|
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("
|
|
523
|
+
console.error("Connection error:", err.message);
|
|
515
524
|
});
|
|
516
525
|
const shutdown = () => {
|
|
517
526
|
shouldReconnect = false;
|