@miraland-labs/conduit-bridge 0.4.0 → 0.4.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/README.md +1 -1
- package/dist/client.js +11 -3
- package/dist/driver.js +50 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -46,7 +46,7 @@ Every driver enforces the assignment's granted actions with its CLI's native mec
|
|
|
46
46
|
| --- | --- | --- |
|
|
47
47
|
| `claude-code` | pump or local | `--allowedTools` / `--disallowedTools` per grant; bounded verification commands |
|
|
48
48
|
| `codex` | pump or local | sandbox `read-only` / `workspace-write`; network enabled only with `pr_create`; resumes by thread id |
|
|
49
|
-
| `opencode` | pump or local | `plan`
|
|
49
|
+
| `opencode` | pump or local | `plan`/`build` agents; per-run `opencode.json` bash deny list (shared `deniedCommands`); `--session` resume; `--format json` |
|
|
50
50
|
| `cursor` | local only | per-run `.cursor/cli.json` allowlist (`approvalMode: allowlist`, shared deny list, no `--force`); read-only runs in `--mode plan` |
|
|
51
51
|
| `kiro` | local only | `--trust-tools=fs_read,fs_write,execute_bash` allowlist; `--no-interactive`; `--resume-id`. Tool-level only — no per-command scoping, so a bash grant trusts `execute_bash` wholesale (Antigravity-tier) |
|
|
52
52
|
| `antigravity` | local only | `-p --mode plan` (read-only) / `--mode accept-edits --sandbox` (write). Mode-level, coarser than per-tool; plain-text output; resume not surfaced |
|
package/dist/client.js
CHANGED
|
@@ -52,13 +52,21 @@ export class ConduitClient {
|
|
|
52
52
|
delete this.config.activeAttempts[taskId];
|
|
53
53
|
await this.persist(this.config);
|
|
54
54
|
}
|
|
55
|
-
/**
|
|
55
|
+
/**
|
|
56
|
+
* Ensure a project-scoped gateway fuel key for agent /v1 calls.
|
|
57
|
+
* GET creates once (returns secret) or reports provisioned; cache miss after
|
|
58
|
+
* that uses POST …/rotate so heartbeats never rotate on every claim.
|
|
59
|
+
*/
|
|
56
60
|
async ensureFuel(projectId) {
|
|
57
61
|
const cached = this.config.fuel?.[projectId]?.gatewayKey;
|
|
58
62
|
if (cached)
|
|
59
63
|
return cached;
|
|
60
|
-
const
|
|
61
|
-
|
|
64
|
+
const peeked = await this.request(`/runner/v1/fuel/${projectId}`);
|
|
65
|
+
let gatewayKey = typeof peeked.gateway_secret === "string" ? peeked.gateway_secret : "";
|
|
66
|
+
if (!gatewayKey) {
|
|
67
|
+
const rotated = await this.request(`/runner/v1/fuel/${projectId}/rotate`, { method: "POST", body: "{}" });
|
|
68
|
+
gatewayKey = String(rotated.gateway_secret);
|
|
69
|
+
}
|
|
62
70
|
this.config.fuel = { ...this.config.fuel, [projectId]: { gatewayKey } };
|
|
63
71
|
await this.persist(this.config);
|
|
64
72
|
return gatewayKey;
|
package/dist/driver.js
CHANGED
|
@@ -73,7 +73,7 @@ export function buildAssignmentPrompt(context) {
|
|
|
73
73
|
}
|
|
74
74
|
if (rework)
|
|
75
75
|
lines.push("", `REWORK FEEDBACK — an independent review returned this delivery; address every point\n${rework}`);
|
|
76
|
-
lines.push("", "RULES", "- Stay within the change scope and boundaries.", "- Run the relevant verification commands if your permissions allow it, and report their real results.", "- Never merge, deploy, push to protected branches, or touch production.", "- Do not invent evidence. Report unknown when you could not verify a criterion.", "", "When the work is finished, end your reply with exactly one fenced ```json block:", '{"outcome": "one-paragraph summary", "changes": ["path — what changed"], "verification": ["command — result"], "acceptance_results": [{"criterion": "exact criterion text", "status": "met|not_met|unknown"}], "evidence": [{"kind": "change|test|preview|research|documentation", "name": "concise evidence name", "uri": "external URL if one exists", "digest": "optional digest", "details": ["observable result"], "acceptance_criteria": ["exact criterion text supported by this evidence"]}], "assumptions": [], "risks": [], "limitations": [], "head_commit": "full sha of your final commit, omit if none"}');
|
|
76
|
+
lines.push("", "RULES", "- Stay within the change scope and boundaries.", "- Run the relevant verification commands if your permissions allow it, and report their real results.", "- Never merge, deploy, 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.", "", "When the work is finished, end your reply with exactly one fenced ```json block:", '{"outcome": "one-paragraph summary", "changes": ["path — what changed"], "verification": ["command — result"], "acceptance_results": [{"criterion": "exact criterion text", "status": "met|not_met|unknown"}], "evidence": [{"kind": "change|test|preview|research|documentation", "name": "concise evidence name", "uri": "external URL if one exists", "digest": "optional digest", "details": ["observable result"], "acceptance_criteria": ["exact criterion text supported by this evidence"]}], "assumptions": [], "risks": [], "limitations": [], "head_commit": "full sha of your final commit, omit if none"}');
|
|
77
77
|
return lines.join("\n");
|
|
78
78
|
}
|
|
79
79
|
/** Parse the agent's final fenced JSON block into a bounded report. */
|
|
@@ -383,6 +383,54 @@ export function openCodeRunArgs(input, agent) {
|
|
|
383
383
|
args.push("--session", input.resumeSessionId);
|
|
384
384
|
return args;
|
|
385
385
|
}
|
|
386
|
+
/** Shared deny list as OpenCode `permission.bash` rules (enforced even with `--auto`). */
|
|
387
|
+
export function openCodePermissionConfig() {
|
|
388
|
+
const bash = {};
|
|
389
|
+
for (const command of deniedCommands)
|
|
390
|
+
bash[`${command} *`] = "deny";
|
|
391
|
+
return { permission: { bash } };
|
|
392
|
+
}
|
|
393
|
+
/** Write opencode.json permission deny rules for the run, restoring any prior file. */
|
|
394
|
+
async function withOpenCodePermissions(workspace, run) {
|
|
395
|
+
const configPath = join(workspace, "opencode.json");
|
|
396
|
+
let previous = null;
|
|
397
|
+
try {
|
|
398
|
+
previous = await readFile(configPath, "utf8");
|
|
399
|
+
}
|
|
400
|
+
catch {
|
|
401
|
+
previous = null;
|
|
402
|
+
}
|
|
403
|
+
let config = {};
|
|
404
|
+
if (previous) {
|
|
405
|
+
try {
|
|
406
|
+
config = { ...JSON.parse(previous) };
|
|
407
|
+
}
|
|
408
|
+
catch {
|
|
409
|
+
config = {};
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
const deny = openCodePermissionConfig();
|
|
413
|
+
const existingPermission = (config.permission && typeof config.permission === "object" && !Array.isArray(config.permission))
|
|
414
|
+
? { ...config.permission }
|
|
415
|
+
: {};
|
|
416
|
+
const existingBash = (existingPermission.bash && typeof existingPermission.bash === "object" && !Array.isArray(existingPermission.bash))
|
|
417
|
+
? { ...existingPermission.bash }
|
|
418
|
+
: {};
|
|
419
|
+
config.permission = {
|
|
420
|
+
...existingPermission,
|
|
421
|
+
bash: { ...existingBash, ...deny.permission.bash },
|
|
422
|
+
};
|
|
423
|
+
await writeFile(configPath, `${JSON.stringify(config, null, 2)}\n`, "utf8");
|
|
424
|
+
try {
|
|
425
|
+
return await run();
|
|
426
|
+
}
|
|
427
|
+
finally {
|
|
428
|
+
if (previous === null)
|
|
429
|
+
await rm(configPath, { force: true });
|
|
430
|
+
else
|
|
431
|
+
await writeFile(configPath, previous, "utf8");
|
|
432
|
+
}
|
|
433
|
+
}
|
|
386
434
|
/**
|
|
387
435
|
* OpenCode `--format json` streams JSONL events. Verified against opencode 1.18:
|
|
388
436
|
* assistant text arrives as `{type:"text", part:{type:"text", text:"…"}}`, the
|
|
@@ -434,7 +482,7 @@ export const openCodeDriver = {
|
|
|
434
482
|
}
|
|
435
483
|
const args = openCodeRunArgs(input, agent);
|
|
436
484
|
args.push(input.prompt);
|
|
437
|
-
const { code, stdout, stderr } = await execute(input.executable ?? "opencode", args, input.workspace, input.timeoutMs ?? 20 * 60_000, fuelSource === "conduit" ? input.fuel : undefined, fuelSource);
|
|
485
|
+
const { code, stdout, stderr } = await withOpenCodePermissions(input.workspace, () => execute(input.executable ?? "opencode", args, input.workspace, input.timeoutMs ?? 20 * 60_000, fuelSource === "conduit" ? input.fuel : undefined, fuelSource));
|
|
438
486
|
const parsed = parseOpenCodeOutput(stdout);
|
|
439
487
|
if (code !== 0 || parsed.isError) {
|
|
440
488
|
return { status: "failed", resultText: parsed.resultText, sessionId: parsed.sessionId, error: (stderr || parsed.resultText || `opencode exited with code ${code}`).slice(0, 20_000) };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@miraland-labs/conduit-bridge",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Conduit Bridge CLI — join, connect, and run Claude Code / Codex / Cursor / OpenCode / Kiro / Antigravity agents for a Conduit organization",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|