fix(onboard): wait for gateway warnings before installing service (#104173)

* fix(onboard): wait for gateway warnings before installing service

* fix(onboard): show gateway warnings after plan errors

* refactor(onboard): drain install warnings from queue

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
qingminlong
2026-07-11 05:49:34 -07:00
committed by GitHub
co-authored by Peter Steinberger
parent 3f33f0bb5f
commit a66d2959e9
2 changed files with 65 additions and 2 deletions
+53 -1
View File
@@ -32,7 +32,7 @@ const resolveDefaultModelAuthStatus = vi.hoisted(() =>
vi.fn(() => ({ provider: "anthropic", model: "claude-opus-4-8", hasAuth: true })),
);
const buildGatewayInstallPlan = vi.hoisted(() =>
vi.fn(async () => ({
vi.fn(async (_params?: { warn?: (message: string, title?: string) => void }) => ({
programArguments: [],
workingDirectory: "/tmp",
environment: {},
@@ -922,6 +922,58 @@ describe("finalizeSetupWizard", () => {
);
});
it("waits for gateway install warnings before installing the service", async () => {
let acknowledgeWarning: (() => void) | undefined;
const warningAcknowledged = new Promise<void>((resolve) => {
acknowledgeWarning = resolve;
});
const prompter = buildWizardPrompter({
select: vi.fn(async () => "later") as never,
confirm: vi.fn(async () => false),
note: vi.fn(async (message: string) => {
if (message === "Gateway install warning") {
await warningAcknowledged;
}
}),
});
buildGatewayInstallPlan.mockImplementationOnce(async (params) => {
params?.warn?.("Gateway install warning", "Gateway service");
return {
programArguments: [],
workingDirectory: "/tmp",
environment: {},
environmentValueSources: {},
};
});
const finalizePromise = finalizeSetupWizard(
createAdvancedFinalizeArgs({ installDaemon: true, prompter }),
);
await vi.waitFor(() => {
expect(prompter.note).toHaveBeenCalledWith("Gateway install warning", "Gateway service");
});
expect(gatewayServiceInstall).not.toHaveBeenCalled();
acknowledgeWarning?.();
await finalizePromise;
expect(gatewayServiceInstall).toHaveBeenCalledTimes(1);
});
it("shows gateway install warnings when planning fails", async () => {
const prompter = createLaterPrompter();
buildGatewayInstallPlan.mockImplementationOnce(async (params) => {
params?.warn?.("Gateway install warning", "Gateway service");
throw new Error("plan failed");
});
await finalizeSetupWizard(createAdvancedFinalizeArgs({ installDaemon: true, prompter }));
expect(prompter.note).toHaveBeenCalledWith("Gateway install warning", "Gateway service");
expectNoteContains(prompter, "plan failed", "Gateway");
expect(gatewayServiceInstall).not.toHaveBeenCalled();
});
it("suppresses token-bearing onboarding output when requested", async () => {
const prompter = createLaterPrompter();
+12 -1
View File
@@ -339,6 +339,15 @@ export async function ensureGatewayServiceForOnboarding(params: {
) {
const progress = prompter.progress(t("wizard.finalize.gatewayService"));
let installError: string | null = null;
const installWarnings: Array<{ message: string; title?: string }> = [];
const flushInstallWarnings = async () => {
let warning: (typeof installWarnings)[number] | undefined;
// Remove before awaiting so a rejected note is not replayed when the
// outer catch drains warnings that remain in the planner's queue.
while ((warning = installWarnings.shift()) !== undefined) {
await prompter.note(warning.message, warning.title);
}
};
try {
progress.update(t("wizard.finalize.gatewayServicePreparing"));
const tokenResolution = await resolveGatewayInstallToken({
@@ -361,10 +370,11 @@ export async function ensureGatewayServiceForOnboarding(params: {
port: settings.port,
runtime: daemonRuntime,
warn: (message, title) => {
void prompter.note(message, title);
installWarnings.push({ message, title });
},
config: nextConfig,
});
await flushInstallWarnings();
progress.update(t("wizard.finalize.gatewayServiceInstalling"));
await service.install({
@@ -377,6 +387,7 @@ export async function ensureGatewayServiceForOnboarding(params: {
});
}
} catch (err) {
await flushInstallWarnings();
installError = formatErrorMessage(err);
} finally {
progress.stop(