fix: gate doctor oauth-dir repair by channel config

This commit is contained in:
Peter Steinberger
2026-02-21 18:08:05 +01:00
parent 905e355f65
commit 35a57bc940
3 changed files with 195 additions and 1 deletions

View File

@@ -132,6 +132,59 @@ function findOtherStateDirs(stateDir: string): string[] {
return found;
}
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null;
}
function isPairingPolicy(value: unknown): boolean {
return typeof value === "string" && value.trim().toLowerCase() === "pairing";
}
function hasPairingPolicy(value: unknown): boolean {
if (!isRecord(value)) {
return false;
}
if (isPairingPolicy(value.dmPolicy)) {
return true;
}
if (isRecord(value.dm) && isPairingPolicy(value.dm.policy)) {
return true;
}
if (!isRecord(value.accounts)) {
return false;
}
for (const accountCfg of Object.values(value.accounts)) {
if (hasPairingPolicy(accountCfg)) {
return true;
}
}
return false;
}
function shouldRequireOAuthDir(cfg: OpenClawConfig, env: NodeJS.ProcessEnv): boolean {
if (env.OPENCLAW_OAUTH_DIR?.trim()) {
return true;
}
const channels = cfg.channels;
if (!isRecord(channels)) {
return false;
}
// WhatsApp auth always uses the credentials tree.
if (isRecord(channels.whatsapp)) {
return true;
}
// Pairing allowlists are persisted under credentials/<channel>-allowFrom.json.
for (const [channelId, channelCfg] of Object.entries(channels)) {
if (channelId === "defaults" || channelId === "modelByChannel") {
continue;
}
if (hasPairingPolicy(channelCfg)) {
return true;
}
}
return false;
}
export async function noteStateIntegrity(
cfg: OpenClawConfig,
prompter: DoctorPrompterLike,
@@ -153,6 +206,7 @@ export async function noteStateIntegrity(
const displaySessionsDir = shortenHomePath(sessionsDir);
const displayStoreDir = shortenHomePath(storeDir);
const displayConfigPath = configPath ? shortenHomePath(configPath) : undefined;
const requireOAuthDir = shouldRequireOAuthDir(cfg, env);
let stateDirExists = existsDir(stateDir);
if (!stateDirExists) {
@@ -250,7 +304,13 @@ export async function noteStateIntegrity(
const dirCandidates = new Map<string, string>();
dirCandidates.set(sessionsDir, "Sessions dir");
dirCandidates.set(storeDir, "Session store dir");
dirCandidates.set(oauthDir, "OAuth dir");
if (requireOAuthDir) {
dirCandidates.set(oauthDir, "OAuth dir");
} else if (!existsDir(oauthDir)) {
warnings.push(
`- OAuth dir not present (${displayOauthDir}). Skipping create because no WhatsApp/pairing channel config is active.`,
);
}
const displayDirFor = (dir: string) => {
if (dir === sessionsDir) {
return displaySessionsDir;