refactor(core): extract shared dedup helpers

This commit is contained in:
Peter Steinberger
2026-03-07 10:40:49 +00:00
parent 14c61bb33f
commit 3c71e2bd48
114 changed files with 3400 additions and 2040 deletions

View File

@@ -31,6 +31,7 @@ const mocks = vi.hoisted(() => ({
fsLstat: vi.fn(async (..._args: unknown[]) => null as import("node:fs").Stats | null),
fsRealpath: vi.fn(async (p: string) => p),
fsOpen: vi.fn(async () => ({}) as unknown),
writeFileWithinRoot: vi.fn(async () => {}),
}));
vi.mock("../../config/config.js", () => ({
@@ -77,6 +78,15 @@ vi.mock("../session-utils.js", () => ({
listAgentsForGateway: mocks.listAgentsForGateway,
}));
vi.mock("../../infra/fs-safe.js", async () => {
const actual =
await vi.importActual<typeof import("../../infra/fs-safe.js")>("../../infra/fs-safe.js");
return {
...actual,
writeFileWithinRoot: mocks.writeFileWithinRoot,
};
});
// Mock node:fs/promises agents.ts uses `import fs from "node:fs/promises"`
// which resolves to the module namespace default, so we spread actual and
// override the methods we need, plus set `default` explicitly.

View File

@@ -732,10 +732,19 @@ export const agentsHandlers: GatewayRequestHandlers = {
return;
}
const content = String(params.content ?? "");
const relativeWritePath = path.relative(resolvedPath.workspaceReal, resolvedPath.ioPath);
if (
!relativeWritePath ||
relativeWritePath.startsWith("..") ||
path.isAbsolute(relativeWritePath)
) {
respondWorkspaceFileUnsafe(respond, name);
return;
}
try {
await writeFileWithinRoot({
rootDir: workspaceDir,
relativePath: name,
rootDir: resolvedPath.workspaceReal,
relativePath: relativeWritePath,
data: content,
encoding: "utf8",
});

View File

@@ -274,20 +274,7 @@ export const nodeHandlers: GatewayRequestHandlers = {
});
return;
}
const p = params as {
nodeId: string;
displayName?: string;
platform?: string;
version?: string;
coreVersion?: string;
uiVersion?: string;
deviceFamily?: string;
modelIdentifier?: string;
caps?: string[];
commands?: string[];
remoteIp?: string;
silent?: boolean;
};
const p = params as Parameters<typeof requestNodePairing>[0];
await respondUnavailableOnThrow(respond, async () => {
const result = await requestNodePairing({
nodeId: p.nodeId,
@@ -300,6 +287,7 @@ export const nodeHandlers: GatewayRequestHandlers = {
modelIdentifier: p.modelIdentifier,
caps: p.caps,
commands: p.commands,
permissions: p.permissions,
remoteIp: p.remoteIp,
silent: p.silent,
});

View File

@@ -17,6 +17,27 @@ async function invokeSecretsReload(params: {
});
}
async function invokeSecretsResolve(params: {
handlers: ReturnType<typeof createSecretsHandlers>;
respond: ReturnType<typeof vi.fn>;
commandName: unknown;
targetIds: unknown;
}) {
await params.handlers["secrets.resolve"]({
req: { type: "req", id: "1", method: "secrets.resolve" },
params: {
commandName: params.commandName,
targetIds: params.targetIds,
},
client: null,
isWebchatConnect: () => false,
respond: params.respond as unknown as Parameters<
ReturnType<typeof createSecretsHandlers>["secrets.resolve"]
>[0]["respond"],
context: {} as never,
});
}
describe("secrets handlers", () => {
function createHandlers(overrides?: {
reloadSecrets?: () => Promise<{ warningCount: number }>;
@@ -73,13 +94,11 @@ describe("secrets handlers", () => {
});
const handlers = createHandlers({ resolveSecrets });
const respond = vi.fn();
await handlers["secrets.resolve"]({
req: { type: "req", id: "1", method: "secrets.resolve" },
params: { commandName: "memory status", targetIds: ["talk.apiKey"] },
client: null,
isWebchatConnect: () => false,
await invokeSecretsResolve({
handlers,
respond,
context: {} as never,
commandName: "memory status",
targetIds: ["talk.apiKey"],
});
expect(resolveSecrets).toHaveBeenCalledWith({
commandName: "memory status",
@@ -96,13 +115,11 @@ describe("secrets handlers", () => {
it("rejects invalid secrets.resolve params", async () => {
const handlers = createHandlers();
const respond = vi.fn();
await handlers["secrets.resolve"]({
req: { type: "req", id: "1", method: "secrets.resolve" },
params: { commandName: "", targetIds: "bad" },
client: null,
isWebchatConnect: () => false,
await invokeSecretsResolve({
handlers,
respond,
context: {} as never,
commandName: "",
targetIds: "bad",
});
expect(respond).toHaveBeenCalledWith(
false,
@@ -117,13 +134,11 @@ describe("secrets handlers", () => {
const resolveSecrets = vi.fn();
const handlers = createHandlers({ resolveSecrets });
const respond = vi.fn();
await handlers["secrets.resolve"]({
req: { type: "req", id: "1", method: "secrets.resolve" },
params: { commandName: "memory status", targetIds: ["talk.apiKey", 12] },
client: null,
isWebchatConnect: () => false,
await invokeSecretsResolve({
handlers,
respond,
context: {} as never,
commandName: "memory status",
targetIds: ["talk.apiKey", 12],
});
expect(resolveSecrets).not.toHaveBeenCalled();
expect(respond).toHaveBeenCalledWith(
@@ -140,13 +155,11 @@ describe("secrets handlers", () => {
const resolveSecrets = vi.fn();
const handlers = createHandlers({ resolveSecrets });
const respond = vi.fn();
await handlers["secrets.resolve"]({
req: { type: "req", id: "1", method: "secrets.resolve" },
params: { commandName: "memory status", targetIds: ["unknown.target"] },
client: null,
isWebchatConnect: () => false,
await invokeSecretsResolve({
handlers,
respond,
context: {} as never,
commandName: "memory status",
targetIds: ["unknown.target"],
});
expect(resolveSecrets).not.toHaveBeenCalled();
expect(respond).toHaveBeenCalledWith(
@@ -167,13 +180,11 @@ describe("secrets handlers", () => {
});
const handlers = createHandlers({ resolveSecrets });
const respond = vi.fn();
await handlers["secrets.resolve"]({
req: { type: "req", id: "1", method: "secrets.resolve" },
params: { commandName: "memory status", targetIds: ["talk.apiKey"] },
client: null,
isWebchatConnect: () => false,
await invokeSecretsResolve({
handlers,
respond,
context: {} as never,
commandName: "memory status",
targetIds: ["talk.apiKey"],
});
expect(respond).toHaveBeenCalledWith(
false,