feat(acp): U11/KTD10 — publish bundled bridge path on plugin load
The acp-runtime plugin's onLoad now publishes the identity-pinned bundled claude-code-cli-acp path to FUSION_CLAUDE_ACP_BRIDGE (when unset), so the pi-claude-cli kill-switch resolves the bridge WITHOUT a manual env var — no engine->plugin static coupling. Publishes the path only; the ACP transport stays OFF until an operator sets FUSION_CLAUDE_ACP=1 (rollout gate). Explicit env override wins; resolver is pinned to the plugin's node_modules/.bin shim. 204/204 plugin tests green (3 new KTD10 tests); typecheck clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -133,3 +133,36 @@ describe("resolveCliSettings", () => {
|
||||
expect(ask.allowUnrestricted).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("KTD10 — onLoad publishes the bundled bridge path (Route A)", () => {
|
||||
const savedBridge = process.env.FUSION_CLAUDE_ACP_BRIDGE;
|
||||
const savedFlag = process.env.FUSION_CLAUDE_ACP;
|
||||
afterEach(() => {
|
||||
if (savedBridge === undefined) delete process.env.FUSION_CLAUDE_ACP_BRIDGE;
|
||||
else process.env.FUSION_CLAUDE_ACP_BRIDGE = savedBridge;
|
||||
if (savedFlag === undefined) delete process.env.FUSION_CLAUDE_ACP;
|
||||
else process.env.FUSION_CLAUDE_ACP = savedFlag;
|
||||
});
|
||||
const fakeCtx = () => ({ settings: {}, logger: { info: () => undefined, warn: () => undefined } });
|
||||
|
||||
it("publishes the bundled bridge path to FUSION_CLAUDE_ACP_BRIDGE when unset", () => {
|
||||
delete process.env.FUSION_CLAUDE_ACP_BRIDGE;
|
||||
plugin.hooks?.onLoad?.(fakeCtx() as never);
|
||||
expect(process.env.FUSION_CLAUDE_ACP_BRIDGE).toBeDefined();
|
||||
expect(isAbsolute(process.env.FUSION_CLAUDE_ACP_BRIDGE!)).toBe(true);
|
||||
expect(process.env.FUSION_CLAUDE_ACP_BRIDGE).toContain("node_modules/.bin/claude-code-cli-acp");
|
||||
});
|
||||
|
||||
it("does NOT enable the transport — FUSION_CLAUDE_ACP stays unset (kill-switch off)", () => {
|
||||
delete process.env.FUSION_CLAUDE_ACP;
|
||||
delete process.env.FUSION_CLAUDE_ACP_BRIDGE;
|
||||
plugin.hooks?.onLoad?.(fakeCtx() as never);
|
||||
expect(process.env.FUSION_CLAUDE_ACP).toBeUndefined();
|
||||
});
|
||||
|
||||
it("respects an explicit FUSION_CLAUDE_ACP_BRIDGE override", () => {
|
||||
process.env.FUSION_CLAUDE_ACP_BRIDGE = "/custom/bridge/path";
|
||||
plugin.hooks?.onLoad?.(fakeCtx() as never);
|
||||
expect(process.env.FUSION_CLAUDE_ACP_BRIDGE).toBe("/custom/bridge/path");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { definePlugin } from "@fusion/plugin-sdk";
|
||||
import type { FusionPlugin, PluginRuntimeFactory, PluginRuntimeManifestMetadata } from "@fusion/plugin-sdk";
|
||||
import { resolveCliSettings } from "./cli-spawn.js";
|
||||
import { resolveCliSettings, resolveBundledClaudeBridgeBinary } from "./cli-spawn.js";
|
||||
import { AcpRuntimeAdapter } from "./runtime-adapter.js";
|
||||
import { killAllProcesses } from "./process-manager.js";
|
||||
import { setupHooks, setupManifest } from "./setup.js";
|
||||
@@ -49,6 +49,25 @@ const plugin: FusionPlugin = definePlugin({
|
||||
"will be auto-approved under an allow-all policy. Prefer an approval-required policy.",
|
||||
);
|
||||
}
|
||||
// KTD10 (Route A): publish the bundled `claude-code-cli-acp` bridge path
|
||||
// process-wide so the pi-claude-cli provider's kill-switch can resolve it
|
||||
// WITHOUT a manual FUSION_CLAUDE_ACP_BRIDGE env var. This only PUBLISHES the
|
||||
// path — the ACP transport stays OFF until an operator sets
|
||||
// FUSION_CLAUDE_ACP=1 (the rollout gate). An explicit env override wins, and
|
||||
// the resolver is identity-pinned to the plugin-owned node_modules/.bin shim
|
||||
// so a same-named global binary cannot replace the reviewed bridge.
|
||||
if (!process.env.FUSION_CLAUDE_ACP_BRIDGE) {
|
||||
const resolved = resolveBundledClaudeBridgeBinary();
|
||||
if (resolved.kind === "resolved") {
|
||||
process.env.FUSION_CLAUDE_ACP_BRIDGE = resolved.path;
|
||||
ctx.logger.info(
|
||||
"ACP Runtime: published bundled Claude bridge path for Route A " +
|
||||
"(transport stays off until FUSION_CLAUDE_ACP=1).",
|
||||
);
|
||||
} else {
|
||||
ctx.logger.info(`ACP Runtime: bundled Claude bridge not resolved (${resolved.reason}); Route A unavailable.`);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
runtime: {
|
||||
|
||||
Reference in New Issue
Block a user