refactor(exec-approvals): share socket default merge

This commit is contained in:
Peter Steinberger
2026-02-15 17:36:08 +00:00
parent 5c88d3c9f1
commit a0e763168f
4 changed files with 54 additions and 26 deletions

View File

@@ -1,9 +1,9 @@
import type { GatewayRequestHandlers, RespondFn } from "./types.js";
import {
ensureExecApprovals,
mergeExecApprovalsSocketDefaults,
normalizeExecApprovals,
readExecApprovalsSnapshot,
resolveExecApprovalsSocketPath,
saveExecApprovals,
type ExecApprovalsFile,
type ExecApprovalsSnapshot,
@@ -134,18 +134,7 @@ export const execApprovalsHandlers: GatewayRequestHandlers = {
return;
}
const normalized = normalizeExecApprovals(incoming as ExecApprovalsFile);
const currentSocketPath = snapshot.file.socket?.path?.trim();
const currentToken = snapshot.file.socket?.token?.trim();
const socketPath =
normalized.socket?.path?.trim() ?? currentSocketPath ?? resolveExecApprovalsSocketPath();
const token = normalized.socket?.token?.trim() ?? currentToken ?? "";
const next: ExecApprovalsFile = {
...normalized,
socket: {
path: socketPath,
token,
},
};
const next = mergeExecApprovalsSocketDefaults({ normalized, current: snapshot.file });
saveExecApprovals(next);
const nextSnapshot = readExecApprovalsSnapshot();
respond(

View File

@@ -0,0 +1,32 @@
import { describe, expect, it } from "vitest";
import { mergeExecApprovalsSocketDefaults, normalizeExecApprovals } from "./exec-approvals.js";
describe("mergeExecApprovalsSocketDefaults", () => {
it("prefers normalized socket, then current, then default path", () => {
const normalized = normalizeExecApprovals({
version: 1,
agents: {},
socket: { path: "/tmp/a.sock", token: "a" },
});
const current = normalizeExecApprovals({
version: 1,
agents: {},
socket: { path: "/tmp/b.sock", token: "b" },
});
const merged = mergeExecApprovalsSocketDefaults({ normalized, current });
expect(merged.socket?.path).toBe("/tmp/a.sock");
expect(merged.socket?.token).toBe("a");
});
it("falls back to current token when missing in normalized", () => {
const normalized = normalizeExecApprovals({ version: 1, agents: {} });
const current = normalizeExecApprovals({
version: 1,
agents: {},
socket: { path: "/tmp/b.sock", token: "b" },
});
const merged = mergeExecApprovalsSocketDefaults({ normalized, current });
expect(merged.socket?.path).toBeTruthy();
expect(merged.socket?.token).toBe("b");
});
});

View File

@@ -241,6 +241,24 @@ export function normalizeExecApprovals(file: ExecApprovalsFile): ExecApprovalsFi
return normalized;
}
export function mergeExecApprovalsSocketDefaults(params: {
normalized: ExecApprovalsFile;
current?: ExecApprovalsFile;
}): ExecApprovalsFile {
const currentSocketPath = params.current?.socket?.path?.trim();
const currentToken = params.current?.socket?.token?.trim();
const socketPath =
params.normalized.socket?.path?.trim() ?? currentSocketPath ?? resolveExecApprovalsSocketPath();
const token = params.normalized.socket?.token?.trim() ?? currentToken ?? "";
return {
...params.normalized,
socket: {
path: socketPath,
token,
},
};
}
function generateToken(): string {
return crypto.randomBytes(24).toString("base64url");
}

View File

@@ -12,12 +12,12 @@ import {
evaluateShellAllowlist,
requiresExecApproval,
normalizeExecApprovals,
mergeExecApprovalsSocketDefaults,
recordAllowlistUse,
resolveExecApprovals,
resolveSafeBins,
ensureExecApprovals,
readExecApprovalsSnapshot,
resolveExecApprovalsSocketPath,
saveExecApprovals,
type ExecAsk,
type ExecApprovalsFile,
@@ -422,18 +422,7 @@ export async function handleInvoke(
const snapshot = readExecApprovalsSnapshot();
requireExecApprovalsBaseHash(params, snapshot);
const normalized = normalizeExecApprovals(params.file);
const currentSocketPath = snapshot.file.socket?.path?.trim();
const currentToken = snapshot.file.socket?.token?.trim();
const socketPath =
normalized.socket?.path?.trim() ?? currentSocketPath ?? resolveExecApprovalsSocketPath();
const token = normalized.socket?.token?.trim() ?? currentToken ?? "";
const next: ExecApprovalsFile = {
...normalized,
socket: {
path: socketPath,
token,
},
};
const next = mergeExecApprovalsSocketDefaults({ normalized, current: snapshot.file });
saveExecApprovals(next);
const nextSnapshot = readExecApprovalsSnapshot();
const payload: ExecApprovalsSnapshot = {