openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
89 lines (88 loc) • 4.52 kB
JavaScript
import { t as hasErrnoCode } from "./errno-FR4CRyya.mjs";
import { t as formatErrorMessage } from "./errors-O33D01XW.mjs";
//#region src/config/io.write-errors.ts
const CONFIG_VALIDATION_FAILED_CODE = "CONFIG_VALIDATION_FAILED";
const CONFIG_INCLUDE_OWNERSHIP_CODE = "CONFIG_INCLUDE_OWNERSHIP";
/** A completed file write must not be handled as a retryable pre-write refusal. */
var ConfigWritePostCommitError = class extends Error {
constructor(params) {
const recovery = {
restored: "The config write was rolled back.",
"not-restored": "The write was not rolled back. Inspect the current config before retrying.",
unknown: "Rollback could not be confirmed. Inspect the current config before retrying."
}[params.rollbackStatus];
super(params.publication === "partial" ? `Config publication failed after removing ${params.configPath}: ${formatErrorMessage(params.cause)}\n${recovery} Inspect recovery backups at ${params.configPath}.bak.` : `Config was written to ${params.configPath}, but post-write processing failed: ${formatErrorMessage(params.cause)}\n${recovery}`, { cause: params.cause });
this.name = "ConfigWritePostCommitError";
this.configPath = params.configPath;
this.rollbackStatus = params.rollbackStatus;
this.publication = params.publication ?? "complete";
this.recoveryBackupPath = params.publication === "partial" ? `${params.configPath}.bak` : void 0;
}
};
async function recoverConfigWriteFailure(params) {
let rollbackStatus = "unknown";
let cause = params.cause;
try {
const restored = await params.restoreFile();
rollbackStatus = restored ? "restored" : "not-restored";
if (restored) await params.restoreEffects?.();
} catch (rollbackError) {
cause = new AggregateError([params.cause, rollbackError], `${formatErrorMessage(params.cause)} Recovery failed: ${formatErrorMessage(rollbackError)}`, { cause: rollbackError });
}
throw new ConfigWritePostCommitError({
configPath: params.configPath,
rollbackStatus,
cause,
publication: params.publication
});
}
/**
* Typed write refusal for a candidate that fails schema validation, so doctor
* can render "config left unchanged" plus the offending paths instead of crashing.
*/
function createConfigValidationFailedError(issues) {
const issue = issues[0];
return Object.assign(new Error(formatConfigValidationFailure(issue?.path || "<root>", issue?.message ?? "invalid")), {
code: CONFIG_VALIDATION_FAILED_CODE,
issues
});
}
/** True when a config write was refused because the candidate failed schema validation. */
function isConfigValidationFailedError(error) {
return error instanceof Error && hasErrnoCode(error, CONFIG_VALIDATION_FAILED_CODE);
}
/**
* Typed write refusal for a candidate that would flatten an $include-owned value
* into the root file, so doctor can record "config left unchanged" with the
* owning path instead of surfacing a raw error after advertising the repair.
*/
function createConfigIncludeOwnershipError(refusal) {
return Object.assign(/* @__PURE__ */ new Error(`Config write would flatten $include-owned config at ${refusal.ownedConfigPath}; edit that include file directly or remove the $include first.`), {
code: CONFIG_INCLUDE_OWNERSHIP_CODE,
...refusal
});
}
/** True when a config write was refused because it would flatten an included file. */
function isConfigIncludeOwnershipError(error) {
return error instanceof Error && hasErrnoCode(error, CONFIG_INCLUDE_OWNERSHIP_CODE);
}
const OPEN_DM_POLICY_ALLOW_FROM_RE = /^(?<policyPath>[a-z0-9_.-]+)\s*=\s*"open"\s+requires\s+(?<allowPath>[a-z0-9_.-]+)(?:\s+\(or\s+[a-z0-9_.-]+\))?\s+to include "\*"$/i;
function formatConfigValidationFailure(pathLabel, issueMessage) {
const match = issueMessage.match(OPEN_DM_POLICY_ALLOW_FROM_RE);
const policyPath = match?.groups?.policyPath?.trim();
const allowPath = match?.groups?.allowPath?.trim();
if (!policyPath || !allowPath) return `Config validation failed: ${pathLabel}: ${issueMessage}`;
return [
`Config validation failed: ${pathLabel}`,
"",
`Configuration mismatch: ${policyPath} is "open", but ${allowPath} does not include "*".`,
"",
"Fix with:",
` openclaw config set ${allowPath} '["*"]'`,
"",
"Or switch policy:",
` openclaw config set ${policyPath} "pairing"`
].join("\n");
}
//#endregion
export { isConfigValidationFailedError as a, isConfigIncludeOwnershipError as i, createConfigIncludeOwnershipError as n, recoverConfigWriteFailure as o, createConfigValidationFailedError as r, ConfigWritePostCommitError as t };