189 lines
5.0 KiB
TypeScript
189 lines
5.0 KiB
TypeScript
import type { OpenClawConfig } from "../config/types.js";
|
|
import { resolveSecretInputRef } from "../config/types.secrets.js";
|
|
import { secretRefKey } from "../secrets/ref-contract.js";
|
|
import { resolveSecretRefValues } from "../secrets/resolve.js";
|
|
|
|
export type SecretInputUnresolvedReasonStyle = "generic" | "detailed"; // pragma: allowlist secret
|
|
export type ConfiguredSecretInputSource =
|
|
| "config"
|
|
| "secretRef" // pragma: allowlist secret
|
|
| "fallback";
|
|
|
|
function trimToUndefined(value: unknown): string | undefined {
|
|
if (typeof value !== "string") {
|
|
return undefined;
|
|
}
|
|
const trimmed = value.trim();
|
|
return trimmed.length > 0 ? trimmed : undefined;
|
|
}
|
|
|
|
function buildUnresolvedReason(params: {
|
|
path: string;
|
|
style: SecretInputUnresolvedReasonStyle;
|
|
kind: "unresolved" | "non-string" | "empty";
|
|
refLabel: string;
|
|
}): string {
|
|
if (params.style === "generic") {
|
|
return `${params.path} SecretRef is unresolved (${params.refLabel}).`;
|
|
}
|
|
if (params.kind === "non-string") {
|
|
return `${params.path} SecretRef resolved to a non-string value.`;
|
|
}
|
|
if (params.kind === "empty") {
|
|
return `${params.path} SecretRef resolved to an empty value.`;
|
|
}
|
|
return `${params.path} SecretRef is unresolved (${params.refLabel}).`;
|
|
}
|
|
|
|
export async function resolveConfiguredSecretInputString(params: {
|
|
config: OpenClawConfig;
|
|
env: NodeJS.ProcessEnv;
|
|
value: unknown;
|
|
path: string;
|
|
unresolvedReasonStyle?: SecretInputUnresolvedReasonStyle;
|
|
}): Promise<{ value?: string; unresolvedRefReason?: string }> {
|
|
const style = params.unresolvedReasonStyle ?? "generic";
|
|
const { ref } = resolveSecretInputRef({
|
|
value: params.value,
|
|
defaults: params.config.secrets?.defaults,
|
|
});
|
|
if (!ref) {
|
|
return { value: trimToUndefined(params.value) };
|
|
}
|
|
|
|
const refLabel = `${ref.source}:${ref.provider}:${ref.id}`;
|
|
try {
|
|
const resolved = await resolveSecretRefValues([ref], {
|
|
config: params.config,
|
|
env: params.env,
|
|
});
|
|
const resolvedValue = resolved.get(secretRefKey(ref));
|
|
if (typeof resolvedValue !== "string") {
|
|
return {
|
|
unresolvedRefReason: buildUnresolvedReason({
|
|
path: params.path,
|
|
style,
|
|
kind: "non-string",
|
|
refLabel,
|
|
}),
|
|
};
|
|
}
|
|
const trimmed = resolvedValue.trim();
|
|
if (trimmed.length === 0) {
|
|
return {
|
|
unresolvedRefReason: buildUnresolvedReason({
|
|
path: params.path,
|
|
style,
|
|
kind: "empty",
|
|
refLabel,
|
|
}),
|
|
};
|
|
}
|
|
return { value: trimmed };
|
|
} catch {
|
|
return {
|
|
unresolvedRefReason: buildUnresolvedReason({
|
|
path: params.path,
|
|
style,
|
|
kind: "unresolved",
|
|
refLabel,
|
|
}),
|
|
};
|
|
}
|
|
}
|
|
|
|
export async function resolveConfiguredSecretInputWithFallback(params: {
|
|
config: OpenClawConfig;
|
|
env: NodeJS.ProcessEnv;
|
|
value: unknown;
|
|
path: string;
|
|
unresolvedReasonStyle?: SecretInputUnresolvedReasonStyle;
|
|
readFallback?: () => string | undefined;
|
|
}): Promise<{
|
|
value?: string;
|
|
source?: ConfiguredSecretInputSource;
|
|
unresolvedRefReason?: string;
|
|
secretRefConfigured: boolean;
|
|
}> {
|
|
const { ref } = resolveSecretInputRef({
|
|
value: params.value,
|
|
defaults: params.config.secrets?.defaults,
|
|
});
|
|
const configValue = !ref ? trimToUndefined(params.value) : undefined;
|
|
if (configValue) {
|
|
return {
|
|
value: configValue,
|
|
source: "config",
|
|
secretRefConfigured: false,
|
|
};
|
|
}
|
|
if (!ref) {
|
|
const fallback = params.readFallback?.();
|
|
if (fallback) {
|
|
return {
|
|
value: fallback,
|
|
source: "fallback",
|
|
secretRefConfigured: false,
|
|
};
|
|
}
|
|
return { secretRefConfigured: false };
|
|
}
|
|
|
|
const resolved = await resolveConfiguredSecretInputString({
|
|
config: params.config,
|
|
env: params.env,
|
|
value: params.value,
|
|
path: params.path,
|
|
unresolvedReasonStyle: params.unresolvedReasonStyle,
|
|
});
|
|
if (resolved.value) {
|
|
return {
|
|
value: resolved.value,
|
|
source: "secretRef",
|
|
secretRefConfigured: true,
|
|
};
|
|
}
|
|
|
|
const fallback = params.readFallback?.();
|
|
if (fallback) {
|
|
return {
|
|
value: fallback,
|
|
source: "fallback",
|
|
secretRefConfigured: true,
|
|
};
|
|
}
|
|
|
|
return {
|
|
unresolvedRefReason: resolved.unresolvedRefReason,
|
|
secretRefConfigured: true,
|
|
};
|
|
}
|
|
|
|
export async function resolveRequiredConfiguredSecretRefInputString(params: {
|
|
config: OpenClawConfig;
|
|
env: NodeJS.ProcessEnv;
|
|
value: unknown;
|
|
path: string;
|
|
unresolvedReasonStyle?: SecretInputUnresolvedReasonStyle;
|
|
}): Promise<string | undefined> {
|
|
const { ref } = resolveSecretInputRef({
|
|
value: params.value,
|
|
defaults: params.config.secrets?.defaults,
|
|
});
|
|
if (!ref) {
|
|
return undefined;
|
|
}
|
|
|
|
const resolved = await resolveConfiguredSecretInputString({
|
|
config: params.config,
|
|
env: params.env,
|
|
value: params.value,
|
|
path: params.path,
|
|
unresolvedReasonStyle: params.unresolvedReasonStyle,
|
|
});
|
|
if (resolved.value) {
|
|
return resolved.value;
|
|
}
|
|
throw new Error(resolved.unresolvedRefReason ?? `${params.path} resolved to an empty value.`);
|
|
}
|