refactor: dedupe cli config cron and install flows

This commit is contained in:
Peter Steinberger
2026-03-02 19:48:38 +00:00
parent 9d30159fcd
commit b1c30f0ba9
80 changed files with 1379 additions and 2027 deletions

View File

@@ -27,6 +27,46 @@ describe("secret ref resolver", () => {
return dir;
};
type ExecProviderConfig = {
source: "exec";
command: string;
passEnv?: string[];
jsonOnly?: boolean;
allowSymlinkCommand?: boolean;
trustedDirs?: string[];
args?: string[];
};
function createExecProviderConfig(
command: string,
overrides: Partial<ExecProviderConfig> = {},
): ExecProviderConfig {
return {
source: "exec",
command,
passEnv: ["PATH"],
...overrides,
};
}
async function resolveExecSecret(
command: string,
overrides: Partial<ExecProviderConfig> = {},
): Promise<string> {
return resolveSecretRefString(
{ source: "exec", provider: "execmain", id: "openai/api-key" },
{
config: {
secrets: {
providers: {
execmain: createExecProviderConfig(command, overrides),
},
},
},
},
);
}
beforeAll(async () => {
fixtureRoot = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-secrets-resolve-"));
const sharedExecDir = path.join(fixtureRoot, "shared-exec");
@@ -134,22 +174,7 @@ describe("secret ref resolver", () => {
return;
}
const value = await resolveSecretRefString(
{ source: "exec", provider: "execmain", id: "openai/api-key" },
{
config: {
secrets: {
providers: {
execmain: {
source: "exec",
command: execProtocolV1ScriptPath,
passEnv: ["PATH"],
},
},
},
},
},
);
const value = await resolveExecSecret(execProtocolV1ScriptPath);
expect(value).toBe("value:openai/api-key");
});
@@ -158,23 +183,7 @@ describe("secret ref resolver", () => {
return;
}
const value = await resolveSecretRefString(
{ source: "exec", provider: "execmain", id: "openai/api-key" },
{
config: {
secrets: {
providers: {
execmain: {
source: "exec",
command: execPlainScriptPath,
passEnv: ["PATH"],
jsonOnly: false,
},
},
},
},
},
);
const value = await resolveExecSecret(execPlainScriptPath, { jsonOnly: false });
expect(value).toBe("plain-secret");
});
@@ -210,25 +219,9 @@ describe("secret ref resolver", () => {
const symlinkPath = path.join(root, "resolver-link.mjs");
await fs.symlink(execPlainScriptPath, symlinkPath);
await expect(
resolveSecretRefString(
{ source: "exec", provider: "execmain", id: "openai/api-key" },
{
config: {
secrets: {
providers: {
execmain: {
source: "exec",
command: symlinkPath,
passEnv: ["PATH"],
jsonOnly: false,
},
},
},
},
},
),
).rejects.toThrow("must not be a symlink");
await expect(resolveExecSecret(symlinkPath, { jsonOnly: false })).rejects.toThrow(
"must not be a symlink",
);
});
it("allows symlink command paths when allowSymlinkCommand is enabled", async () => {
@@ -240,25 +233,11 @@ describe("secret ref resolver", () => {
await fs.symlink(execPlainScriptPath, symlinkPath);
const trustedRoot = await fs.realpath(fixtureRoot);
const value = await resolveSecretRefString(
{ source: "exec", provider: "execmain", id: "openai/api-key" },
{
config: {
secrets: {
providers: {
execmain: {
source: "exec",
command: symlinkPath,
passEnv: ["PATH"],
jsonOnly: false,
allowSymlinkCommand: true,
trustedDirs: [trustedRoot],
},
},
},
},
},
);
const value = await resolveExecSecret(symlinkPath, {
jsonOnly: false,
allowSymlinkCommand: true,
trustedDirs: [trustedRoot],
});
expect(value).toBe("plain-secret");
});
@@ -287,44 +266,15 @@ describe("secret ref resolver", () => {
await fs.symlink(targetCommand, symlinkCommand);
const trustedRoot = await fs.realpath(root);
await expect(
resolveSecretRefString(
{ source: "exec", provider: "execmain", id: "openai/api-key" },
{
config: {
secrets: {
providers: {
execmain: {
source: "exec",
command: symlinkCommand,
args: ["brew"],
passEnv: ["PATH"],
},
},
},
},
},
),
).rejects.toThrow("must not be a symlink");
const value = await resolveSecretRefString(
{ source: "exec", provider: "execmain", id: "openai/api-key" },
{
config: {
secrets: {
providers: {
execmain: {
source: "exec",
command: symlinkCommand,
args: ["brew"],
allowSymlinkCommand: true,
trustedDirs: [trustedRoot],
},
},
},
},
},
await expect(resolveExecSecret(symlinkCommand, { args: ["brew"] })).rejects.toThrow(
"must not be a symlink",
);
const value = await resolveExecSecret(symlinkCommand, {
args: ["brew"],
allowSymlinkCommand: true,
trustedDirs: [trustedRoot],
});
expect(value).toBe("brew:openai/api-key");
});
@@ -337,25 +287,11 @@ describe("secret ref resolver", () => {
await fs.symlink(execPlainScriptPath, symlinkPath);
await expect(
resolveSecretRefString(
{ source: "exec", provider: "execmain", id: "openai/api-key" },
{
config: {
secrets: {
providers: {
execmain: {
source: "exec",
command: symlinkPath,
passEnv: ["PATH"],
jsonOnly: false,
allowSymlinkCommand: true,
trustedDirs: [root],
},
},
},
},
},
),
resolveExecSecret(symlinkPath, {
jsonOnly: false,
allowSymlinkCommand: true,
trustedDirs: [root],
}),
).rejects.toThrow("outside trustedDirs");
});
@@ -363,73 +299,27 @@ describe("secret ref resolver", () => {
if (process.platform === "win32") {
return;
}
await expect(
resolveSecretRefString(
{ source: "exec", provider: "execmain", id: "openai/api-key" },
{
config: {
secrets: {
providers: {
execmain: {
source: "exec",
command: execProtocolV2ScriptPath,
passEnv: ["PATH"],
},
},
},
},
},
),
).rejects.toThrow("protocolVersion must be 1");
await expect(resolveExecSecret(execProtocolV2ScriptPath)).rejects.toThrow(
"protocolVersion must be 1",
);
});
it("rejects exec refs when response omits requested id", async () => {
if (process.platform === "win32") {
return;
}
await expect(
resolveSecretRefString(
{ source: "exec", provider: "execmain", id: "openai/api-key" },
{
config: {
secrets: {
providers: {
execmain: {
source: "exec",
command: execMissingIdScriptPath,
passEnv: ["PATH"],
},
},
},
},
},
),
).rejects.toThrow('response missing id "openai/api-key"');
await expect(resolveExecSecret(execMissingIdScriptPath)).rejects.toThrow(
'response missing id "openai/api-key"',
);
});
it("rejects exec refs with invalid JSON when jsonOnly is true", async () => {
if (process.platform === "win32") {
return;
}
await expect(
resolveSecretRefString(
{ source: "exec", provider: "execmain", id: "openai/api-key" },
{
config: {
secrets: {
providers: {
execmain: {
source: "exec",
command: execInvalidJsonScriptPath,
passEnv: ["PATH"],
jsonOnly: true,
},
},
},
},
},
),
).rejects.toThrow("returned invalid JSON");
await expect(resolveExecSecret(execInvalidJsonScriptPath, { jsonOnly: true })).rejects.toThrow(
"returned invalid JSON",
);
});
it("supports file singleValue mode with id=value", async () => {