mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-16 13:04:08 +00:00
feat(coding-agent): support shell commands and env vars in auth.json API keys
API keys in auth.json now support the same resolution as models.json: - Shell command: "\!command" executes and uses stdout (cached) - Environment variable: uses the value of the named variable - Literal value: used directly Extracted shared resolveConfigValue() to new resolve-config-value.ts module.
This commit is contained in:
parent
7c39a12a28
commit
9cf5758b68
6 changed files with 408 additions and 58 deletions
64
packages/coding-agent/src/core/resolve-config-value.ts
Normal file
64
packages/coding-agent/src/core/resolve-config-value.ts
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/**
|
||||
* Resolve configuration values that may be shell commands, environment variables, or literals.
|
||||
* Used by auth-storage.ts and model-registry.ts.
|
||||
*/
|
||||
|
||||
import { execSync } from "child_process";
|
||||
|
||||
// Cache for shell command results (persists for process lifetime)
|
||||
const commandResultCache = new Map<string, string | undefined>();
|
||||
|
||||
/**
|
||||
* Resolve a config value (API key, header value, etc.) to an actual value.
|
||||
* - If starts with "!", executes the rest as a shell command and uses stdout (cached)
|
||||
* - Otherwise checks environment variable first, then treats as literal (not cached)
|
||||
*/
|
||||
export function resolveConfigValue(config: string): string | undefined {
|
||||
if (config.startsWith("!")) {
|
||||
return executeCommand(config);
|
||||
}
|
||||
const envValue = process.env[config];
|
||||
return envValue || config;
|
||||
}
|
||||
|
||||
function executeCommand(commandConfig: string): string | undefined {
|
||||
if (commandResultCache.has(commandConfig)) {
|
||||
return commandResultCache.get(commandConfig);
|
||||
}
|
||||
|
||||
const command = commandConfig.slice(1);
|
||||
let result: string | undefined;
|
||||
try {
|
||||
const output = execSync(command, {
|
||||
encoding: "utf-8",
|
||||
timeout: 10000,
|
||||
stdio: ["ignore", "pipe", "ignore"],
|
||||
});
|
||||
result = output.trim() || undefined;
|
||||
} catch {
|
||||
result = undefined;
|
||||
}
|
||||
|
||||
commandResultCache.set(commandConfig, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve all header values using the same resolution logic as API keys.
|
||||
*/
|
||||
export function resolveHeaders(headers: Record<string, string> | undefined): Record<string, string> | undefined {
|
||||
if (!headers) return undefined;
|
||||
const resolved: Record<string, string> = {};
|
||||
for (const [key, value] of Object.entries(headers)) {
|
||||
const resolvedValue = resolveConfigValue(value);
|
||||
if (resolvedValue) {
|
||||
resolved[key] = resolvedValue;
|
||||
}
|
||||
}
|
||||
return Object.keys(resolved).length > 0 ? resolved : undefined;
|
||||
}
|
||||
|
||||
/** Clear the config value command cache. Exported for testing. */
|
||||
export function clearConfigValueCache(): void {
|
||||
commandResultCache.clear();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue