feat(FN-4717): complete Step 3 — widen worktrunk installer contract

Fusion-Task-Id: FN-4717
Fusion-Task-Lineage: a2533cd8-52c4-43a0-9daf-e0a49745306b
This commit is contained in:
Fusion (runfusion.ai)
2026-05-16 01:51:11 -07:00
committed by gsxdsm
parent f02482f8f3
commit 3f77bc2534
2 changed files with 22 additions and 1 deletions

View File

@@ -1,5 +1,6 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
import type { WorktrunkSettings } from "@fusion/core";
import type { AgentActionGateContext } from "../agent-action-gate.js";
import type { RunAuditor } from "../run-audit.js";
vi.mock("node:child_process", () => ({
@@ -173,6 +174,19 @@ describe("worktrunk-installer", () => {
});
});
it("accepts actionGateContext and preserves current disabled-install behavior", async () => {
mockExecSequence([{ stdout: "worktrunk 0.4.2\n" }]);
const result = await resolveWorktrunkBinary({
settings: makeSettings({ binaryPath: "/opt/worktrunk" }),
actionGateContext: {} as AgentActionGateContext,
});
const _sourceUnionAssertion: {
source: "override" | "path" | "cached" | "installed-release" | "installed-cargo";
} = result;
expect(_sourceUnionAssertion.source).toBe("override");
});
it("resolveWorktrunkBinary resolves PATH hit when override is absent", async () => {
mockExecSequence([
{ stdout: "/usr/bin/worktrunk\n" },

View File

@@ -5,6 +5,7 @@ import { promisify } from "node:util";
import type { ApprovalRequest, ApprovalRequestActorSnapshot, ApprovalRequestStore, WorktrunkSettings } from "@fusion/core";
import { createLogger } from "./logger.js";
import type { EngineRunContext, RunAuditor } from "./run-audit.js";
import type { AgentActionGateContext } from "./agent-action-gate.js";
const execAsync = promisify(exec);
const logger = createLogger("worktrunk-installer");
@@ -97,7 +98,11 @@ export async function resolveWorktrunkBinary(opts: {
settings: WorktrunkSettings;
auditor?: RunAuditor;
runContext?: EngineRunContext;
}): Promise<{ binaryPath: string; source: "override" | "path" | "cached" }> {
actionGateContext?: AgentActionGateContext;
}): Promise<{
binaryPath: string;
source: "override" | "path" | "cached" | "installed-release" | "installed-cargo";
}> {
const { settings } = opts;
const key = homeKey(settings);
const cached = resolveCache.get(key);
@@ -199,6 +204,7 @@ async function applyInstallGate(opts: {
auditor?: RunAuditor;
runContext?: EngineRunContext;
gateOverride?: "pre-approved";
actionGateContext?: AgentActionGateContext;
}): Promise<{ satisfied: boolean }> {
if (opts.gateOverride === "pre-approved") {
await emitBinaryAudit(opts.auditor, "binary:install-requested", {
@@ -222,6 +228,7 @@ export async function installWorktrunk(opts: {
auditor?: RunAuditor;
runContext?: EngineRunContext;
gateOverride?: "pre-approved";
actionGateContext?: AgentActionGateContext;
}): Promise<{ binaryPath: string; source: "installed-release" | "installed-cargo" }> {
await applyInstallGate(opts);
await emitBinaryAudit(opts.auditor, "binary:install-success", {