From eed199d4309572d8f7424088239ad2f359927267 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 3 Jun 2026 19:23:32 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20align=20manager.ts=20with=20dev=20?= =?UTF-8?q?=E2=80=94=20use=20replaceConfigObject=20+=20override=20instead?= =?UTF-8?q?=20of=20updateConfigObject?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The merge auto-resolved manager.ts by keeping the updateConfigObject import, but dev switched to the simpler replaceConfigObject + override approach. The transitive import chain through updateConfigObject pulled the Claude agent SDK into the Edge Runtime bundle, causing Turbopack build failures (crypto, fs, path, process.exit are not available in Edge Runtime). Co-Authored-By: mantra --- .../remote-development-environment/manager.ts | 40 +++++++------------ 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/apps/dashboard/src/lib/remote-development-environment/manager.ts b/apps/dashboard/src/lib/remote-development-environment/manager.ts index 1fea9d71c..a7eaccf2d 100644 --- a/apps/dashboard/src/lib/remote-development-environment/manager.ts +++ b/apps/dashboard/src/lib/remote-development-environment/manager.ts @@ -3,7 +3,7 @@ import "server-only"; import { getPublicEnvVar } from "@/lib/env"; import { stackAppInternalsSymbol } from "@/lib/stack-app-internals"; import { AdminOwnedProject, StackClientApp } from "@hexclave/next"; -import { Config } from "@hexclave/shared/dist/config/format"; +import { Config, override } from "@hexclave/shared/dist/config/format"; import { ProjectOnboardingStatus } from "@hexclave/shared/dist/schema-fields"; import { AccessToken } from "@hexclave/shared/dist/sessions"; import { errorToNiceString } from "@hexclave/shared/dist/utils/errors"; @@ -16,7 +16,7 @@ import { readConfigFile, resolveConfigFilePath, sha256String, - updateConfigObject, + replaceConfigObject, } from "./config-file"; import { assertRemoteDevelopmentEnvironmentEnabled } from "./env"; import { @@ -646,29 +646,19 @@ export async function applyRemoteDevelopmentEnvironmentConfigUpdate(options: { projectId: options.projectId, configFilePath, }); - // Suppress watcher-driven syncs for the whole (potentially slow, AI-driven, - // multi-file) update so we never sync a partially-edited intermediate state. - // The membership is held until the update resolves and then cleared after a - // debounce so the file-change events our own edits produce are ignored too. - state.synchronouslyUpdatingConfigFiles.add(configFilePath); - let updateSucceeded = false; - try { - await updateConfigObject(configFilePath, options.configUpdate); - updateSucceeded = true; - } finally { - setTimeout(() => { - state.synchronouslyUpdatingConfigFiles.delete(configFilePath); - // For fire-and-forget updates the sync is scheduled only after the - // suppression window clears, otherwise scheduleSync would be swallowed - // by its own guard while the path is still marked as synchronously - // updating. Only sync a successful update: a failed one is rolled back, - // so there is nothing new to push. - if (options.waitForSync === false && updateSucceeded) { - scheduleSync(configFilePath); - } - }, SYNC_DEBOUNCE_MS).unref(); - } - if (options.waitForSync !== false) { + const currentConfig = (await readConfigFile(configFilePath)).config; + if (options.waitForSync === false) { + await replaceConfigObject(configFilePath, override(currentConfig, options.configUpdate)); + scheduleSync(configFilePath); + } else { + state.synchronouslyUpdatingConfigFiles.add(configFilePath); + try { + await replaceConfigObject(configFilePath, override(currentConfig, options.configUpdate)); + } finally { + setTimeout(() => { + state.synchronouslyUpdatingConfigFiles.delete(configFilePath); + }, SYNC_DEBOUNCE_MS).unref(); + } await syncConfigToRemoteNow(configFilePath); } logRemoteDevelopmentEnvironment("Applied config update from local dashboard", {