Commit 43d6c777 authored by edy's avatar edy

fix(desktop): keep bundle sync timeout non-fatal

parent a110b28a
...@@ -2163,6 +2163,7 @@ async function bootstrap(): Promise<void> { ...@@ -2163,6 +2163,7 @@ async function bootstrap(): Promise<void> {
configVersion: string | undefined, configVersion: string | undefined,
action: RuntimeCloudFetchAction action: RuntimeCloudFetchAction
) => { ) => {
const timeoutMessage = `Project bundle sync timed out after ${Math.round(PROJECT_BUNDLE_BOOTSTRAP_TIMEOUT_MS / 1000)}s.`;
console.info("[bundle-bootstrap]", "bundle.sync.start", { console.info("[bundle-bootstrap]", "bundle.sync.start", {
action, action,
configVersion, configVersion,
...@@ -2174,7 +2175,7 @@ async function bootstrap(): Promise<void> { ...@@ -2174,7 +2175,7 @@ async function bootstrap(): Promise<void> {
await withTimeout( await withTimeout(
projectBundleService.syncRemoteBundles(skills, configVersion, action), projectBundleService.syncRemoteBundles(skills, configVersion, action),
PROJECT_BUNDLE_BOOTSTRAP_TIMEOUT_MS, PROJECT_BUNDLE_BOOTSTRAP_TIMEOUT_MS,
`Project bundle sync timed out after ${Math.round(PROJECT_BUNDLE_BOOTSTRAP_TIMEOUT_MS / 1000)}s.` timeoutMessage
); );
console.info("[bundle-bootstrap]", "bundle.sync.done", { console.info("[bundle-bootstrap]", "bundle.sync.done", {
action, action,
...@@ -2185,13 +2186,24 @@ async function bootstrap(): Promise<void> { ...@@ -2185,13 +2186,24 @@ async function bootstrap(): Promise<void> {
} catch (error) { } catch (error) {
const rawMessage = error instanceof Error ? (error.stack ?? error.message) : String(error); const rawMessage = error instanceof Error ? (error.stack ?? error.message) : String(error);
const userMessage = error instanceof Error ? error.message : String(error); const userMessage = error instanceof Error ? error.message : String(error);
const timedOut = userMessage === timeoutMessage;
if (!timedOut) {
projectBundleService.setSyncError(userMessage); projectBundleService.setSyncError(userMessage);
console.error("[bundle-bootstrap]", "bundle.sync.error", { }
const logContext = {
action, action,
configVersion, configVersion,
skillIds: skills.map((skill) => skill.skillId), skillIds: skills.map((skill) => skill.skillId),
error: rawMessage, error: rawMessage,
timedOut: userMessage.includes("timed out") timedOut
};
if (timedOut) {
console.warn("[bundle-bootstrap]", "bundle.sync.timeout", logContext);
await traceBootstrap("project-bundle-sync:" + action + ":timeout:" + rawMessage.replace(/\r?\n/g, " | "));
return;
}
console.error("[bundle-bootstrap]", "bundle.sync.error", {
...logContext
}); });
await traceBootstrap("project-bundle-sync:" + action + ":error:" + rawMessage.replace(/\r?\n/g, " | ")); await traceBootstrap("project-bundle-sync:" + action + ":error:" + rawMessage.replace(/\r?\n/g, " | "));
} }
......
...@@ -1170,10 +1170,13 @@ export function registerDesktopIpc(services: MainServices): RegisteredDesktopIpc ...@@ -1170,10 +1170,13 @@ export function registerDesktopIpc(services: MainServices): RegisteredDesktopIpc
runtimeStatus, runtimeStatus,
gatewayStatus gatewayStatus
}); });
const hasUsableProjectInventory = projects.some((project) => project.ready)
|| Boolean(currentProject?.isBuiltinHome && currentProject.ready);
const shouldWaitForProjectSync = config.apiKeyConfigured const shouldWaitForProjectSync = config.apiKeyConfigured
&& config.setupMode === "employee-key" && config.setupMode === "employee-key"
&& runtimeCloudStatus.state === "ready" && runtimeCloudStatus.state === "ready"
&& bundleSyncStatus.state === "syncing" && bundleSyncStatus.state === "syncing"
&& !hasUsableProjectInventory
&& baseChatSummary.chatLaunchState !== "error"; && baseChatSummary.chatLaunchState !== "error";
if (shouldWaitForProjectSync && baseChatSummary.startupPhase !== "syncing-projects") { if (shouldWaitForProjectSync && baseChatSummary.startupPhase !== "syncing-projects") {
void startupLogger.warn("workspace-summary", "phase.override", "Project sync phase is overriding the base startup phase because no project inventory is available yet.", { void startupLogger.warn("workspace-summary", "phase.override", "Project sync phase is overriding the base startup phase because no project inventory is available yet.", {
...@@ -1181,6 +1184,7 @@ export function registerDesktopIpc(services: MainServices): RegisteredDesktopIpc ...@@ -1181,6 +1184,7 @@ export function registerDesktopIpc(services: MainServices): RegisteredDesktopIpc
basePhase: baseChatSummary.startupPhase, basePhase: baseChatSummary.startupPhase,
runtimeCloudState: runtimeCloudStatus.state, runtimeCloudState: runtimeCloudStatus.state,
projectCount: projects.length, projectCount: projects.length,
hasUsableProjectInventory,
bundleSyncState: bundleSyncStatus.state bundleSyncState: bundleSyncStatus.state
}); });
} }
......
...@@ -289,6 +289,15 @@ export class ProjectBundleService { ...@@ -289,6 +289,15 @@ export class ProjectBundleService {
}; };
} }
private setSyncFailure(error: unknown): void {
const message = error instanceof Error ? error.message : String(error);
this.syncStatus = {
...this.syncStatus,
state: "error",
lastError: message
};
}
async syncRemoteBundles(remoteSkills: RemoteSkillAsset[], configVersion?: string, _action?: RuntimeCloudFetchAction): Promise<void> { async syncRemoteBundles(remoteSkills: RemoteSkillAsset[], configVersion?: string, _action?: RuntimeCloudFetchAction): Promise<void> {
const runSync = async (): Promise<void> => { const runSync = async (): Promise<void> => {
const startedAt = Date.now(); const startedAt = Date.now();
...@@ -376,7 +385,24 @@ export class ProjectBundleService { ...@@ -376,7 +385,24 @@ export class ProjectBundleService {
}); });
}; };
const nextRun = this.syncTail.then(runSync); const nextRun = this.syncTail.then(async () => {
try {
await runSync();
} catch (error) {
this.setSyncFailure(error);
logBundle("bundle.sync.error", {
action: _action ?? "unknown",
configVersion,
error: error instanceof Error ? error.message : String(error)
});
await this.startupLogger?.error("project-bundle", "sync.error", "Project bundle sync failed.", {
action: _action ?? "unknown",
configVersion,
error: error instanceof Error ? (error.stack ?? error.message) : String(error)
});
throw error;
}
});
this.syncTail = nextRun.catch(() => undefined); this.syncTail = nextRun.catch(() => undefined);
await nextRun; await nextRun;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment