openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
316 lines (315 loc) • 17 kB
JavaScript
import { l as toErrorObject } from "./error-coercion-D_-xJ90S.js";
import { i as registerSecretValueForRedaction } from "./secret-redaction-registry-BOLC6DkF.js";
import "./errors-Db3Ymjlb.js";
import { f as secretRefKey } from "./ref-contract-D92DqQ-r.js";
import { a as getSecretAssignmentValidationFailures, d as pushWarning, h as combineSecretOwnerContractDigests, t as applyResolvedAssignments } from "./runtime-shared-CqbaocQu.js";
import { i as resolveSecretRefValuesSettledByProvider, r as resolveSecretRefValues } from "./resolve-224YoYfx.js";
import { a as isProviderScopedSecretResolutionError, o as isSecretResolutionError, t as describeSecretResolutionError } from "./resolve-errors-C2XRFrol.js";
import { t as resolveAuthProfileSecretOwnerId } from "./runtime-auth-profile-owner-DA1NOJfL.js";
import { i as associateSecretResolutionErrorOwners, l as isRetryableSecretDegradationReason } from "./runtime-degraded-state-D5EZZ925.js";
import { h as hasSameSecretProviderDefinition, l as getActiveSecretsRuntimeSnapshotState } from "./runtime-state-C4aJ8Hzz.js";
import { isDeepStrictEqual } from "node:util";
//#region src/secrets/runtime-assignment-provenance.ts
const assignmentSources = /* @__PURE__ */ new WeakMap();
function setSecretAssignmentSource(assignment, source) {
assignmentSources.set(assignment, source);
}
function getSecretAssignmentSource(assignment) {
return assignmentSources.get(assignment) ?? "config";
}
//#endregion
//#region src/secrets/runtime-owner-assignments.ts
/** Resolves SecretRef assignments atomically by owning runtime surface. */
/** Classifies whether an unresolved owner has an unchanged active SecretRef snapshot. */
function classifySecretOwnerDegradationState(params) {
if (params.refs.some((ref) => params.forceColdRefKeys?.has(secretRefKey(ref)))) return "cold";
const active = getActiveSecretsRuntimeSnapshotState();
if (!active || active.degradedOwners?.some((entry) => entry.ownerKind === params.ownerKind && entry.ownerId === params.ownerId && entry.degradationState !== "stale")) return "cold";
const activeOwner = active.secretOwners?.find((entry) => entry.ownerKind === params.ownerKind && entry.ownerId === params.ownerId);
const refKeys = params.refs.map(secretRefKey).toSorted();
const providerDefinitionsMatch = params.refs.every((ref) => hasSameSecretProviderDefinition(ref, [active.sourceConfig, params.config]));
return activeOwner && Boolean(params.contractDigest) && activeOwner.contractDigest === params.contractDigest && isDeepStrictEqual(activeOwner.refKeys.toSorted(), refKeys) && providerDefinitionsMatch ? "stale" : "cold";
}
function registerResolvedValuesForRedaction(resolved) {
for (const value of resolved.values()) if (typeof value === "string") registerSecretValueForRedaction(value);
}
function assignmentOwnerKey(assignment) {
return `${getSecretAssignmentSource(assignment)}\0${assignment.ownerKind}\0${assignment.ownerId}`;
}
const AUTH_STORE_PROVIDER_UNCONFIGURED_REASON = "secret provider is not configured";
function resolveOwnerFailureReason(params) {
if (params.fallback) return params.fallback;
const owner = params.assignments[0];
return owner && getSecretAssignmentSource(owner) === "auth-store" && isProviderScopedSecretResolutionError(params.error) && params.error.code === "SECRET_PROVIDER_NOT_CONFIGURED" ? AUTH_STORE_PROVIDER_UNCONFIGURED_REASON : void 0;
}
function groupAssignmentsByOwner(assignments) {
const groups = /* @__PURE__ */ new Map();
for (const assignment of assignments) {
const key = assignmentOwnerKey(assignment);
const group = groups.get(key);
if (group) {
const owner = group[0];
if (owner.requiredForGateway !== assignment.requiredForGateway || owner.disposition !== assignment.disposition) throw new Error(`Secret owner ${assignment.ownerKind}:${assignment.ownerId} has conflicting assignment policy.`);
group.push(assignment);
continue;
}
groups.set(key, [assignment]);
}
return [...groups.values()];
}
/** Captures every typed owner/ref relationship for later reload classification. */
function listSecretAssignmentOwners(assignments, resolvedValues) {
return groupAssignmentsByOwner(assignments).flatMap((ownerAssignments) => {
const owner = ownerAssignments[0];
return !owner || owner.ownerKind === "unknown" ? [] : [{
ownerKind: owner.ownerKind,
ownerId: owner.ownerId,
refKeys: ownerAssignments.map((assignment) => secretRefKey(assignment.ref)).toSorted(),
contractDigest: combineSecretOwnerContractDigests(ownerAssignments.flatMap((assignment) => assignment.ownerContractDigest ? [assignment.ownerContractDigest] : [])),
resolvedValues: ownerAssignments.flatMap((assignment) => {
const refKey = secretRefKey(assignment.ref);
return resolvedValues.has(refKey) ? [{
refKey,
value: structuredClone(resolvedValues.get(refKey))
}] : [];
})
}];
});
}
function createDegradedOwner(assignments, reason, degradationState = "cold", providerFailures, refFailureReason) {
const owner = assignments[0];
if (owner.ownerKind === "unknown") throw new Error(`Secret assignment ${owner.path} has no runtime owner.`);
return {
ownerKind: owner.ownerKind,
ownerId: owner.ownerId,
state: "unavailable",
degradationState,
paths: assignments.map((assignment) => assignment.path),
refKeys: assignments.map((assignment) => secretRefKey(assignment.ref)),
reason,
...providerFailures?.length ? { providerFailures } : {},
...refFailureReason ? { refFailureReason } : {}
};
}
function associateAssignmentFailureOwners(params) {
const validationFailures = getSecretAssignmentValidationFailures(params.error);
const validationFailureRefKeys = new Set(validationFailures.map((failure) => failure.refKey));
const validationFailureOwnerKeys = new Set(validationFailures.flatMap((failure) => params.assignments.filter((assignment) => assignment.ownerKind === failure.ownerKind && assignment.ownerId === failure.ownerId && assignment.expected === failure.expected && secretRefKey(assignment.ref) === failure.refKey).map(assignmentOwnerKey)));
const sharedReason = validationFailures.length > 0 ? "resolved secret value was invalid" : describeSecretResolutionError(params.error);
const authStoreProviderUnconfigured = isProviderScopedSecretResolutionError(params.error) && params.error.code === "SECRET_PROVIDER_NOT_CONFIGURED";
if (!sharedReason && !authStoreProviderUnconfigured) return;
const owners = groupAssignmentsByOwner(params.assignments).flatMap((assignments) => {
if (assignments[0]?.ownerKind === "unknown") return [];
const failureMatched = assignments.some((assignment) => validationFailures.length > 0 ? validationFailureOwnerKeys.has(assignmentOwnerKey(assignment)) : assignmentMatchesResolutionFailure(assignment, params.error));
if (!failureMatched) return [];
const reason = resolveOwnerFailureReason({
assignments,
error: params.error,
fallback: sharedReason
});
if (!reason) return [];
const degradedOwner = createDegradedOwner(assignments, reason);
return [{
...degradedOwner,
degradationState: classifySecretOwnerDegradationState({
ownerKind: degradedOwner.ownerKind,
ownerId: degradedOwner.ownerId,
refs: assignments.map((assignment) => assignment.ref),
config: params.config,
contractDigest: combineSecretOwnerContractDigests(assignments.flatMap((assignment) => assignment.ownerContractDigest ? [assignment.ownerContractDigest] : [])),
forceColdRefKeys: params.forceColdRefKeys
}),
failureMatched,
source: getSecretAssignmentSource(assignments[0])
}];
});
const failureRefs = new Map(validationFailures.length > 0 ? params.assignments.filter((assignment) => validationFailureRefKeys.has(secretRefKey(assignment.ref))).map((assignment) => [secretRefKey(assignment.ref), assignment.ref]) : params.assignments.filter((assignment) => assignmentMatchesResolutionFailure(assignment, params.error)).map((assignment) => [secretRefKey(assignment.ref), assignment.ref]));
const providerFailure = validationFailures.length === 0 && isProviderScopedSecretResolutionError(params.error) ? params.error : null;
const providerRefPrefix = providerFailure ? `${providerFailure.source}:${providerFailure.provider}:` : null;
const ownerKeys = new Set(owners.map((owner) => `${owner.source}\0${owner.ownerKind}\0${owner.ownerId}`));
const collectedOwnerKeys = new Set(params.assignments.map(assignmentOwnerKey));
const activeSnapshot = getActiveSecretsRuntimeSnapshotState();
const activeAuthOwnerIds = new Set((activeSnapshot?.authStores ?? []).flatMap(({ agentDir, store }) => Object.keys(store.profiles).map((profileId) => resolveAuthProfileSecretOwnerId({
agentDir,
profileId
}))));
const activeCoOwners = (activeSnapshot?.secretOwners ?? []).flatMap((owner) => {
const source = owner.ownerKind === "account" && activeAuthOwnerIds.has(owner.ownerId) ? "auth-store" : "config";
const ownerKey = `${source}\0${owner.ownerKind}\0${owner.ownerId}`;
if (ownerKeys.has(ownerKey) || collectedOwnerKeys.has(ownerKey)) return [];
const refs = owner.refKeys.flatMap((refKey) => {
const ref = failureRefs.get(refKey);
if (ref) return [ref];
if (!providerFailure || !providerRefPrefix || !refKey.startsWith(providerRefPrefix)) return [];
return [{
source: providerFailure.source,
provider: providerFailure.provider,
id: refKey.slice(providerRefPrefix.length)
}];
});
if (refs.length === 0) return [];
const reason = sharedReason ?? (source === "auth-store" && authStoreProviderUnconfigured ? AUTH_STORE_PROVIDER_UNCONFIGURED_REASON : void 0);
if (!reason) return [];
return [{
ownerKind: owner.ownerKind,
ownerId: owner.ownerId,
state: "unavailable",
paths: [],
refKeys: [...owner.refKeys],
reason,
degradationState: classifySecretOwnerDegradationState({
ownerKind: owner.ownerKind,
ownerId: owner.ownerId,
refs,
config: params.config,
contractDigest: owner.contractDigest,
forceColdRefKeys: params.forceColdRefKeys
}),
failureMatched: true,
source
}];
});
associateSecretResolutionErrorOwners(params.error, [...owners, ...activeCoOwners]);
}
/** Emits the canonical warning for one isolated runtime secret owner. */
function warnDegradedSecretOwner(context, owner) {
pushWarning(context, {
code: "SECRETS_OWNER_UNAVAILABLE",
path: owner.paths[0],
message: `Secret owner ${owner.ownerKind}:${owner.ownerId} is ${owner.degradationState === "stale" ? "using last-known-good" : "configured-unavailable"}; paths: ${owner.paths.join(", ")}; reason: ${owner.reason}.`
});
}
async function resolveStrictAssignments(params) {
try {
const resolved = await resolveSecretRefValues(params.assignments.map((assignment) => assignment.ref), params.options);
registerResolvedValuesForRedaction(resolved);
applyResolvedAssignments({
assignments: params.assignments,
resolved
});
return resolved;
} catch (error) {
associateAssignmentFailureOwners({
assignments: params.assignments,
error,
config: params.options.config,
forceColdRefKeys: params.forceColdRefKeys
});
throw error;
}
}
function assignmentMatchesResolutionFailure(assignment, error) {
if (!isSecretResolutionError(error)) return false;
if (assignment.ref.source !== error.source || assignment.ref.provider !== error.provider) return false;
return isProviderScopedSecretResolutionError(error) || assignment.ref.id.trim() === error.refId;
}
function assertOwnerCanBeIsolated(assignments, error) {
const owner = assignments[0];
const reason = resolveOwnerFailureReason({
assignments,
error,
fallback: describeSecretResolutionError(error)
});
const isolatableFailure = reason === AUTH_STORE_PROVIDER_UNCONFIGURED_REASON || reason !== void 0 && isRetryableSecretDegradationReason(reason);
if (!reason || !isolatableFailure || owner.ownerKind === "unknown" || owner.requiredForGateway || owner.disposition === "fail-closed") throw error;
return reason;
}
async function resolveAndApplySecretAssignments(params) {
if (!params.allowOwnerIsolation) return {
degradedOwners: [],
resolvedValues: await resolveStrictAssignments(params)
};
const degradedOwners = [];
const resolvedValues = /* @__PURE__ */ new Map();
let pendingOwners = groupAssignmentsByOwner(params.assignments);
while (pendingOwners.length > 0) {
const resolution = await resolveSecretRefValuesSettledByProvider(pendingOwners.flat().map((assignment) => assignment.ref), params.options);
registerResolvedValuesForRedaction(resolution.resolved);
const failedOwners = /* @__PURE__ */ new Map();
for (const failure of resolution.failures) {
associateAssignmentFailureOwners({
assignments: pendingOwners.flat(),
error: failure.error,
config: params.options.config,
forceColdRefKeys: params.forceColdRefKeys
});
const matchingOwners = pendingOwners.filter((assignments) => assignments.some((assignment) => assignmentMatchesResolutionFailure(assignment, failure.error)));
if (matchingOwners.length === 0) throw failure.error;
for (const assignments of matchingOwners) {
const reason = assertOwnerCanBeIsolated(assignments, failure.error);
const existing = failedOwners.get(assignments);
const providerFailure = isProviderScopedSecretResolutionError(failure.error) ? {
source: failure.error.source,
provider: failure.error.provider
} : void 0;
if (!existing) failedOwners.set(assignments, {
reason,
providerFailures: providerFailure ? [providerFailure] : [],
...!providerFailure ? { refFailureReason: reason } : {}
});
else if (providerFailure && !existing.providerFailures.some((entry) => entry.source === providerFailure.source && entry.provider === providerFailure.provider)) existing.providerFailures.push(providerFailure);
else if (!providerFailure && !existing.refFailureReason) existing.refFailureReason = reason;
}
}
const readyAssignments = pendingOwners.filter((assignments) => !failedOwners.has(assignments) && assignments.every((assignment) => resolution.resolved.has(secretRefKey(assignment.ref)))).flat();
if (readyAssignments.length > 0) try {
applyResolvedAssignments({
assignments: readyAssignments,
resolved: resolution.resolved
});
for (const assignment of readyAssignments) {
const refKey = secretRefKey(assignment.ref);
resolvedValues.set(refKey, structuredClone(resolution.resolved.get(refKey)));
}
} catch (error) {
associateAssignmentFailureOwners({
assignments: readyAssignments,
error,
config: params.options.config,
forceColdRefKeys: params.forceColdRefKeys
});
throw error;
}
const nextPendingOwners = [];
for (const assignments of pendingOwners) {
const failure = failedOwners.get(assignments);
if (failure) {
const owner = assignments[0];
let degradationState = classifySecretOwnerDegradationState({
ownerKind: owner.ownerKind,
ownerId: owner.ownerId,
refs: assignments.map((assignment) => assignment.ref),
config: params.options.config,
contractDigest: combineSecretOwnerContractDigests(assignments.flatMap((assignment) => assignment.ownerContractDigest ? [assignment.ownerContractDigest] : [])),
forceColdRefKeys: params.forceColdRefKeys
});
const activeOwner = degradationState === "stale" ? getActiveSecretsRuntimeSnapshotState()?.secretOwners?.find((entry) => entry.ownerKind === owner.ownerKind && entry.ownerId === owner.ownerId) : void 0;
const activeValues = new Map((activeOwner?.resolvedValues ?? []).map((entry) => [entry.refKey, entry.value]));
if (degradationState === "stale" && assignments.some((assignment) => !activeValues.has(secretRefKey(assignment.ref)))) degradationState = "cold";
for (const assignment of assignments) {
const refKey = secretRefKey(assignment.ref);
if (degradationState === "stale") {
const value = activeValues.get(refKey);
assignment.apply(structuredClone(value));
resolvedValues.set(refKey, structuredClone(value));
} else if (assignment.applyUnavailable) assignment.applyUnavailable();
else assignment.apply({ ...assignment.ref });
}
const degradedOwner = createDegradedOwner(assignments, failure.refFailureReason ?? failure.reason, degradationState, failure.providerFailures, failure.refFailureReason);
degradedOwners.push(degradedOwner);
warnDegradedSecretOwner(params.context, degradedOwner);
continue;
}
if (assignments.every((assignment) => resolution.resolved.has(secretRefKey(assignment.ref)))) continue;
nextPendingOwners.push(assignments);
}
if (nextPendingOwners.length === pendingOwners.length) throw toErrorObject(resolution.failures[0]?.error, "Secret resolution made no progress.");
pendingOwners = nextPendingOwners;
}
return {
degradedOwners,
resolvedValues
};
}
//#endregion
export { setSecretAssignmentSource as a, warnDegradedSecretOwner as i, listSecretAssignmentOwners as n, resolveAndApplySecretAssignments as r, classifySecretOwnerDegradationState as t };