docs: document doctor repair policy helpers

This commit is contained in:
Peter Steinberger
2026-06-04 12:06:06 -04:00
parent 9d6e8b872a
commit 0913b6989c
6 changed files with 27 additions and 6 deletions

View File

@@ -1,5 +1,7 @@
/** Shared report types for post-upgrade doctor plugin probes. */
export type PostUpgradeFindingLevel = "ok" | "warn" | "error";
/** One post-upgrade validation finding, optionally tied to a plugin package entry. */
export type PostUpgradeFinding = {
level: PostUpgradeFindingLevel;
code: string;
@@ -8,11 +10,13 @@ export type PostUpgradeFinding = {
entry?: string;
};
/** Structured post-upgrade probe report returned by the probe runner. */
export type PostUpgradeReport = {
probesRun: string[];
findings: PostUpgradeFinding[];
};
/** Probe codes emitted by post-upgrade validation. */
export const POST_UPGRADE_PROBE_CODES = [
"plugin.index_unavailable",
"plugin.entry_unresolved",

View File

@@ -1,3 +1,4 @@
/** Doctor prompt adapter that centralizes repair, force, update, and noninteractive behavior. */
import { confirm, select } from "@clack/prompts";
import {
stylePromptHint,
@@ -30,6 +31,7 @@ export type DoctorPrompter = {
repairMode: DoctorRepairMode;
};
/** Creates a doctor prompter honoring --fix, --yes, --force, noninteractive, and update modes. */
export function createDoctorPrompter(params: {
runtime: RuntimeEnv;
options: DoctorOptions;

View File

@@ -1,3 +1,4 @@
/** Resolves doctor repair mode from CLI flags, TTY state, and update environment. */
import { isTruthyEnvValue } from "../infra/env.js";
import type { DoctorOptions } from "./doctor.types.js";
@@ -9,6 +10,7 @@ export type DoctorRepairMode = {
updateInProgress: boolean;
};
/** Resolves the effective repair/prompting mode for a doctor invocation. */
export function resolveDoctorRepairMode(options: DoctorOptions): DoctorRepairMode {
const yes = options.yes === true;
const requestedNonInteractive = options.nonInteractive === true;
@@ -28,10 +30,12 @@ export function resolveDoctorRepairMode(options: DoctorOptions): DoctorRepairMod
};
}
/** Returns true for noninteractive updater-driven doctor repair runs. */
export function isDoctorUpdateRepairMode(mode: DoctorRepairMode): boolean {
return mode.updateInProgress && mode.nonInteractive;
}
/** Returns whether a doctor repair prompt should be auto-approved under the current mode. */
export function shouldAutoApproveDoctorFix(
mode: DoctorRepairMode,
params: {

View File

@@ -1,3 +1,4 @@
/** Doctor checks and repairs for Docker sandbox images, namespaces, and registry state. */
import fs from "node:fs";
import path from "node:path";
import { note } from "../../packages/terminal-core/src/note.js";
@@ -266,6 +267,12 @@ async function handleMissingSandboxImage(
}
}
/**
* Checks configured sandbox images and optionally runs repo build scripts for missing defaults.
*
* Non-Docker backends skip Docker image checks; Docker mode also probes Codex bwrap namespace
* support because nested app-server shells rely on host user/network namespace policy.
*/
export async function maybeRepairSandboxImages(
cfg: OpenClawConfig,
runtime: RuntimeEnv,
@@ -369,6 +376,7 @@ function formatLegacyRegistryMigrationLine(result: LegacySandboxRegistryMigratio
return "";
}
/** Migrates legacy sandbox registry files into the current sharded registry layout. */
export async function maybeRepairSandboxRegistryFiles(prompter: DoctorPrompter): Promise<void> {
const legacyFiles = (await inspectLegacySandboxRegistryFiles()).filter((file) => file.exists);
if (legacyFiles.length === 0) {
@@ -396,6 +404,7 @@ export async function maybeRepairSandboxRegistryFiles(prompter: DoctorPrompter):
}
}
/** Warns when agent sandbox overrides are ignored because sandbox scope resolves to shared. */
export function noteSandboxScopeWarnings(cfg: OpenClawConfig) {
const globalSandbox = cfg.agents?.defaults?.sandbox;
const agents = Array.isArray(cfg.agents?.list) ? cfg.agents.list : [];

View File

@@ -1,3 +1,4 @@
/** Security warnings for gateway exposure, exec policy drift, channel DMs, and plaintext secrets. */
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
import { note } from "../../packages/terminal-core/src/note.js";
import { resolveDmAllowAuditState } from "../channels/message-access/dm-allow-state.js";
@@ -235,6 +236,7 @@ function collectPlaintextConfigSecretWarnings(cfg: OpenClawConfig): string[] {
];
}
/** Collects doctor security warnings without emitting terminal notes. */
export async function collectSecurityWarnings(
cfg: OpenClawConfig,
env: NodeJS.ProcessEnv = process.env,
@@ -255,12 +257,7 @@ export async function collectSecurityWarnings(
warnings.push(...collectPlaintextConfigSecretWarnings(cfg));
warnings.push(...collectDurableExecApprovalWarnings(cfg));
// ===========================================
// GATEWAY NETWORK EXPOSURE CHECK
// ===========================================
// Check for dangerous gateway binding configurations
// that expose the gateway to network without proper auth
// Network exposure needs auth proof before doctor can treat non-loopback bind as intentional.
const tailscaleMode = cfg.gateway?.tailscale?.mode ?? "off";
const gatewayBind = (cfg.gateway?.bind ?? "loopback") as string;
const customBindHost = cfg.gateway?.customBindHost?.trim();
@@ -438,6 +435,7 @@ export async function collectSecurityWarnings(
return warnings;
}
/** Emits security warnings plus the deep audit follow-up command. */
export async function noteSecurityWarnings(cfg: OpenClawConfig) {
const warnings = await collectSecurityWarnings(cfg);
const auditHint = `- Run: ${formatCliCommand("openclaw security audit --deep")}`;

View File

@@ -1,3 +1,4 @@
/** Policy wrapper for doctor repairs to services managed by external supervisors. */
import type { DoctorPrompter } from "./doctor-prompter.js";
type ServiceRepairPolicy = "auto" | "external";
@@ -7,6 +8,7 @@ export const SERVICE_REPAIR_POLICY_ENV = "OPENCLAW_SERVICE_REPAIR_POLICY";
export const EXTERNAL_SERVICE_REPAIR_NOTE =
"Gateway service is managed externally; skipped service install/start repair. Start or repair the gateway through your supervisor.";
/** Resolves whether doctor may repair managed services or must defer to an external supervisor. */
export function resolveServiceRepairPolicy(
env: NodeJS.ProcessEnv = process.env,
): ServiceRepairPolicy {
@@ -20,12 +22,14 @@ export function resolveServiceRepairPolicy(
}
}
/** Returns true when service repairs should only emit external-supervisor guidance. */
export function isServiceRepairExternallyManaged(
policy: ServiceRepairPolicy = resolveServiceRepairPolicy(),
): boolean {
return policy === "external";
}
/** Confirms a service repair unless the service repair policy is external. */
export async function confirmDoctorServiceRepair(
prompter: DoctorPrompter,
params: Parameters<DoctorPrompter["confirmRuntimeRepair"]>[0],