@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.
- package/dist/index.js +41 -105
- 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
|
-
).
|
|
42
|
+
).action(
|
|
29
43
|
async (options) => {
|
|
30
44
|
if (options.apiUrl) {
|
|
31
45
|
config.set("apiUrl", options.apiUrl);
|
|
32
46
|
}
|
|
33
|
-
|
|
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
|
|
42
|
-
|
|
43
|
-
"
|
|
44
|
-
|
|
45
|
-
|
|
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("
|
|
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
|
-
"
|
|
468
|
-
|
|
469
|
-
|
|
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("
|
|
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("
|
|
512
|
+
console.error("Connection error:", err.message);
|
|
577
513
|
});
|
|
578
514
|
const shutdown = () => {
|
|
579
515
|
shouldReconnect = false;
|