@miraland-labs/conduit-bridge 0.14.3 → 0.14.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/cli.js +17 -13
- package/dist/client.js +6 -2
- package/dist/ops.js +40 -1
- package/dist/preflight.js +3 -1
- package/dist/version.js +13 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -4,7 +4,7 @@ import { resolve, dirname, join as pathJoin } from "node:path";
|
|
|
4
4
|
import { hostname, homedir, platform, userInfo } from "node:os";
|
|
5
5
|
import { spawn } from "node:child_process";
|
|
6
6
|
import { createInterface } from "node:readline/promises";
|
|
7
|
-
import { chmodSync, cpSync, existsSync, mkdirSync,
|
|
7
|
+
import { chmodSync, cpSync, existsSync, mkdirSync, readdirSync } from "node:fs";
|
|
8
8
|
import { stdin as input, stdout as output } from "node:process";
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
10
10
|
import { ConduitClient, ConduitRequestError } from "./client.js";
|
|
@@ -20,17 +20,8 @@ import { loadOpsEnv, OPS_VERBS, runOps } from "./ops.js";
|
|
|
20
20
|
import { pumpExecutionSlots, renewLeases } from "./execution.js";
|
|
21
21
|
import { installRunnerService, uninstallRunnerService } from "./service.js";
|
|
22
22
|
import { BRIDGE_PROTOCOL_VERSION, cachedBridgePreflight, unavailableWorkspacePreflight } from "./preflight.js";
|
|
23
|
+
import { bridgeVersion } from "./version.js";
|
|
23
24
|
const [command] = process.argv.slice(2);
|
|
24
|
-
/** Read our own package version so every runner start logs exactly which build is live. */
|
|
25
|
-
function bridgeVersion() {
|
|
26
|
-
try {
|
|
27
|
-
const packagePath = pathJoin(dirname(fileURLToPath(import.meta.url)), "..", "package.json");
|
|
28
|
-
return JSON.parse(readFileSync(packagePath, "utf8")).version ?? "unknown";
|
|
29
|
-
}
|
|
30
|
-
catch {
|
|
31
|
-
return "unknown";
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
25
|
/** Install-free form shown in help/output so clean laptops never need `conduit` on PATH. */
|
|
35
26
|
const BRIDGE_NPX = "npx @miraland-labs/conduit-bridge";
|
|
36
27
|
function bridgeUsage(...args) {
|
|
@@ -305,7 +296,7 @@ async function installService() {
|
|
|
305
296
|
agentTimeoutMinutes: values["agent-timeout-minutes"],
|
|
306
297
|
ensureCheckout: values["ensure-checkout"],
|
|
307
298
|
});
|
|
308
|
-
console.log(`Installed Conduit runner service (${result.platform}): ${result.path}`);
|
|
299
|
+
console.log(`Installed Conduit Bridge v${bridgeVersion()} (protocol ${BRIDGE_PROTOCOL_VERSION}) runner service (${result.platform}): ${result.path}`);
|
|
309
300
|
console.log(`Online lanes: ${onlineDriverIds(config).join(", ")}. Toggle with \`${bridgeUsage("drivers", "online|offline", "…")}\`.`);
|
|
310
301
|
console.log("The runner keeps executing after this terminal closes. Laptop sleep still pauses work.");
|
|
311
302
|
}
|
|
@@ -475,6 +466,11 @@ async function runner() {
|
|
|
475
466
|
? await cachedBridgePreflight({ config, workspace, brief: currentBrief ?? undefined })
|
|
476
467
|
: unavailableWorkspacePreflight({ config });
|
|
477
468
|
const hb = await heartbeat(client, config, currentBrief, preflight);
|
|
469
|
+
if (hb.staleBridge) {
|
|
470
|
+
console.error(`Bridge update required: running protocol ${BRIDGE_PROTOCOL_VERSION}; control plane requires protocol ${hb.requiredProtocol ?? "unknown"} (minimum Bridge ${hb.minimumVersion ?? "unknown"}). Reinstall the latest Bridge, then restart this runner.`);
|
|
471
|
+
await new Promise((resolveSleep) => setTimeout(resolveSleep, Math.max(intervalMs, 60_000)));
|
|
472
|
+
continue;
|
|
473
|
+
}
|
|
478
474
|
const applied = await maybeApplyOnShiftIntent({
|
|
479
475
|
client,
|
|
480
476
|
currentWorkspace: workspace,
|
|
@@ -535,10 +531,18 @@ async function heartbeat(client, config, brief, preflight) {
|
|
|
535
531
|
preflight,
|
|
536
532
|
...(brief ? { workspace_brief: brief } : {}),
|
|
537
533
|
}) });
|
|
534
|
+
const compatibility = response.bridge_compatibility && typeof response.bridge_compatibility === "object"
|
|
535
|
+
? response.bridge_compatibility
|
|
536
|
+
: null;
|
|
538
537
|
const onShift = response.on_shift && typeof response.on_shift === "object"
|
|
539
538
|
? response.on_shift
|
|
540
539
|
: null;
|
|
541
|
-
return {
|
|
540
|
+
return {
|
|
541
|
+
on_shift: onShift,
|
|
542
|
+
staleBridge: response.stale_bridge === true,
|
|
543
|
+
...(typeof compatibility?.minimum_version === "string" ? { minimumVersion: compatibility.minimum_version } : {}),
|
|
544
|
+
...(typeof compatibility?.required_protocol === "number" ? { requiredProtocol: compatibility.required_protocol } : {}),
|
|
545
|
+
};
|
|
542
546
|
}
|
|
543
547
|
async function disconnect() {
|
|
544
548
|
const { values } = parseArgs({ args: process.argv.slice(3), options: { yes: { type: "boolean", short: "y" } } });
|
package/dist/client.js
CHANGED
|
@@ -40,11 +40,15 @@ export class ConduitClient {
|
|
|
40
40
|
return data;
|
|
41
41
|
}
|
|
42
42
|
async claim(taskId, attemptId, extras) {
|
|
43
|
-
|
|
43
|
+
// The lease token is this machine's bearer credential for the attempt. The server stores only
|
|
44
|
+
// its hash, so the client mints it and re-sends the identical token if it ever retries the
|
|
45
|
+
// claim (the idempotency key is deterministic per attempt exactly so that replay works).
|
|
46
|
+
const leaseToken = `lease_${crypto.randomUUID()}${crypto.randomUUID()}`;
|
|
47
|
+
const data = await this.request(`/runner/v1/tasks/${taskId}/claim`, { method: "POST", body: JSON.stringify({ attempt_id: attemptId, lease_token: leaseToken, idempotency_key: `bridge:claim:${attemptId}` }) });
|
|
44
48
|
const active = {
|
|
45
49
|
taskId,
|
|
46
50
|
attemptId,
|
|
47
|
-
leaseToken
|
|
51
|
+
leaseToken,
|
|
48
52
|
leaseExpiresAt: String(data.lease_expires_at),
|
|
49
53
|
phase: "claimed",
|
|
50
54
|
...(extras?.driverId ? { driverId: extras.driverId } : {}),
|
package/dist/ops.js
CHANGED
|
@@ -11,11 +11,27 @@ import { ConduitClient } from "./client.js";
|
|
|
11
11
|
import { loadConfig } from "./config.js";
|
|
12
12
|
import { detectInstalledClients } from "./detect.js";
|
|
13
13
|
import { driverIdsFromDetectedLabels } from "./drivers.js";
|
|
14
|
-
import { describePreflightIssue, runBridgePreflight } from "./preflight.js";
|
|
14
|
+
import { BRIDGE_PROTOCOL_VERSION, describePreflightIssue, runBridgePreflight } from "./preflight.js";
|
|
15
|
+
import { bridgeVersion } from "./version.js";
|
|
15
16
|
export const OPS_VERBS = [
|
|
16
17
|
"connect", "install", "switch", "online", "offline", "status", "doctor", "disconnect", "uninstall",
|
|
17
18
|
];
|
|
18
19
|
const LOCAL_FUEL_DRIVERS = new Set(["cursor", "pi", "kiro", "antigravity"]);
|
|
20
|
+
function bridgeVersionAtLeast(value, minimum) {
|
|
21
|
+
const parse = (input) => {
|
|
22
|
+
const match = /^(\d+)\.(\d+)\.(\d+)/.exec(input);
|
|
23
|
+
return match ? [Number(match[1]), Number(match[2]), Number(match[3])] : null;
|
|
24
|
+
};
|
|
25
|
+
const actual = parse(value);
|
|
26
|
+
const required = parse(minimum);
|
|
27
|
+
if (!actual || !required)
|
|
28
|
+
return false;
|
|
29
|
+
for (let index = 0; index < 3; index += 1) {
|
|
30
|
+
if (actual[index] !== required[index])
|
|
31
|
+
return actual[index] > required[index];
|
|
32
|
+
}
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
19
35
|
export function defaultOpsEnvPath(home = homedir()) {
|
|
20
36
|
return join(home, ".config", "conduit", "ops.env");
|
|
21
37
|
}
|
|
@@ -177,6 +193,27 @@ export async function runOps(verb, argv = [], deps = {}) {
|
|
|
177
193
|
return;
|
|
178
194
|
}
|
|
179
195
|
if (verb === "status") {
|
|
196
|
+
const packageVersion = bridgeVersion();
|
|
197
|
+
console.log(`Bridge: v${packageVersion} (protocol ${BRIDGE_PROTOCOL_VERSION})`);
|
|
198
|
+
try {
|
|
199
|
+
const config = await (deps.loadBridgeConfig ?? loadConfig)();
|
|
200
|
+
const controller = new AbortController();
|
|
201
|
+
const timeout = setTimeout(() => controller.abort(), 5_000);
|
|
202
|
+
let compatibility;
|
|
203
|
+
try {
|
|
204
|
+
compatibility = await new ConduitClient(config).request("/runner/v1/compatibility", { signal: controller.signal });
|
|
205
|
+
}
|
|
206
|
+
finally {
|
|
207
|
+
clearTimeout(timeout);
|
|
208
|
+
}
|
|
209
|
+
const minimumVersion = typeof compatibility.minimum_version === "string" ? compatibility.minimum_version : "unknown";
|
|
210
|
+
const requiredProtocol = typeof compatibility.required_protocol === "number" ? compatibility.required_protocol : "unknown";
|
|
211
|
+
const compatible = minimumVersion === "unknown" || (bridgeVersionAtLeast(packageVersion, minimumVersion) && (requiredProtocol === "unknown" || BRIDGE_PROTOCOL_VERSION >= requiredProtocol));
|
|
212
|
+
console.log(`Control plane: requires Bridge ${minimumVersion} (protocol ${requiredProtocol})${compatible ? "" : " — UPDATE REQUIRED"}`);
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
console.log("Control plane: compatibility check unavailable (showing local Bridge only)");
|
|
216
|
+
}
|
|
180
217
|
if (env.loadedFrom) {
|
|
181
218
|
console.log(`Env: ${env.loadedFrom}`);
|
|
182
219
|
console.log(`URL: ${env.CONDUIT_URL || "(unset)"}`);
|
|
@@ -368,6 +405,7 @@ export async function runOps(verb, argv = [], deps = {}) {
|
|
|
368
405
|
const runnerArgs = ["runner", "--workspace", workspace];
|
|
369
406
|
if (installEnv.CONDUIT_REPO)
|
|
370
407
|
runnerArgs.push("--ensure-checkout", installEnv.CONDUIT_REPO);
|
|
408
|
+
console.log(`Bridge: v${bridgeVersion()} (protocol ${BRIDGE_PROTOCOL_VERSION})`);
|
|
371
409
|
console.log("Windows: background install-service is not available.");
|
|
372
410
|
console.log("Keep a terminal open and run:");
|
|
373
411
|
console.log(` npx @miraland-labs/conduit-bridge@latest ${shellQuoteArgs(runnerArgs)}`);
|
|
@@ -377,6 +415,7 @@ export async function runOps(verb, argv = [], deps = {}) {
|
|
|
377
415
|
const installArgs = ["install-service", "--workspace", workspace];
|
|
378
416
|
if (installEnv.CONDUIT_REPO)
|
|
379
417
|
installArgs.push("--ensure-checkout", installEnv.CONDUIT_REPO);
|
|
418
|
+
console.log(`Bridge: v${bridgeVersion()} (protocol ${BRIDGE_PROTOCOL_VERSION})`);
|
|
380
419
|
console.log(`Installing runner for ${workspace} (drivers: ${drivers.join(", ")})`);
|
|
381
420
|
runBridge(installArgs);
|
|
382
421
|
console.log("Done. Check with: npx @miraland-labs/conduit-bridge@latest ops status");
|
package/dist/preflight.js
CHANGED
|
@@ -16,7 +16,9 @@ function modelsFingerprint(config) {
|
|
|
16
16
|
}).join(";");
|
|
17
17
|
return createHash("sha256").update(canonical).digest("hex").slice(0, 16);
|
|
18
18
|
}
|
|
19
|
-
|
|
19
|
+
// Protocol 5 makes the lease-token claim contract explicit. Older Bridges may report a green
|
|
20
|
+
// preflight but cannot claim against the current control plane, so they must be admitted as stale.
|
|
21
|
+
export const BRIDGE_PROTOCOL_VERSION = 5;
|
|
20
22
|
const execFileAsync = promisify(execFile);
|
|
21
23
|
async function defaultCommandRunner(command, args, cwd) {
|
|
22
24
|
try {
|
package/dist/version.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
/** Read the version of the Bridge package that is actually running. */
|
|
5
|
+
export function bridgeVersion() {
|
|
6
|
+
try {
|
|
7
|
+
const packagePath = join(dirname(fileURLToPath(import.meta.url)), "..", "package.json");
|
|
8
|
+
return JSON.parse(readFileSync(packagePath, "utf8")).version ?? "unknown";
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return "unknown";
|
|
12
|
+
}
|
|
13
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@miraland-labs/conduit-bridge",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.5",
|
|
4
4
|
"description": "Conduit Bridge CLI — join, connect, disconnect, multi-driver lanes, and run Claude Code / Codex / Cursor / OpenCode / Pi / Kiro / Antigravity agents for a Conduit organization",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|