mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-06 00:52:53 +00:00
* secret-provider-integrations Signed-off-by: sallyom <somalley@redhat.com> * feat(secrets): configure plugin provider presets * secrets: use plugin-managed provider refs Signed-off-by: sallyom <somalley@redhat.com> * fix secretref auth profile service env * test secret provider integration e2e * fix secretref plugin config service env * fix secret provider preset schema alignment * stabilize secret provider service proof * validate secret provider plugin integrations * harden secret provider resolver paths * scope secret provider config validation * stabilize openai secret provider proof * fix secret provider metadata proof * stabilize config baseline proof * fix secret provider e2e lint --------- Signed-off-by: sallyom <somalley@redhat.com> Co-authored-by: joshavant <830519+joshavant@users.noreply.github.com>
185 lines
5.9 KiB
TypeScript
185 lines
5.9 KiB
TypeScript
import fs from "node:fs";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
import type { PluginManifestRecord } from "../plugins/manifest-registry.js";
|
|
import { asConfig, setupSecretsRuntimeSnapshotTestHooks } from "./runtime.test-support.ts";
|
|
|
|
const manifestMocks = vi.hoisted(() => ({
|
|
listPluginOriginsFromMetadataSnapshot: vi.fn(
|
|
(snapshot: { plugins: Array<{ id: string; origin: string }> }) =>
|
|
new Map(snapshot.plugins.map((record) => [record.id, record.origin])),
|
|
),
|
|
loadPluginMetadataSnapshot: vi.fn<() => { plugins: Array<{ id: string; origin: string }> }>(
|
|
() => ({
|
|
plugins: [],
|
|
}),
|
|
),
|
|
}));
|
|
|
|
vi.mock("./runtime-manifest.runtime.js", () => ({
|
|
listPluginOriginsFromMetadataSnapshot: manifestMocks.listPluginOriginsFromMetadataSnapshot,
|
|
loadPluginMetadataSnapshot: manifestMocks.loadPluginMetadataSnapshot,
|
|
}));
|
|
|
|
const { prepareSecretsRuntimeSnapshot } = setupSecretsRuntimeSnapshotTestHooks();
|
|
|
|
describe("prepareSecretsRuntimeSnapshot loadable plugin origins", () => {
|
|
afterEach(() => {
|
|
manifestMocks.listPluginOriginsFromMetadataSnapshot.mockClear();
|
|
manifestMocks.loadPluginMetadataSnapshot.mockReset();
|
|
manifestMocks.loadPluginMetadataSnapshot.mockReturnValue({ plugins: [] });
|
|
});
|
|
|
|
it("skips metadata snapshot loading when plugin entries are absent", async () => {
|
|
await prepareSecretsRuntimeSnapshot({
|
|
config: asConfig({
|
|
models: {
|
|
providers: {
|
|
openai: {
|
|
apiKey: { source: "env", provider: "default", id: "OPENAI_API_KEY" },
|
|
models: [{ id: "gpt-5.4", name: "gpt-5.4" }],
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
env: { OPENAI_API_KEY: "sk-test" },
|
|
includeAuthStoreRefs: false,
|
|
});
|
|
|
|
expect(manifestMocks.loadPluginMetadataSnapshot).not.toHaveBeenCalled();
|
|
expect(manifestMocks.listPluginOriginsFromMetadataSnapshot).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("derives loadable plugin origins from the shared metadata snapshot", async () => {
|
|
const snapshot = {
|
|
plugins: [{ id: "demo", origin: "workspace" }],
|
|
};
|
|
manifestMocks.loadPluginMetadataSnapshot.mockReturnValue(snapshot);
|
|
|
|
await prepareSecretsRuntimeSnapshot({
|
|
config: asConfig({
|
|
plugins: {
|
|
entries: {
|
|
demo: {
|
|
config: {
|
|
apiKey: { source: "env", provider: "default", id: "DEMO_API_KEY" },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
env: { HOME: "/home/demo", DEMO_API_KEY: "sk-demo" },
|
|
includeAuthStoreRefs: false,
|
|
});
|
|
|
|
const snapshotCalls = manifestMocks.loadPluginMetadataSnapshot.mock.calls as unknown as Array<
|
|
[
|
|
{
|
|
config: {
|
|
plugins?: unknown;
|
|
};
|
|
workspaceDir: unknown;
|
|
env: Record<string, unknown>;
|
|
},
|
|
]
|
|
>;
|
|
const snapshotParams = snapshotCalls[0]?.[0];
|
|
expect(snapshotParams?.config.plugins).toStrictEqual({
|
|
entries: {
|
|
demo: {
|
|
config: {
|
|
apiKey: { source: "env", provider: "default", id: "DEMO_API_KEY" },
|
|
},
|
|
},
|
|
},
|
|
});
|
|
expect(typeof snapshotParams?.workspaceDir).toBe("string");
|
|
expect(snapshotParams?.env.HOME).toBe("/home/demo");
|
|
expect(snapshotParams?.env.DEMO_API_KEY).toBe("sk-demo");
|
|
expect(manifestMocks.listPluginOriginsFromMetadataSnapshot).toHaveBeenCalledWith(snapshot);
|
|
});
|
|
|
|
it("carries the shared manifest registry into plugin-managed SecretRef resolution", async () => {
|
|
const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "oc-runtime-secret-provider-"));
|
|
fs.writeFileSync(path.join(rootDir, "index.ts"), "export default {};\n", "utf8");
|
|
fs.writeFileSync(
|
|
path.join(rootDir, "resolve.mjs"),
|
|
[
|
|
"import process from 'node:process';",
|
|
"let input = '';",
|
|
"process.stdin.setEncoding('utf8');",
|
|
"process.stdin.on('data', (chunk) => input += chunk);",
|
|
"process.stdin.on('end', () => {",
|
|
" const request = JSON.parse(input);",
|
|
" process.stdout.write(JSON.stringify({ protocolVersion: 1, values: Object.fromEntries(request.ids.map((id) => [id, `value:${id}`])) }));",
|
|
"});",
|
|
"",
|
|
].join("\n"),
|
|
"utf8",
|
|
);
|
|
const plugin: PluginManifestRecord = {
|
|
id: "vault-secrets",
|
|
rootDir,
|
|
source: path.join(rootDir, "index.ts"),
|
|
manifestPath: path.join(rootDir, "openclaw.plugin.json"),
|
|
origin: "global",
|
|
channels: [],
|
|
providers: [],
|
|
cliBackends: [],
|
|
skills: [],
|
|
hooks: [],
|
|
secretProviderIntegrations: {
|
|
vault: {
|
|
providerAlias: "vault",
|
|
source: "exec",
|
|
command: "${node}",
|
|
args: ["./resolve.mjs"],
|
|
},
|
|
},
|
|
};
|
|
const pluginMetadataSnapshot = {
|
|
plugins: [plugin],
|
|
manifestRegistry: {
|
|
plugins: [plugin],
|
|
diagnostics: [],
|
|
},
|
|
};
|
|
|
|
try {
|
|
const snapshot = await prepareSecretsRuntimeSnapshot({
|
|
config: asConfig({
|
|
gateway: {
|
|
auth: {
|
|
mode: "token",
|
|
token: { source: "exec", provider: "vault", id: "gateway/token" },
|
|
},
|
|
},
|
|
secrets: {
|
|
providers: {
|
|
vault: {
|
|
source: "exec",
|
|
pluginIntegration: {
|
|
pluginId: "vault-secrets",
|
|
integrationId: "vault",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
env: { HOME: rootDir },
|
|
includeAuthStoreRefs: false,
|
|
pluginMetadataSnapshot,
|
|
});
|
|
|
|
expect(snapshot.config.gateway?.auth?.token).toBe("value:gateway/token");
|
|
expect(manifestMocks.loadPluginMetadataSnapshot).not.toHaveBeenCalled();
|
|
expect(manifestMocks.listPluginOriginsFromMetadataSnapshot).toHaveBeenCalledWith(
|
|
pluginMetadataSnapshot,
|
|
);
|
|
} finally {
|
|
fs.rmSync(rootDir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
});
|