fix(ci): repair main type drift

This commit is contained in:
Vincent Koc
2026-04-10 08:02:47 +01:00
parent 7e7a8d6b0f
commit 78d2e9e2a8
6 changed files with 71 additions and 46 deletions

View File

@@ -1,4 +1,5 @@
import { describe, expect, it } from "vitest";
import type { SecretRef } from "../config/types.secrets.js";
import {
buildProviderRequestDispatcherPolicy,
mergeModelProviderRequestOverrides,
@@ -296,17 +297,32 @@ describe("provider request config", () => {
});
it("fails fast when configured request overrides still contain unresolved SecretRefs", () => {
const tenantRef: SecretRef = {
source: "env",
provider: "default",
id: "MEDIA_AUDIO_TENANT",
};
const tokenRef: SecretRef = {
source: "env",
provider: "default",
id: "MEDIA_AUDIO_TOKEN",
};
const certRef: SecretRef = {
source: "env",
provider: "default",
id: "MEDIA_AUDIO_CERT",
};
expect(() =>
sanitizeConfiguredProviderRequest({
headers: {
"X-Tenant": { source: "env", provider: "default", id: "MEDIA_AUDIO_TENANT" },
"X-Tenant": tenantRef,
},
auth: {
mode: "authorization-bearer",
token: { source: "env", provider: "default", id: "MEDIA_AUDIO_TOKEN" },
token: tokenRef,
},
tls: {
cert: { source: "env", provider: "default", id: "MEDIA_AUDIO_CERT" },
cert: certRef,
},
}),
).toThrow(/request\.(headers\.X-Tenant|auth\.token|tls\.cert): unresolved SecretRef/i);

View File

@@ -1,6 +1,9 @@
import type { Api } from "@mariozechner/pi-ai";
import type { ModelDefinitionConfig } from "../config/types.js";
import type { ConfiguredModelProviderRequest } from "../config/types.provider-request.js";
import type {
ConfiguredModelProviderRequest,
ConfiguredProviderRequest,
} from "../config/types.provider-request.js";
import { assertSecretInputResolved } from "../config/types.secrets.js";
import type { PinnedDispatcherPolicy } from "../infra/net/ssrf.js";
import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js";
@@ -177,7 +180,7 @@ function sanitizeConfiguredRequestString(value: unknown, path: string): string |
}
export function sanitizeConfiguredProviderRequest(
request: ProviderRequestTransportOverrides | undefined,
request: ConfiguredProviderRequest | undefined,
): ProviderRequestTransportOverrides | undefined {
if (!request || typeof request !== "object" || Array.isArray(request)) {
return undefined;
@@ -338,9 +341,6 @@ export function mergeProviderRequestOverrides(
...(current.auth ? { auth: current.auth } : {}),
...(current.proxy ? { proxy: current.proxy } : {}),
...(current.tls ? { tls: current.tls } : {}),
...(current.allowPrivateNetwork !== undefined
? { allowPrivateNetwork: current.allowPrivateNetwork }
: {}),
};
}
return merged;
@@ -349,7 +349,9 @@ export function mergeProviderRequestOverrides(
export function mergeModelProviderRequestOverrides(
...overrides: Array<ModelProviderRequestTransportOverrides | undefined>
): ModelProviderRequestTransportOverrides | undefined {
let merged = mergeProviderRequestOverrides(...overrides);
let merged: ModelProviderRequestTransportOverrides | undefined = mergeProviderRequestOverrides(
...overrides,
);
for (const current of overrides) {
if (current?.allowPrivateNetwork !== undefined) {
merged = {

View File

@@ -80,12 +80,14 @@ const mocks = vi.hoisted(() => {
};
},
),
readConfigFileSnapshot: vi.fn(async () => ({
readConfigFileSnapshot: vi.fn<
() => Promise<{ path: string; hash: string; config: OpenClawConfig }>
>(async () => ({
path: "/tmp/openclaw.json",
hash: "config-hash-1",
config: configState,
})),
readExecApprovalsSnapshot: vi.fn(() => ({
readExecApprovalsSnapshot: vi.fn<() => ExecApprovalsSnapshot>(() => ({
path: "/tmp/exec-approvals.json",
exists: true,
raw: "{}",
@@ -286,10 +288,10 @@ describe("exec-policy CLI", () => {
}),
0,
);
const [{ effectivePolicy }] = mocks.defaultRuntime.writeJson.mock.calls.at(-1) as [Record<
string,
unknown
>, number];
const [{ effectivePolicy }] = mocks.defaultRuntime.writeJson.mock.calls.at(-1) as [
Record<string, unknown>,
number,
];
expect((effectivePolicy as { scopes: Record<string, unknown>[] }).scopes[0]).not.toHaveProperty(
"allowedDecisions",
);
@@ -356,6 +358,7 @@ describe("exec-policy CLI", () => {
});
mocks.readConfigFileSnapshot.mockImplementationOnce(async () => ({
path: "/tmp/openclaw.json\u001B[2J\nforged",
hash: "config-hash-1",
config: mocks.getConfig(),
}));
mocks.readExecApprovalsSnapshot.mockImplementationOnce(() => ({
@@ -444,24 +447,23 @@ describe("exec-policy CLI", () => {
it("rolls back approvals if the config write fails after approvals save", async () => {
const originalApprovals = structuredClone(mocks.getApprovals());
const originalRaw = JSON.stringify(originalApprovals, null, 2);
const originalSnapshot = {
const originalSnapshot: ExecApprovalsSnapshot = {
path: "/tmp/exec-approvals.json",
exists: true,
raw: originalRaw,
hash: "approvals-hash",
file: originalApprovals,
} as ExecApprovalsSnapshot as ReturnType<typeof mocks.readExecApprovalsSnapshot>;
};
mocks.readExecApprovalsSnapshot
.mockImplementationOnce(() => originalSnapshot)
.mockImplementationOnce(
() =>
({
path: "/tmp/exec-approvals.json",
exists: true,
raw: JSON.stringify(mocks.getApprovals(), null, 2),
hash: hashApprovalsFile(mocks.getApprovals()),
file: structuredClone(mocks.getApprovals()),
}) as ExecApprovalsSnapshot as ReturnType<typeof mocks.readExecApprovalsSnapshot>,
(): ExecApprovalsSnapshot => ({
path: "/tmp/exec-approvals.json",
exists: true,
raw: JSON.stringify(mocks.getApprovals(), null, 2),
hash: hashApprovalsFile(mocks.getApprovals()),
file: structuredClone(mocks.getApprovals()),
}),
);
mocks.replaceConfigFile.mockImplementationOnce(async () => {
throw new Error("config write failed");
@@ -477,24 +479,23 @@ describe("exec-policy CLI", () => {
});
it("removes a newly-written approvals file when config replacement fails and the original file was missing", async () => {
const missingSnapshot = {
const missingSnapshot: ExecApprovalsSnapshot = {
path: "/tmp/missing-exec-approvals.json",
exists: false,
raw: null,
hash: "approvals-hash",
file: { version: 1, agents: {} },
} as ExecApprovalsSnapshot as ReturnType<typeof mocks.readExecApprovalsSnapshot>;
};
mocks.readExecApprovalsSnapshot
.mockImplementationOnce(() => missingSnapshot)
.mockImplementationOnce(
() =>
({
path: "/tmp/missing-exec-approvals.json",
exists: true,
raw: JSON.stringify(mocks.getApprovals(), null, 2),
hash: hashApprovalsFile(mocks.getApprovals()),
file: structuredClone(mocks.getApprovals()),
}) as ExecApprovalsSnapshot as ReturnType<typeof mocks.readExecApprovalsSnapshot>,
(): ExecApprovalsSnapshot => ({
path: "/tmp/missing-exec-approvals.json",
exists: true,
raw: JSON.stringify(mocks.getApprovals(), null, 2),
hash: hashApprovalsFile(mocks.getApprovals()),
file: structuredClone(mocks.getApprovals()),
}),
);
mocks.replaceConfigFile.mockImplementationOnce(async () => {
throw new Error("config write failed");
@@ -526,13 +527,13 @@ describe("exec-policy CLI", () => {
},
agents: {},
};
const concurrentSnapshot = {
const concurrentSnapshot: ExecApprovalsSnapshot = {
path: "/tmp/exec-approvals.json",
exists: true,
raw: JSON.stringify(concurrentFile, null, 2),
hash: "concurrent-write-hash",
file: concurrentFile,
} as ExecApprovalsSnapshot as ReturnType<typeof mocks.readExecApprovalsSnapshot>;
};
let snapshotReadCount = 0;
mocks.readExecApprovalsSnapshot.mockImplementation(() => {
snapshotReadCount += 1;

View File

@@ -1,5 +1,4 @@
import crypto from "node:crypto";
import fs from "node:fs";
import type { Command } from "commander";
import type { OpenClawConfig } from "../config/config.js";
import { readConfigFileSnapshot, replaceConfigFile } from "../config/config.js";
@@ -230,7 +229,9 @@ async function buildLocalExecPolicyShowPayload(): Promise<ExecPolicyShowPayload>
approvals: approvalsSnapshot.file,
hostPath: approvalsSnapshot.path,
}).map(buildExecPolicyShowScope);
const hasNodeRuntimeScope = scopes.some((scope) => scope.runtimeApprovalsSource === "node-runtime");
const hasNodeRuntimeScope = scopes.some(
(scope) => scope.runtimeApprovalsSource === "node-runtime",
);
return {
configPath: configSnapshot.path,
approvalsPath: approvalsSnapshot.path,

View File

@@ -33,8 +33,12 @@ function createStubChild(pid = 1234) {
child.emit("close", code, signal);
};
const emitExit = (code: number | null, signal: NodeJS.Signals | null = null) => {
child.exitCode = code;
child.signalCode = signal;
Object.defineProperty(child, "exitCode", { value: code, configurable: true, writable: true });
Object.defineProperty(child, "signalCode", {
value: signal,
configurable: true,
writable: true,
});
child.emit("exit", code, signal);
};
return { child, killMock, emitClose, emitExit };