From 30655170da1d3ffcacf46ef0ac49eafa7add83d6 Mon Sep 17 00:00:00 2001 From: abose Date: Mon, 18 May 2026 10:52:20 +0530 Subject: [PATCH 1/2] build: remove unused deps --- package-lock.json | 4 +- src-node/package-lock.json | 140 ------------------------------------- src-node/package.json | 1 - 3 files changed, 2 insertions(+), 143 deletions(-) diff --git a/package-lock.json b/package-lock.json index 58fe9d8df9..28574e9f22 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "phoenix", - "version": "5.1.16-0", + "version": "5.1.17-0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "phoenix", - "version": "5.1.16-0", + "version": "5.1.17-0", "hasInstallScript": true, "dependencies": { "@bugsnag/js": "^7.18.0", diff --git a/src-node/package-lock.json b/src-node/package-lock.json index c8dc2833b2..706395b31e 100644 --- a/src-node/package-lock.json +++ b/src-node/package-lock.json @@ -11,7 +11,6 @@ "license": "GNU-AGPL3.0", "dependencies": { "@anthropic-ai/claude-agent-sdk": "^0.2.126", - "@anthropic-ai/claude-code": "^2.1.118", "@expo/sudo-prompt": "^9.3.2", "@phcode/fs": "^4.0.2", "cross-spawn": "^7.0.6", @@ -170,145 +169,6 @@ "win32" ] }, - "node_modules/@anthropic-ai/claude-code": { - "version": "2.1.142", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-code/-/claude-code-2.1.142.tgz", - "integrity": "sha512-ErXNw9qrWaswvHkiohtW2/x0iNIzmU27pTvDraBJ55u4R7VzvZyU7U/10rBQHn7g2kndERog5dpul1IJCDNm+g==", - "hasInstallScript": true, - "license": "SEE LICENSE IN README.md", - "bin": { - "claude": "bin/claude.exe" - }, - "engines": { - "node": ">=18.0.0" - }, - "optionalDependencies": { - "@anthropic-ai/claude-code-darwin-arm64": "2.1.142", - "@anthropic-ai/claude-code-darwin-x64": "2.1.142", - "@anthropic-ai/claude-code-linux-arm64": "2.1.142", - "@anthropic-ai/claude-code-linux-arm64-musl": "2.1.142", - "@anthropic-ai/claude-code-linux-x64": "2.1.142", - "@anthropic-ai/claude-code-linux-x64-musl": "2.1.142", - "@anthropic-ai/claude-code-win32-arm64": "2.1.142", - "@anthropic-ai/claude-code-win32-x64": "2.1.142" - } - }, - "node_modules/@anthropic-ai/claude-code-darwin-arm64": { - "version": "2.1.142", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-code-darwin-arm64/-/claude-code-darwin-arm64-2.1.142.tgz", - "integrity": "sha512-T6GB2kbJ9eiZ2eL25lshk1lwuCkSrrwOz+7ruADfiTd6R5nqMEbB6VplMpYrj67ydt7II6ZpHdjW9/TkwTEQAw==", - "cpu": [ - "arm64" - ], - "license": "SEE LICENSE IN LICENSE.md", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@anthropic-ai/claude-code-darwin-x64": { - "version": "2.1.142", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-code-darwin-x64/-/claude-code-darwin-x64-2.1.142.tgz", - "integrity": "sha512-8GNrDyJQQ1u9pR7aAeQaB8tp7nTJrBnBdaHGknwRYYPxpmfvpGMZZQz7MuSyh36emKAuJahoq+Lh2o4oVj+apg==", - "cpu": [ - "x64" - ], - "license": "SEE LICENSE IN LICENSE.md", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@anthropic-ai/claude-code-linux-arm64": { - "version": "2.1.142", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-code-linux-arm64/-/claude-code-linux-arm64-2.1.142.tgz", - "integrity": "sha512-kfVkdeZwpjrxlWYY9Ceucwv4jxpXmexs1jlHkH862qTuYJbvO+q1mqL3ynQ6WRm7/v09kRMZXUpkN8eVIZRgZQ==", - "cpu": [ - "arm64" - ], - "libc": [ - "glibc" - ], - "license": "SEE LICENSE IN LICENSE.md", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@anthropic-ai/claude-code-linux-arm64-musl": { - "version": "2.1.142", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-code-linux-arm64-musl/-/claude-code-linux-arm64-musl-2.1.142.tgz", - "integrity": "sha512-yQ9WmrTL+b4qe1ibSEe65qQFhS8wSdf4boIvDx8kPmQZ+K0uDgqUQHyFHVK4yYs0nXMAZmTN7rw3bxQgEB+HwQ==", - "cpu": [ - "arm64" - ], - "libc": [ - "musl" - ], - "license": "SEE LICENSE IN LICENSE.md", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@anthropic-ai/claude-code-linux-x64": { - "version": "2.1.142", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-code-linux-x64/-/claude-code-linux-x64-2.1.142.tgz", - "integrity": "sha512-s3RY1JAAKT3NV0c7q8xAp7lxI+awfCG0GqhmzFb7D/Grv1Duh8TiTdO4gm7We9wC/RW8PdP2YHlYTzEYzK8Zrg==", - "cpu": [ - "x64" - ], - "libc": [ - "glibc" - ], - "license": "SEE LICENSE IN LICENSE.md", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@anthropic-ai/claude-code-linux-x64-musl": { - "version": "2.1.142", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-code-linux-x64-musl/-/claude-code-linux-x64-musl-2.1.142.tgz", - "integrity": "sha512-IjWjVdzJL/eRbcItWM2TK1QOkLzCfWGIdEH5WPjIB0vvGHg5OIE4SHDA7l05trqNKNA7Pgt0I12qjGiG+tlR8Q==", - "cpu": [ - "x64" - ], - "libc": [ - "musl" - ], - "license": "SEE LICENSE IN LICENSE.md", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@anthropic-ai/claude-code-win32-arm64": { - "version": "2.1.142", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-code-win32-arm64/-/claude-code-win32-arm64-2.1.142.tgz", - "integrity": "sha512-IxyTLz4XflQaR5+hfnwHvx6WqJldjSH8ZrXaJeSIWgIjsUWhELbsp9QnrBK9t1ZP8sRs0AWagRpzoj0ogwq/8g==", - "cpu": [ - "arm64" - ], - "license": "SEE LICENSE IN LICENSE.md", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@anthropic-ai/claude-code-win32-x64": { - "version": "2.1.142", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-code-win32-x64/-/claude-code-win32-x64-2.1.142.tgz", - "integrity": "sha512-TKmT3cjQWpg/Idf/FvNEESijo2W9nR9CAlNzhOAqYy8vCbU0iRA+c2x9K/l1ng0GZGkxnMTJTfhkUHDw3Xi9/g==", - "cpu": [ - "x64" - ], - "license": "SEE LICENSE IN LICENSE.md", - "optional": true, - "os": [ - "win32" - ] - }, "node_modules/@anthropic-ai/sdk": { "version": "0.93.0", "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.93.0.tgz", diff --git a/src-node/package.json b/src-node/package.json index 2c78bfefb1..e3a820527f 100644 --- a/src-node/package.json +++ b/src-node/package.json @@ -21,7 +21,6 @@ "IMPORTANT!!": "Adding things here will bloat up the package size", "dependencies": { "@anthropic-ai/claude-agent-sdk": "^0.2.126", - "@anthropic-ai/claude-code": "^2.1.118", "@expo/sudo-prompt": "^9.3.2", "@phcode/fs": "^4.0.2", "cross-spawn": "^7.0.6", From e95582a66d22e6df7be24d1d2164d6c9d2303af6 Mon Sep 17 00:00:00 2001 From: abose Date: Mon, 18 May 2026 14:30:36 +0530 Subject: [PATCH 2/2] refactor(updater): run Linux installer in external terminal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous quit-time install flow piped installer output into an internal read-only modal. That worked for fully non-interactive scripts, but any command needing stdin (most importantly sudo's password prompt) would either silently fail or hang because the spawned child has no TTY. Replace the internal log dialog with an external terminal emulator (gnome-terminal -> konsole -> xterm -> x-terminal-emulator probe). The terminal becomes the install UI: sudo and other interactive prompts work natively, the user sees full coloured output, and the script can pause on a "Press Enter to close..." line before exit. quitTimeAppUpdateHandler is now minimal — call launchLinuxUpdater, emit a 'launched' metric on success, and on launch failure show a blocking error dialog (UPDATE_FAILED_TITLE + the error message + a "visit site" link). The blocking dialog both surfaces the failure to the user and gives the async Metrics.countEvent + logger.reportError calls time to flush before Phoenix exits. Drop the now-unused MAX_LOG_LINES const and the in-app log buffer. --- .../appUpdater/update-electron.js | 186 +++++------------- 1 file changed, 53 insertions(+), 133 deletions(-) diff --git a/src/extensionsIntegrated/appUpdater/update-electron.js b/src/extensionsIntegrated/appUpdater/update-electron.js index 5fe98907ff..9629b88d2b 100644 --- a/src/extensionsIntegrated/appUpdater/update-electron.js +++ b/src/extensionsIntegrated/appUpdater/update-electron.js @@ -56,7 +56,6 @@ define(function (require, exports, module) { KEY_UPDATE_AVAILABLE = "PH_UPDATE_AVAILABLE"; const PREFS_AUTO_UPDATE = "autoUpdate"; - const MAX_LOG_LINES = 500; let isAutoUpdateFlow = true; let updateScheduled = false; let cachedUpdateDetails = null; @@ -281,6 +280,10 @@ define(function (require, exports, module) { */ function launchLinuxUpdater(onOutput) { return new Promise((resolve, reject) => { + // Spawn the installer in an external terminal emulator so sudo / + // interactive prompts work natively. The external terminal IS the + // install UI — no internal dialog. Probes common terminals in + // order; first hit wins. const stageValue = Phoenix.config.environment; console.log('Stage:', stageValue); let scriptUrl = 'https://updates.phcode.io/linux/installer.sh'; @@ -288,18 +291,33 @@ define(function (require, exports, module) { scriptUrl = "https://updates.phcode.io/linux/installer-latest-experimental-build.sh"; } - // Use spawnProcess to run bash with the wget|bash command - const command = '/bin/bash'; - const args = ['-c', `wget -qO- ${scriptUrl} | bash -s -- --upgrade`]; + // Inner command run inside the spawned terminal: fetch installer + // from $UPDATE_URL and pipe to bash, print exit code, pause so the + // user can read output before close. + const innerCmd = 'wget -qO- "$UPDATE_URL" | bash -s -- --upgrade; ec=$?; echo; echo "Installer exit code: $ec"; read -rp "Press Enter to close..."'; - window.electronAppAPI.spawnProcess(command, args) + // Use UPDATE_URL env var to avoid quoting the URL through multiple + // shell layers. nohup/disown so terminals that don't daemonize + // (xterm) still let the outer spawn return immediately. + const launcherScript = ` + export UPDATE_URL='${scriptUrl}' + if command -v gnome-terminal >/dev/null 2>&1; then + nohup gnome-terminal -- bash -c '${innerCmd}' >/dev/null 2>&1 & + elif command -v konsole >/dev/null 2>&1; then + nohup konsole -e bash -c '${innerCmd}' >/dev/null 2>&1 & + elif command -v xterm >/dev/null 2>&1; then + nohup xterm -e bash -c '${innerCmd}' >/dev/null 2>&1 & + elif command -v x-terminal-emulator >/dev/null 2>&1; then + nohup x-terminal-emulator -e bash -c '${innerCmd}' >/dev/null 2>&1 & + else + echo "No supported terminal emulator found" >&2 + exit 1 + fi + disown + `; + + window.electronAppAPI.spawnProcess('/bin/bash', ['-c', launcherScript]) .then(instanceId => { - // Set up output handlers - window.electronAppAPI.onProcessStdout((id, line) => { - if (id === instanceId && onOutput) { - onOutput('stdout', line); - } - }); window.electronAppAPI.onProcessStderr((id, line) => { if (id === instanceId && onOutput) { onOutput('stderr', line); @@ -310,13 +328,13 @@ define(function (require, exports, module) { if (data.code === 0) { resolve(); } else { - reject(new Error(`Update script exited with code: ${data.code}`)); + reject(new Error(`Failed to launch installer terminal (exit ${data.code})`)); } } }); window.electronAppAPI.onProcessError((id, err) => { if (id === instanceId) { - reject(new Error(`Update process error: ${err}`)); + reject(new Error(`Terminal spawn error: ${err}`)); } }); }) @@ -330,133 +348,35 @@ define(function (require, exports, module) { } // Clear the scheduled flag in shared state await window.electronAPI.setUpdateScheduled(false); - console.log("Installing update at quit time"); - return new Promise(resolve => { - let dialog; - let logLines = []; - - function appendLogLine(text) { - // Split text into lines and add each - const lines = text.split('\n').filter(l => l.trim()); - for (const line of lines) { - logLines.push(line); - // Keep only last MAX_LOG_LINES - if (logLines.length > MAX_LOG_LINES) { - logLines.shift(); - } - } - // Update the log display - const logElement = document.getElementById('update-log-output'); - if (logElement) { - logElement.textContent = logLines.join('\n'); - logElement.scrollTop = logElement.scrollHeight; - } - } - - function failUpdateDialogAndExit(err) { - console.error("error updating: ", err); - dialog && dialog.close(); - // Build full log text for copying - const fullLogText = logLines.join('\n') + '\n\nError: ' + (err.message || err); - // Show failure dialog with log output and hover copy icon - const failContent = ` -

${Strings.UPDATE_FAILED_VISIT_SITE_MESSAGE}

-
-
${fullLogText}
- -
- `; - const failDialog = Dialogs.showModalDialog( + console.log("Launching external terminal for update"); + try { + await launchLinuxUpdater(); + Metrics.countEvent(Metrics.EVENT_TYPE.UPDATES, 'install', 'launched' + Phoenix.platform); + // Success: the terminal is now the user's UI. Let the quit proceed. + } catch (err) { + console.error("Failed to launch installer terminal:", err); + logger.reportError(err, "Failed to launch installer terminal at quit time"); + Metrics.countEvent(Metrics.EVENT_TYPE.UPDATES, 'install', 'launchFailed' + Phoenix.platform); + // Block the quit on a failure dialog. This both surfaces the error + // to the user AND gives the async metrics/Bugsnag calls above time + // to flush before the process exits. + const errDetails = (err && err.message) ? err.message : String(err); + await new Promise(resolve => { + Dialogs.showModalDialog( DefaultDialogs.DIALOG_ID_ERROR, Strings.UPDATE_FAILED_TITLE, - failContent, + `

${Strings.UPDATE_FAILED_VISIT_SITE_MESSAGE}

` + + `
${errDetails}
`, [{ className: Dialogs.DIALOG_BTN_CLASS_PRIMARY, id: Dialogs.DIALOG_BTN_OK, text: Strings.OK }] - ); - // Set up hover and click handlers for copy icon - const $container = $('#update-fail-log-container'); - const $copyBtn = $('#update-log-copy-btn'); - $container.on('mouseenter', () => $copyBtn.css('opacity', '1')); - $container.on('mouseleave', () => $copyBtn.css('opacity', '0')); - $copyBtn.on('click', () => { - Phoenix.app.copyToClipboard(fullLogText); - $copyBtn.removeClass('fa-copy').addClass('fa-check'); - setTimeout(() => { - $copyBtn.removeClass('fa-check').addClass('fa-copy'); - }, 1500); - }); - $copyBtn.on('mouseenter', () => $copyBtn.css({ 'background': '#333', 'color': '#fff' })); - $copyBtn.on('mouseleave', () => $copyBtn.css({ 'background': 'transparent', 'color': '#888' })); - - failDialog.done(() => { + ).done(() => { NativeApp.openURLInDefaultBrowser(Phoenix.config.update_download_page) .catch(console.error) .finally(resolve); }); - } - - // Create dialog with terminal-style log output - const dialogContent = ` -

${Strings.UPDATE_INSTALLING_MESSAGE}

-

-            `;
-
-            dialog = Dialogs.showModalDialog(
-                DefaultDialogs.DIALOG_ID_INFO,
-                Strings.UPDATE_INSTALLING,
-                dialogContent,
-                [
-                    {
-                        className: "forced-hidden",
-                        id: Dialogs.DIALOG_BTN_OK,
-                        text: Strings.OK
-                    }
-                ],
-                false
-            );
-
-            launchLinuxUpdater((type, text) => {
-                appendLogLine(text);
-            })
-                .then(resolve)
-                .catch(failUpdateDialogAndExit);
-        });
+            });
+        }
     }
 
     AppInit.appReady(async function () {