@miraland-labs/conduit-bridge 0.10.0 → 0.11.0
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/README.md +2 -2
- package/dist/brief.js +41 -5
- package/dist/driver.js +21 -18
- package/dist/execution-class.js +34 -1
- package/dist/execution.js +40 -20
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Local Bridge CLI for [Conduit](https://github.com/miralandlabs/conduit). Connects a computer to one organization, claims work, and drives a local agent.
|
|
4
4
|
|
|
5
|
-
**Package version:** `0.
|
|
5
|
+
**Package version:** `0.11.0` — **ExecutionClass inversion:** the control plane stamps `execution_class` on the assignment; Bridge launches from that stamp (fail closed if missing), asserts the full class floor after ceilings, and projects permissions locally (“Bridge wins” on tools). Ordinary grants are derived server-side as classMax ∩ project ceiling; landing is package `lands`, not a class axiom. Workspace briefs discover verification commands for Node, Make, Cargo, Go, pytest, and Gradle. Control-plane `instruction` is **additive** evidence/class prose; Bridge always appends local RULES (shell allow-list, verbatim paste, criterion mapping, hard-denied). Prompt is data; permissions stay local — residual risk if the control plane is compromised (sharpest for `observe_network`). Artifact digests still normalize bare 64-hex to `sha256:<hex>`. Experimental Pi lane remains; `observe_network` is refused on Pi. Heartbeat protocol 2, worktrees, ops helpers, multi-driver lanes, and slots **1–4** unchanged. Protocol 1 heartbeats remain compatible for presence but cannot receive work.
|
|
6
6
|
|
|
7
7
|
## Prerequisites
|
|
8
8
|
|
|
@@ -78,7 +78,7 @@ repository delivery with PR/evidence and a successful rework cycle. All other la
|
|
|
78
78
|
|
|
79
79
|
## Grant enforcement
|
|
80
80
|
|
|
81
|
-
|
|
81
|
+
The control plane authors **ExecutionClass**; Bridge requires the stamped class and projects it:
|
|
82
82
|
|
|
83
83
|
| Driver | Fuel | Mechanism |
|
|
84
84
|
| --- | --- | --- |
|
package/dist/brief.js
CHANGED
|
@@ -3,9 +3,13 @@ import { join, resolve } from "node:path";
|
|
|
3
3
|
import { execFile } from "node:child_process";
|
|
4
4
|
import { promisify } from "node:util";
|
|
5
5
|
const execFileAsync = promisify(execFile);
|
|
6
|
-
const MANIFESTS = [
|
|
6
|
+
const MANIFESTS = [
|
|
7
|
+
"package.json", "wrangler.jsonc", "wrangler.toml", "tsconfig.json",
|
|
8
|
+
"Cargo.toml", "pyproject.toml", "pytest.ini", "setup.cfg", "go.mod",
|
|
9
|
+
"Makefile", "build.gradle", "build.gradle.kts", "gradlew",
|
|
10
|
+
];
|
|
7
11
|
const VERIFICATION_SCRIPTS = ["verify", "typecheck", "lint", "test", "build"];
|
|
8
|
-
const EXCLUDED_DIRECTORIES = new Set(["node_modules", "dist", "build", "target", "coverage"]);
|
|
12
|
+
const EXCLUDED_DIRECTORIES = new Set(["node_modules", "dist", "build", "target", "coverage", ".venv", "venv"]);
|
|
9
13
|
export async function buildWorkspaceBrief(workspace) {
|
|
10
14
|
const entries = await readdir(workspace, { withFileTypes: true });
|
|
11
15
|
const modules = entries
|
|
@@ -18,7 +22,7 @@ export async function buildWorkspaceBrief(workspace) {
|
|
|
18
22
|
base_commit: await gitHeadCommit(workspace),
|
|
19
23
|
modules,
|
|
20
24
|
manifests,
|
|
21
|
-
verification: await
|
|
25
|
+
verification: await discoverVerificationCommands(workspace, files),
|
|
22
26
|
};
|
|
23
27
|
}
|
|
24
28
|
export function normalizeRepositoryUrl(url) {
|
|
@@ -191,7 +195,8 @@ async function gitDirectories(workspace) {
|
|
|
191
195
|
return { worktree, common: worktree };
|
|
192
196
|
}
|
|
193
197
|
}
|
|
194
|
-
|
|
198
|
+
/** Exported for tests — discovers bounded verification commands from workspace manifests. */
|
|
199
|
+
export async function discoverVerificationCommands(workspace, files) {
|
|
195
200
|
const commands = [];
|
|
196
201
|
if (files.has("package.json")) {
|
|
197
202
|
try {
|
|
@@ -209,5 +214,36 @@ async function verificationCommands(workspace, files) {
|
|
|
209
214
|
}
|
|
210
215
|
catch { /* unreadable Makefiles do not broaden execution */ }
|
|
211
216
|
}
|
|
212
|
-
|
|
217
|
+
if (files.has("Cargo.toml")) {
|
|
218
|
+
commands.push("cargo test");
|
|
219
|
+
}
|
|
220
|
+
if (files.has("go.mod")) {
|
|
221
|
+
commands.push("go test ./...");
|
|
222
|
+
}
|
|
223
|
+
if (files.has("pyproject.toml") || files.has("pytest.ini") || files.has("setup.cfg")) {
|
|
224
|
+
let hasPytest = files.has("pytest.ini");
|
|
225
|
+
if (!hasPytest && files.has("pyproject.toml")) {
|
|
226
|
+
try {
|
|
227
|
+
const pyproject = await readFile(join(workspace, "pyproject.toml"), "utf8");
|
|
228
|
+
hasPytest = /\bpytest\b/i.test(pyproject);
|
|
229
|
+
}
|
|
230
|
+
catch { /* unreadable manifests do not broaden execution */ }
|
|
231
|
+
}
|
|
232
|
+
if (!hasPytest && files.has("setup.cfg")) {
|
|
233
|
+
try {
|
|
234
|
+
const setup = await readFile(join(workspace, "setup.cfg"), "utf8");
|
|
235
|
+
hasPytest = /\[tool:\s*pytest\]|\bpytest\b/i.test(setup);
|
|
236
|
+
}
|
|
237
|
+
catch { /* unreadable manifests do not broaden execution */ }
|
|
238
|
+
}
|
|
239
|
+
// Only when pytest is evidenced — bare pyproject.toml is often unittest/nox/poetry without pytest.
|
|
240
|
+
if (hasPytest) {
|
|
241
|
+
commands.push("python -m pytest");
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
if (files.has("build.gradle") || files.has("build.gradle.kts")) {
|
|
245
|
+
if (files.has("gradlew"))
|
|
246
|
+
commands.push("./gradlew test");
|
|
247
|
+
}
|
|
248
|
+
return [...new Set(commands)];
|
|
213
249
|
}
|
package/dist/driver.js
CHANGED
|
@@ -3,8 +3,8 @@ import { existsSync } from "node:fs";
|
|
|
3
3
|
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { z } from "zod";
|
|
6
|
-
import { deniedCommands, executionClassPromptRules, parsePiJsonl, projectClaude, projectCoarseMode, projectCodex, projectCursor, projectKiroTools, projectPi, resolveExecutionClass, } from "./execution-class.js";
|
|
7
|
-
export { branchCreateCommands, deniedCommands, isBoundedVerificationCommand, prCreateCommands, resolveExecutionClass, } from "./execution-class.js";
|
|
6
|
+
import { deniedCommands, executionClassPromptRules, parsePiJsonl, projectClaude, projectCoarseMode, projectCodex, projectCursor, projectKiroTools, projectPi, requireStampedExecutionClass, resolveExecutionClass, } from "./execution-class.js";
|
|
7
|
+
export { branchCreateCommands, deniedCommands, isBoundedVerificationCommand, prCreateCommands, requireStampedExecutionClass, resolveExecutionClass, } from "./execution-class.js";
|
|
8
8
|
function deliveryLanguageRule(language) {
|
|
9
9
|
if (language === "zh") {
|
|
10
10
|
return "- Write every human-facing delivery packet string (outcome, changes, verification, assumptions, risks, limitations, criterion text) in Chinese (Simplified). Keep JSON keys and enum token values in English.";
|
|
@@ -55,12 +55,7 @@ export const claudeDeniedTools = deniedCommands.map((command) => `Bash(${command
|
|
|
55
55
|
function resolveRunClass(input) {
|
|
56
56
|
if (input.executionClass)
|
|
57
57
|
return input.executionClass;
|
|
58
|
-
|
|
59
|
-
grants: input.grants,
|
|
60
|
-
capabilities: input.capabilities,
|
|
61
|
-
deliverable: input.deliverable,
|
|
62
|
-
workRole: input.workRole,
|
|
63
|
-
});
|
|
58
|
+
throw new Error("Driver run is missing stamped execution_class. Pass executionClass from the assignment stamp.");
|
|
64
59
|
}
|
|
65
60
|
export function buildAssignmentPrompt(context) {
|
|
66
61
|
const packageContext = context.workPackage;
|
|
@@ -91,7 +86,7 @@ export function buildAssignmentPrompt(context) {
|
|
|
91
86
|
lines.push("", `BOUNDARIES — never violate these\n${boundaries.map((item) => `- ${item}`).join("\n")}`);
|
|
92
87
|
if (acceptance?.length)
|
|
93
88
|
lines.push("", `ACCEPTANCE CRITERIA — the delivery is judged against these\n${acceptance.map((item) => `- ${item}`).join("\n")}`);
|
|
94
|
-
if (evidence?.length) {
|
|
89
|
+
if (evidence?.length && !spec.instruction) {
|
|
95
90
|
lines.push("", "REQUIRED EVIDENCE");
|
|
96
91
|
for (const kind of evidence) {
|
|
97
92
|
if (kind === "test") {
|
|
@@ -115,12 +110,14 @@ export function buildAssignmentPrompt(context) {
|
|
|
115
110
|
lines.push("", `REWORK FEEDBACK — an independent review returned this delivery; address every point\n${rework}`);
|
|
116
111
|
const mustCommit = context.grants.includes("repo_write") && context.grants.includes("branch_create");
|
|
117
112
|
const mustOpenPr = mustCommit && context.grants.includes("pr_create") && Boolean(changeScope?.length);
|
|
118
|
-
const executionClass = context.executionClass
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
113
|
+
const executionClass = context.executionClass
|
|
114
|
+
?? requireStampedExecutionClass(spec.execution_class);
|
|
115
|
+
// Phase B: control-plane instruction is additive evidence/class prose.
|
|
116
|
+
// Local permission projection + hardened delivery rules stay Bridge-authored (Bridge wins on tools;
|
|
117
|
+
// skipping them when instruction was present recreated the summarize/guess-shell canary failures).
|
|
118
|
+
if (typeof spec.instruction === "string" && spec.instruction.trim()) {
|
|
119
|
+
lines.push("", spec.instruction.trim());
|
|
120
|
+
}
|
|
124
121
|
const languageRule = deliveryLanguageRule(packageContext?.working_language ?? spec.working_language);
|
|
125
122
|
const classRules = executionClassPromptRules({
|
|
126
123
|
executionClass,
|
|
@@ -131,6 +128,11 @@ export function buildAssignmentPrompt(context) {
|
|
|
131
128
|
mustOpenPr,
|
|
132
129
|
});
|
|
133
130
|
lines.push("", "RULES", "- Stay within the change scope and boundaries.", ...(languageRule ? [languageRule] : []), ...classRules, "- Never merge, deploy, force-push, push to protected branches, or touch production.", `- Hard-denied commands (all drivers): ${deniedCommands.join("; ")}.`, "- Do not invent evidence. Report unknown when you could not verify a criterion.",
|
|
131
|
+
// The reviewer requests changes when "verification output is only summarized (not verbatim)"
|
|
132
|
+
// (REVIEWER_PROMPT). Only the bounded-shell rule said to report real output, so publish_artifact
|
|
133
|
+
// — the class the reviewer scrutinises hardest — summarized, and every criterion came back
|
|
134
|
+
// unsupported. Stated once here so it holds for every class rather than per branch.
|
|
135
|
+
"- Paste the actual command output into the evidence details: the HTTP status line, the digest command's output, the fetched body. A summary of what a command printed (\"returned HTTP 200\", \"digest matches\") is treated as unsupported and returns the delivery. Writing the proof to a file does not count — only the delivery packet is reviewed, so the output must be in the evidence entry itself.",
|
|
134
136
|
// A met claim with nothing backing it is rejected server-side ("Met acceptance criteria require
|
|
135
137
|
// mapped evidence"). Observed live: six criteria marked met, evidence mapped to four, whole
|
|
136
138
|
// delivery lost. Say it here rather than let the agent discover it by failing.
|
|
@@ -422,9 +424,10 @@ export const codexDriver = {
|
|
|
422
424
|
* publish shell under open class policy. Research-only networked assignments
|
|
423
425
|
* deny Shell(*) and Write(**).
|
|
424
426
|
*/
|
|
425
|
-
export function cursorPermissionsForGrants(grants, verificationCommands = [], capabilities = [], deliverable = "repository", workRole) {
|
|
426
|
-
const
|
|
427
|
-
|
|
427
|
+
export function cursorPermissionsForGrants(grants, verificationCommands = [], capabilities = [], deliverable = "repository", workRole, executionClass) {
|
|
428
|
+
const cls = executionClass
|
|
429
|
+
?? resolveExecutionClass({ grants, capabilities, deliverable, workRole });
|
|
430
|
+
const { allow, deny } = projectCursor(cls, { grants, verificationCommands, capabilities });
|
|
428
431
|
return { allow, deny };
|
|
429
432
|
}
|
|
430
433
|
/**
|
package/dist/execution-class.js
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
/** Bounded verification commands a `test_run` grant may execute. */
|
|
6
6
|
export function isBoundedVerificationCommand(command) {
|
|
7
|
-
|
|
7
|
+
// Exact forms only — a trailing `.*` on pytest would admit `pytest ; curl … | sh`.
|
|
8
|
+
return /^(npm run (verify|typecheck|lint|test|build)|pnpm (verify|typecheck|lint|test|build)|yarn (verify|typecheck|lint|test|build)|cargo (test|check)|go test(?: \.\/\.\.\.)?|make (test|check)|python -m pytest|pytest(?: [\w./=-]+)?|\.\/gradlew test)$/.test(command);
|
|
8
9
|
}
|
|
9
10
|
/** Shell commands each grant authorizes — mapped per driver so they cannot drift. */
|
|
10
11
|
export const branchCreateCommands = [
|
|
@@ -26,6 +27,10 @@ export const EXECUTION_CLASSES = [
|
|
|
26
27
|
"verify",
|
|
27
28
|
"mutate_repo",
|
|
28
29
|
];
|
|
30
|
+
/**
|
|
31
|
+
* Migrator / test helper only. Launch must use the control-plane stamped class
|
|
32
|
+
* via `requireStampedExecutionClass` — do not re-derive as source of truth.
|
|
33
|
+
*/
|
|
29
34
|
export function resolveExecutionClass(input) {
|
|
30
35
|
const grants = input.grants;
|
|
31
36
|
const capabilities = input.capabilities ?? [];
|
|
@@ -47,6 +52,34 @@ export function resolveExecutionClass(input) {
|
|
|
47
52
|
return "observe";
|
|
48
53
|
return "mutate_repo";
|
|
49
54
|
}
|
|
55
|
+
const STAMPED_CLASSES = new Set(EXECUTION_CLASSES);
|
|
56
|
+
/** Fail closed when the assignment has no stamped class (upgrade Bridge / redeploy CP). */
|
|
57
|
+
export function requireStampedExecutionClass(stamped) {
|
|
58
|
+
if (typeof stamped === "string" && STAMPED_CLASSES.has(stamped))
|
|
59
|
+
return stamped;
|
|
60
|
+
throw new Error("Assignment is missing stamped execution_class. Upgrade Bridge to 0.11+ and redeploy the control plane so plans author ExecutionClass.");
|
|
61
|
+
}
|
|
62
|
+
/** Minimum ordinary grants that must survive project/machine ceilings (mirrors control-plane classFloor). */
|
|
63
|
+
export function classFloor(executionClass) {
|
|
64
|
+
switch (executionClass) {
|
|
65
|
+
case "observe":
|
|
66
|
+
case "observe_network":
|
|
67
|
+
case "verify":
|
|
68
|
+
return ["repo_read"];
|
|
69
|
+
case "publish_artifact":
|
|
70
|
+
case "mutate_repo":
|
|
71
|
+
return ["repo_read", "repo_write"];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
export function assertClassFloor(executionClass, ordinaryGrants) {
|
|
75
|
+
const grants = new Set(ordinaryGrants);
|
|
76
|
+
for (const grant of classFloor(executionClass)) {
|
|
77
|
+
if (!grants.has(grant)) {
|
|
78
|
+
return { ok: false, reason: `${executionClass}_requires_${grant}` };
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return { ok: true };
|
|
82
|
+
}
|
|
50
83
|
/**
|
|
51
84
|
* Live HTTP is a property of the assignment's capabilities, not of its class.
|
|
52
85
|
*
|
package/dist/execution.js
CHANGED
|
@@ -2,7 +2,8 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { ConduitRequestError } from "./client.js";
|
|
4
4
|
import { redactSecrets } from "./config.js";
|
|
5
|
-
import { DRIVERS, agentReportTemplate, buildAssignmentPrompt, evidenceKinds, fuelEndpoint, normalizeEvidenceDigest, parseAgentReport, pickModelCandidate,
|
|
5
|
+
import { DRIVERS, agentReportTemplate, buildAssignmentPrompt, evidenceKinds, fuelEndpoint, normalizeEvidenceDigest, parseAgentReport, pickModelCandidate, requireStampedExecutionClass, tierForRisk } from "./driver.js";
|
|
6
|
+
import { assertClassFloor } from "./execution-class.js";
|
|
6
7
|
import { pickDriverForClaim, resolveDriverFuel } from "./drivers.js";
|
|
7
8
|
import { attemptWorktreePath, createAttemptWorktree, proveResumeWorktree, quarantineAttemptWorktree, removeAttemptWorktree, } from "./attempt-worktree.js";
|
|
8
9
|
import { buildWorkspaceBrief, ensureCommitAvailable, normalizeRepositoryUrl, resolveAttemptStartCommit } from "./brief.js";
|
|
@@ -50,6 +51,13 @@ const taskSpecSchema = z.object({
|
|
|
50
51
|
repository: z.object({ url: z.string().optional(), base_commit: z.string().optional() }).nullable().optional(),
|
|
51
52
|
risk_level: z.string().optional(),
|
|
52
53
|
deliverable: z.enum(["repository", "artifact"]).optional().default("repository"),
|
|
54
|
+
execution_class: z.enum(["observe", "observe_network", "publish_artifact", "verify", "mutate_repo"]).optional(),
|
|
55
|
+
lands: z.boolean().optional(),
|
|
56
|
+
privileged_grants: z.array(z.string()).optional(),
|
|
57
|
+
external_network: z.boolean().optional(),
|
|
58
|
+
instruction: z.string().optional(),
|
|
59
|
+
evidence_standard: z.unknown().optional(),
|
|
60
|
+
working_language: z.enum(["en", "zh"]).optional(),
|
|
53
61
|
});
|
|
54
62
|
const executionContractSchema = z.object({
|
|
55
63
|
repository_fingerprint: z.string().nullable().optional().default(null),
|
|
@@ -324,33 +332,43 @@ async function runClaimedAssignment(client, config, driver, workspace, brief, ta
|
|
|
324
332
|
}
|
|
325
333
|
await client.updateAttempt(taskId, { worktreePath: attemptWorkspace });
|
|
326
334
|
}
|
|
327
|
-
//
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
335
|
+
// Stamped class is the source of truth (Bridge 0.11+). Do not re-derive from grants.
|
|
336
|
+
let executionClass;
|
|
337
|
+
try {
|
|
338
|
+
executionClass = requireStampedExecutionClass(spec.execution_class);
|
|
339
|
+
}
|
|
340
|
+
catch (error) {
|
|
341
|
+
const detail = error instanceof Error ? error.message : "missing stamped execution_class";
|
|
342
|
+
console.error(`Assignment ${taskId}: ${detail}`);
|
|
343
|
+
// Retryable: a machine that ops-installed Bridge 0.11 before the control-plane deploy can recover
|
|
344
|
+
// once assignments carry stamped execution_class (deploy CP → publish → ops install; see handoff).
|
|
345
|
+
await queueTerminal(client, taskId, {
|
|
346
|
+
action: "fail",
|
|
347
|
+
body: { error: detail, retryable: true, idempotency_key: `bridge:fail:${active.attemptId}:missing-class` },
|
|
348
|
+
});
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
// An artifact deliverable that is not publish_artifact cannot write/publish — fail closed.
|
|
343
352
|
if (artifactDelivery && executionClass !== "publish_artifact") {
|
|
344
|
-
const detail = `Artifact deliverable
|
|
353
|
+
const detail = `Artifact deliverable stamped as execution class ${executionClass}, which cannot write or publish it. Revise the plan to use publish_artifact.`;
|
|
345
354
|
console.error(`Assignment ${taskId}: ${detail}`);
|
|
346
|
-
// Not retryable: retrying cannot add a grant, so this must reach the owner as a plan correction
|
|
347
|
-
// rather than burn attempts. Same terminal path every other launch failure uses.
|
|
348
355
|
await queueTerminal(client, taskId, {
|
|
349
356
|
action: "fail",
|
|
350
357
|
body: { error: detail, retryable: false, idempotency_key: `bridge:fail:${active.attemptId}:artifact-class` },
|
|
351
358
|
});
|
|
352
359
|
return;
|
|
353
360
|
}
|
|
361
|
+
// Full classFloor must survive project/machine ceilings (observe/verify need repo_read too).
|
|
362
|
+
const floor = assertClassFloor(executionClass, grants);
|
|
363
|
+
if (!floor.ok) {
|
|
364
|
+
const detail = `Class floor unmet: ${floor.reason}`;
|
|
365
|
+
console.error(`Assignment ${taskId}: ${detail}`);
|
|
366
|
+
await queueTerminal(client, taskId, {
|
|
367
|
+
action: "fail",
|
|
368
|
+
body: { error: detail, retryable: false, idempotency_key: `bridge:fail:${active.attemptId}:class-floor` },
|
|
369
|
+
});
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
354
372
|
const prompt = buildAssignmentPrompt({ taskId, objective: task.objective, spec, grants, workspace: attemptWorkspace, currentHead: worktreeStart, reworkFeedback, workPackage, verificationCommands: liveBrief?.verification ?? [], executionClass });
|
|
355
373
|
const resuming = Boolean(options.forceResumeSessionId);
|
|
356
374
|
await client.attemptRequest(taskId, "progress", {
|
|
@@ -449,6 +467,8 @@ async function runClaimedAssignment(client, config, driver, workspace, brief, ta
|
|
|
449
467
|
grants: grants.filter((grant) => grant === "repo_read"),
|
|
450
468
|
capabilities: [],
|
|
451
469
|
verificationCommands: [],
|
|
470
|
+
// Envelope repair is read-only observe authority regardless of the original class.
|
|
471
|
+
executionClass: "observe",
|
|
452
472
|
resumeSessionId: result.sessionId ?? undefined,
|
|
453
473
|
timeoutMs,
|
|
454
474
|
model: selection.model,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@miraland-labs/conduit-bridge",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
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": {
|