@miraland-labs/conduit-bridge 0.16.110 → 0.16.112
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/driver.js +14 -2
- package/dist/investigation.js +45 -5
- package/dist/preflight.js +32 -6
- package/package.json +1 -1
package/dist/driver.js
CHANGED
|
@@ -7,6 +7,18 @@ import { mkdir, readFile, rm, rmdir, writeFile } from "node:fs/promises";
|
|
|
7
7
|
import { join, resolve } from "node:path";
|
|
8
8
|
import { z } from "zod";
|
|
9
9
|
import { allowsExternalFetch, deniedCommands, executionClassPromptRules, isRunnableVerificationCommand, parsePiJsonl, projectClaude, projectCoarseMode, projectCodex, projectCursor, projectGrok, projectKiroTools, projectPi, requireStampedExecutionClass, } from "./execution-class.js";
|
|
10
|
+
/**
|
|
11
|
+
* Classify one Cursor `status` / Kiro `whoami` probe. Signed-out phrases win; an unanswered
|
|
12
|
+
* probe (timeout, spawn error, empty output, or text that names neither state) is unknown.
|
|
13
|
+
*/
|
|
14
|
+
export function readSignInStatus(result, signedInPattern) {
|
|
15
|
+
const text = `${result.stdout ?? ""}\n${result.stderr ?? ""}`;
|
|
16
|
+
if (/not logged in|not authenticated|login required/i.test(text))
|
|
17
|
+
return "signed_out";
|
|
18
|
+
if (result.code === 0 && signedInPattern.test(text))
|
|
19
|
+
return "signed_in";
|
|
20
|
+
return "unknown";
|
|
21
|
+
}
|
|
10
22
|
/**
|
|
11
23
|
* The bound for this turn.
|
|
12
24
|
*
|
|
@@ -1017,10 +1029,10 @@ export const cursorDriver = {
|
|
|
1017
1029
|
return { status: "failed", resultText: null, sessionId: null, error: "cursor preflight found no bounded verification command for the test_run grant" };
|
|
1018
1030
|
}
|
|
1019
1031
|
// A ~/.cursor directory exists even after logout — ask the CLI itself.
|
|
1020
|
-
// `agent status` exits 0 either way
|
|
1032
|
+
// `agent status` exits 0 either way; only a definite signed_out fails closed.
|
|
1021
1033
|
if (!process.env.CURSOR_API_KEY) {
|
|
1022
1034
|
const status = await execute(executable, ["status"], input.workspace, 15_000, undefined, "local");
|
|
1023
|
-
if (status
|
|
1035
|
+
if (readSignInStatus(status, /logged in as/i) === "signed_out") {
|
|
1024
1036
|
return { status: "failed", resultText: null, sessionId: null, error: "cursor has no login (set CURSOR_API_KEY or run `agent login`)" };
|
|
1025
1037
|
}
|
|
1026
1038
|
}
|
package/dist/investigation.js
CHANGED
|
@@ -18,11 +18,20 @@ import { diffStatSince } from "./git-witness.js";
|
|
|
18
18
|
import { switchManagedWorkspace } from "./on-shift-apply.js";
|
|
19
19
|
import { execFile } from "node:child_process";
|
|
20
20
|
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
21
|
+
import { createRequire } from "node:module";
|
|
21
22
|
import { tmpdir } from "node:os";
|
|
22
23
|
import { join } from "node:path";
|
|
23
24
|
import { promisify } from "node:util";
|
|
24
25
|
import { isRunnableVerificationCommand } from "./execution-class.js";
|
|
25
26
|
const execFileAsync = promisify(execFile);
|
|
27
|
+
const tsxLoader = (() => {
|
|
28
|
+
try {
|
|
29
|
+
return createRequire(import.meta.url).resolve("tsx");
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return "tsx";
|
|
33
|
+
}
|
|
34
|
+
})();
|
|
26
35
|
const budgetSchema = z.object({
|
|
27
36
|
max_files: z.number().int().min(1).max(24).optional().default(8),
|
|
28
37
|
max_duration_ms: z.number().int().min(10_000).max(15 * 60_000).optional().default(5 * 60_000),
|
|
@@ -265,6 +274,29 @@ const PROBE_MAX = 20;
|
|
|
265
274
|
const PROBE_COMMAND_MAX = 300;
|
|
266
275
|
const PROBE_CHECKS_MAX = 500;
|
|
267
276
|
const PROBE_KEY_MAX = 40;
|
|
277
|
+
const PROBE_HOLDS_LINE = "PROBE HOLDS";
|
|
278
|
+
const PROBE_FAILS_LINE = "PROBE FAILS";
|
|
279
|
+
function lastNonEmptyLine(text) {
|
|
280
|
+
const lines = text.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
281
|
+
return lines.at(-1) ?? "";
|
|
282
|
+
}
|
|
283
|
+
function sourceProbeVerdict(exitCode, stdout) {
|
|
284
|
+
const last = lastNonEmptyLine(stdout);
|
|
285
|
+
if (exitCode === 0 && last === PROBE_HOLDS_LINE)
|
|
286
|
+
return "holds";
|
|
287
|
+
if (exitCode === 1 && last === PROBE_FAILS_LINE)
|
|
288
|
+
return "fails";
|
|
289
|
+
return "broken";
|
|
290
|
+
}
|
|
291
|
+
function testProbeVerdict(ran, exitCode) {
|
|
292
|
+
if (!ran)
|
|
293
|
+
return "broken";
|
|
294
|
+
return exitCode === 0 ? "holds" : "fails";
|
|
295
|
+
}
|
|
296
|
+
function probeTail(stderr, stdout) {
|
|
297
|
+
const stderrTail = stderr.length > PROBE_TAIL_CHARS ? stderr.slice(stderr.length - PROBE_TAIL_CHARS) : stderr;
|
|
298
|
+
return boundedTail(`${stderrTail}\n${stdout}`, PROBE_TAIL_CHARS);
|
|
299
|
+
}
|
|
268
300
|
function probeEnv() {
|
|
269
301
|
const env = {};
|
|
270
302
|
if (process.env.PATH !== undefined)
|
|
@@ -282,14 +314,14 @@ function probeInterpreter(language) {
|
|
|
282
314
|
}
|
|
283
315
|
function probeFilename(language) {
|
|
284
316
|
if (language === "ts")
|
|
285
|
-
return "probe.
|
|
317
|
+
return "probe.mts";
|
|
286
318
|
if (language === "py")
|
|
287
319
|
return "probe.py";
|
|
288
320
|
return "probe.sh";
|
|
289
321
|
}
|
|
290
322
|
function probeArgv(language, file) {
|
|
291
323
|
if (language === "ts")
|
|
292
|
-
return ["node", "--import",
|
|
324
|
+
return ["node", "--import", tsxLoader, file];
|
|
293
325
|
if (language === "py")
|
|
294
326
|
return ["python3", file];
|
|
295
327
|
return ["bash", file];
|
|
@@ -314,9 +346,11 @@ async function execProbe(argv, workspace) {
|
|
|
314
346
|
const err = error;
|
|
315
347
|
if (typeof err.code === "string")
|
|
316
348
|
throw error;
|
|
349
|
+
const stdout = String(err.stdout ?? "");
|
|
350
|
+
const stderr = String(err.stderr ?? "");
|
|
317
351
|
return {
|
|
318
|
-
stdout
|
|
319
|
-
stderr:
|
|
352
|
+
stdout,
|
|
353
|
+
stderr: stdout === "" && stderr === "" ? String(err.message || "probe failed") : stderr,
|
|
320
354
|
code: err.killed === true || typeof err.code !== "number" ? -1 : err.code,
|
|
321
355
|
};
|
|
322
356
|
}
|
|
@@ -327,8 +361,9 @@ function witnessRecord(input) {
|
|
|
327
361
|
kind: input.kind,
|
|
328
362
|
command: clipWitnessField(input.command, PROBE_COMMAND_MAX),
|
|
329
363
|
exit_code: input.exitCode,
|
|
330
|
-
tail:
|
|
364
|
+
tail: probeTail(input.stderr, input.stdout),
|
|
331
365
|
checks: clipWitnessField(input.checks, PROBE_CHECKS_MAX),
|
|
366
|
+
verdict: input.verdict,
|
|
332
367
|
};
|
|
333
368
|
}
|
|
334
369
|
/**
|
|
@@ -346,17 +381,20 @@ export async function runDeliveryVerificationProbes(input) {
|
|
|
346
381
|
if (!isRunnableVerificationCommand(named)) {
|
|
347
382
|
witnesses.push(witnessRecord({
|
|
348
383
|
key: probe.key, kind: "test", command: named, exitCode: -1, stdout: "", stderr: "not a registered command", checks,
|
|
384
|
+
verdict: "broken",
|
|
349
385
|
}));
|
|
350
386
|
continue;
|
|
351
387
|
}
|
|
352
388
|
let exitCode = -1;
|
|
353
389
|
let stdout = "";
|
|
354
390
|
let stderr = "";
|
|
391
|
+
let ran = false;
|
|
355
392
|
try {
|
|
356
393
|
const result = await (input.runCommand ?? runBoundedVerificationCommand)(named, input.workspace);
|
|
357
394
|
exitCode = result.code;
|
|
358
395
|
stdout = result.stdout;
|
|
359
396
|
stderr = result.stderr;
|
|
397
|
+
ran = true;
|
|
360
398
|
}
|
|
361
399
|
catch (error) {
|
|
362
400
|
stderr = error instanceof Error ? error.message : String(error);
|
|
@@ -364,6 +402,7 @@ export async function runDeliveryVerificationProbes(input) {
|
|
|
364
402
|
}
|
|
365
403
|
witnesses.push(witnessRecord({
|
|
366
404
|
key: probe.key, kind: "test", command: named, exitCode, stdout, stderr, checks,
|
|
405
|
+
verdict: testProbeVerdict(ran, exitCode),
|
|
367
406
|
}));
|
|
368
407
|
continue;
|
|
369
408
|
}
|
|
@@ -394,6 +433,7 @@ export async function runDeliveryVerificationProbes(input) {
|
|
|
394
433
|
}
|
|
395
434
|
witnesses.push(witnessRecord({
|
|
396
435
|
key: probe.key, kind: "probe", command, exitCode, stdout, stderr, checks,
|
|
436
|
+
verdict: sourceProbeVerdict(exitCode, stdout),
|
|
397
437
|
}));
|
|
398
438
|
}
|
|
399
439
|
return witnesses;
|
package/dist/preflight.js
CHANGED
|
@@ -3,7 +3,7 @@ import { createHash } from "node:crypto";
|
|
|
3
3
|
import { promisify } from "node:util";
|
|
4
4
|
import { buildWorkspaceBrief, normalizeRepositoryUrl } from "./brief.js";
|
|
5
5
|
import { resolveMaxTurns } from "./execution-budget.js";
|
|
6
|
-
import { hasAntigravityLogin, hasClaudeLogin, hasGrokLogin, hasOpenAiLogin, hasOpenCodeLogin, driverExecutable, } from "./driver.js";
|
|
6
|
+
import { hasAntigravityLogin, hasClaudeLogin, hasGrokLogin, hasOpenAiLogin, hasOpenCodeLogin, driverExecutable, readSignInStatus, } from "./driver.js";
|
|
7
7
|
import { laneQuota, laneTierBindings, localFuelOnlyDriver, onlineDriverIds, resolveDriverFuel, supportsReadOnlyDiagnosis } from "./drivers.js";
|
|
8
8
|
/** Protocol 3: read-only diagnosis can reuse a retained failed-attempt worktree. */
|
|
9
9
|
// 4: each driver snapshot reports diagnosis_read_only, so the control plane can require a
|
|
@@ -128,7 +128,35 @@ async function driverCompatibilityFingerprint(driver, workspace, run) {
|
|
|
128
128
|
features,
|
|
129
129
|
})).digest("hex");
|
|
130
130
|
}
|
|
131
|
-
|
|
131
|
+
/** Last definite Cursor/Kiro sign-in answer. Unknown never writes here. */
|
|
132
|
+
export const lastSignIn = new Map();
|
|
133
|
+
export function resetSignInMemoryForTests() {
|
|
134
|
+
lastSignIn.clear();
|
|
135
|
+
}
|
|
136
|
+
/** Retry once on unknown, then the last definite answer, then signed-in with a warning. */
|
|
137
|
+
export async function resolveSignInAnswer(driver, probe) {
|
|
138
|
+
const once = async () => {
|
|
139
|
+
try {
|
|
140
|
+
return await probe();
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
return "unknown";
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
let answer = await once();
|
|
147
|
+
if (answer === "unknown")
|
|
148
|
+
answer = await once();
|
|
149
|
+
if (answer !== "unknown") {
|
|
150
|
+
lastSignIn.set(driver, answer);
|
|
151
|
+
return answer;
|
|
152
|
+
}
|
|
153
|
+
const previous = lastSignIn.get(driver);
|
|
154
|
+
if (previous)
|
|
155
|
+
return previous;
|
|
156
|
+
console.warn(`sign-in unverified (${driver})`);
|
|
157
|
+
return "signed_in";
|
|
158
|
+
}
|
|
159
|
+
export async function localAuthenticationReady(driver, workspace, run) {
|
|
132
160
|
if (driver === "claude-code")
|
|
133
161
|
return hasClaudeLogin();
|
|
134
162
|
if (driver === "codex")
|
|
@@ -142,14 +170,12 @@ async function localAuthenticationReady(driver, workspace, run) {
|
|
|
142
170
|
if (driver === "cursor") {
|
|
143
171
|
if (process.env.CURSOR_API_KEY)
|
|
144
172
|
return true;
|
|
145
|
-
|
|
146
|
-
return status.code === 0 && /logged in as/i.test(`${status.stdout}\n${status.stderr}`);
|
|
173
|
+
return (await resolveSignInAnswer(driver, async () => readSignInStatus(await run(driverExecutable(driver), ["status"], workspace), /logged in as/i))) !== "signed_out";
|
|
147
174
|
}
|
|
148
175
|
if (driver === "kiro") {
|
|
149
176
|
if (process.env.KIRO_API_KEY)
|
|
150
177
|
return true;
|
|
151
|
-
|
|
152
|
-
return status.code === 0 && !/not logged in|not authenticated|login required/i.test(`${status.stdout}\n${status.stderr}`);
|
|
178
|
+
return (await resolveSignInAnswer(driver, async () => readSignInStatus(await run(driverExecutable(driver), ["whoami"], workspace), /\S/))) !== "signed_out";
|
|
153
179
|
}
|
|
154
180
|
// Pi stores provider auth in its own config; CLI has no stable whoami — version probe is enough.
|
|
155
181
|
if (driver === "pi")
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@miraland-labs/conduit-bridge",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.112",
|
|
4
4
|
"description": "Conduit Bridge CLI \u2014 join, connect, disconnect, multi-driver lanes, and run Claude Code / Codex / Cursor / OpenCode / Pi / Kiro / Antigravity / Grok Build agents for a Conduit organization",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|