claude-bridge-cli 1.4.0 → 1.5.1
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/bin/cli.js +28 -11
- package/lib/bridge.js +12 -5
- package/lib/relay-client.js +19 -0
- package/package.json +1 -1
package/bin/cli.js
CHANGED
|
@@ -41,9 +41,10 @@ function relayHttpBase(wsUrl) {
|
|
|
41
41
|
return `${proto}//${u.host}`;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
async function pairInteractive(relayWsUrl, machine) {
|
|
44
|
+
async function pairInteractive(relayWsUrl, machine, ephemeral) {
|
|
45
45
|
const base = relayHttpBase(relayWsUrl);
|
|
46
46
|
process.stdout.write(`[bridge] Starting pair flow for "${machine}" against ${base}\n`);
|
|
47
|
+
if (ephemeral) process.stdout.write(`[bridge] Ephemeral mode — bearer will NOT be saved to disk.\n`);
|
|
47
48
|
|
|
48
49
|
const startResp = await fetch(`${base}/pair-cli/start`, {
|
|
49
50
|
method: "POST",
|
|
@@ -87,13 +88,16 @@ async function pairInteractive(relayWsUrl, machine) {
|
|
|
87
88
|
throw new Error(`pair claim failed (${resp.status}): ${text}`);
|
|
88
89
|
}
|
|
89
90
|
const { bearer, machine: confirmed } = await resp.json();
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
91
|
+
if (ephemeral) {
|
|
92
|
+
console.log(`[bridge] ✓ Paired as "${confirmed}" (in-memory only, nothing written to disk).`);
|
|
93
|
+
} else {
|
|
94
|
+
console.log(`[bridge] ✓ Paired as "${confirmed}". Bearer saved to ${authFilePath()}`);
|
|
95
|
+
const auth = readAuthFile();
|
|
96
|
+
auth.bearers = auth.bearers || {};
|
|
97
|
+
auth.bearers[base] = auth.bearers[base] || {};
|
|
98
|
+
auth.bearers[base][confirmed] = bearer;
|
|
99
|
+
writeAuthFile(auth);
|
|
100
|
+
}
|
|
97
101
|
return bearer;
|
|
98
102
|
}
|
|
99
103
|
throw new Error("pair window expired — re-run to start over");
|
|
@@ -127,6 +131,9 @@ Relay options (connect to a remote relay server):
|
|
|
127
131
|
--token <bearer> Machine bearer (optional — if omitted, the CLI starts
|
|
128
132
|
a browser-OTP pair flow and caches the bearer at
|
|
129
133
|
~/.claude-bridge/auth.json for next time)
|
|
134
|
+
--ephemeral Pair on every start, never read/write auth.json.
|
|
135
|
+
Forces a fresh browser-OTP each launch — use on
|
|
136
|
+
machines you don't want to leave any saved bearer on.
|
|
130
137
|
--cf-id <id> Cloudflare Access Client ID (optional)
|
|
131
138
|
--cf-secret <secret> Cloudflare Access Client Secret (optional)
|
|
132
139
|
|
|
@@ -134,6 +141,7 @@ Environment variables:
|
|
|
134
141
|
All options can be set via env vars with BRIDGE_ prefix:
|
|
135
142
|
BRIDGE_PORT, BRIDGE_HOST, BRIDGE_CWD, BRIDGE_TIMEOUT,
|
|
136
143
|
BRIDGE_RELAY_URL, BRIDGE_MACHINE_NAME, BRIDGE_MACHINE_TOKEN,
|
|
144
|
+
BRIDGE_EPHEMERAL=1 (force fresh pair every start, no saved bearer),
|
|
137
145
|
BRIDGE_CF_ID, BRIDGE_CF_SECRET
|
|
138
146
|
`;
|
|
139
147
|
|
|
@@ -161,6 +169,7 @@ function main() {
|
|
|
161
169
|
"relay-url": { type: "string", default: env("RELAY_URL", "") },
|
|
162
170
|
machine: { type: "string", default: env("MACHINE_NAME", "") },
|
|
163
171
|
token: { type: "string", default: env("MACHINE_TOKEN", "") },
|
|
172
|
+
ephemeral: { type: "boolean", default: env("EPHEMERAL", "") === "1" },
|
|
164
173
|
"cf-id": { type: "string", default: env("CF_ID", "") },
|
|
165
174
|
"cf-secret": { type: "string", default: env("CF_SECRET", "") },
|
|
166
175
|
},
|
|
@@ -177,6 +186,7 @@ function main() {
|
|
|
177
186
|
url: values["relay-url"],
|
|
178
187
|
machine: values.machine,
|
|
179
188
|
token: values.token,
|
|
189
|
+
ephemeral: !!values.ephemeral,
|
|
180
190
|
cfId: values["cf-id"],
|
|
181
191
|
cfSecret: values["cf-secret"],
|
|
182
192
|
} : null,
|
|
@@ -238,15 +248,22 @@ async function run(config) {
|
|
|
238
248
|
console.error("[bridge] ERROR: --machine required when using --relay-url");
|
|
239
249
|
process.exit(1);
|
|
240
250
|
}
|
|
241
|
-
// Resolve a bearer:
|
|
251
|
+
// Resolve a bearer:
|
|
252
|
+
// --token → use it directly (never look at auth.json)
|
|
253
|
+
// --ephemeral → always pair fresh, never read or write auth.json
|
|
254
|
+
// default → saved auth.json → fall back to pair flow on miss
|
|
242
255
|
if (!config.relay.token) {
|
|
243
|
-
const saved =
|
|
256
|
+
const saved = config.relay.ephemeral
|
|
257
|
+
? null
|
|
258
|
+
: lookupSavedBearer(config.relay.url, config.relay.machine);
|
|
244
259
|
if (saved) {
|
|
245
260
|
config.relay.token = saved;
|
|
246
261
|
console.log(`[bridge] Using saved bearer from ${authFilePath()}`);
|
|
247
262
|
} else {
|
|
248
263
|
try {
|
|
249
|
-
config.relay.token = await pairInteractive(
|
|
264
|
+
config.relay.token = await pairInteractive(
|
|
265
|
+
config.relay.url, config.relay.machine, config.relay.ephemeral
|
|
266
|
+
);
|
|
250
267
|
} catch (e) {
|
|
251
268
|
console.error(`[bridge] ERROR: ${e.message}`);
|
|
252
269
|
process.exit(1);
|
package/lib/bridge.js
CHANGED
|
@@ -418,11 +418,18 @@ function askClaude(config, { prompt, session_id, images, cwd, plan_mode, allow_t
|
|
|
418
418
|
const isWin = process.platform === "win32";
|
|
419
419
|
const isCmdShim = isWin && config.claudeBin.endsWith(".cmd");
|
|
420
420
|
|
|
421
|
-
//
|
|
422
|
-
// cmd.exe's ~8K arg limit
|
|
423
|
-
//
|
|
424
|
-
//
|
|
425
|
-
|
|
421
|
+
// Fall back to @file syntax in two cases:
|
|
422
|
+
// 1) Prompt exceeds Windows cmd.exe's ~8K arg limit (threshold 6000).
|
|
423
|
+
// 2) Prompt contains a newline AND we're on Windows with a .cmd shim
|
|
424
|
+
// — cmd.exe parses arg strings line-by-line and silently truncates
|
|
425
|
+
// at the first \n, even with Node.js's proper argv quoting. Without
|
|
426
|
+
// this fallback, any multi-line prompt to Claude on Windows lost
|
|
427
|
+
// everything after line 1, and the model saw a stub message + the
|
|
428
|
+
// header but none of the body. Manifested as "your message got
|
|
429
|
+
// cut off" responses for prompts that LOOK short.
|
|
430
|
+
const useTempFile =
|
|
431
|
+
finalPrompt.length > 6000 ||
|
|
432
|
+
(isWin && isCmdShim && /\r|\n/.test(finalPrompt));
|
|
426
433
|
let tmpFile = null;
|
|
427
434
|
if (useTempFile) {
|
|
428
435
|
tmpFile = path.join(os.tmpdir(), `claude-prompt-${Date.now()}-${Math.random().toString(36).slice(2)}.txt`);
|
package/lib/relay-client.js
CHANGED
|
@@ -46,6 +46,25 @@ function startRelay(config) {
|
|
|
46
46
|
});
|
|
47
47
|
|
|
48
48
|
ws.on("close", (code, reason) => {
|
|
49
|
+
// 4003 = bearer rejected by the relay (verify_machine_token failed).
|
|
50
|
+
// The bearer has been rotated or admin-removed; the cached one in
|
|
51
|
+
// auth.json is useless. Stop the reconnect loop and tell the user
|
|
52
|
+
// exactly how to recover.
|
|
53
|
+
if (code === 4003) {
|
|
54
|
+
const reasonStr = reason && reason.toString ? reason.toString() : "unauthorized";
|
|
55
|
+
console.error("");
|
|
56
|
+
console.error(`[relay] Authorization rejected (${code} ${reasonStr}).`);
|
|
57
|
+
console.error("[relay] The saved bearer no longer matches what the relay expects — it was probably rotated or revoked.");
|
|
58
|
+
console.error("[relay] Recover by deleting the cached auth and re-pairing:");
|
|
59
|
+
if (process.platform === "win32") {
|
|
60
|
+
console.error(" del %USERPROFILE%\\.claude-bridge\\auth.json");
|
|
61
|
+
} else {
|
|
62
|
+
console.error(" rm ~/.claude-bridge/auth.json");
|
|
63
|
+
}
|
|
64
|
+
console.error(` claude-bridge-cli start --relay-url ${config.relay.url} --machine ${config.relay.machine}`);
|
|
65
|
+
console.error("");
|
|
66
|
+
process.exit(2);
|
|
67
|
+
}
|
|
49
68
|
console.log(`[relay] Disconnected (${code}). Reconnecting in ${backoff / 1000}s...`);
|
|
50
69
|
setTimeout(connect, backoff);
|
|
51
70
|
backoff = Math.min(60000, backoff * 2);
|
package/package.json
CHANGED