refactor: move whatsapp allowFrom config

This commit is contained in:
Peter Steinberger
2026-01-02 12:59:47 +01:00
parent 58d32d4542
commit 0766c5e3cb
39 changed files with 452 additions and 117 deletions

View File

@@ -5,6 +5,7 @@ import type { ClawdisConfig } from "../config/config.js";
import {
CONFIG_PATH_CLAWDIS,
readConfigFileSnapshot,
validateConfigObject,
writeConfigFile,
} from "../config/config.js";
import { resolveGatewayService } from "../daemon/service.js";
@@ -19,6 +20,65 @@ import {
printWizardHeader,
} from "./onboard-helpers.js";
type LegacyMigration = {
id: string;
describe: string;
apply: (raw: Record<string, unknown>, changes: string[]) => void;
};
const LEGACY_MIGRATIONS: LegacyMigration[] = [
// Legacy migration (2026-01-02, commit: TBD) — normalize per-provider allowlists; move WhatsApp gating into whatsapp.allowFrom.
{
id: "routing.allowFrom->whatsapp.allowFrom",
describe: "Move routing.allowFrom to whatsapp.allowFrom",
apply: (raw, changes) => {
const routing = raw.routing;
if (!routing || typeof routing !== "object") return;
const allowFrom = (routing as Record<string, unknown>).allowFrom;
if (allowFrom === undefined) return;
const whatsapp =
raw.whatsapp && typeof raw.whatsapp === "object"
? (raw.whatsapp as Record<string, unknown>)
: {};
if (whatsapp.allowFrom === undefined) {
whatsapp.allowFrom = allowFrom;
changes.push("Moved routing.allowFrom → whatsapp.allowFrom.");
} else {
changes.push("Removed routing.allowFrom (whatsapp.allowFrom already set).");
}
delete (routing as Record<string, unknown>).allowFrom;
if (Object.keys(routing as Record<string, unknown>).length === 0) {
delete raw.routing;
}
raw.whatsapp = whatsapp;
},
},
];
function applyLegacyMigrations(raw: unknown): {
config: ClawdisConfig | null;
changes: string[];
} {
if (!raw || typeof raw !== "object") return { config: null, changes: [] };
const next = structuredClone(raw) as Record<string, unknown>;
const changes: string[] = [];
for (const migration of LEGACY_MIGRATIONS) {
migration.apply(next, changes);
}
if (changes.length === 0) return { config: null, changes: [] };
const validated = validateConfigObject(next);
if (!validated.ok) {
changes.push(
"Migration applied, but config still invalid; fix remaining issues manually.",
);
return { config: null, changes };
}
return { config: validated.config, changes };
}
function resolveMode(cfg: ClawdisConfig): "local" | "remote" {
return cfg.gateway?.mode === "remote" ? "remote" : "local";
}
@@ -29,10 +89,37 @@ export async function doctorCommand(runtime: RuntimeEnv = defaultRuntime) {
const snapshot = await readConfigFileSnapshot();
let cfg: ClawdisConfig = snapshot.valid ? snapshot.config : {};
if (snapshot.exists && !snapshot.valid) {
if (snapshot.exists && !snapshot.valid && snapshot.legacyIssues.length === 0) {
note("Config invalid; doctor will run with defaults.", "Config");
}
if (snapshot.legacyIssues.length > 0) {
note(
snapshot.legacyIssues
.map((issue) => `- ${issue.path}: ${issue.message}`)
.join("\n"),
"Legacy config keys detected",
);
const migrate = guardCancel(
await confirm({
message: "Migrate legacy config entries now?",
initialValue: true,
}),
runtime,
);
if (migrate) {
const { config: migrated, changes } = applyLegacyMigrations(
snapshot.parsed,
);
if (changes.length > 0) {
note(changes.join("\n"), "Doctor changes");
}
if (migrated) {
cfg = migrated;
}
}
}
const workspaceDir = resolveUserPath(
cfg.agent?.workspace ?? DEFAULT_WORKSPACE,
);