fix: honor non-interactive legacy migrations

This commit is contained in:
Peter Steinberger
2026-01-08 22:13:48 +01:00
parent e75ca23e7d
commit 35ba99c245
6 changed files with 35 additions and 17 deletions

View File

@@ -82,6 +82,7 @@
- **Multi-agent safety:** running multiple agents is OK as long as each agent has its own session.
- **Multi-agent safety:** when you see unrecognized files, keep going; focus on your changes and commit only those.
- Bug investigations: read source code of relevant npm dependencies and all related local code before concluding; aim for high-confidence root cause.
- Code style: add brief comments for tricky logic; keep files under ~500 LOC when feasible (split/refactor as needed).
- When asked to open a “session” file, open the Pi session logs under `~/.clawdbot/sessions/*.jsonl` (newest unless a specific ID is given), not the default `sessions.json`. If logs are needed from another machine, SSH via Tailscale and read the same path there.
- Menubar dimming + restart flow mirrors Trimmy: use `scripts/restart-mac.sh` (kills all Clawdbot variants, runs `swift build`, packages, relaunches). Icon dimming depends on MenuBarExtraAccess wiring in AppMain; keep `appearsDisabled` updates intact when touching the status item.
- Do not rebuild the macOS app over SSH; rebuilds must be run directly on the Mac.

View File

@@ -329,7 +329,11 @@ export function buildProgram() {
)
.option("--yes", "Accept defaults without prompting", false)
.option("--repair", "Apply recommended repairs without prompting", false)
.option("--force", "Apply aggressive repairs (overwrites custom service config)", false)
.option(
"--force",
"Apply aggressive repairs (overwrites custom service config)",
false,
)
.option(
"--non-interactive",
"Run without prompts (safe migrations only)",

View File

@@ -170,7 +170,7 @@ export async function maybeRepairGatewayServiceConfig(
const aggressiveIssues = audit.issues.filter(
(issue) => issue.level === "aggressive",
);
const recommendedIssues = audit.issues.filter(
const _recommendedIssues = audit.issues.filter(
(issue) => issue.level !== "aggressive",
);
const needsAggressive = aggressiveIssues.length > 0;
@@ -184,12 +184,12 @@ export async function maybeRepairGatewayServiceConfig(
const repair = needsAggressive
? await prompter.confirmAggressive({
message:
"Overwrite gateway service config with current defaults now?",
message: "Overwrite gateway service config with current defaults now?",
initialValue: Boolean(prompter.shouldForce),
})
: await prompter.confirmRepair({
message: "Update gateway service config to the recommended defaults now?",
message:
"Update gateway service config to the recommended defaults now?",
initialValue: true,
});
if (!repair) return;

View File

@@ -15,7 +15,9 @@ export type DoctorOptions = {
export type DoctorPrompter = {
confirm: (params: Parameters<typeof confirm>[0]) => Promise<boolean>;
confirmRepair: (params: Parameters<typeof confirm>[0]) => Promise<boolean>;
confirmAggressive: (params: Parameters<typeof confirm>[0]) => Promise<boolean>;
confirmAggressive: (
params: Parameters<typeof confirm>[0],
) => Promise<boolean>;
confirmSkipInNonInteractive: (
params: Parameters<typeof confirm>[0],
) => Promise<boolean>;

View File

@@ -129,10 +129,13 @@ export async function doctorCommand(
const legacyState = await detectLegacyStateMigrations({ cfg });
if (legacyState.preview.length > 0) {
note(legacyState.preview.join("\n"), "Legacy state detected");
const migrate = await prompter.confirm({
message: "Migrate legacy state (sessions/agent/WhatsApp auth) now?",
initialValue: true,
});
const migrate =
options.nonInteractive === true
? true
: await prompter.confirm({
message: "Migrate legacy state (sessions/agent/WhatsApp auth) now?",
initialValue: true,
});
if (migrate) {
const migrated = await runLegacyStateMigrations({
detected: legacyState,
@@ -146,7 +149,11 @@ export async function doctorCommand(
}
}
await noteStateIntegrity(cfg, prompter, snapshot.path ?? CONFIG_PATH_CLAWDBOT);
await noteStateIntegrity(
cfg,
prompter,
snapshot.path ?? CONFIG_PATH_CLAWDBOT,
);
cfg = await maybeRepairSandboxImages(cfg, runtime, prompter);
noteSandboxScopeWarnings(cfg);

View File

@@ -148,6 +148,10 @@ describe("web monitor inbox", () => {
const listener = await monitorWebInbox({ verbose: false, onMessage });
const sock = await createWaSocket();
const getPNForLID = vi.spyOn(
sock.signalRepository.lidMapping,
"getPNForLID",
);
sock.signalRepository.lidMapping.getPNForLID.mockResolvedValueOnce(
"999:0@s.whatsapp.net",
);
@@ -166,9 +170,7 @@ describe("web monitor inbox", () => {
sock.ev.emit("messages.upsert", upsert);
await new Promise((resolve) => setImmediate(resolve));
expect(sock.signalRepository.lidMapping.getPNForLID).toHaveBeenCalledWith(
"999@lid",
);
expect(getPNForLID).toHaveBeenCalledWith("999@lid");
expect(onMessage).toHaveBeenCalledWith(
expect.objectContaining({ body: "ping", from: "+999", to: "+123" }),
);
@@ -183,6 +185,10 @@ describe("web monitor inbox", () => {
const listener = await monitorWebInbox({ verbose: false, onMessage });
const sock = await createWaSocket();
const getPNForLID = vi.spyOn(
sock.signalRepository.lidMapping,
"getPNForLID",
);
sock.signalRepository.lidMapping.getPNForLID.mockResolvedValueOnce(
"444:0@s.whatsapp.net",
);
@@ -205,9 +211,7 @@ describe("web monitor inbox", () => {
sock.ev.emit("messages.upsert", upsert);
await new Promise((resolve) => setImmediate(resolve));
expect(sock.signalRepository.lidMapping.getPNForLID).toHaveBeenCalledWith(
"444@lid",
);
expect(getPNForLID).toHaveBeenCalledWith("444@lid");
expect(onMessage).toHaveBeenCalledWith(
expect.objectContaining({
body: "ping",