From 5b5d4ea8a09626bfc069a26366b913a1651f545b Mon Sep 17 00:00:00 2001 From: Arul Sharma <31745423+arul28@users.noreply.github.com> Date: Sun, 31 May 2026 20:16:21 -0400 Subject: [PATCH 1/2] Fix ADE-74 platform service regressions --- apps/desktop/src/main/main.ts | 1 + .../appControlLaunchCommand.test.ts | 73 + .../appControl/appControlLaunchCommand.ts | 82 + .../services/appControl/appControlService.ts | 132 +- .../automations/automationService.test.ts | 4 +- .../services/automations/automationService.ts | 21 +- .../builtInBrowserNavigation.test.ts | 26 + .../builtInBrowserNavigation.ts | 35 + .../builtInBrowserPermissions.test.ts | 45 + .../builtInBrowserPermissions.ts | 56 + .../builtInBrowser/builtInBrowserService.ts | 94 +- .../main/services/ios/iosSimulatorService.ts | 19 +- .../main/services/macosVm/macosVmService.ts | 119 +- .../services/macosVm/macosVmStores.test.ts | 81 + .../main/services/macosVm/macosVmStores.ts | 119 ++ .../services/onboarding/onboardingService.ts | 170 +- .../onboarding/onboardingSuggestedConfig.ts | 171 ++ .../services/shared/imageDimensions.test.ts | 43 + .../main/services/shared/imageDimensions.ts | 46 + .../updates/autoUpdateService.test.ts | 20 + .../services/updates/autoUpdateService.ts | 10 +- .../services/usage/budgetCapService.test.ts | 36 +- .../main/services/usage/budgetCapService.ts | 20 +- .../usage/ledgers/localUsageLedgers.ts | 1415 +++++++++++++++++ .../src/main/services/usage/usagePricing.ts | 5 +- .../services/usage/usageTrackingService.ts | 1415 +---------------- docs/ARCHITECTURE.md | 16 +- docs/features/automations/guardrails.md | 2 +- docs/features/chat/README.md | 2 +- docs/features/computer-use/README.md | 6 +- docs/features/computer-use/app-control.md | 4 +- .../onboarding-and-settings/README.md | 9 +- 32 files changed, 2353 insertions(+), 1944 deletions(-) create mode 100644 apps/desktop/src/main/services/appControl/appControlLaunchCommand.test.ts create mode 100644 apps/desktop/src/main/services/appControl/appControlLaunchCommand.ts create mode 100644 apps/desktop/src/main/services/builtInBrowser/builtInBrowserNavigation.test.ts create mode 100644 apps/desktop/src/main/services/builtInBrowser/builtInBrowserNavigation.ts create mode 100644 apps/desktop/src/main/services/builtInBrowser/builtInBrowserPermissions.test.ts create mode 100644 apps/desktop/src/main/services/builtInBrowser/builtInBrowserPermissions.ts create mode 100644 apps/desktop/src/main/services/macosVm/macosVmStores.test.ts create mode 100644 apps/desktop/src/main/services/macosVm/macosVmStores.ts create mode 100644 apps/desktop/src/main/services/onboarding/onboardingSuggestedConfig.ts create mode 100644 apps/desktop/src/main/services/shared/imageDimensions.test.ts create mode 100644 apps/desktop/src/main/services/shared/imageDimensions.ts create mode 100644 apps/desktop/src/main/services/usage/ledgers/localUsageLedgers.ts diff --git a/apps/desktop/src/main/main.ts b/apps/desktop/src/main/main.ts index fb29e0dba..7cd4c770c 100644 --- a/apps/desktop/src/main/main.ts +++ b/apps/desktop/src/main/main.ts @@ -1749,6 +1749,7 @@ app.whenReady().then(async () => { currentVersion: app.getVersion(), globalStatePath, updaterCacheDir: app.isPackaged ? resolveAutoUpdaterCacheDir() : undefined, + autoCheckEnabled: app.isPackaged, }); const initContextForProjectRoot = async ({ diff --git a/apps/desktop/src/main/services/appControl/appControlLaunchCommand.test.ts b/apps/desktop/src/main/services/appControl/appControlLaunchCommand.test.ts new file mode 100644 index 000000000..5f5579a38 --- /dev/null +++ b/apps/desktop/src/main/services/appControl/appControlLaunchCommand.test.ts @@ -0,0 +1,73 @@ +import fs from "node:fs"; +import os from "node:os"; +import path from "node:path"; +import { describe, expect, it } from "vitest"; +import { + commandForwardsAppControlDebug, + commandLooksLikeDirectElectronLaunch, + commandLooksLikePackageScriptLaunch, + insertDebugFlagsIntoDirectElectronCommand, + rewritePackageScriptElectronLaunch, +} from "./appControlLaunchCommand"; + +const DEBUG_FLAGS = ["--remote-debugging-port=9222"]; + +describe("appControlLaunchCommand", () => { + it("detects direct Electron launches and injects debug flags after electron", () => { + expect(commandLooksLikeDirectElectronLaunch("FOO=bar npx electron .")).toBe(true); + + expect(insertDebugFlagsIntoDirectElectronCommand("FOO=bar npx electron .", DEBUG_FLAGS)) + .toBe("FOO=bar npx electron --remote-debugging-port=9222 ."); + }); + + it("detects package script launches and rewrites direct Electron scripts", () => { + const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), "ade-app-control-launch-")); + try { + fs.writeFileSync(path.join(projectRoot, "package.json"), JSON.stringify({ + scripts: { + dev: "electron .", + }, + }), "utf8"); + + expect(commandLooksLikePackageScriptLaunch("npm run dev")).toBe(true); + expect(rewritePackageScriptElectronLaunch("npm run dev", DEBUG_FLAGS, projectRoot)) + .toBe(`PATH=${path.join(projectRoot, "node_modules", ".bin")}:$PATH electron --remote-debugging-port=9222 .`); + } finally { + fs.rmSync(projectRoot, { recursive: true, force: true }); + } + }); + + it("does not rewrite scripts that already forward App Control flags", () => { + const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), "ade-app-control-launch-")); + try { + fs.writeFileSync(path.join(projectRoot, "package.json"), JSON.stringify({ + scripts: { + dev: "electron . {ADE_APP_CONTROL_DEBUG_FLAGS}", + }, + }), "utf8"); + + expect(commandForwardsAppControlDebug("electron . {ADE_APP_CONTROL_DEBUG_FLAGS}")).toBe(true); + expect(rewritePackageScriptElectronLaunch("npm run dev", DEBUG_FLAGS, projectRoot)).toBeNull(); + } finally { + fs.rmSync(projectRoot, { recursive: true, force: true }); + } + }); + + it("resolves the last cd segment before reading package.json", () => { + const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), "ade-app-control-launch-")); + const appDir = path.join(projectRoot, "apps", "desktop"); + try { + fs.mkdirSync(appDir, { recursive: true }); + fs.writeFileSync(path.join(appDir, "package.json"), JSON.stringify({ + scripts: { + dev: "electron .", + }, + }), "utf8"); + + expect(rewritePackageScriptElectronLaunch("cd apps/desktop && npm run dev", DEBUG_FLAGS, projectRoot)) + .toBe(`cd apps/desktop && PATH=${path.join(appDir, "node_modules", ".bin")}:$PATH electron --remote-debugging-port=9222 .`); + } finally { + fs.rmSync(projectRoot, { recursive: true, force: true }); + } + }); +}); diff --git a/apps/desktop/src/main/services/appControl/appControlLaunchCommand.ts b/apps/desktop/src/main/services/appControl/appControlLaunchCommand.ts new file mode 100644 index 000000000..efcd667ab --- /dev/null +++ b/apps/desktop/src/main/services/appControl/appControlLaunchCommand.ts @@ -0,0 +1,82 @@ +import fs from "node:fs"; +import path from "node:path"; + +export function shellQuote(value: string): string { + if (/^[A-Za-z0-9_/:=.,@%+-]+$/.test(value)) return value; + return `'${value.replace(/'/g, "'\\''")}'`; +} + +export function commandForwardsAppControlDebug(command: string): boolean { + return /\{ADE_APP_CONTROL_DEBUG_FLAGS\}|\bADE_APP_CONTROL_(?:DEBUG_FLAGS|CDP_PORT|REMOTE_DEBUGGING_PORT)\b|--remote-debugging-port\b/.test(command); +} + +export function commandLooksLikePackageScriptLaunch(command: string): boolean { + return /(?:^|[;&|]\s*)(?:[A-Za-z_][A-Za-z0-9_]*=(?:"[^"]*"|'[^']*'|[^\s;&|]+)\s+)*(?:(?:npm|pnpm|yarn|bun)\s+(?:run\s+)?[A-Za-z0-9:_./-]+)\s*$/.test(command.trim()); +} + +export function commandLooksLikeDirectElectronLaunch(command: string): boolean { + return /(?:^|[;&|]\s*)(?:[A-Za-z_][A-Za-z0-9_]*=(?:"[^"]*"|'[^']*'|[^\s;&|]+)\s+)*(?:npx\s+)?electron(?:\s+[^;&|]*)?\s*$/.test(command.trim()); +} + +export function unquoteShellValue(value: string): string { + const trimmed = value.trim(); + if ( + (trimmed.startsWith("'") && trimmed.endsWith("'")) + || (trimmed.startsWith("\"") && trimmed.endsWith("\"")) + ) { + return trimmed.slice(1, -1); + } + return trimmed; +} + +export function insertDebugFlagsIntoDirectElectronCommand(command: string, debugFlags: string[]): string { + const flags = debugFlags.map(shellQuote).join(" "); + return command.replace( + /((?:^|[;&|]\s*)(?:[A-Za-z_][A-Za-z0-9_]*=(?:"[^"]*"|'[^']*'|[^\s;&|]+)\s+)*(?:npx\s+)?electron)(?=\s|$)/, + `$1 ${flags}`, + ); +} + +function prependEnvToShellSegments(script: string, envPrefix: string): string { + const trimmedEnv = envPrefix.trim(); + if (!trimmedEnv) return script; + return script + .split(/(\s*&&\s*)/) + .map((segment) => { + if (/^\s*&&\s*$/.test(segment)) return segment; + const trimmed = segment.trim(); + return trimmed ? `${trimmedEnv} ${trimmed}` : segment; + }) + .join(""); +} + +export function rewritePackageScriptElectronLaunch(command: string, debugFlags: string[], fallbackCwd: string): string | null { + const match = command.trim().match(/^(?.*?)(?(?:[A-Za-z_][A-Za-z0-9_]*=(?:"[^"]*"|'[^']*'|[^\s;&|]+)\s+)*)(?npm|pnpm|yarn|bun)\s+(?:run\s+)?(?