From 016c7bda4a8895633f1d1c077e9b9c38f45de1ec Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Mon, 20 Apr 2026 08:49:50 +0100 Subject: [PATCH 01/18] fix(tauri): use in-app certificate install confirmation --- packages/tauri-app/src-tauri/src/main.rs | 59 ++++++------------- .../lib/i18n/messages/en/folderSelection.ts | 5 ++ .../lib/i18n/messages/es/folderSelection.ts | 5 ++ .../lib/i18n/messages/fr/folderSelection.ts | 5 ++ .../lib/i18n/messages/he/folderSelection.ts | 5 ++ .../lib/i18n/messages/ja/folderSelection.ts | 5 ++ .../lib/i18n/messages/ru/folderSelection.ts | 5 ++ .../i18n/messages/zh-Hans/folderSelection.ts | 5 ++ packages/ui/src/lib/native/remote-window.ts | 24 ++++++++ 9 files changed, 78 insertions(+), 40 deletions(-) diff --git a/packages/tauri-app/src-tauri/src/main.rs b/packages/tauri-app/src-tauri/src/main.rs index d8204059a..92a0f634b 100644 --- a/packages/tauri-app/src-tauri/src/main.rs +++ b/packages/tauri-app/src-tauri/src/main.rs @@ -20,7 +20,6 @@ use tauri::webview::Webview; use tauri::{ AppHandle, Emitter, Manager, Runtime, WebviewUrl, WebviewWindowBuilder, WindowEvent, Wry, }; -use tauri_plugin_dialog::{DialogExt, MessageDialogButtons, MessageDialogKind}; use tauri_plugin_global_shortcut::{ Code as ShortcutCode, GlobalShortcutExt, Shortcut, ShortcutState, }; @@ -78,34 +77,6 @@ fn schedule_remote_proxy_session_cleanup(app: AppHandle, session_id: String) { }); } -async fn confirm_local_certificate_install(app: &AppHandle) -> Result { - let (sender, receiver) = std::sync::mpsc::sync_channel(1); - - let mut dialog = app - .dialog() - .message( - "CodeNomad needs to install a local certificate to open self-signed HTTPS remote windows. This certificate is only used for local desktop proxy traffic on your machine. Your operating system may show a second certificate prompt after this.", - ) - .title("Install Local Certificate") - .kind(MessageDialogKind::Warning) - .buttons(MessageDialogButtons::OkCancelCustom( - "Continue".into(), - "Cancel".into(), - )); - - if let Some(window) = app.get_webview_window("main") { - dialog = dialog.parent(&window); - } - - dialog.show(move |accepted| { - let _ = sender.send(accepted); - }); - - tauri::async_runtime::spawn_blocking(move || receiver.recv().unwrap_or(false)) - .await - .map_err(|err| err.to_string()) -} - async fn cleanup_remote_proxy_session(app: &AppHandle, session_id: &str) -> Result<(), String> { let status = app.state::().manager.status(); let Some(base_url) = status.url else { @@ -367,6 +338,24 @@ async fn open_remote_window_impl( Ok(()) } +#[tauri::command] +fn needs_local_certificate_install() -> Result { + #[cfg(not(target_os = "linux"))] + { + let local_cert = cert_manager::ensure_local_cert().map_err(|err| { + format!("Failed to load the local HTTPS certificate for the remote proxy window: {err}") + })?; + return cert_manager::needs_trust_in_store(&local_cert.ca_cert_der).map_err(|err| { + format!("Failed to inspect the local CodeNomad certificate trust state: {err}") + }); + } + + #[cfg(target_os = "linux")] + { + Ok(false) + } +} + #[tauri::command] async fn open_remote_window(app: AppHandle, payload: RemoteWindowPayload) -> Result<(), String> { #[cfg(not(target_os = "linux"))] @@ -379,17 +368,6 @@ async fn open_remote_window(app: AppHandle, payload: RemoteWindowPayload) -> Res "Failed to load the local HTTPS certificate for the remote proxy window: {err}" ) })?; - if cert_manager::needs_trust_in_store(&local_cert.ca_cert_der).map_err(|err| { - format!("Failed to inspect the local CodeNomad certificate trust state: {err}") - })? { - let accepted = confirm_local_certificate_install(&app).await?; - if !accepted { - return Err( - "CodeNomad needs the local certificate to be trusted before it can open self-signed HTTPS remote windows." - .to_string(), - ); - } - } if let Err(err) = cert_manager::trust_cert_in_store(&local_cert.ca_cert_der) { return Err(format!( "Failed to trust the local CodeNomad CA certificate. Accept the certificate installation prompt and try again: {err}" @@ -598,6 +576,7 @@ fn main() { cli_restart, wake_lock_start, wake_lock_stop, + needs_local_certificate_install, open_remote_window ]) .on_menu_event(|app_handle, event| { diff --git a/packages/ui/src/lib/i18n/messages/en/folderSelection.ts b/packages/ui/src/lib/i18n/messages/en/folderSelection.ts index 16eac13e2..841086c5e 100644 --- a/packages/ui/src/lib/i18n/messages/en/folderSelection.ts +++ b/packages/ui/src/lib/i18n/messages/en/folderSelection.ts @@ -69,5 +69,10 @@ export const folderSelectionMessages = { "folderSelection.servers.dialog.connecting": "Connecting...", "folderSelection.servers.dialog.errorRequired": "Server name and URL are required.", "folderSelection.servers.dialog.errorConnect": "Could not connect to the remote server.", + "folderSelection.servers.certificateInstall.title": "Install Local Certificate", + "folderSelection.servers.certificateInstall.confirmMessage": "CodeNomad needs to install a local certificate to open self-signed HTTPS remote windows. This certificate is only used for local desktop proxy traffic on your machine. Your operating system may show a second certificate prompt after this.", + "folderSelection.servers.certificateInstall.confirmLabel": "Continue", + "folderSelection.servers.certificateInstall.cancelLabel": "Cancel", + "folderSelection.servers.certificateInstall.cancelled": "CodeNomad needs the local certificate to be trusted before it can open self-signed HTTPS remote windows.", "folderSelection.sidecars.button": "Open SideCar", } as const diff --git a/packages/ui/src/lib/i18n/messages/es/folderSelection.ts b/packages/ui/src/lib/i18n/messages/es/folderSelection.ts index ad618a94d..1c1e47dca 100644 --- a/packages/ui/src/lib/i18n/messages/es/folderSelection.ts +++ b/packages/ui/src/lib/i18n/messages/es/folderSelection.ts @@ -69,5 +69,10 @@ export const folderSelectionMessages = { "folderSelection.servers.dialog.connecting": "Conectando...", "folderSelection.servers.dialog.errorRequired": "El nombre y la URL del servidor son obligatorios.", "folderSelection.servers.dialog.errorConnect": "No se pudo conectar al servidor remoto.", + "folderSelection.servers.certificateInstall.title": "Instalar certificado local", + "folderSelection.servers.certificateInstall.confirmMessage": "CodeNomad necesita instalar un certificado local para abrir ventanas remotas HTTPS autofirmadas. Este certificado solo se usa para el trafico del proxy local de escritorio en tu equipo. Es posible que tu sistema operativo muestre un segundo aviso de certificado despues de esto.", + "folderSelection.servers.certificateInstall.confirmLabel": "Continuar", + "folderSelection.servers.certificateInstall.cancelLabel": "Cancelar", + "folderSelection.servers.certificateInstall.cancelled": "CodeNomad necesita que el certificado local sea de confianza antes de poder abrir ventanas remotas HTTPS autofirmadas.", "folderSelection.sidecars.button": "Open SideCar", } as const diff --git a/packages/ui/src/lib/i18n/messages/fr/folderSelection.ts b/packages/ui/src/lib/i18n/messages/fr/folderSelection.ts index 1372a78e9..6932e434b 100644 --- a/packages/ui/src/lib/i18n/messages/fr/folderSelection.ts +++ b/packages/ui/src/lib/i18n/messages/fr/folderSelection.ts @@ -69,5 +69,10 @@ export const folderSelectionMessages = { "folderSelection.servers.dialog.connecting": "Connexion...", "folderSelection.servers.dialog.errorRequired": "Le nom du serveur et l'URL sont requis.", "folderSelection.servers.dialog.errorConnect": "Impossible de se connecter au serveur distant.", + "folderSelection.servers.certificateInstall.title": "Installer le certificat local", + "folderSelection.servers.certificateInstall.confirmMessage": "CodeNomad doit installer un certificat local pour ouvrir des fenetres distantes HTTPS auto-signees. Ce certificat est utilise uniquement pour le trafic du proxy local de bureau sur votre machine. Votre systeme d'exploitation peut afficher une seconde invite de certificat apres cela.", + "folderSelection.servers.certificateInstall.confirmLabel": "Continuer", + "folderSelection.servers.certificateInstall.cancelLabel": "Annuler", + "folderSelection.servers.certificateInstall.cancelled": "CodeNomad a besoin que le certificat local soit approuve avant de pouvoir ouvrir des fenetres distantes HTTPS auto-signees.", "folderSelection.sidecars.button": "Open SideCar", } as const diff --git a/packages/ui/src/lib/i18n/messages/he/folderSelection.ts b/packages/ui/src/lib/i18n/messages/he/folderSelection.ts index 6e772cd12..69de36c09 100644 --- a/packages/ui/src/lib/i18n/messages/he/folderSelection.ts +++ b/packages/ui/src/lib/i18n/messages/he/folderSelection.ts @@ -69,5 +69,10 @@ export const folderSelectionMessages = { "folderSelection.servers.dialog.connecting": "מתחבר...", "folderSelection.servers.dialog.errorRequired": "שם השרת והכתובת הם שדות חובה.", "folderSelection.servers.dialog.errorConnect": "לא ניתן היה להתחבר לשרת המרוחק.", + "folderSelection.servers.certificateInstall.title": "התקנת אישור מקומי", + "folderSelection.servers.certificateInstall.confirmMessage": "CodeNomad צריך להתקין אישור מקומי כדי לפתוח חלונות HTTPS מרוחקים עם אישור בחתימה עצמית. האישור הזה משמש רק לתעבורת ה-proxy המקומי של האפליקציה במחשב שלך. ייתכן שמערכת ההפעלה תציג לאחר מכן בקשת אישור נוספת.", + "folderSelection.servers.certificateInstall.confirmLabel": "המשך", + "folderSelection.servers.certificateInstall.cancelLabel": "ביטול", + "folderSelection.servers.certificateInstall.cancelled": "CodeNomad צריך שהאישור המקומי יהיה מהימן לפני שיוכל לפתוח חלונות HTTPS מרוחקים עם אישור בחתימה עצמית.", "folderSelection.sidecars.button": "Open SideCar", } as const diff --git a/packages/ui/src/lib/i18n/messages/ja/folderSelection.ts b/packages/ui/src/lib/i18n/messages/ja/folderSelection.ts index 82edb63f9..b1b2f4787 100644 --- a/packages/ui/src/lib/i18n/messages/ja/folderSelection.ts +++ b/packages/ui/src/lib/i18n/messages/ja/folderSelection.ts @@ -69,5 +69,10 @@ export const folderSelectionMessages = { "folderSelection.servers.dialog.connecting": "接続中...", "folderSelection.servers.dialog.errorRequired": "サーバー名と URL は必須です。", "folderSelection.servers.dialog.errorConnect": "リモートサーバーに接続できませんでした。", + "folderSelection.servers.certificateInstall.title": "ローカル証明書をインストール", + "folderSelection.servers.certificateInstall.confirmMessage": "CodeNomad は自己署名 HTTPS のリモートウィンドウを開くために、ローカル証明書をインストールする必要があります。この証明書は、このマシン上のローカルデスクトッププロキシ通信にのみ使用されます。この後、OS が追加の証明書プロンプトを表示する場合があります。", + "folderSelection.servers.certificateInstall.confirmLabel": "続行", + "folderSelection.servers.certificateInstall.cancelLabel": "キャンセル", + "folderSelection.servers.certificateInstall.cancelled": "自己署名 HTTPS のリモートウィンドウを開くには、CodeNomad のローカル証明書を信頼する必要があります。", "folderSelection.sidecars.button": "Open SideCar", } as const diff --git a/packages/ui/src/lib/i18n/messages/ru/folderSelection.ts b/packages/ui/src/lib/i18n/messages/ru/folderSelection.ts index dd293097d..bc88c6013 100644 --- a/packages/ui/src/lib/i18n/messages/ru/folderSelection.ts +++ b/packages/ui/src/lib/i18n/messages/ru/folderSelection.ts @@ -69,5 +69,10 @@ export const folderSelectionMessages = { "folderSelection.servers.dialog.connecting": "Подключение...", "folderSelection.servers.dialog.errorRequired": "Имя сервера и URL обязательны.", "folderSelection.servers.dialog.errorConnect": "Не удалось подключиться к удаленному серверу.", + "folderSelection.servers.certificateInstall.title": "Установить локальный сертификат", + "folderSelection.servers.certificateInstall.confirmMessage": "CodeNomad должен установить локальный сертификат, чтобы открывать удаленные HTTPS-окна с самоподписанным сертификатом. Этот сертификат используется только для трафика локального настольного прокси на вашем устройстве. После этого ваша операционная система может показать второе предупреждение о сертификате.", + "folderSelection.servers.certificateInstall.confirmLabel": "Продолжить", + "folderSelection.servers.certificateInstall.cancelLabel": "Отмена", + "folderSelection.servers.certificateInstall.cancelled": "CodeNomad должен доверять локальному сертификату, прежде чем сможет открывать удаленные HTTPS-окна с самоподписанным сертификатом.", "folderSelection.sidecars.button": "Open SideCar", } as const diff --git a/packages/ui/src/lib/i18n/messages/zh-Hans/folderSelection.ts b/packages/ui/src/lib/i18n/messages/zh-Hans/folderSelection.ts index e3ea87276..445cdb000 100644 --- a/packages/ui/src/lib/i18n/messages/zh-Hans/folderSelection.ts +++ b/packages/ui/src/lib/i18n/messages/zh-Hans/folderSelection.ts @@ -69,5 +69,10 @@ export const folderSelectionMessages = { "folderSelection.servers.dialog.connecting": "连接中...", "folderSelection.servers.dialog.errorRequired": "服务器名称和 URL 为必填项。", "folderSelection.servers.dialog.errorConnect": "无法连接到远程服务器。", + "folderSelection.servers.certificateInstall.title": "安装本地证书", + "folderSelection.servers.certificateInstall.confirmMessage": "CodeNomad 需要安装本地证书,才能打开使用自签名 HTTPS 的远程窗口。此证书仅用于你这台设备上的本地桌面代理流量。之后你的操作系统可能还会显示第二个证书提示。", + "folderSelection.servers.certificateInstall.confirmLabel": "继续", + "folderSelection.servers.certificateInstall.cancelLabel": "取消", + "folderSelection.servers.certificateInstall.cancelled": "CodeNomad 需要先信任本地证书,才能打开使用自签名 HTTPS 的远程窗口。", "folderSelection.sidecars.button": "Open SideCar", } as const diff --git a/packages/ui/src/lib/native/remote-window.ts b/packages/ui/src/lib/native/remote-window.ts index 8412c6b3a..54506bda5 100644 --- a/packages/ui/src/lib/native/remote-window.ts +++ b/packages/ui/src/lib/native/remote-window.ts @@ -1,5 +1,7 @@ import { invoke } from "@tauri-apps/api/core" import type { RemoteServerProfile } from "../../../../server/src/api-types" +import { showConfirmDialog } from "../../stores/alerts" +import { tGlobal } from "../i18n" import { runtimeEnv } from "../runtime-env" export interface RemoteWindowOpenPayload { @@ -34,6 +36,28 @@ export async function openRemoteServerWindow( } if (runtimeEnv.host === "tauri") { + const requiresLocalCertificate = + proxySessionId !== undefined && (entryUrl ?? profile.baseUrl).startsWith("https://") + + if (requiresLocalCertificate) { + const needsInstall = await invoke("needs_local_certificate_install") + if (needsInstall) { + const accepted = await showConfirmDialog( + tGlobal("folderSelection.servers.certificateInstall.confirmMessage"), + { + title: tGlobal("folderSelection.servers.certificateInstall.title"), + variant: "warning", + confirmLabel: tGlobal("folderSelection.servers.certificateInstall.confirmLabel"), + cancelLabel: tGlobal("folderSelection.servers.certificateInstall.cancelLabel"), + }, + ) + + if (!accepted) { + throw new Error(tGlobal("folderSelection.servers.certificateInstall.cancelled")) + } + } + } + await invoke("open_remote_window", { payload }) return } From 3b08bc3262b8b1d02985028d2d4015bbab561745 Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Mon, 20 Apr 2026 12:58:56 +0100 Subject: [PATCH 02/18] fix(desktop): align standalone server startup and proxy workspace instances --- package-lock.json | 806 ++++++++++++------ packages/electron-app/electron/main/main.ts | 20 +- .../electron/main/process-manager.ts | 77 +- packages/electron-app/scripts/build.js | 44 +- .../electron-app/scripts/prepare-resources.js | 76 ++ packages/opencode-config/package.json | 4 +- packages/server/package.json | 11 +- packages/server/scripts/build-standalone.mjs | 78 ++ packages/server/src/index.ts | 5 +- packages/server/src/opencode-config.ts | 17 +- packages/server/src/runtime-paths.ts | 79 ++ packages/server/src/server/http-server.ts | 150 +++- packages/server/src/server/routes/auth.ts | 13 +- packages/server/src/workspaces/manager.ts | 70 ++ packages/tauri-app/scripts/prebuild.js | 91 ++ .../tauri-app/src-tauri/src/cli_manager.rs | 154 ++-- 16 files changed, 1246 insertions(+), 449 deletions(-) create mode 100644 packages/server/scripts/build-standalone.mjs create mode 100644 packages/server/src/runtime-paths.ts diff --git a/package-lock.json b/package-lock.json index 6e9afbcff..4d525e3fb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -72,7 +72,6 @@ "version": "7.28.5", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -1891,23 +1890,46 @@ } }, "node_modules/@fastify/accept-negotiator": { - "version": "1.1.0", - "license": "MIT", - "engines": { - "node": ">=14" - } + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@fastify/accept-negotiator/-/accept-negotiator-2.0.1.tgz", + "integrity": "sha512-/c/TW2bO/v9JeEgoD/g1G5GxGeCF1Hafdf79WPmUlgYiBXummY0oX3VVq4yFkKKVBKDNlaDUYoab7g38RpPqCQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" }, "node_modules/@fastify/ajv-compiler": { - "version": "3.6.0", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-4.0.5.tgz", + "integrity": "sha512-KoWKW+MhvfTRWL4qrhUwAAZoaChluo0m0vbiJlGMt2GXvL4LVPQEjt8kSpHI3IBq5Rez8fg+XeH3cneztq+C7A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], "license": "MIT", "dependencies": { - "ajv": "^8.11.0", - "ajv-formats": "^2.1.1", - "fast-uri": "^2.0.0" + "ajv": "^8.12.0", + "ajv-formats": "^3.0.1", + "fast-uri": "^3.0.0" } }, "node_modules/@fastify/ajv-compiler/node_modules/ajv": { - "version": "8.17.1", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", @@ -1920,8 +1942,16 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@fastify/ajv-compiler/node_modules/ajv/node_modules/fast-uri": { - "version": "3.1.0", + "node_modules/@fastify/ajv-compiler/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/@fastify/cors": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@fastify/cors/-/cors-11.2.0.tgz", + "integrity": "sha512-LbLHBuSAdGdSFZYTLVA3+Ch2t+sA6nq3Ejc6XLAKiQ6ViS2qFnvicpj0htsx03FyYeLs04HfRNBsz/a8SvbcUw==", "funding": [ { "type": "github", @@ -1932,81 +1962,163 @@ "url": "https://opencollective.com/fastify" } ], - "license": "BSD-3-Clause" - }, - "node_modules/@fastify/ajv-compiler/node_modules/json-schema-traverse": { - "version": "1.0.0", - "license": "MIT" - }, - "node_modules/@fastify/busboy": { - "version": "2.1.1", - "license": "MIT", - "engines": { - "node": ">=14" - } - }, - "node_modules/@fastify/cors": { - "version": "8.5.0", "license": "MIT", "dependencies": { - "fastify-plugin": "^4.0.0", - "mnemonist": "0.39.6" + "fastify-plugin": "^5.0.0", + "toad-cache": "^3.7.0" } }, "node_modules/@fastify/error": { - "version": "3.4.1", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@fastify/error/-/error-4.2.0.tgz", + "integrity": "sha512-RSo3sVDXfHskiBZKBPRgnQTtIqpi/7zhJOEmAxCiBcM7d0uwdGdxLlsCaLzGs8v8NnxIRlfG0N51p5yFaOentQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], "license": "MIT" }, "node_modules/@fastify/fast-json-stringify-compiler": { - "version": "4.3.0", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-5.0.3.tgz", + "integrity": "sha512-uik7yYHkLr6fxd8hJSZ8c+xF4WafPK+XzneQDPU+D10r5X19GW8lJcom2YijX2+qtFF1ENJlHXKFM9ouXNJYgQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], "license": "MIT", "dependencies": { - "fast-json-stringify": "^5.7.0" + "fast-json-stringify": "^6.0.0" } }, + "node_modules/@fastify/forwarded": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@fastify/forwarded/-/forwarded-3.0.1.tgz", + "integrity": "sha512-JqDochHFqXs3C3Ml3gOY58zM7OqO9ENqPo0UqAjAjH8L01fRZqwX9iLeX34//kiJubF7r2ZQHtBRU36vONbLlw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, "node_modules/@fastify/merge-json-schemas": { - "version": "0.1.1", + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@fastify/merge-json-schemas/-/merge-json-schemas-0.2.1.tgz", + "integrity": "sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.3" + "dequal": "^2.0.3" + } + }, + "node_modules/@fastify/proxy-addr": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@fastify/proxy-addr/-/proxy-addr-5.1.0.tgz", + "integrity": "sha512-INS+6gh91cLUjB+PVHfu1UqcB76Sqtpyp7bnL+FYojhjygvOPA9ctiD/JDKsyD9Xgu4hUhCSJBPig/w7duNajw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "@fastify/forwarded": "^3.0.0", + "ipaddr.js": "^2.1.0" } }, "node_modules/@fastify/reply-from": { - "version": "9.8.0", + "version": "12.6.2", + "resolved": "https://registry.npmjs.org/@fastify/reply-from/-/reply-from-12.6.2.tgz", + "integrity": "sha512-FhMvsRJa4HMG0q0/Yi06sgwVFd/Wc8E/+RbHsqB0Q+883C66RPrS6qwZKBPje2mVzsyEb9sjq7wlyvi35zRW0A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], "license": "MIT", "dependencies": { - "@fastify/error": "^3.0.0", + "@fastify/error": "^4.0.0", "end-of-stream": "^1.4.4", - "fast-content-type-parse": "^1.1.0", - "fast-querystring": "^1.0.0", - "fastify-plugin": "^4.0.0", + "fast-content-type-parse": "^3.0.0", + "fast-querystring": "^1.1.2", + "fastify-plugin": "^5.0.1", "toad-cache": "^3.7.0", - "undici": "^5.19.1" + "undici": "^7.0.0" } }, "node_modules/@fastify/reply-from/node_modules/undici": { - "version": "5.29.0", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.25.0.tgz", + "integrity": "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==", "license": "MIT", - "dependencies": { - "@fastify/busboy": "^2.0.0" - }, "engines": { - "node": ">=14.0" + "node": ">=20.18.1" } }, "node_modules/@fastify/send": { - "version": "2.1.0", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@fastify/send/-/send-4.1.0.tgz", + "integrity": "sha512-TMYeQLCBSy2TOFmV95hQWkiTYgC/SEx7vMdV+wnZVX4tt8VBLKzmH8vV9OzJehV0+XBfg+WxPMt5wp+JBUKsVw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], "license": "MIT", "dependencies": { - "@lukeed/ms": "^2.0.1", + "@lukeed/ms": "^2.0.2", "escape-html": "~1.0.3", "fast-decode-uri-component": "^1.0.1", - "http-errors": "2.0.0", - "mime": "^3.0.0" + "http-errors": "^2.0.0", + "mime": "^3" } }, "node_modules/@fastify/send/node_modules/mime": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", "license": "MIT", "bin": { "mime": "cli.js" @@ -2016,55 +2128,116 @@ } }, "node_modules/@fastify/static": { - "version": "7.0.4", + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@fastify/static/-/static-9.1.1.tgz", + "integrity": "sha512-LHxFea3qdwe0Pbbkh/yux7/k6nFNLGTNcbLKVYgmRDB6LdDE/8TFSO7qWZ0IzM/nF6iwR8W03oFlwe4v79R1Ow==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], "license": "MIT", "dependencies": { - "@fastify/accept-negotiator": "^1.0.0", - "@fastify/send": "^2.0.0", - "content-disposition": "^0.5.3", - "fastify-plugin": "^4.0.0", - "fastq": "^1.17.0", - "glob": "^10.3.4" + "@fastify/accept-negotiator": "^2.0.0", + "@fastify/send": "^4.0.0", + "content-disposition": "^1.0.1", + "fastify-plugin": "^5.0.0", + "fastq": "^1.17.1", + "glob": "^13.0.0" + } + }, + "node_modules/@fastify/static/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@fastify/static/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" } }, "node_modules/@fastify/static/node_modules/glob": { - "version": "10.5.0", - "license": "ISC", + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "license": "BlueOak-1.0.0", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" }, - "bin": { - "glob": "dist/esm/bin.mjs" + "engines": { + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@fastify/static/node_modules/lru-cache": { + "version": "11.3.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.5.tgz", + "integrity": "sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, "node_modules/@fastify/static/node_modules/minimatch": { - "version": "9.0.5", - "license": "ISC", + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^5.0.5" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/@fastify/static/node_modules/minipass": { - "version": "7.1.2", - "license": "ISC", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "license": "BlueOak-1.0.0", "engines": { "node": ">=16 || 14 >=14.17" } }, + "node_modules/@fastify/static/node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@floating-ui/core": { "version": "1.7.3", "license": "MIT", @@ -2703,6 +2876,8 @@ }, "node_modules/@lukeed/ms": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@lukeed/ms/-/ms-2.0.2.tgz", + "integrity": "sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==", "license": "MIT", "engines": { "node": ">=8" @@ -3677,7 +3852,6 @@ "version": "7.20.5", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", @@ -3779,7 +3953,6 @@ "version": "22.19.0", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -3854,7 +4027,6 @@ "integrity": "sha512-MCbrb508JZHqe7bUibmZj/lyojdhLRnfkmyXnkrCM2zVrjTgL89U8UEfInpKTvPeTnxsw2hmyZxnhsdNR6yhwg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "cac": "^6.7.14", "colorette": "^2.0.20", @@ -3937,7 +4109,6 @@ "version": "6.12.6", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -3950,7 +4121,9 @@ } }, "node_modules/ajv-formats": { - "version": "2.1.1", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", "license": "MIT", "dependencies": { "ajv": "^8.0.0" @@ -3965,7 +4138,9 @@ } }, "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.17.1", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", @@ -3978,22 +4153,10 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ajv-formats/node_modules/fast-uri": { - "version": "3.1.0", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, "node_modules/ajv-formats/node_modules/json-schema-traverse": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT" }, "node_modules/ajv-keywords": { @@ -4140,6 +4303,7 @@ "version": "5.3.2", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "archiver-utils": "^2.1.0", "async": "^3.2.4", @@ -4157,6 +4321,7 @@ "version": "2.1.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "glob": "^7.1.4", "graceful-fs": "^4.2.0", @@ -4177,6 +4342,7 @@ "version": "2.3.8", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -4190,12 +4356,14 @@ "node_modules/archiver-utils/node_modules/safe-buffer": { "version": "5.1.2", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/archiver-utils/node_modules/string_decoder": { "version": "1.1.1", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "safe-buffer": "~5.1.0" } @@ -4363,10 +4531,22 @@ } }, "node_modules/avvio": { - "version": "8.4.0", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/avvio/-/avvio-9.2.0.tgz", + "integrity": "sha512-2t/sy01ArdHHE0vRH5Hsay+RtCZt3dLPji7W7/MMOCEgze5b7SNDC4j5H6FnVgPkI1MTNFGzHdHrVXDDl7QSSQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], "license": "MIT", "dependencies": { - "@fastify/error": "^3.3.0", + "@fastify/error": "^4.0.0", "fastq": "^1.17.1" } }, @@ -4509,6 +4689,7 @@ "version": "4.1.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", @@ -4572,7 +4753,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -5063,6 +5243,7 @@ "version": "4.1.2", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "buffer-crc32": "^0.2.13", "crc32-stream": "^4.0.2", @@ -5139,13 +5320,16 @@ } }, "node_modules/content-disposition": { - "version": "0.5.4", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", + "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/convert-source-map": { @@ -5154,10 +5338,16 @@ "license": "MIT" }, "node_modules/cookie": { - "version": "0.7.2", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/core-js-compat": { @@ -5192,6 +5382,7 @@ "version": "1.2.2", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "crc32": "bin/crc32.njs" }, @@ -5203,6 +5394,7 @@ "version": "4.0.3", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "crc-32": "^1.2.0", "readable-stream": "^3.4.0" @@ -5474,6 +5666,8 @@ }, "node_modules/depd": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -5568,7 +5762,6 @@ "version": "24.13.3", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "app-builder-lib": "24.13.3", "builder-util": "24.13.1", @@ -5735,6 +5928,7 @@ "version": "24.13.3", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "app-builder-lib": "24.13.3", "archiver": "^5.3.1", @@ -5746,6 +5940,7 @@ "version": "10.1.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -5759,6 +5954,7 @@ "version": "6.2.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "universalify": "^2.0.0" }, @@ -5770,6 +5966,7 @@ "version": "2.0.1", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 10.0.0" } @@ -6106,6 +6303,8 @@ }, "node_modules/escape-html": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "license": "MIT" }, "node_modules/escape-string-regexp": { @@ -6170,7 +6369,19 @@ "optional": true }, "node_modules/fast-content-type-parse": { - "version": "1.1.0", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz", + "integrity": "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], "license": "MIT" }, "node_modules/fast-decode-uri-component": { @@ -6217,20 +6428,33 @@ "license": "MIT" }, "node_modules/fast-json-stringify": { - "version": "5.16.1", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-6.3.0.tgz", + "integrity": "sha512-oRCntNDY/329HJPlmdNLIdogNtt6Vyjb1WuT01Soss3slIdyUp8kAcDU3saQTOquEK8KFVfwIIF7FebxUAu+yA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], "license": "MIT", "dependencies": { - "@fastify/merge-json-schemas": "^0.1.0", - "ajv": "^8.10.0", + "@fastify/merge-json-schemas": "^0.2.0", + "ajv": "^8.12.0", "ajv-formats": "^3.0.1", - "fast-deep-equal": "^3.1.3", - "fast-uri": "^2.1.0", - "json-schema-ref-resolver": "^1.0.1", + "fast-uri": "^3.0.0", + "json-schema-ref-resolver": "^3.0.0", "rfdc": "^1.2.0" } }, "node_modules/fast-json-stringify/node_modules/ajv": { - "version": "8.17.1", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", @@ -6243,23 +6467,23 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/fast-json-stringify/node_modules/ajv-formats": { - "version": "3.0.1", + "node_modules/fast-json-stringify/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/fast-querystring": { + "version": "1.1.2", "license": "MIT", "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } + "fast-decode-uri-component": "^1.0.1" } }, - "node_modules/fast-json-stringify/node_modules/ajv/node_modules/fast-uri": { + "node_modules/fast-uri": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", "funding": [ { "type": "github", @@ -6272,23 +6496,10 @@ ], "license": "BSD-3-Clause" }, - "node_modules/fast-json-stringify/node_modules/json-schema-traverse": { - "version": "1.0.0", - "license": "MIT" - }, - "node_modules/fast-querystring": { - "version": "1.1.2", - "license": "MIT", - "dependencies": { - "fast-decode-uri-component": "^1.0.1" - } - }, - "node_modules/fast-uri": { - "version": "2.4.0", - "license": "MIT" - }, "node_modules/fastify": { - "version": "4.29.1", + "version": "5.8.5", + "resolved": "https://registry.npmjs.org/fastify/-/fastify-5.8.5.tgz", + "integrity": "sha512-Yqptv59pQzPgQUSIm87hMqHJmdkb1+GPxdE6vW6FRyVE9G86mt7rOghitiU4JHRaTyDUk9pfeKmDeu70lAwM4Q==", "funding": [ { "type": "github", @@ -6301,26 +6512,37 @@ ], "license": "MIT", "dependencies": { - "@fastify/ajv-compiler": "^3.5.0", - "@fastify/error": "^3.4.0", - "@fastify/fast-json-stringify-compiler": "^4.3.0", + "@fastify/ajv-compiler": "^4.0.5", + "@fastify/error": "^4.0.0", + "@fastify/fast-json-stringify-compiler": "^5.0.0", + "@fastify/proxy-addr": "^5.0.0", "abstract-logging": "^2.0.1", - "avvio": "^8.3.0", - "fast-content-type-parse": "^1.1.0", - "fast-json-stringify": "^5.8.0", - "find-my-way": "^8.0.0", - "light-my-request": "^5.11.0", - "pino": "^9.0.0", - "process-warning": "^3.0.0", - "proxy-addr": "^2.0.7", - "rfdc": "^1.3.0", - "secure-json-parse": "^2.7.0", - "semver": "^7.5.4", - "toad-cache": "^3.3.0" + "avvio": "^9.0.0", + "fast-json-stringify": "^6.0.0", + "find-my-way": "^9.0.0", + "light-my-request": "^6.0.0", + "pino": "^9.14.0 || ^10.1.0", + "process-warning": "^5.0.0", + "rfdc": "^1.3.1", + "secure-json-parse": "^4.0.0", + "semver": "^7.6.0", + "toad-cache": "^3.7.0" } }, "node_modules/fastify-plugin": { - "version": "4.5.1", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-5.1.0.tgz", + "integrity": "sha512-FAIDA8eovSt5qcDgcBvDuX/v0Cjz0ohGhENZ/wpc3y+oZCY2afZ9Baqql3g/lC+OHRnciQol4ww7tuthOb9idw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], "license": "MIT" }, "node_modules/fastify/node_modules/semver": { @@ -6388,15 +6610,17 @@ } }, "node_modules/find-my-way": { - "version": "8.2.2", + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-9.5.0.tgz", + "integrity": "sha512-VW2RfnmscZO5KgBY5XVyKREMW5nMZcxDy+buTOsL+zIPnBlbKm+00sgzoQzq1EVh4aALZLfKdwv6atBGcjvjrQ==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", "fast-querystring": "^1.0.0", - "safe-regex2": "^3.1.0" + "safe-regex2": "^5.0.0" }, "engines": { - "node": ">=14" + "node": ">=20" } }, "node_modules/find-up": { @@ -6465,13 +6689,6 @@ "node": ">=12.20.0" } }, - "node_modules/forwarded": { - "version": "0.2.0", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/fraction.js": { "version": "4.3.7", "dev": true, @@ -6487,7 +6704,8 @@ "node_modules/fs-constants": { "version": "1.0.0", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/fs-extra": { "version": "8.1.0", @@ -7085,17 +7303,23 @@ "license": "BSD-2-Clause" }, "node_modules/http-errors": { - "version": "2.0.0", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "license": "MIT", "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" }, "engines": { "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/http-proxy-agent": { @@ -7224,10 +7448,12 @@ } }, "node_modules/ipaddr.js": { - "version": "1.9.1", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.3.0.tgz", + "integrity": "sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==", "license": "MIT", "engines": { - "node": ">= 0.10" + "node": ">= 10" } }, "node_modules/is-array-buffer": { @@ -7704,7 +7930,8 @@ "node_modules/isarray": { "version": "1.0.0", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/isbinaryfile": { "version": "5.0.6", @@ -7754,7 +7981,6 @@ "version": "1.21.7", "dev": true, "license": "MIT", - "peer": true, "bin": { "jiti": "bin/jiti.js" } @@ -7806,10 +8032,22 @@ "license": "(AFL-2.1 OR BSD-3-Clause)" }, "node_modules/json-schema-ref-resolver": { - "version": "1.0.1", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-schema-ref-resolver/-/json-schema-ref-resolver-3.0.0.tgz", + "integrity": "sha512-hOrZIVL5jyYFjzk7+y7n5JDzGlU8rfWDuYyHwGa2WA8/pcmMHezp2xsVwxrebD/Q9t8Nc5DboieySDpCp4WG4A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.3" + "dequal": "^2.0.3" } }, "node_modules/json-schema-traverse": { @@ -7886,6 +8124,7 @@ "version": "1.0.1", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "readable-stream": "^2.0.5" }, @@ -7897,6 +8136,7 @@ "version": "2.3.8", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -7910,12 +8150,14 @@ "node_modules/lazystream/node_modules/safe-buffer": { "version": "5.1.2", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/lazystream/node_modules/string_decoder": { "version": "1.1.1", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "safe-buffer": "~5.1.0" } @@ -7931,14 +8173,42 @@ } }, "node_modules/light-my-request": { - "version": "5.14.0", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-6.6.0.tgz", + "integrity": "sha512-CHYbu8RtboSIoVsHZ6Ye4cj4Aw/yg2oAFimlF7mNvfDV192LR7nDiKtSIfCuLT7KokPSTn/9kfVLm5OGN0A28A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], "license": "BSD-3-Clause", "dependencies": { - "cookie": "^0.7.0", - "process-warning": "^3.0.0", - "set-cookie-parser": "^2.4.1" + "cookie": "^1.0.1", + "process-warning": "^4.0.0", + "set-cookie-parser": "^2.6.0" } }, + "node_modules/light-my-request/node_modules/process-warning": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-4.0.1.tgz", + "integrity": "sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, "node_modules/lilconfig": { "version": "3.1.3", "dev": true, @@ -7980,22 +8250,26 @@ "node_modules/lodash.defaults": { "version": "4.2.0", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/lodash.difference": { "version": "4.5.0", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/lodash.flatten": { "version": "4.4.0", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/lodash.isplainobject": { "version": "4.0.6", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/lodash.sortby": { "version": "4.7.0", @@ -8007,7 +8281,8 @@ "node_modules/lodash.union": { "version": "4.6.0", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/lowercase-keys": { "version": "2.0.0", @@ -8323,13 +8598,6 @@ "node": ">=10" } }, - "node_modules/mnemonist": { - "version": "0.39.6", - "license": "MIT", - "dependencies": { - "obliterator": "^2.0.1" - } - }, "node_modules/monaco-editor": { "version": "0.52.2", "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.52.2.tgz", @@ -8505,10 +8773,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/obliterator": { - "version": "2.0.5", - "license": "MIT" - }, "node_modules/on-exit-leak-free": { "version": "2.1.2", "license": "MIT", @@ -8732,20 +8996,6 @@ "version": "7.0.0", "license": "MIT" }, - "node_modules/pino/node_modules/process-warning": { - "version": "5.0.0", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT" - }, "node_modules/pirates": { "version": "4.0.7", "dev": true, @@ -8811,7 +9061,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -8959,10 +9208,23 @@ "node_modules/process-nextick-args": { "version": "2.0.1", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/process-warning": { - "version": "3.0.0", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz", + "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], "license": "MIT" }, "node_modules/progress": { @@ -8993,17 +9255,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, "node_modules/pump": { "version": "3.0.3", "dev": true, @@ -9208,6 +9459,7 @@ "version": "3.6.2", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -9221,6 +9473,7 @@ "version": "1.1.3", "dev": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "minimatch": "^5.1.0" } @@ -9425,7 +9678,9 @@ } }, "node_modules/ret": { - "version": "0.4.3", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.5.0.tgz", + "integrity": "sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw==", "license": "MIT", "engines": { "node": ">=10" @@ -9449,6 +9704,8 @@ }, "node_modules/rfdc": { "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", "license": "MIT" }, "node_modules/rimraf": { @@ -9523,7 +9780,6 @@ "version": "4.52.5", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -9670,10 +9926,25 @@ } }, "node_modules/safe-regex2": { - "version": "3.1.0", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-5.1.1.tgz", + "integrity": "sha512-mOSBvHGDZMuIEZMdOz/aCEYDCv0E7nfcNsIhUF+/P+xC7Hyf3FkvymqgPbg9D1EdSGu+uKbJgy09K/RKKc7kJA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], "license": "MIT", "dependencies": { - "ret": "~0.4.0" + "ret": "~0.5.0" + }, + "bin": { + "safe-regex2": "bin/safe-regex2.js" } }, "node_modules/safe-stable-stringify": { @@ -9702,7 +9973,19 @@ "license": "BlueOak-1.0.0" }, "node_modules/secure-json-parse": { - "version": "2.7.0", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-4.1.0.tgz", + "integrity": "sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], "license": "BSD-3-Clause" }, "node_modules/semver": { @@ -9747,7 +10030,6 @@ "node_modules/seroval": { "version": "1.3.2", "license": "MIT", - "peer": true, "engines": { "node": ">=10" } @@ -9768,6 +10050,8 @@ }, "node_modules/set-cookie-parser": { "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", "license": "MIT" }, "node_modules/set-function-length": { @@ -9821,6 +10105,8 @@ }, "node_modules/setprototypeof": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, "node_modules/sharp": { @@ -10071,7 +10357,6 @@ "node_modules/solid-js": { "version": "1.9.10", "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.1.0", "seroval": "~1.3.0", @@ -10188,7 +10473,9 @@ } }, "node_modules/statuses": { - "version": "2.0.1", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -10212,6 +10499,7 @@ "version": "1.3.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "safe-buffer": "~5.2.0" } @@ -10545,6 +10833,7 @@ "version": "2.2.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", @@ -10737,7 +11026,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -10788,6 +11076,8 @@ }, "node_modules/toidentifier": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "license": "MIT", "engines": { "node": ">=0.6" @@ -10987,7 +11277,6 @@ "version": "5.9.3", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -11057,10 +11346,12 @@ } }, "node_modules/undici": { - "version": "6.22.0", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-8.1.0.tgz", + "integrity": "sha512-E9MkTS4xXLnRPYqxH2e6Hr2/49e7WFDKczKcCaFH4VaZs2iNvHMqeIkyUAD9vM8kujy9TjVrRlQ5KkdEJxB2pw==", "license": "MIT", "engines": { - "node": ">=18.17" + "node": ">=22.19.0" } }, "node_modules/undici-types": { @@ -11335,7 +11626,6 @@ "version": "5.4.21", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", @@ -11820,7 +12110,6 @@ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -11839,23 +12128,6 @@ "dev": true, "license": "MIT" }, - "node_modules/workbox-build/node_modules/fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, "node_modules/workbox-build/node_modules/fs-extra": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", @@ -12015,7 +12287,6 @@ "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", "dev": true, "license": "MIT", - "peer": true, "bin": { "rollup": "dist/bin/rollup" }, @@ -12304,6 +12575,7 @@ "version": "4.1.1", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "archiver-utils": "^3.0.4", "compress-commons": "^4.1.2", @@ -12317,6 +12589,7 @@ "version": "3.0.4", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "glob": "^7.2.3", "graceful-fs": "^4.2.0", @@ -12336,7 +12609,6 @@ "node_modules/zod": { "version": "3.25.76", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -12391,16 +12663,16 @@ "version": "0.14.0", "license": "MIT", "dependencies": { - "@fastify/cors": "^8.5.0", - "@fastify/reply-from": "^9.8.0", - "@fastify/static": "^7.0.4", + "@fastify/cors": "^11.2.0", + "@fastify/reply-from": "^12.6.2", + "@fastify/static": "^9.1.1", "commander": "^12.1.0", - "fastify": "^4.28.1", + "fastify": "^5.8.5", "fuzzysort": "^2.0.4", "node-forge": "^1.3.3", "openai": "^6.27.0", "pino": "^9.4.0", - "undici": "^6.19.8", + "undici": "^8.1.0", "yaml": "^2.4.2", "yauzl": "^2.10.0", "zod": "^3.23.8" diff --git a/packages/electron-app/electron/main/main.ts b/packages/electron-app/electron/main/main.ts index eeee81e47..27e3fbfb8 100644 --- a/packages/electron-app/electron/main/main.ts +++ b/packages/electron-app/electron/main/main.ts @@ -118,6 +118,8 @@ function loadLoadingScreen(window: BrowserWindow) { loader.catch((error) => { console.error("[cli] failed to load loading screen:", error) }) + + return loader } function getAllowedRendererOrigins(window?: BrowserWindow | null): string[] { @@ -291,7 +293,7 @@ function createWindow() { showingLoadingScreen = true currentCliUrl = null clearWindowAllowedOrigin(window) - loadLoadingScreen(window) + const loadingReady = loadLoadingScreen(window) if (process.env.NODE_ENV === "development") { window.webContents.openDevTools({ mode: "detach" }) @@ -310,11 +312,7 @@ function createWindow() { showingLoadingScreen = false }) - if (pendingCliUrl) { - const url = pendingCliUrl - pendingCliUrl = null - startCliPreload(url) - } + return loadingReady } function showLoadingScreen(force = false) { @@ -620,7 +618,8 @@ app.whenReady().then(() => { // ignore } - startCli() + const loadingReady = createWindow() + ;(mainWindow as BrowserWindow & { __codenomadOpenRemoteWindow?: typeof openRemoteWindow }).__codenomadOpenRemoteWindow = openRemoteWindow if (isMac) { session.defaultSession.setSpellCheckerEnabled(false) @@ -637,8 +636,11 @@ app.whenReady().then(() => { } } - createWindow() - ;(mainWindow as BrowserWindow & { __codenomadOpenRemoteWindow?: typeof openRemoteWindow }).__codenomadOpenRemoteWindow = openRemoteWindow + void loadingReady.finally(() => { + setTimeout(() => { + void startCli() + }, 0) + }) app.on("certificate-error", (event, _webContents, url, error, _certificate, callback) => { if (isInsecureOriginAllowed(url)) { diff --git a/packages/electron-app/electron/main/process-manager.ts b/packages/electron-app/electron/main/process-manager.ts index 9cb4a041b..6d0d36cca 100644 --- a/packages/electron-app/electron/main/process-manager.ts +++ b/packages/electron-app/electron/main/process-manager.ts @@ -38,7 +38,7 @@ interface StartOptions { interface CliEntryResolution { entry: string - runner: "node" | "tsx" + runner: "node" | "tsx" | "standalone" runnerPath?: string } @@ -148,15 +148,15 @@ export class CliProcessManager extends EventEmitter { const listeningMode = this.resolveListeningMode() const host = resolveHostForMode(listeningMode) const args = this.buildCliArgs(options, host) + const cliEntry = this.resolveCliEntry(options) let child: ManagedChild - if (this.shouldUsePackagedShellSupervisor(options)) { - const runtimePath = this.resolveShellNodeCommand() - const entryPath = this.resolveBundledProdEntry() + if (this.shouldUsePackagedShellSupervisor(options, cliEntry)) { const supervisorPath = this.resolveCliSupervisorPath() const shellEnv = supportsUserShell() ? getUserShellEnv() : { ...process.env } - const shellCommand = buildUserShellCommand(`exec ${this.buildExecutableCommand(runtimePath, [entryPath, ...args])}`) + const shellTarget = cliEntry.runner === "standalone" ? this.buildExecutableCommand(cliEntry.entry, args) : this.buildCommand(cliEntry, args) + const shellCommand = buildUserShellCommand(`exec ${shellTarget}`) const supervisorPayload = JSON.stringify({ command: shellCommand.command, args: shellCommand.args, @@ -164,28 +164,33 @@ export class CliProcessManager extends EventEmitter { }) console.info( - `[cli] launching CodeNomad CLI (${options.dev ? "dev" : "prod"}) via utility supervisor using node at ${runtimePath} (host=${host})`, + `[cli] launching CodeNomad CLI (${options.dev ? "dev" : "prod"}) via utility supervisor using ${cliEntry.runner} at ${cliEntry.entry} (host=${host})`, ) console.info(`[cli] utility supervisor: ${supervisorPath}`) console.info(`[cli] shell command: ${shellCommand.command} ${shellCommand.args.join(" ")}`) child = utilityProcess.fork(supervisorPath, [supervisorPayload], { - env: shellEnv, + env: cliEntry.runner === "standalone" ? shellEnv : { ...shellEnv, ELECTRON_RUN_AS_NODE: "1" }, stdio: "pipe", serviceName: "CodeNomad CLI Supervisor", }) this.childLaunchMode = "utility" } else { - const cliEntry = this.resolveCliEntry(options) console.info( `[cli] launching CodeNomad CLI (${options.dev ? "dev" : "prod"}) using ${cliEntry.runner} at ${cliEntry.entry} (host=${host})`, ) const env = supportsUserShell() ? getUserShellEnv() : { ...process.env } - env.ELECTRON_RUN_AS_NODE = "1" + if (cliEntry.runner !== "standalone") { + env.ELECTRON_RUN_AS_NODE = "1" + } const spawnDetails = supportsUserShell() - ? buildUserShellCommand(`ELECTRON_RUN_AS_NODE=1 exec ${this.buildCommand(cliEntry, args)}`) + ? buildUserShellCommand( + `${cliEntry.runner === "standalone" ? "" : "ELECTRON_RUN_AS_NODE=1 "}exec ${ + cliEntry.runner === "standalone" ? this.buildExecutableCommand(cliEntry.entry, args) : this.buildCommand(cliEntry, args) + }`, + ) : this.buildDirectSpawn(cliEntry, args) const detached = process.platform !== "win32" @@ -563,6 +568,10 @@ export class CliProcessManager extends EventEmitter { } private buildCommand(cliEntry: CliEntryResolution, args: string[]): string { + if (cliEntry.runner === "standalone") { + return this.buildExecutableCommand(cliEntry.entry, args) + } + const parts = [JSON.stringify(process.execPath)] if (cliEntry.runner === "tsx" && cliEntry.runnerPath) { parts.push(JSON.stringify(cliEntry.runnerPath)) @@ -577,6 +586,10 @@ export class CliProcessManager extends EventEmitter { } private buildDirectSpawn(cliEntry: CliEntryResolution, args: string[]) { + if (cliEntry.runner === "standalone") { + return { command: cliEntry.entry, args } + } + if (cliEntry.runner === "tsx") { return { command: process.execPath, args: [cliEntry.runnerPath!, cliEntry.entry, ...args] } } @@ -593,9 +606,8 @@ export class CliProcessManager extends EventEmitter { const devEntry = this.resolveDevEntry() return { entry: devEntry, runner: "tsx", runnerPath: tsxPath } } - - const distEntry = this.resolveProdEntry() - return { entry: distEntry, runner: "node" } + + return { entry: this.resolveStandaloneProdEntry(), runner: "standalone" } } private resolveTsx(): string | null { @@ -635,26 +647,12 @@ export class CliProcessManager extends EventEmitter { return entry } - private resolveProdEntry(): string { - try { - const entry = nodeRequire.resolve("@neuralnomads/codenomad/dist/bin.js") - if (existsSync(entry)) { - return entry - } - } catch { - // fall through to error below - } - throw new Error("Unable to locate CodeNomad CLI build (dist/bin.js). Run npm run build --workspace @neuralnomads/codenomad.") - } - - private shouldUsePackagedShellSupervisor(options: StartOptions): boolean { - return !options.dev && app.isPackaged && process.platform === "darwin" - } - - private resolveCliSupervisorPath(): string { + private resolveStandaloneProdEntry(): string { + const executableName = process.platform === "win32" ? "codenomad-server.exe" : "codenomad-server" const candidates = [ - path.join(process.resourcesPath, "cli-supervisor.cjs"), - path.join(mainDirname, "../resources/cli-supervisor.cjs"), + path.join(process.resourcesPath, "server", "dist", executableName), + path.join(mainDirname, "../resources/server/dist", executableName), + path.resolve(process.cwd(), "..", "server", "dist", executableName), ] for (const candidate of candidates) { @@ -663,18 +661,17 @@ export class CliProcessManager extends EventEmitter { } } - throw new Error("Unable to locate CodeNomad CLI supervisor script.") + throw new Error(`Unable to locate standalone CodeNomad server executable (${executableName}). Run npm run build:standalone --workspace @neuralnomads/codenomad.`) } - private resolveShellNodeCommand(): string { - const configured = process.env.NODE_BINARY?.trim() - return configured && configured.length > 0 ? configured : "node" + private shouldUsePackagedShellSupervisor(options: StartOptions, cliEntry: CliEntryResolution): boolean { + return !options.dev && app.isPackaged && process.platform === "darwin" && cliEntry.runner !== "standalone" } - private resolveBundledProdEntry(): string { + private resolveCliSupervisorPath(): string { const candidates = [ - path.join(process.resourcesPath, "server", "dist", "bin.js"), - path.join(mainDirname, "../resources/server/dist/bin.js"), + path.join(process.resourcesPath, "cli-supervisor.cjs"), + path.join(mainDirname, "../resources/cli-supervisor.cjs"), ] for (const candidate of candidates) { @@ -683,7 +680,7 @@ export class CliProcessManager extends EventEmitter { } } - throw new Error("Unable to locate bundled CodeNomad CLI build in app resources.") + throw new Error("Unable to locate CodeNomad CLI supervisor script.") } private describeUtilityProcessError(error: unknown): string { diff --git a/packages/electron-app/scripts/build.js b/packages/electron-app/scripts/build.js index 636170d84..3651c3bd0 100644 --- a/packages/electron-app/scripts/build.js +++ b/packages/electron-app/scripts/build.js @@ -1,7 +1,7 @@ #!/usr/bin/env node import { spawn } from "child_process" -import { existsSync } from "fs" +import { existsSync, readFileSync } from "fs" import path, { join } from "path" import { fileURLToPath } from "url" @@ -14,6 +14,46 @@ const npxCmd = process.platform === "win32" ? "npx.cmd" : "npx" const nodeModulesPath = join(appDir, "node_modules") const workspaceNodeModulesPath = join(workspaceRoot, "node_modules") +function getPlatformEsbuildPackage() { + const platformKey = `${process.platform}-${process.arch}` + const platformPackages = { + "linux-x64": "@esbuild/linux-x64", + "linux-arm64": "@esbuild/linux-arm64", + "darwin-arm64": "@esbuild/darwin-arm64", + "darwin-x64": "@esbuild/darwin-x64", + "win32-arm64": "@esbuild/win32-arm64", + "win32-x64": "@esbuild/win32-x64", + } + + return platformPackages[platformKey] ?? null +} + +async function ensureEsbuildPlatformBinary() { + const pkgName = getPlatformEsbuildPackage() + if (!pkgName) { + return + } + + const platformPackagePath = join(workspaceNodeModulesPath, ...pkgName.split("/")) + if (existsSync(platformPackagePath)) { + return + } + + let esbuildVersion = "" + try { + esbuildVersion = JSON.parse(readFileSync(join(workspaceNodeModulesPath, "esbuild", "package.json"), "utf-8")).version ?? "" + } catch { + // leave version empty; fallback install will use latest compatible + } + + const packageSpec = esbuildVersion ? `${pkgName}@${esbuildVersion}` : pkgName + console.log("📦 Step 0/3: Restoring esbuild platform binary...\n") + await run(npmCmd, ["install", packageSpec, "--no-save", "--ignore-scripts", "--fund=false", "--audit=false"], { + cwd: workspaceRoot, + env: { NODE_PATH: workspaceNodeModulesPath }, + }) +} + const platforms = { mac: { args: ["--mac", "--x64", "--arm64"], @@ -105,6 +145,8 @@ async function build(platform) { console.log(`\n🔨 Building for: ${config.description}\n`) try { + await ensureEsbuildPlatformBinary() + console.log("📦 Step 1/3: Building CLI dependency...\n") await run(npmCmd, ["run", "build", "--workspace", "@neuralnomads/codenomad"], { cwd: workspaceRoot, diff --git a/packages/electron-app/scripts/prepare-resources.js b/packages/electron-app/scripts/prepare-resources.js index d44a4508e..b2df71f9e 100644 --- a/packages/electron-app/scripts/prepare-resources.js +++ b/packages/electron-app/scripts/prepare-resources.js @@ -16,6 +16,7 @@ const npmNodeExecPath = process.env.npm_node_execpath const serverSources = ["dist", "public", "node_modules", "package.json"] const serverDepsMarker = join(serverRoot, "node_modules", "fastify", "package.json") +const standaloneMarker = join(serverRoot, "dist", process.platform === "win32" ? "codenomad-server.exe" : "codenomad-server") function log(message) { console.log(`[prepare-resources] ${message}`) @@ -29,6 +30,34 @@ function ensureServerBuild() { } } +function ensureStandaloneServerBuild() { + log("building standalone server executable") + const result = spawnSync( + "npm", + ["run", "build:standalone", "--workspace", "@neuralnomads/codenomad"], + { + cwd: workspaceRoot, + stdio: "inherit", + env: { + ...process.env, + PATH: `${join(workspaceRoot, "node_modules", ".bin")}${path.delimiter}${process.env.PATH ?? ""}`, + }, + shell: process.platform === "win32", + }, + ) + + if (result.status !== 0) { + if (result.error) { + throw result.error + } + throw new Error(`standalone server build exited with code ${result.status ?? 1}`) + } + + if (!fs.existsSync(standaloneMarker)) { + throw new Error(`Standalone server executable missing after build: ${standaloneMarker}`) + } +} + function ensureServerDependencies() { if (fs.existsSync(serverDepsMarker)) { return @@ -65,6 +94,51 @@ function ensureServerDependencies() { } } +function ensureEsbuildPlatformBinary() { + const platformKey = `${process.platform}-${process.arch}` + const platformPackages = { + "linux-x64": "@esbuild/linux-x64", + "linux-arm64": "@esbuild/linux-arm64", + "darwin-arm64": "@esbuild/darwin-arm64", + "darwin-x64": "@esbuild/darwin-x64", + "win32-arm64": "@esbuild/win32-arm64", + "win32-x64": "@esbuild/win32-x64", + } + + const pkgName = platformPackages[platformKey] + if (!pkgName) { + return + } + + const platformPackagePath = join(workspaceRoot, "node_modules", ...pkgName.split("/")) + if (fs.existsSync(platformPackagePath)) { + return + } + + let esbuildVersion = "" + try { + esbuildVersion = JSON.parse(fs.readFileSync(join(workspaceRoot, "node_modules", "esbuild", "package.json"), "utf-8")).version ?? "" + } catch { + // leave version empty; fallback install will use latest compatible + } + + const packageSpec = esbuildVersion ? `${pkgName}@${esbuildVersion}` : pkgName + log("installing esbuild platform binary (optional dep workaround)") + + const result = spawnSync("npm", ["install", packageSpec, "--no-save", "--ignore-scripts", "--fund=false", "--audit=false"], { + cwd: workspaceRoot, + stdio: "inherit", + shell: process.platform === "win32", + }) + + if (result.status !== 0) { + if (result.error) { + throw result.error + } + throw new Error(`esbuild platform install exited with code ${result.status ?? 1}`) + } +} + function copyServerArtifacts() { fs.rmSync(serverDest, { recursive: true, force: true }) fs.mkdirSync(serverDest, { recursive: true }) @@ -121,7 +195,9 @@ function stripNodeModuleBins() { async function main() { ensureServerBuild() + ensureStandaloneServerBuild() ensureServerDependencies() + ensureEsbuildPlatformBinary() copyServerArtifacts() stripNodeModuleBins() } diff --git a/packages/opencode-config/package.json b/packages/opencode-config/package.json index 635f77167..a1c0f1345 100644 --- a/packages/opencode-config/package.json +++ b/packages/opencode-config/package.json @@ -4,6 +4,6 @@ "private": true, "license": "MIT", "dependencies": { - "@opencode-ai/plugin": "1.3.7" + "@opencode-ai/plugin": "1.14.19" } -} \ No newline at end of file +} diff --git a/packages/server/package.json b/packages/server/package.json index dafda21eb..04141d1c0 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -18,6 +18,7 @@ }, "scripts": { "build": "npm run build:ui && npm run prepare-ui && tsc -p tsconfig.json && node ./scripts/copy-auth-pages.mjs && npm run prepare-config", + "build:standalone": "node ./scripts/build-standalone.mjs", "build:ui": "npm run build --prefix ../ui", "prepare-ui": "node ./scripts/copy-ui-dist.mjs", "prepare-config": "node ./scripts/copy-opencode-config.mjs", @@ -25,16 +26,16 @@ "typecheck": "tsc --noEmit -p tsconfig.json" }, "dependencies": { - "@fastify/cors": "^8.5.0", - "@fastify/reply-from": "^9.8.0", - "@fastify/static": "^7.0.4", + "@fastify/cors": "^11.2.0", + "@fastify/reply-from": "^12.6.2", + "@fastify/static": "^9.1.1", "commander": "^12.1.0", - "fastify": "^4.28.1", + "fastify": "^5.8.5", "fuzzysort": "^2.0.4", "node-forge": "^1.3.3", "openai": "^6.27.0", "pino": "^9.4.0", - "undici": "^6.19.8", + "undici": "^8.1.0", "yaml": "^2.4.2", "yauzl": "^2.10.0", "zod": "^3.23.8" diff --git a/packages/server/scripts/build-standalone.mjs b/packages/server/scripts/build-standalone.mjs new file mode 100644 index 000000000..e0ba18bd4 --- /dev/null +++ b/packages/server/scripts/build-standalone.mjs @@ -0,0 +1,78 @@ +#!/usr/bin/env node +import fs from "fs" +import path from "path" +import { spawnSync } from "child_process" +import { fileURLToPath } from "url" + +const __filename = fileURLToPath(import.meta.url) +const __dirname = path.dirname(__filename) +const cliRoot = path.resolve(__dirname, "..") +const distDir = path.join(cliRoot, "dist") +const publicDir = path.join(cliRoot, "public") +const authPagesSourceDir = path.join(distDir, "server", "routes", "auth-pages") +const authPagesTargetDir = path.join(distDir, "auth-pages") +const explicitTarget = process.env.CODENOMAD_STANDALONE_TARGET?.trim() +const outputName = (explicitTarget?.includes("windows") || process.platform === "win32") ? "codenomad-server.exe" : "codenomad-server" +const outputPath = path.join(distDir, outputName) +const packageJsonPath = path.join(cliRoot, "package.json") + +function fail(message) { + console.error(`[build-standalone] ${message}`) + process.exit(1) +} + +function ensureArtifacts() { + const requiredPaths = [distDir, publicDir, authPagesSourceDir, packageJsonPath] + const missing = requiredPaths.filter((filePath) => !fs.existsSync(filePath)) + if (missing.length > 0) { + fail(`Missing required build artifacts: ${missing.join(", ")}. Run npm run build first.`) + } + + const bunResult = spawnSync("bun", ["-v"], { cwd: cliRoot, encoding: "utf-8" }) + if (bunResult.status !== 0) { + fail("Bun is required to build the standalone server executable.") + } +} + +function syncStandaloneAuthPages() { + fs.rmSync(authPagesTargetDir, { recursive: true, force: true }) + fs.mkdirSync(path.dirname(authPagesTargetDir), { recursive: true }) + fs.cpSync(authPagesSourceDir, authPagesTargetDir, { recursive: true }) +} + +function buildStandaloneExecutable() { + fs.rmSync(outputPath, { force: true }) + + const args = ["build", "--compile"] + if (explicitTarget) { + args.push(`--target=${explicitTarget}`) + } + args.push(path.join(cliRoot, "src", "index.ts"), "--outfile", outputPath) + + const result = spawnSync("bun", args, { + cwd: cliRoot, + stdio: "inherit", + }) + + if (result.status !== 0) { + if (result.error) { + throw result.error + } + throw new Error(`bun build --compile exited with code ${result.status ?? 1}`) + } +} + +function main() { + ensureArtifacts() + syncStandaloneAuthPages() + + buildStandaloneExecutable() + console.log(`[build-standalone] built ${outputPath}`) +} + +try { + main() +} catch (error) { + console.error("[build-standalone] failed:", error) + process.exit(1) +} diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index c021d220d..f8418c5c8 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -29,13 +29,14 @@ import { SideCarManager } from "./sidecars/manager" import { ClientConnectionManager } from "./clients/connection-manager" import { PluginChannelManager } from "./plugins/channel" import { VoiceModeManager } from "./plugins/voice-mode" +import { readServerPackageVersion, resolveServerPublicDir } from "./runtime-paths" const require = createRequire(import.meta.url) -const packageJson = require("../package.json") as { version: string } +const packageJson = { version: readServerPackageVersion(import.meta.url) } const __filename = fileURLToPath(import.meta.url) const __dirname = path.dirname(__filename) -const DEFAULT_UI_STATIC_DIR = path.resolve(__dirname, "../public") +const DEFAULT_UI_STATIC_DIR = resolveServerPublicDir(import.meta.url) interface CliOptions { host: string diff --git a/packages/server/src/opencode-config.ts b/packages/server/src/opencode-config.ts index f61cb9ca6..4495b6e9f 100644 --- a/packages/server/src/opencode-config.ts +++ b/packages/server/src/opencode-config.ts @@ -1,22 +1,11 @@ import { existsSync } from "fs" -import path from "path" -import { fileURLToPath } from "url" import { createLogger } from "./logger" +import { resolveOpencodeTemplateDir } from "./runtime-paths" const log = createLogger({ component: "opencode-config" }) -const __filename = fileURLToPath(import.meta.url) -const __dirname = path.dirname(__filename) -const devTemplateDir = path.resolve(__dirname, "../../opencode-config") -const resourcesPath = (process as NodeJS.Process & { resourcesPath?: string }).resourcesPath -const prodTemplateDirs = [ - resourcesPath ? path.resolve(resourcesPath, "opencode-config") : undefined, - path.resolve(__dirname, "opencode-config"), -].filter((dir): dir is string => Boolean(dir)) +const templateDir = resolveOpencodeTemplateDir(import.meta.url) -const isDevBuild = Boolean(process.env.CODENOMAD_DEV ?? process.env.CLI_UI_DEV_SERVER) || existsSync(devTemplateDir) -const templateDir = isDevBuild - ? devTemplateDir - : prodTemplateDirs.find((dir) => existsSync(dir)) ?? prodTemplateDirs[0] +const isDevBuild = Boolean(process.env.CODENOMAD_DEV ?? process.env.CLI_UI_DEV_SERVER) export function getOpencodeConfigDir(): string { if (!existsSync(templateDir)) { diff --git a/packages/server/src/runtime-paths.ts b/packages/server/src/runtime-paths.ts new file mode 100644 index 000000000..afae95d1d --- /dev/null +++ b/packages/server/src/runtime-paths.ts @@ -0,0 +1,79 @@ +import fs from "fs" +import path from "path" +import { fileURLToPath } from "url" + +function safeModuleDir(importMetaUrl: string): string | null { + try { + return path.dirname(fileURLToPath(importMetaUrl)) + } catch { + return null + } +} + +function firstExistingPath(candidates: Array, predicate: (value: string) => boolean): string | null { + for (const candidate of candidates) { + if (!candidate) continue + if (predicate(candidate)) { + return candidate + } + } + return null +} + +export function getPackagedDistDir(): string { + return path.dirname(process.execPath) +} + +export function resolveServerPackageRoot(importMetaUrl: string): string { + const moduleDir = safeModuleDir(importMetaUrl) + const configuredRoot = process.env.CODENOMAD_SERVER_ROOT?.trim() + const candidates = [ + configuredRoot ? path.resolve(configuredRoot) : null, + moduleDir ? path.resolve(moduleDir, "..") : null, + path.resolve(getPackagedDistDir(), ".."), + ] + + return ( + firstExistingPath(candidates, (value) => fs.existsSync(path.join(value, "package.json"))) ?? + candidates.find((value): value is string => Boolean(value)) ?? + process.cwd() + ) +} + +export function resolveServerPublicDir(importMetaUrl: string): string { + const moduleDir = safeModuleDir(importMetaUrl) + const candidates = [moduleDir ? path.resolve(moduleDir, "../public") : null, path.join(resolveServerPackageRoot(importMetaUrl), "public")] + + return firstExistingPath(candidates, (value) => fs.existsSync(value)) ?? candidates[candidates.length - 1]! +} + +export function resolveAuthTemplatePath(importMetaUrl: string, fileName: string): string { + const moduleDir = safeModuleDir(importMetaUrl) + const distDir = getPackagedDistDir() + const candidates = [ + moduleDir ? path.join(moduleDir, "auth-pages", fileName) : null, + path.join(distDir, "auth-pages", fileName), + path.join(distDir, "server", "routes", "auth-pages", fileName), + ] + + return firstExistingPath(candidates, (value) => fs.existsSync(value)) ?? candidates[0]! +} + +export function resolveOpencodeTemplateDir(importMetaUrl: string): string { + const moduleDir = safeModuleDir(importMetaUrl) + const resourcesPath = (process as NodeJS.Process & { resourcesPath?: string }).resourcesPath + const candidates = [ + moduleDir ? path.resolve(moduleDir, "../../opencode-config") : null, + resourcesPath ? path.resolve(resourcesPath, "opencode-config") : null, + moduleDir ? path.resolve(moduleDir, "opencode-config") : null, + path.join(getPackagedDistDir(), "opencode-config"), + ] + + return firstExistingPath(candidates, (value) => fs.existsSync(value)) ?? candidates[candidates.length - 1]! +} + +export function readServerPackageVersion(importMetaUrl: string): string { + const packageJsonPath = path.join(resolveServerPackageRoot(importMetaUrl), "package.json") + const parsed = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8")) as { version?: unknown } + return typeof parsed.version === "string" && parsed.version.trim().length > 0 ? parsed.version : "0.0.0" +} diff --git a/packages/server/src/server/http-server.ts b/packages/server/src/server/http-server.ts index cf7dae364..def237a18 100644 --- a/packages/server/src/server/http-server.ts +++ b/packages/server/src/server/http-server.ts @@ -5,6 +5,8 @@ import replyFrom from "@fastify/reply-from" import fs from "fs" import { connect as connectTcp, type Socket } from "net" import path from "path" +import { Readable } from "stream" +import { pipeline } from "stream/promises" import { connect as connectTls, type TLSSocket } from "tls" import { fetch } from "undici" import type { Logger } from "../logger" @@ -626,57 +628,57 @@ async function proxyWorkspaceRequest(args: { logger.trace({ workspaceId, targetUrl, body: request.body }, "Instance proxy payload") } - return reply.from(targetUrl, { - rewriteRequestHeaders: (_originalRequest, headers) => { - if (instanceAuthHeader) { - headers.authorization = instanceAuthHeader - } + const headers = buildWorkspaceInstanceProxyHeaders(request.headers, instanceAuthHeader, directory) - // OpenCode expects the *full* path; we send it via header to avoid query tampering. - const isNonASCII = /[^\x00-\x7F]/.test(directory) - const encodedDirectory = isNonASCII ? encodeURIComponent(directory) : directory + if (logger.isLevelEnabled("trace")) { + logger.trace( + { + workspaceId, + method: request.method, + targetUrl, + worktreeSlug, + directory, + contentType: request.headers["content-type"], + body: bodyToJson(request.body), + headers: redactProxyHeadersForLogs(headers), + }, + "Proxy -> OpenCode request", + ) + } - // Overwrite any client-provided value (case-insensitive headers are normalized by Node). - ;(headers as Record)["x-opencode-directory"] = encodedDirectory + const init: any = { + method: request.method, + headers, + redirect: "manual", + } - if (logger.isLevelEnabled("trace")) { - const outgoing: Record = {} - for (const [key, value] of Object.entries(headers as Record)) { - outgoing[key] = value - } + if (request.method !== "GET" && request.method !== "HEAD") { + const body = toProxyRequestBody(request.body) + if (body !== undefined) { + init.body = body + init.duplex = "half" + } + } - // Redact sensitive headers. - for (const key of Object.keys(outgoing)) { - const lower = key.toLowerCase() - if (lower === "authorization" || lower === "cookie" || lower === "set-cookie") { - outgoing[key] = "" - } - } + try { + const response = await fetch(targetUrl, init) + reply.code(response.status) + applyInstanceProxyResponseHeaders(reply, response) - logger.trace( - { - workspaceId, - method: request.method, - targetUrl, - worktreeSlug, - directory, - contentType: request.headers["content-type"], - body: bodyToJson(request.body), - headers: outgoing, - }, - "Proxy -> OpenCode request", - ) - } + if (!response.body || request.method === "HEAD") { + reply.send() + return + } - return headers - }, - onError: (proxyReply, { error }) => { - logger.error({ err: error, workspaceId, targetUrl }, "Failed to proxy workspace request") - if (!proxyReply.sent) { - proxyReply.code(502).send({ error: "Workspace instance proxy failed" }) - } - }, - }) + reply.hijack() + reply.raw.writeHead(reply.statusCode, toOutgoingHeaders(reply.getHeaders())) + await pipeline(Readable.fromWeb(response.body as any), reply.raw) + } catch (error) { + logger.error({ err: error, workspaceId, targetUrl }, "Failed to proxy workspace request") + if (!reply.sent) { + reply.code(502).send({ error: "Workspace instance proxy failed" }) + } + } } function extractOpencodeDirectoryOverride(pathSuffix: string | undefined): { @@ -873,6 +875,64 @@ function buildProxyHeaders(headers: FastifyRequest["headers"]): Record { + const next = buildProxyHeaders(headers) + if (instanceAuthHeader) { + next.authorization = instanceAuthHeader + } + + const isNonASCII = /[^\x00-\x7F]/.test(directory) + next["x-opencode-directory"] = isNonASCII ? encodeURIComponent(directory) : directory + return next +} + +function redactProxyHeadersForLogs(headers: Record): Record { + const outgoing = { ...headers } + for (const key of Object.keys(outgoing)) { + const lower = key.toLowerCase() + if (lower === "authorization" || lower === "cookie" || lower === "set-cookie") { + outgoing[key] = "" + } + } + return outgoing +} + +function applyInstanceProxyResponseHeaders(reply: FastifyReply, response: any) { + response.headers.forEach((value: string, key: string) => { + const lower = key.toLowerCase() + if (lower === "content-length" || lower === "content-encoding") { + return + } + + reply.header(key, value) + }) +} + +function toOutgoingHeaders(headers: ReturnType): Record { + const next: Record = {} + for (const [key, value] of Object.entries(headers)) { + if (value === undefined) { + continue + } + next[key] = Array.isArray(value) ? value.map(String) : String(value) + } + return next +} + async function proxySideCarRequest(args: { request: FastifyRequest reply: FastifyReply diff --git a/packages/server/src/server/routes/auth.ts b/packages/server/src/server/routes/auth.ts index 6bb7d3d35..0e9545891 100644 --- a/packages/server/src/server/routes/auth.ts +++ b/packages/server/src/server/routes/auth.ts @@ -3,6 +3,7 @@ import fs from "fs" import { z } from "zod" import type { AuthManager } from "../../auth/manager" import { isLoopbackAddress } from "../../auth/http-auth" +import { resolveAuthTemplatePath } from "../../runtime-paths" interface RouteDeps { authManager: AuthManager @@ -21,21 +22,21 @@ const PasswordSchema = z.object({ password: z.string().min(8), }) -const LOGIN_TEMPLATE_URL = new URL("./auth-pages/login.html", import.meta.url) -const TOKEN_TEMPLATE_URL = new URL("./auth-pages/token.html", import.meta.url) +const LOGIN_TEMPLATE_PATH = resolveAuthTemplatePath(import.meta.url, "login.html") +const TOKEN_TEMPLATE_PATH = resolveAuthTemplatePath(import.meta.url, "token.html") let cachedLoginTemplate: string | null = null let cachedTokenTemplate: string | null = null -function readTemplate(url: URL, cache: string | null): string { +function readTemplate(filePath: string, cache: string | null): string { if (cache) return cache - const content = fs.readFileSync(url, "utf-8") + const content = fs.readFileSync(filePath, "utf-8") return content } function getLoginHtml(defaultUsername: string): string { if (!cachedLoginTemplate) { - cachedLoginTemplate = readTemplate(LOGIN_TEMPLATE_URL, null) + cachedLoginTemplate = readTemplate(LOGIN_TEMPLATE_PATH, null) } const escapedUsername = escapeHtml(defaultUsername) @@ -44,7 +45,7 @@ function getLoginHtml(defaultUsername: string): string { function getTokenHtml(): string { if (!cachedTokenTemplate) { - cachedTokenTemplate = readTemplate(TOKEN_TEMPLATE_URL, null) + cachedTokenTemplate = readTemplate(TOKEN_TEMPLATE_PATH, null) } return cachedTokenTemplate diff --git a/packages/server/src/workspaces/manager.ts b/packages/server/src/workspaces/manager.ts index dc939758b..56ebef172 100644 --- a/packages/server/src/workspaces/manager.ts +++ b/packages/server/src/workspaces/manager.ts @@ -21,6 +21,70 @@ import { const STARTUP_STABILITY_DELAY_MS = 1500 +function defaultShellPath(): string { + const configured = process.env.SHELL?.trim() + if (configured) { + return configured + } + + return process.platform === "darwin" ? "/bin/zsh" : "/bin/bash" +} + +function shellEscape(input: string): string { + if (!input) return "''" + return `'${input.replace(/'/g, `'\\''`)}'` +} + +function wrapCommandForShell(command: string, shellPath: string): string { + const shellName = path.basename(shellPath).toLowerCase() + + if (shellName.includes("bash")) { + return `if [ -f ~/.bashrc ]; then source ~/.bashrc >/dev/null 2>&1; fi; ${command}` + } + + if (shellName.includes("zsh")) { + return `if [ -f ~/.zshrc ]; then source ~/.zshrc >/dev/null 2>&1; fi; ${command}` + } + + return command +} + +function buildShellArgs(shellPath: string, command: string): string[] { + const shellName = path.basename(shellPath).toLowerCase() + if (shellName.includes("zsh")) { + return ["-l", "-i", "-c", command] + } + return ["-l", "-c", command] +} + +function resolveBinaryPathFromUserShell(identifier: string): string | null { + if (process.platform === "win32") { + return null + } + + const shellPath = defaultShellPath() + const lookupCommand = wrapCommandForShell(`command -v ${shellEscape(identifier)}`, shellPath) + const result = spawnSync(shellPath, buildShellArgs(shellPath, lookupCommand), { + encoding: "utf8", + env: { + ...process.env, + npm_config_prefix: undefined, + NPM_CONFIG_PREFIX: undefined, + }, + }) + + if (result.status !== 0) { + return null + } + + const resolved = String(result.stdout ?? "") + .split(/\r?\n/) + .map((line) => line.trim()) + .find((line) => line.length > 0) + + return resolved ?? null +} + interface WorkspaceManagerOptions { rootDir: string settings: SettingsService @@ -266,6 +330,12 @@ export class WorkspaceManager { this.options.logger.warn({ identifier, err: error }, "Failed to resolve binary path from system PATH") } + const shellResolved = resolveBinaryPathFromUserShell(identifier) + if (shellResolved) { + this.options.logger.debug({ identifier, resolved: shellResolved }, "Resolved binary path from user shell") + return shellResolved + } + return identifier } diff --git a/packages/tauri-app/scripts/prebuild.js b/packages/tauri-app/scripts/prebuild.js index a234af46a..d348179ac 100644 --- a/packages/tauri-app/scripts/prebuild.js +++ b/packages/tauri-app/scripts/prebuild.js @@ -21,6 +21,7 @@ const serverDevInstallCommand = const uiDevInstallCommand = "npm install --workspace @codenomad/ui --include-workspace-root=false --install-strategy=nested --fund=false --audit=false" const serverPrepareUiCommand = "npm run prepare-ui --workspace @neuralnomads/codenomad" +const serverStandaloneBuildCommand = "npm run build:standalone --workspace @neuralnomads/codenomad" const envWithRootBin = { ...process.env, @@ -77,6 +78,15 @@ function ensureServerBuild() { } } +function ensureStandaloneServerBuild() { + console.log("[prebuild] building standalone server executable...") + execSync(serverStandaloneBuildCommand, { + cwd: workspaceRoot, + stdio: "inherit", + env: envWithRootBin, + }) +} + function ensureUiBuild() { const loadingHtml = path.join(uiDist, "loading.html") if (fs.existsSync(loadingHtml)) { @@ -178,6 +188,84 @@ function ensureRollupPlatformBinary() { }) } +function ensureEsbuildPlatformBinary() { + const platformKey = `${process.platform}-${process.arch}` + const platformPackages = { + "linux-x64": "@esbuild/linux-x64", + "linux-arm64": "@esbuild/linux-arm64", + "darwin-arm64": "@esbuild/darwin-arm64", + "darwin-x64": "@esbuild/darwin-x64", + "win32-arm64": "@esbuild/win32-arm64", + "win32-x64": "@esbuild/win32-x64", + } + + const pkgName = platformPackages[platformKey] + if (!pkgName) { + return + } + + const platformPackagePath = path.join(workspaceRoot, "node_modules", ...pkgName.split("/")) + if (fs.existsSync(platformPackagePath)) { + return + } + + let esbuildVersion = "" + try { + esbuildVersion = require(path.join(workspaceRoot, "node_modules", "esbuild", "package.json")).version + } catch { + try { + esbuildVersion = require(path.join(workspaceRoot, "node_modules", "vite", "node_modules", "esbuild", "package.json")).version + } catch { + // leave version empty; fallback install will use latest compatible + } + } + + const packageSpec = esbuildVersion ? `${pkgName}@${esbuildVersion}` : pkgName + + console.log("[prebuild] installing esbuild platform binary (optional dep workaround)...") + execSync(`npm install ${packageSpec} --no-save --ignore-scripts --fund=false --audit=false`, { + cwd: workspaceRoot, + stdio: "inherit", + }) +} + +function ensureTauriCliPlatformBinary() { + const platformKey = `${process.platform}-${process.arch}` + const platformPackages = { + "darwin-arm64": "@tauri-apps/cli-darwin-arm64", + "darwin-x64": "@tauri-apps/cli-darwin-x64", + "linux-arm64": "@tauri-apps/cli-linux-arm64-gnu", + "linux-x64": "@tauri-apps/cli-linux-x64-gnu", + "win32-arm64": "@tauri-apps/cli-win32-arm64-msvc", + "win32-x64": "@tauri-apps/cli-win32-x64-msvc", + } + + const pkgName = platformPackages[platformKey] + if (!pkgName) { + return + } + + const platformPackagePath = path.join(workspaceRoot, "node_modules", ...pkgName.split("/")) + if (fs.existsSync(platformPackagePath)) { + return + } + + let cliVersion = "" + try { + cliVersion = require(path.join(root, "node_modules", "@tauri-apps", "cli", "package.json")).version + } catch { + // leave version empty; fallback install will use latest compatible + } + + const packageSpec = cliVersion ? `${pkgName}@${cliVersion}` : pkgName + + console.log("[prebuild] installing tauri CLI platform binary (optional dep workaround)...") + execSync(`npm install ${packageSpec} --no-save --ignore-scripts --fund=false --audit=false`, { + cwd: workspaceRoot, + stdio: "inherit", + }) +} + function copyServerArtifacts() { fs.rmSync(serverDest, { recursive: true, force: true }) fs.mkdirSync(serverDest, { recursive: true }) @@ -256,13 +344,16 @@ function copyUiLoadingAssets() { ensureUiDevDependencies() await ensureMonacoAssets() ensureRollupPlatformBinary() + ensureEsbuildPlatformBinary() ensureServerDependencies() ensureServerBuild() + ensureStandaloneServerBuild() ensureUiBuild() syncServerUiBundle() copyServerArtifacts() stripNodeModuleBins() copyUiLoadingAssets() + ensureTauriCliPlatformBinary() })().catch((err) => { console.error("[prebuild] failed:", err) process.exit(1) diff --git a/packages/tauri-app/src-tauri/src/cli_manager.rs b/packages/tauri-app/src-tauri/src/cli_manager.rs index a0efd38a6..ea2e3360a 100644 --- a/packages/tauri-app/src-tauri/src/cli_manager.rs +++ b/packages/tauri-app/src-tauri/src/cli_manager.rs @@ -136,6 +136,10 @@ fn workspace_root() -> Option { }) } +fn launch_cwd() -> Option { + std::env::current_dir().ok() +} + const SESSION_COOKIE_NAME_PREFIX: &str = "codenomad_session"; const CLI_STOP_GRACE_SECS: u64 = 30; @@ -624,14 +628,17 @@ impl CliProcessManager { log_line("development mode: will prefer tsx + source if present"); } - let cwd = workspace_root(); + let cwd = launch_cwd(); if let Some(ref c) = cwd { log_line(&format!("using cwd={}", c.display())); } let use_user_shell = supports_user_shell(); - if !use_user_shell && which::which(&resolution.node_binary).is_err() { + if resolution.runner != Runner::Standalone + && !use_user_shell + && which::which(&resolution.node_binary).is_err() + { return Err(anyhow::anyhow!( "Node binary '{}' not found. CodeNomad desktop currently requires Node.js installed on the system, or set NODE_BINARY to a valid runtime path.", resolution.node_binary @@ -642,9 +649,17 @@ impl CliProcessManager { log_line("spawning via user shell"); ShellCommandType::UserShell(build_shell_command_string(&resolution, &args)?) } else { - log_line("spawning directly with node"); + log_line(if resolution.runner == Runner::Standalone { + "spawning directly with standalone executable" + } else { + "spawning directly with node" + }); ShellCommandType::Direct(DirectCommand { - program: resolution.node_binary.clone(), + program: if resolution.runner == Runner::Standalone { + resolution.entry.clone() + } else { + resolution.node_binary.clone() + }, args: resolution.runner_args(&args), }) }; @@ -654,11 +669,13 @@ impl CliProcessManager { log_line(&format!("spawn command: {} {:?}", cmd.shell, cmd.args)); let mut c = Command::new(&cmd.shell); c.args(&cmd.args) - .env("ELECTRON_RUN_AS_NODE", "1") .env_remove("npm_config_prefix") .env_remove("NPM_CONFIG_PREFIX") .stdout(Stdio::piped()) .stderr(Stdio::piped()); + if resolution.runner != Runner::Standalone { + c.env("ELECTRON_RUN_AS_NODE", "1"); + } configure_spawn(&mut c); if let Some(ref cwd) = cwd { c.current_dir(cwd); @@ -670,10 +687,10 @@ impl CliProcessManager { ShellCommandType::Direct(cmd) => { log_line(&format!("spawn command: {} {:?}", cmd.program, cmd.args)); let mut c = Command::new(&cmd.program); - c.args(&cmd.args) - .env("ELECTRON_RUN_AS_NODE", "1") - .stdout(Stdio::piped()) - .stderr(Stdio::piped()); + c.args(&cmd.args).stdout(Stdio::piped()).stderr(Stdio::piped()); + if resolution.runner != Runner::Standalone { + c.env("ELECTRON_RUN_AS_NODE", "1"); + } configure_spawn(&mut c); if let Some(ref cwd) = cwd { c.current_dir(cwd); @@ -1047,7 +1064,7 @@ struct CliEntry { #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum Runner { - Node, + Standalone, Tsx, } @@ -1068,17 +1085,17 @@ impl CliEntry { } } - if let Some(entry) = resolve_dist_entry(app) { + if let Some(entry) = resolve_standalone_entry(app) { return Ok(Self { entry, - runner: Runner::Node, + runner: Runner::Standalone, runner_path: None, - node_binary, + node_binary: String::new(), }); } Err(anyhow::anyhow!( - "Unable to locate CodeNomad CLI build (dist/bin.js). Please build @neuralnomads/codenomad." + "Unable to locate standalone CodeNomad server executable. Please run `npm run build:standalone --workspace @neuralnomads/codenomad`." )) } @@ -1132,6 +1149,10 @@ impl CliEntry { } fn runner_args(&self, cli_args: &[String]) -> Vec { + if self.runner == Runner::Standalone { + return cli_args.to_vec(); + } + let mut args = VecDeque::new(); if self.runner == Runner::Tsx { if let Some(path) = &self.runner_path { @@ -1204,45 +1225,29 @@ fn resolve_dev_entry(_app: &AppHandle) -> Option { first_existing(candidates) } -fn resolve_dist_entry(_app: &AppHandle) -> Option { +fn resolve_standalone_entry(_app: &AppHandle) -> Option { + let executable_name = if cfg!(windows) { + "codenomad-server.exe" + } else { + "codenomad-server" + }; let base = workspace_root(); - let mut candidates: Vec> = vec![ - base.as_ref().map(|p| p.join("packages/server/dist/bin.js")), - base.as_ref() - .map(|p| p.join("packages/server/dist/index.js")), - base.as_ref().map(|p| p.join("server/dist/bin.js")), - base.as_ref().map(|p| p.join("server/dist/index.js")), - ]; + let mut candidates = vec![base + .as_ref() + .map(|p| p.join("packages/server/dist").join(executable_name))]; if let Ok(exe) = std::env::current_exe() { if let Some(dir) = exe.parent() { - candidates.push(Some(dir.join("resources/server/dist/bin.js"))); - candidates.push(Some(dir.join("resources/server/dist/index.js"))); - candidates.push(Some(dir.join("resources/server/dist/server/bin.js"))); - candidates.push(Some(dir.join("resources/server/dist/server/index.js"))); + candidates.push(Some(dir.join("resources/server/dist").join(executable_name))); let resources = dir.join("../Resources"); - candidates.push(Some(resources.join("server/dist/bin.js"))); - candidates.push(Some(resources.join("server/dist/index.js"))); - candidates.push(Some(resources.join("server/dist/server/bin.js"))); - candidates.push(Some(resources.join("server/dist/server/index.js"))); - candidates.push(Some(resources.join("resources/server/dist/bin.js"))); - candidates.push(Some(resources.join("resources/server/dist/index.js"))); - candidates.push(Some(resources.join("resources/server/dist/server/bin.js"))); - candidates.push(Some( - resources.join("resources/server/dist/server/index.js"), - )); + candidates.push(Some(resources.join("server/dist").join(executable_name))); + candidates.push(Some(resources.join("resources/server/dist").join(executable_name))); let linux_resource_roots = [dir.join("../lib/CodeNomad"), dir.join("../lib/codenomad")]; for root in linux_resource_roots { - candidates.push(Some(root.join("server/dist/bin.js"))); - candidates.push(Some(root.join("server/dist/index.js"))); - candidates.push(Some(root.join("server/dist/server/bin.js"))); - candidates.push(Some(root.join("server/dist/server/index.js"))); - candidates.push(Some(root.join("resources/server/dist/bin.js"))); - candidates.push(Some(root.join("resources/server/dist/index.js"))); - candidates.push(Some(root.join("resources/server/dist/server/bin.js"))); - candidates.push(Some(root.join("resources/server/dist/server/index.js"))); + candidates.push(Some(root.join("server/dist").join(executable_name))); + candidates.push(Some(root.join("resources/server/dist").join(executable_name))); } } } @@ -1256,22 +1261,55 @@ fn build_shell_command_string( ) -> anyhow::Result { let shell = default_shell(); let mut quoted: Vec = Vec::new(); - quoted.push(shell_escape(&entry.node_binary)); - for arg in entry.runner_args(cli_args) { - quoted.push(shell_escape(&arg)); - } - let command = format!( - "if command -v {} >/dev/null 2>&1; then ELECTRON_RUN_AS_NODE=1 exec {}; else printf '%s%s\\n' '{}' {} >&2; exit 127; fi", - shell_escape(&entry.node_binary), - quoted.join(" "), - MISSING_NODE_PREFIX, - shell_escape(&entry.node_binary), - ); - let args = build_shell_args(&shell, &command); + let command = if entry.runner == Runner::Standalone { + quoted.push(shell_escape(&entry.entry)); + for arg in cli_args { + quoted.push(shell_escape(arg)); + } + format!("exec {}", quoted.join(" ")) + } else { + quoted.push(shell_escape(&entry.node_binary)); + for arg in entry.runner_args(cli_args) { + quoted.push(shell_escape(&arg)); + } + format!( + "if command -v {} >/dev/null 2>&1; then ELECTRON_RUN_AS_NODE=1 exec {}; else printf '%s%s\\n' '{}' {} >&2; exit 127; fi", + shell_escape(&entry.node_binary), + quoted.join(" "), + MISSING_NODE_PREFIX, + shell_escape(&entry.node_binary), + ) + }; + let wrapped_command = wrap_command_for_shell(&command, &shell); + let args = build_shell_args(&shell, &wrapped_command); log_line(&format!("user shell command: {} {:?}", shell, args)); Ok(ShellCommand { shell, args }) } +fn wrap_command_for_shell(command: &str, shell: &str) -> String { + let shell_name = std::path::Path::new(shell) + .file_name() + .and_then(OsStr::to_str) + .unwrap_or("") + .to_lowercase(); + + if shell_name.contains("bash") { + return format!( + "if [ -f ~/.bashrc ]; then source ~/.bashrc >/dev/null 2>&1; fi; {}", + command + ); + } + + if shell_name.contains("zsh") { + return format!( + "if [ -f ~/.zshrc ]; then source ~/.zshrc >/dev/null 2>&1; fi; {}", + command + ); + } + + command.to_string() +} + fn default_shell() -> String { if let Ok(shell) = std::env::var("SHELL") { if !shell.trim().is_empty() { @@ -1306,8 +1344,8 @@ fn build_shell_args(shell: &str, command: &str) -> Vec { .unwrap_or("") .to_lowercase(); - if shell_name.contains("zsh") || shell_name.contains("bash") { - vec!["-i".into(), "-l".into(), "-c".into(), command.into()] + if shell_name.contains("zsh") { + vec!["-l".into(), "-i".into(), "-c".into(), command.into()] } else { vec!["-l".into(), "-c".into(), command.into()] } From 1c2ec1558eaa5046803be2f753a9b6d540ec263b Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Mon, 20 Apr 2026 13:16:23 +0100 Subject: [PATCH 03/18] fix(build): use bundled Bun for standalone server builds --- package-lock.json | 204 +++++++++++++++++++ packages/server/package.json | 1 + packages/server/scripts/build-standalone.mjs | 27 ++- 3 files changed, 229 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4d525e3fb..558023e23 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2997,6 +2997,174 @@ "integrity": "sha512-dWMF8Aku4h7fh8sw5tQ2FtbqRLbIFT8FcsukpxTird49ax7oUXP+gzqxM/VdxHjfksQvzLBjLZyMdDStc5g7xA==", "license": "MIT" }, + "node_modules/@oven/bun-darwin-aarch64": { + "version": "1.3.13", + "resolved": "https://registry.npmjs.org/@oven/bun-darwin-aarch64/-/bun-darwin-aarch64-1.3.13.tgz", + "integrity": "sha512-qAS6Hg8Q14ckfBuqJ2Zh7gBQSVSUHeibSq4OFqBTv6DzyJuxYlr0sdYQzmYmnbPxbqobekqUDTa/4XEaqRi7vg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oven/bun-darwin-x64": { + "version": "1.3.13", + "resolved": "https://registry.npmjs.org/@oven/bun-darwin-x64/-/bun-darwin-x64-1.3.13.tgz", + "integrity": "sha512-kGePeDD4IN4imo+H4uLjQGZLmvyYQg+nKr2P0nt4ksXXrWA4HE+mb0/TUPHfRI127DocXQpew+fvrHuHR5mpJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oven/bun-darwin-x64-baseline": { + "version": "1.3.13", + "resolved": "https://registry.npmjs.org/@oven/bun-darwin-x64-baseline/-/bun-darwin-x64-baseline-1.3.13.tgz", + "integrity": "sha512-gMEQayUpmCPYaE9zkNBj9TiQqHupnhjOYcuSzxFjzIjHJBUO4VjNnrpbKVeXNs+rKHFothORDd2QKquu5paSPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oven/bun-linux-aarch64": { + "version": "1.3.13", + "resolved": "https://registry.npmjs.org/@oven/bun-linux-aarch64/-/bun-linux-aarch64-1.3.13.tgz", + "integrity": "sha512-NbLOJdr+RBFO1vFZ2YUFg4oVJ+2ua6zrwo4ZWRs0jKKcGJWtbY2wY5uz+i0PkwH6b9HYaYDgVTzE4ev06ncYZw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oven/bun-linux-aarch64-musl": { + "version": "1.3.13", + "resolved": "https://registry.npmjs.org/@oven/bun-linux-aarch64-musl/-/bun-linux-aarch64-musl-1.3.13.tgz", + "integrity": "sha512-UV9EE18VE5aRhWtV2L6MTAGGn3slhJJ2OW/m+FJM15maHm0qf1V7TaZY0FovxhdQRvnklSiQ7Ntv0H5TUX4w0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oven/bun-linux-x64": { + "version": "1.3.13", + "resolved": "https://registry.npmjs.org/@oven/bun-linux-x64/-/bun-linux-x64-1.3.13.tgz", + "integrity": "sha512-UwttIUXoe9fS+40OcjoaRHgZw+HCPFqBVWEXkXqAJ3W7wA0XPZrWsoMAD9sGh3TaLqrwdiMo5xPogwpXhOtVXA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oven/bun-linux-x64-baseline": { + "version": "1.3.13", + "resolved": "https://registry.npmjs.org/@oven/bun-linux-x64-baseline/-/bun-linux-x64-baseline-1.3.13.tgz", + "integrity": "sha512-fOi4ziKzgJG4UrrNd4AicBs6Fu9GY5xOqg+9tC76nuZNDAdSh6++kzab6TNi1Ck0Yzq6zIBIdGit6/0uSbBn8A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oven/bun-linux-x64-musl": { + "version": "1.3.13", + "resolved": "https://registry.npmjs.org/@oven/bun-linux-x64-musl/-/bun-linux-x64-musl-1.3.13.tgz", + "integrity": "sha512-+VHhE44kEjCXcTFHyc81zfTxL9+vzh9RqIh7gM1iWNhxpctD9kzntbUkP3UTFTwwNjoou1o8VRyxQafvc4OepA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oven/bun-linux-x64-musl-baseline": { + "version": "1.3.13", + "resolved": "https://registry.npmjs.org/@oven/bun-linux-x64-musl-baseline/-/bun-linux-x64-musl-baseline-1.3.13.tgz", + "integrity": "sha512-fqBKuiiWLEu2dVkowZaXgKS98xfrvBqivdoxRtRP3eINcpI1dcelGbsOz+Xphn7tbGAuBiE1/0AelvvvdqS9rg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oven/bun-windows-aarch64": { + "version": "1.3.13", + "resolved": "https://registry.npmjs.org/@oven/bun-windows-aarch64/-/bun-windows-aarch64-1.3.13.tgz", + "integrity": "sha512-+EvdRWRCRg95Xea4M2lqSJFTjzQBTJDQTMlbG8bmwFkVTN16MdmSH7xhfxVQWUOyZBLEpIwuNFIlBBxVCwSUyQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@oven/bun-windows-x64": { + "version": "1.3.13", + "resolved": "https://registry.npmjs.org/@oven/bun-windows-x64/-/bun-windows-x64-1.3.13.tgz", + "integrity": "sha512-vqDEFX63ZZQF3YstPSpPD+RxNm5AILPdUuuKpNwsj7ld4NjhdHUYkAmLXDtKNWt9JMRL10bop//W8faY/LV+RQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@oven/bun-windows-x64-baseline": { + "version": "1.3.13", + "resolved": "https://registry.npmjs.org/@oven/bun-windows-x64-baseline/-/bun-windows-x64-baseline-1.3.13.tgz", + "integrity": "sha512-6gy4hhQSjq/T/S9hC9m3NxY0RY+9Ww+XNlB+8koIMTsMSYEjk7Ho+hFHQz1Bn4W61Ub7Vykufg+jgDgPfa2GFA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@pinojs/redact": { "version": "0.4.0", "license": "MIT" @@ -4884,6 +5052,41 @@ "node": ">= 10.0.0" } }, + "node_modules/bun": { + "version": "1.3.13", + "resolved": "https://registry.npmjs.org/bun/-/bun-1.3.13.tgz", + "integrity": "sha512-b9T4xZ8KqCHs4+TkHJv540LG1B8OD7noKu0Qaizusx3jFtMDHY6osNqgbaOlwW2B8RB2AKzz+sjzlGKIGxIjZw==", + "cpu": [ + "arm64", + "x64" + ], + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "os": [ + "darwin", + "linux", + "win32" + ], + "bin": { + "bun": "bin/bun.exe", + "bunx": "bin/bunx.exe" + }, + "optionalDependencies": { + "@oven/bun-darwin-aarch64": "1.3.13", + "@oven/bun-darwin-x64": "1.3.13", + "@oven/bun-darwin-x64-baseline": "1.3.13", + "@oven/bun-linux-aarch64": "1.3.13", + "@oven/bun-linux-aarch64-musl": "1.3.13", + "@oven/bun-linux-x64": "1.3.13", + "@oven/bun-linux-x64-baseline": "1.3.13", + "@oven/bun-linux-x64-musl": "1.3.13", + "@oven/bun-linux-x64-musl-baseline": "1.3.13", + "@oven/bun-windows-aarch64": "1.3.13", + "@oven/bun-windows-x64": "1.3.13", + "@oven/bun-windows-x64-baseline": "1.3.13" + } + }, "node_modules/cac": { "version": "6.7.14", "dev": true, @@ -12683,6 +12886,7 @@ "devDependencies": { "@types/node-forge": "^1.3.14", "@types/yauzl": "^2.10.0", + "bun": "^1.3.13", "cross-env": "^7.0.3", "ts-node": "^10.9.2", "tsx": "^4.20.6", diff --git a/packages/server/package.json b/packages/server/package.json index 04141d1c0..ce667f0f6 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -43,6 +43,7 @@ "devDependencies": { "@types/node-forge": "^1.3.14", "@types/yauzl": "^2.10.0", + "bun": "^1.3.13", "cross-env": "^7.0.3", "ts-node": "^10.9.2", "tsx": "^4.20.6", diff --git a/packages/server/scripts/build-standalone.mjs b/packages/server/scripts/build-standalone.mjs index e0ba18bd4..3802636f8 100644 --- a/packages/server/scripts/build-standalone.mjs +++ b/packages/server/scripts/build-standalone.mjs @@ -16,6 +16,25 @@ const outputName = (explicitTarget?.includes("windows") || process.platform === const outputPath = path.join(distDir, outputName) const packageJsonPath = path.join(cliRoot, "package.json") +function resolveBunCommand() { + const executableName = process.platform === "win32" ? "bun.exe" : "bun" + const localBinName = process.platform === "win32" ? "bun.cmd" : "bun" + const candidates = [ + path.join(cliRoot, "node_modules", ".bin", localBinName), + path.join(cliRoot, "..", "..", "node_modules", ".bin", localBinName), + path.join(cliRoot, "node_modules", "bun", "bin", executableName), + path.join(cliRoot, "..", "..", "node_modules", "bun", "bin", executableName), + ] + + for (const candidate of candidates) { + if (fs.existsSync(candidate)) { + return candidate + } + } + + return "bun" +} + function fail(message) { console.error(`[build-standalone] ${message}`) process.exit(1) @@ -28,9 +47,9 @@ function ensureArtifacts() { fail(`Missing required build artifacts: ${missing.join(", ")}. Run npm run build first.`) } - const bunResult = spawnSync("bun", ["-v"], { cwd: cliRoot, encoding: "utf-8" }) + const bunResult = spawnSync(resolveBunCommand(), ["-v"], { cwd: cliRoot, encoding: "utf-8", shell: process.platform === "win32" }) if (bunResult.status !== 0) { - fail("Bun is required to build the standalone server executable.") + fail("Bun is required to build the standalone server executable. Install dependencies so the local Bun binary is available.") } } @@ -42,6 +61,7 @@ function syncStandaloneAuthPages() { function buildStandaloneExecutable() { fs.rmSync(outputPath, { force: true }) + const bunCommand = resolveBunCommand() const args = ["build", "--compile"] if (explicitTarget) { @@ -49,9 +69,10 @@ function buildStandaloneExecutable() { } args.push(path.join(cliRoot, "src", "index.ts"), "--outfile", outputPath) - const result = spawnSync("bun", args, { + const result = spawnSync(bunCommand, args, { cwd: cliRoot, stdio: "inherit", + shell: process.platform === "win32", }) if (result.status !== 0) { From 79dbbd4cb4854349fda605045f39f07b1cdc76b3 Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Mon, 20 Apr 2026 14:13:05 +0100 Subject: [PATCH 04/18] fix(server): preserve streamed proxy bodies and strip hop headers --- packages/server/src/server/http-server.ts | 24 +++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/packages/server/src/server/http-server.ts b/packages/server/src/server/http-server.ts index def237a18..f7ef029be 100644 --- a/packages/server/src/server/http-server.ts +++ b/packages/server/src/server/http-server.ts @@ -869,7 +869,8 @@ function isApiRequest(rawUrl: string | null | undefined) { function buildProxyHeaders(headers: FastifyRequest["headers"]): Record { const result: Record = {} for (const [key, value] of Object.entries(headers ?? {})) { - if (!value || key.toLowerCase() === "host") continue + const lower = key.toLowerCase() + if (!value || lower === "host" || isHopByHopHeader(lower)) continue result[key] = Array.isArray(value) ? value.join(",") : value } return result @@ -879,6 +880,12 @@ function toProxyRequestBody(body: unknown): any { if (body == null) { return undefined } + if (typeof (body as { pipe?: unknown }).pipe === "function") { + return body + } + if (typeof (body as { [Symbol.asyncIterator]?: unknown })[Symbol.asyncIterator] === "function") { + return body + } if (Buffer.isBuffer(body) || typeof body === "string" || body instanceof Uint8Array) { return body } @@ -914,7 +921,7 @@ function redactProxyHeadersForLogs(headers: Record): Record { const lower = key.toLowerCase() - if (lower === "content-length" || lower === "content-encoding") { + if (isHopByHopHeader(lower) || lower === "content-length" || lower === "content-encoding") { return } @@ -933,6 +940,19 @@ function toOutgoingHeaders(headers: ReturnType): Rec return next } +function isHopByHopHeader(name: string): boolean { + return new Set([ + "connection", + "keep-alive", + "proxy-authenticate", + "proxy-authorization", + "te", + "trailer", + "transfer-encoding", + "upgrade", + ]).has(name) +} + async function proxySideCarRequest(args: { request: FastifyRequest reply: FastifyReply From 166edd2e307a40de687dd6adb9e05e578209b443 Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Mon, 20 Apr 2026 14:30:29 +0100 Subject: [PATCH 05/18] fix(ci): align Node and Tauri versions for desktop builds --- .github/workflows/build-and-upload.yml | 2 +- .github/workflows/manual-npm-publish.yml | 2 +- .github/workflows/release-ui.yml | 2 +- .github/workflows/reusable-release.yml | 2 +- .../server/scripts/copy-opencode-config.mjs | 44 ++++++++++++++++++- packages/tauri-app/src-tauri/Cargo.toml | 4 +- 6 files changed, 49 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-and-upload.yml b/.github/workflows/build-and-upload.yml index 9121c5f7a..2ac04d324 100644 --- a/.github/workflows/build-and-upload.yml +++ b/.github/workflows/build-and-upload.yml @@ -53,7 +53,7 @@ on: # least-privilege (e.g. dev CI uses read-only; releases grant write). env: - NODE_VERSION: 20 + NODE_VERSION: 22 jobs: build-macos: diff --git a/.github/workflows/manual-npm-publish.yml b/.github/workflows/manual-npm-publish.yml index 51f5a3281..34a12246b 100644 --- a/.github/workflows/manual-npm-publish.yml +++ b/.github/workflows/manual-npm-publish.yml @@ -46,7 +46,7 @@ jobs: publish: runs-on: ubuntu-latest env: - NODE_VERSION: 20 + NODE_VERSION: 22 steps: - name: Checkout uses: actions/checkout@v4 diff --git a/.github/workflows/release-ui.yml b/.github/workflows/release-ui.yml index 2a021832e..abe659c5a 100644 --- a/.github/workflows/release-ui.yml +++ b/.github/workflows/release-ui.yml @@ -14,7 +14,7 @@ permissions: contents: read env: - NODE_VERSION: 20 + NODE_VERSION: 22 jobs: release-ui: diff --git a/.github/workflows/reusable-release.yml b/.github/workflows/reusable-release.yml index e839e76b4..f6e14ceab 100644 --- a/.github/workflows/reusable-release.yml +++ b/.github/workflows/reusable-release.yml @@ -39,7 +39,7 @@ permissions: contents: write env: - NODE_VERSION: 20 + NODE_VERSION: 22 jobs: prepare-release: diff --git a/packages/server/scripts/copy-opencode-config.mjs b/packages/server/scripts/copy-opencode-config.mjs index 5e7143956..6a9482395 100644 --- a/packages/server/scripts/copy-opencode-config.mjs +++ b/packages/server/scripts/copy-opencode-config.mjs @@ -1,6 +1,6 @@ #!/usr/bin/env node import { spawnSync } from "child_process" -import { cpSync, existsSync, mkdirSync, rmSync } from "fs" +import { cpSync, existsSync, mkdirSync, readdirSync, rmSync } from "fs" import path from "path" import { fileURLToPath } from "url" @@ -14,6 +14,43 @@ const selfLinkDir = path.resolve(nodeModulesDir, "@codenomad", "opencode-config" const npmExecPath = process.env.npm_execpath const npmNodeExecPath = process.env.npm_node_execpath +function stripNodeModuleBins(rootDir) { + const root = path.join(rootDir, "node_modules") + if (!existsSync(root)) { + return 0 + } + + const stack = [root] + let removed = 0 + + while (stack.length > 0) { + const current = stack.pop() + if (!current) break + + let entries + try { + entries = readdirSync(current, { withFileTypes: true }) + } catch { + continue + } + + for (const entry of entries) { + const full = path.join(current, entry.name) + if (entry.name === ".bin") { + rmSync(full, { recursive: true, force: true }) + removed += 1 + continue + } + + if (entry.isDirectory()) { + stack.push(full) + } + } + } + + return removed +} + if (!existsSync(sourceDir)) { console.error(`[copy-opencode-config] Missing source directory at ${sourceDir}`) process.exit(1) @@ -58,4 +95,9 @@ rmSync(targetDir, { recursive: true, force: true }) mkdirSync(path.dirname(targetDir), { recursive: true }) cpSync(sourceDir, targetDir, { recursive: true }) +const removedBins = stripNodeModuleBins(targetDir) +if (removedBins > 0) { + console.log(`[copy-opencode-config] Removed ${removedBins} node_modules/.bin directories`) +} + console.log(`[copy-opencode-config] Copied ${sourceDir} -> ${targetDir}`) diff --git a/packages/tauri-app/src-tauri/Cargo.toml b/packages/tauri-app/src-tauri/Cargo.toml index 940e9fbfd..43f3137e0 100644 --- a/packages/tauri-app/src-tauri/Cargo.toml +++ b/packages/tauri-app/src-tauri/Cargo.toml @@ -5,10 +5,10 @@ edition = "2021" license = "MIT" [build-dependencies] -tauri-build = { version = "2.5.2", features = [] } +tauri-build = { version = "2.5.6", features = [] } [dependencies] -tauri = { version = "2.5.2", features = [ "devtools"] } +tauri = { version = "2.10.3", features = [ "devtools"] } serde = { version = "1", features = ["derive"] } serde_json = "1" serde_yaml = "0.9" From 6c50564df6af32e4d1806f3ce87063f319b7ce7d Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Mon, 20 Apr 2026 14:45:32 +0100 Subject: [PATCH 06/18] fix(ci): align Tauri CLI with packaged desktop builds --- .github/workflows/build-and-upload.yml | 8 +- package-lock.json | 206 ++++++++++++++++++++++-- packages/tauri-app/package.json | 2 +- packages/tauri-app/src-tauri/Cargo.toml | 2 +- 4 files changed, 195 insertions(+), 23 deletions(-) diff --git a/.github/workflows/build-and-upload.yml b/.github/workflows/build-and-upload.yml index 2ac04d324..2cbe34628 100644 --- a/.github/workflows/build-and-upload.yml +++ b/.github/workflows/build-and-upload.yml @@ -372,7 +372,7 @@ jobs: if [ "$attempt" -gt 1 ]; then echo "Retrying Tauri CLI install (attempt $attempt)..." fi - npm install @tauri-apps/cli@2.9.4 @tauri-apps/cli-darwin-x64@2.9.4 --no-save --no-audit --no-fund --workspaces=false + npm install @tauri-apps/cli@2.10.1 @tauri-apps/cli-darwin-x64@2.10.1 --no-save --no-audit --no-fund --workspaces=false node -e "require('@tauri-apps/cli'); console.log('Tauri CLI loaded')" && exit 0 done echo "Tauri CLI failed to load after retries" >&2 @@ -456,7 +456,7 @@ jobs: if [ "$attempt" -gt 1 ]; then echo "Retrying Tauri CLI install (attempt $attempt)..." fi - npm install @tauri-apps/cli@2.9.4 @tauri-apps/cli-darwin-arm64@2.9.4 --no-save --no-audit --no-fund --workspaces=false + npm install @tauri-apps/cli@2.10.1 @tauri-apps/cli-darwin-arm64@2.10.1 --no-save --no-audit --no-fund --workspaces=false node -e "require('@tauri-apps/cli'); console.log('Tauri CLI loaded')" && exit 0 done echo "Tauri CLI failed to load after retries" >&2 @@ -542,7 +542,7 @@ jobs: if [ "$attempt" -gt 1 ]; then echo "Retrying Tauri CLI install (attempt $attempt)..." fi - npm install @tauri-apps/cli@2.9.4 @tauri-apps/cli-win32-x64-msvc@2.9.4 --no-save --no-audit --no-fund --workspaces=false + npm install @tauri-apps/cli@2.10.1 @tauri-apps/cli-win32-x64-msvc@2.10.1 --no-save --no-audit --no-fund --workspaces=false node -e "require('@tauri-apps/cli'); console.log('Tauri CLI loaded')" && exit 0 done echo "Tauri CLI failed to load after retries" >&2 @@ -642,7 +642,7 @@ jobs: if [ "$attempt" -gt 1 ]; then echo "Retrying Tauri CLI install (attempt $attempt)..." fi - npm install @tauri-apps/cli@2.9.4 @tauri-apps/cli-linux-x64-gnu@2.9.4 --no-save --no-audit --no-fund --workspaces=false + npm install @tauri-apps/cli@2.10.1 @tauri-apps/cli-linux-x64-gnu@2.10.1 --no-save --no-audit --no-fund --workspaces=false node -e "require('@tauri-apps/cli'); console.log('Tauri CLI loaded')" && exit 0 done echo "Tauri CLI failed to load after retries" >&2 diff --git a/package-lock.json b/package-lock.json index 558023e23..3e28d769e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3902,7 +3902,9 @@ } }, "node_modules/@tauri-apps/cli": { - "version": "2.9.4", + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.10.1.tgz", + "integrity": "sha512-jQNGF/5quwORdZSSLtTluyKQ+o6SMa/AUICfhf4egCGFdMHqWssApVgYSbg+jmrZoc8e1DscNvjTnXtlHLS11g==", "dev": true, "license": "Apache-2.0 OR MIT", "bin": { @@ -3916,21 +3918,40 @@ "url": "https://opencollective.com/tauri" }, "optionalDependencies": { - "@tauri-apps/cli-darwin-arm64": "2.9.4", - "@tauri-apps/cli-darwin-x64": "2.9.4", - "@tauri-apps/cli-linux-arm-gnueabihf": "2.9.4", - "@tauri-apps/cli-linux-arm64-gnu": "2.9.4", - "@tauri-apps/cli-linux-arm64-musl": "2.9.4", - "@tauri-apps/cli-linux-riscv64-gnu": "2.9.4", - "@tauri-apps/cli-linux-x64-gnu": "2.9.4", - "@tauri-apps/cli-linux-x64-musl": "2.9.4", - "@tauri-apps/cli-win32-arm64-msvc": "2.9.4", - "@tauri-apps/cli-win32-ia32-msvc": "2.9.4", - "@tauri-apps/cli-win32-x64-msvc": "2.9.4" + "@tauri-apps/cli-darwin-arm64": "2.10.1", + "@tauri-apps/cli-darwin-x64": "2.10.1", + "@tauri-apps/cli-linux-arm-gnueabihf": "2.10.1", + "@tauri-apps/cli-linux-arm64-gnu": "2.10.1", + "@tauri-apps/cli-linux-arm64-musl": "2.10.1", + "@tauri-apps/cli-linux-riscv64-gnu": "2.10.1", + "@tauri-apps/cli-linux-x64-gnu": "2.10.1", + "@tauri-apps/cli-linux-x64-musl": "2.10.1", + "@tauri-apps/cli-win32-arm64-msvc": "2.10.1", + "@tauri-apps/cli-win32-ia32-msvc": "2.10.1", + "@tauri-apps/cli-win32-x64-msvc": "2.10.1" + } + }, + "node_modules/@tauri-apps/cli-darwin-arm64": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.10.1.tgz", + "integrity": "sha512-Z2OjCXiZ+fbYZy7PmP3WRnOpM9+Fy+oonKDEmUE6MwN4IGaYqgceTjwHucc/kEEYZos5GICve35f7ZiizgqEnQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" } }, "node_modules/@tauri-apps/cli-darwin-x64": { - "version": "2.9.4", + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.10.1.tgz", + "integrity": "sha512-V/irQVvjPMGOTQqNj55PnQPVuH4VJP8vZCN7ajnj+ZS8Kom1tEM2hR3qbbIRoS3dBKs5mbG8yg1WC+97dq17Pw==", "cpu": [ "x64" ], @@ -3944,10 +3965,161 @@ "node": ">= 10" } }, + "node_modules/@tauri-apps/cli-linux-arm-gnueabihf": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.10.1.tgz", + "integrity": "sha512-Hyzwsb4VnCWKGfTw+wSt15Z2pLw2f0JdFBfq2vHBOBhvg7oi6uhKiF87hmbXOBXUZaGkyRDkCHsdzJcIfoJC2w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-arm64-gnu": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.10.1.tgz", + "integrity": "sha512-OyOYs2t5GkBIvyWjA1+h4CZxTcdz1OZPCWAPz5DYEfB0cnWHERTnQ/SLayQzncrT0kwRoSfSz9KxenkyJoTelA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-arm64-musl": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.10.1.tgz", + "integrity": "sha512-MIj78PDDGjkg3NqGptDOGgfXks7SYJwhiMh8SBoZS+vfdz7yP5jN18bNaLnDhsVIPARcAhE1TlsZe/8Yxo2zqg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-riscv64-gnu": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.10.1.tgz", + "integrity": "sha512-X0lvOVUg8PCVaoEtEAnpxmnkwlE1gcMDTqfhbefICKDnOTJ5Est3qL0SrWxizDackIOKBcvtpejrSiVpuJI1kw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-x64-gnu": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.10.1.tgz", + "integrity": "sha512-2/12bEzsJS9fAKybxgicCDFxYD1WEI9kO+tlDwX5znWG2GwMBaiWcmhGlZ8fi+DMe9CXlcVarMTYc0L3REIRxw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-x64-musl": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.10.1.tgz", + "integrity": "sha512-Y8J0ZzswPz50UcGOFuXGEMrxbjwKSPgXftx5qnkuMs2rmwQB5ssvLb6tn54wDSYxe7S6vlLob9vt0VKuNOaCIQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-win32-arm64-msvc": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.10.1.tgz", + "integrity": "sha512-iSt5B86jHYAPJa/IlYw++SXtFPGnWtFJriHn7X0NFBVunF6zu9+/zOn8OgqIWSl8RgzhLGXQEEtGBdR4wzpVgg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-win32-ia32-msvc": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.10.1.tgz", + "integrity": "sha512-gXyxgEzsFegmnWywYU5pEBURkcFN/Oo45EAwvZrHMh+zUSEAvO5E8TXsgPADYm31d1u7OQU3O3HsYfVBf2moHw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@tauri-apps/cli-win32-x64-msvc": { - "version": "2.9.4", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.9.4.tgz", - "integrity": "sha512-EdYd4c9wGvtPB95kqtEyY+bUR+k4kRw3IA30mAQ1jPH6z57AftT8q84qwv0RDp6kkEqOBKxeInKfqi4BESYuqg==", + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.10.1.tgz", + "integrity": "sha512-6Cn7YpPFwzChy0ERz6djKEmUehWrYlM+xTaNzGPgZocw3BD7OfwfWHKVWxXzdjEW2KfKkHddfdxK1XXTYqBRLg==", "cpu": [ "x64" ], @@ -12909,7 +13081,7 @@ "version": "0.14.0", "license": "MIT", "devDependencies": { - "@tauri-apps/cli": "^2.9.4" + "@tauri-apps/cli": "^2.10.1" } }, "packages/ui": { diff --git a/packages/tauri-app/package.json b/packages/tauri-app/package.json index c09a70ac8..6f627aefb 100644 --- a/packages/tauri-app/package.json +++ b/packages/tauri-app/package.json @@ -14,6 +14,6 @@ "build": "tauri build" }, "devDependencies": { - "@tauri-apps/cli": "^2.9.4" + "@tauri-apps/cli": "^2.10.1" } } diff --git a/packages/tauri-app/src-tauri/Cargo.toml b/packages/tauri-app/src-tauri/Cargo.toml index 43f3137e0..a17d771bb 100644 --- a/packages/tauri-app/src-tauri/Cargo.toml +++ b/packages/tauri-app/src-tauri/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT" tauri-build = { version = "2.5.6", features = [] } [dependencies] -tauri = { version = "2.10.3", features = [ "devtools"] } +tauri = { version = "2.10.1", features = [ "devtools"] } serde = { version = "1", features = ["derive"] } serde_json = "1" serde_yaml = "0.9" From 95f47ebbe412f30dc479799f84b5646a38b2600b Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Mon, 20 Apr 2026 20:35:26 +0100 Subject: [PATCH 07/18] fix(tauri): avoid AppImage linuxdeploy desktop alias conflict --- packages/tauri-app/src-tauri/tauri.conf.json | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/tauri-app/src-tauri/tauri.conf.json b/packages/tauri-app/src-tauri/tauri.conf.json index 86cd49133..188f21787 100644 --- a/packages/tauri-app/src-tauri/tauri.conf.json +++ b/packages/tauri-app/src-tauri/tauri.conf.json @@ -43,11 +43,6 @@ "bundle": { "active": true, "linux": { - "appimage": { - "files": { - "/usr/share/applications/ai.neuralnomads.codenomad.client.desktop": "icons/linux/ai.neuralnomads.codenomad.client.desktop" - } - }, "deb": { "files": { "/usr/share/applications/ai.neuralnomads.codenomad.client.desktop": "icons/linux/ai.neuralnomads.codenomad.client.desktop", From 9ecd5131a678c3b6266a16de58ca884267ef8bf7 Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Mon, 20 Apr 2026 21:00:19 +0100 Subject: [PATCH 08/18] fix(ci): stabilize Linux Tauri AppImage bundling --- .github/workflows/build-and-upload.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/build-and-upload.yml b/.github/workflows/build-and-upload.yml index 2cbe34628..781d9d2c6 100644 --- a/.github/workflows/build-and-upload.yml +++ b/.github/workflows/build-and-upload.yml @@ -614,6 +614,7 @@ jobs: sudo apt-get install -y \ build-essential \ pkg-config \ + xdg-utils \ libgtk-3-dev \ libglib2.0-dev \ libwebkit2gtk-4.1-dev \ @@ -650,6 +651,8 @@ jobs: - name: Build Linux bundle (Tauri) working-directory: packages/tauri-app + env: + APPIMAGE_EXTRACT_AND_RUN: "1" run: npm exec -- tauri build - name: Package Tauri artifacts (Linux) @@ -741,6 +744,7 @@ jobs: sudo apt-get install -y \ build-essential \ pkg-config \ + xdg-utils \ gcc-aarch64-linux-gnu \ g++-aarch64-linux-gnu \ libgtk-3-dev:arm64 \ From 76f14e21895260557766e2cfaf656671be246b28 Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Mon, 20 Apr 2026 21:12:56 +0100 Subject: [PATCH 09/18] fix(ci): pin Linux Tauri CLI to known-good version --- .github/workflows/build-and-upload.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-and-upload.yml b/.github/workflows/build-and-upload.yml index 781d9d2c6..e229db1ff 100644 --- a/.github/workflows/build-and-upload.yml +++ b/.github/workflows/build-and-upload.yml @@ -643,7 +643,8 @@ jobs: if [ "$attempt" -gt 1 ]; then echo "Retrying Tauri CLI install (attempt $attempt)..." fi - npm install @tauri-apps/cli@2.10.1 @tauri-apps/cli-linux-x64-gnu@2.10.1 --no-save --no-audit --no-fund --workspaces=false + # Tauri CLI 2.10.1 regresses Linux AppImage bundling in CI; keep Linux on the last known-good CLI. + npm install @tauri-apps/cli@2.9.4 @tauri-apps/cli-linux-x64-gnu@2.9.4 --no-save --no-audit --no-fund --workspaces=false node -e "require('@tauri-apps/cli'); console.log('Tauri CLI loaded')" && exit 0 done echo "Tauri CLI failed to load after retries" >&2 From b60d86116a960f82967d76535536467ee206e668 Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Mon, 20 Apr 2026 22:54:21 +0100 Subject: [PATCH 10/18] fix(tauri): fall back to Node server on Linux --- packages/tauri-app/scripts/prebuild.js | 10 ++- .../tauri-app/src-tauri/src/cli_manager.rs | 76 +++++++++++++++++-- 2 files changed, 80 insertions(+), 6 deletions(-) diff --git a/packages/tauri-app/scripts/prebuild.js b/packages/tauri-app/scripts/prebuild.js index d348179ac..72159e736 100644 --- a/packages/tauri-app/scripts/prebuild.js +++ b/packages/tauri-app/scripts/prebuild.js @@ -87,6 +87,10 @@ function ensureStandaloneServerBuild() { }) } +function shouldBuildStandaloneServer() { + return process.platform !== "linux" +} + function ensureUiBuild() { const loadingHtml = path.join(uiDist, "loading.html") if (fs.existsSync(loadingHtml)) { @@ -347,7 +351,11 @@ function copyUiLoadingAssets() { ensureEsbuildPlatformBinary() ensureServerDependencies() ensureServerBuild() - ensureStandaloneServerBuild() + if (shouldBuildStandaloneServer()) { + ensureStandaloneServerBuild() + } else { + console.log("[prebuild] skipping standalone server executable for Linux packaging; linuxdeploy fails on the bundled ELF") + } ensureUiBuild() syncServerUiBundle() copyServerArtifacts() diff --git a/packages/tauri-app/src-tauri/src/cli_manager.rs b/packages/tauri-app/src-tauri/src/cli_manager.rs index ea2e3360a..780e6bac4 100644 --- a/packages/tauri-app/src-tauri/src/cli_manager.rs +++ b/packages/tauri-app/src-tauri/src/cli_manager.rs @@ -687,7 +687,9 @@ impl CliProcessManager { ShellCommandType::Direct(cmd) => { log_line(&format!("spawn command: {} {:?}", cmd.program, cmd.args)); let mut c = Command::new(&cmd.program); - c.args(&cmd.args).stdout(Stdio::piped()).stderr(Stdio::piped()); + c.args(&cmd.args) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); if resolution.runner != Runner::Standalone { c.env("ELECTRON_RUN_AS_NODE", "1"); } @@ -1064,6 +1066,7 @@ struct CliEntry { #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum Runner { + Node, Standalone, Tsx, } @@ -1094,8 +1097,17 @@ impl CliEntry { }); } + if let Some(entry) = resolve_dist_entry(app) { + return Ok(Self { + entry, + runner: Runner::Node, + runner_path: None, + node_binary, + }); + } + Err(anyhow::anyhow!( - "Unable to locate standalone CodeNomad server executable. Please run `npm run build:standalone --workspace @neuralnomads/codenomad`." + "Unable to locate CodeNomad CLI build. Please run `npm run build --workspace @neuralnomads/codenomad`." )) } @@ -1238,16 +1250,70 @@ fn resolve_standalone_entry(_app: &AppHandle) -> Option { if let Ok(exe) = std::env::current_exe() { if let Some(dir) = exe.parent() { - candidates.push(Some(dir.join("resources/server/dist").join(executable_name))); + candidates.push(Some( + dir.join("resources/server/dist").join(executable_name), + )); let resources = dir.join("../Resources"); candidates.push(Some(resources.join("server/dist").join(executable_name))); - candidates.push(Some(resources.join("resources/server/dist").join(executable_name))); + candidates.push(Some( + resources + .join("resources/server/dist") + .join(executable_name), + )); let linux_resource_roots = [dir.join("../lib/CodeNomad"), dir.join("../lib/codenomad")]; for root in linux_resource_roots { candidates.push(Some(root.join("server/dist").join(executable_name))); - candidates.push(Some(root.join("resources/server/dist").join(executable_name))); + candidates.push(Some( + root.join("resources/server/dist").join(executable_name), + )); + } + } + } + + first_existing(candidates) +} + +fn resolve_dist_entry(_app: &AppHandle) -> Option { + let base = workspace_root(); + let mut candidates: Vec> = vec![ + base.as_ref().map(|p| p.join("packages/server/dist/bin.js")), + base.as_ref() + .map(|p| p.join("packages/server/dist/index.js")), + base.as_ref().map(|p| p.join("server/dist/bin.js")), + base.as_ref().map(|p| p.join("server/dist/index.js")), + ]; + + if let Ok(exe) = std::env::current_exe() { + if let Some(dir) = exe.parent() { + candidates.push(Some(dir.join("resources/server/dist/bin.js"))); + candidates.push(Some(dir.join("resources/server/dist/index.js"))); + candidates.push(Some(dir.join("resources/server/dist/server/bin.js"))); + candidates.push(Some(dir.join("resources/server/dist/server/index.js"))); + + let resources = dir.join("../Resources"); + candidates.push(Some(resources.join("server/dist/bin.js"))); + candidates.push(Some(resources.join("server/dist/index.js"))); + candidates.push(Some(resources.join("server/dist/server/bin.js"))); + candidates.push(Some(resources.join("server/dist/server/index.js"))); + candidates.push(Some(resources.join("resources/server/dist/bin.js"))); + candidates.push(Some(resources.join("resources/server/dist/index.js"))); + candidates.push(Some(resources.join("resources/server/dist/server/bin.js"))); + candidates.push(Some( + resources.join("resources/server/dist/server/index.js"), + )); + + let linux_resource_roots = [dir.join("../lib/CodeNomad"), dir.join("../lib/codenomad")]; + for root in linux_resource_roots { + candidates.push(Some(root.join("server/dist/bin.js"))); + candidates.push(Some(root.join("server/dist/index.js"))); + candidates.push(Some(root.join("server/dist/server/bin.js"))); + candidates.push(Some(root.join("server/dist/server/index.js"))); + candidates.push(Some(root.join("resources/server/dist/bin.js"))); + candidates.push(Some(root.join("resources/server/dist/index.js"))); + candidates.push(Some(root.join("resources/server/dist/server/bin.js"))); + candidates.push(Some(root.join("resources/server/dist/server/index.js"))); } } } From 4af8cc08b91c420c90a7ac5883c5b0db4745c7ee Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Mon, 20 Apr 2026 23:04:32 +0100 Subject: [PATCH 11/18] fix(ci): restore dev Linux Tauri bundling env --- .github/workflows/build-and-upload.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/build-and-upload.yml b/.github/workflows/build-and-upload.yml index e229db1ff..3f8aef8da 100644 --- a/.github/workflows/build-and-upload.yml +++ b/.github/workflows/build-and-upload.yml @@ -614,7 +614,6 @@ jobs: sudo apt-get install -y \ build-essential \ pkg-config \ - xdg-utils \ libgtk-3-dev \ libglib2.0-dev \ libwebkit2gtk-4.1-dev \ @@ -652,8 +651,6 @@ jobs: - name: Build Linux bundle (Tauri) working-directory: packages/tauri-app - env: - APPIMAGE_EXTRACT_AND_RUN: "1" run: npm exec -- tauri build - name: Package Tauri artifacts (Linux) From ca880451e71f23ff2b6482bd17eba2f225cc1a27 Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Mon, 20 Apr 2026 23:14:16 +0100 Subject: [PATCH 12/18] fix(tauri): prune Bun from Linux app bundle --- packages/tauri-app/scripts/prebuild.js | 30 ++++++++++++++++++++------ 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/packages/tauri-app/scripts/prebuild.js b/packages/tauri-app/scripts/prebuild.js index 72159e736..a22409fba 100644 --- a/packages/tauri-app/scripts/prebuild.js +++ b/packages/tauri-app/scripts/prebuild.js @@ -131,15 +131,30 @@ function ensureServerDevDependencies() { } function ensureServerDependencies() { - if (fs.existsSync(braceExpansionPath)) { - return - } - - console.log("[prebuild] ensuring server production dependencies...") - execSync(serverInstallCommand, { + console.log("[prebuild] pruning server to production dependencies...") + execSync("npm prune --omit=dev --ignore-scripts --workspaces=false --fund=false --audit=false", { cwd: serverRoot, stdio: "inherit", }) + + if (!fs.existsSync(braceExpansionPath)) { + console.log("[prebuild] restoring missing server production dependencies...") + execSync(serverInstallCommand, { + cwd: serverRoot, + stdio: "inherit", + }) + } +} + +function removeStaleStandaloneServerBuild() { + const staleNames = ["codenomad-server", "codenomad-server.exe"] + for (const name of staleNames) { + const stalePath = path.join(serverRoot, "dist", name) + if (fs.existsSync(stalePath)) { + fs.rmSync(stalePath, { force: true }) + console.log(`[prebuild] removed stale standalone server artifact ${stalePath}`) + } + } } function ensureUiDevDependencies() { @@ -349,13 +364,14 @@ function copyUiLoadingAssets() { await ensureMonacoAssets() ensureRollupPlatformBinary() ensureEsbuildPlatformBinary() - ensureServerDependencies() ensureServerBuild() if (shouldBuildStandaloneServer()) { ensureStandaloneServerBuild() } else { + removeStaleStandaloneServerBuild() console.log("[prebuild] skipping standalone server executable for Linux packaging; linuxdeploy fails on the bundled ELF") } + ensureServerDependencies() ensureUiBuild() syncServerUiBundle() copyServerArtifacts() From a5f38ee625b8e6f4ea0de593642b94a5976ac537 Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Mon, 20 Apr 2026 23:25:39 +0100 Subject: [PATCH 13/18] fix(tauri): align packaged CLI with Linux bundler --- package-lock.json | 466 ++++++++++++++++---------------- packages/tauri-app/package.json | 2 +- 2 files changed, 234 insertions(+), 234 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3e28d769e..0821a9281 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3901,238 +3901,6 @@ "url": "https://opencollective.com/tauri" } }, - "node_modules/@tauri-apps/cli": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.10.1.tgz", - "integrity": "sha512-jQNGF/5quwORdZSSLtTluyKQ+o6SMa/AUICfhf4egCGFdMHqWssApVgYSbg+jmrZoc8e1DscNvjTnXtlHLS11g==", - "dev": true, - "license": "Apache-2.0 OR MIT", - "bin": { - "tauri": "tauri.js" - }, - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/tauri" - }, - "optionalDependencies": { - "@tauri-apps/cli-darwin-arm64": "2.10.1", - "@tauri-apps/cli-darwin-x64": "2.10.1", - "@tauri-apps/cli-linux-arm-gnueabihf": "2.10.1", - "@tauri-apps/cli-linux-arm64-gnu": "2.10.1", - "@tauri-apps/cli-linux-arm64-musl": "2.10.1", - "@tauri-apps/cli-linux-riscv64-gnu": "2.10.1", - "@tauri-apps/cli-linux-x64-gnu": "2.10.1", - "@tauri-apps/cli-linux-x64-musl": "2.10.1", - "@tauri-apps/cli-win32-arm64-msvc": "2.10.1", - "@tauri-apps/cli-win32-ia32-msvc": "2.10.1", - "@tauri-apps/cli-win32-x64-msvc": "2.10.1" - } - }, - "node_modules/@tauri-apps/cli-darwin-arm64": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.10.1.tgz", - "integrity": "sha512-Z2OjCXiZ+fbYZy7PmP3WRnOpM9+Fy+oonKDEmUE6MwN4IGaYqgceTjwHucc/kEEYZos5GICve35f7ZiizgqEnQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0 OR MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tauri-apps/cli-darwin-x64": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.10.1.tgz", - "integrity": "sha512-V/irQVvjPMGOTQqNj55PnQPVuH4VJP8vZCN7ajnj+ZS8Kom1tEM2hR3qbbIRoS3dBKs5mbG8yg1WC+97dq17Pw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0 OR MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tauri-apps/cli-linux-arm-gnueabihf": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.10.1.tgz", - "integrity": "sha512-Hyzwsb4VnCWKGfTw+wSt15Z2pLw2f0JdFBfq2vHBOBhvg7oi6uhKiF87hmbXOBXUZaGkyRDkCHsdzJcIfoJC2w==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "Apache-2.0 OR MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tauri-apps/cli-linux-arm64-gnu": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.10.1.tgz", - "integrity": "sha512-OyOYs2t5GkBIvyWjA1+h4CZxTcdz1OZPCWAPz5DYEfB0cnWHERTnQ/SLayQzncrT0kwRoSfSz9KxenkyJoTelA==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "Apache-2.0 OR MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tauri-apps/cli-linux-arm64-musl": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.10.1.tgz", - "integrity": "sha512-MIj78PDDGjkg3NqGptDOGgfXks7SYJwhiMh8SBoZS+vfdz7yP5jN18bNaLnDhsVIPARcAhE1TlsZe/8Yxo2zqg==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "Apache-2.0 OR MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tauri-apps/cli-linux-riscv64-gnu": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.10.1.tgz", - "integrity": "sha512-X0lvOVUg8PCVaoEtEAnpxmnkwlE1gcMDTqfhbefICKDnOTJ5Est3qL0SrWxizDackIOKBcvtpejrSiVpuJI1kw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "Apache-2.0 OR MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tauri-apps/cli-linux-x64-gnu": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.10.1.tgz", - "integrity": "sha512-2/12bEzsJS9fAKybxgicCDFxYD1WEI9kO+tlDwX5znWG2GwMBaiWcmhGlZ8fi+DMe9CXlcVarMTYc0L3REIRxw==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "Apache-2.0 OR MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tauri-apps/cli-linux-x64-musl": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.10.1.tgz", - "integrity": "sha512-Y8J0ZzswPz50UcGOFuXGEMrxbjwKSPgXftx5qnkuMs2rmwQB5ssvLb6tn54wDSYxe7S6vlLob9vt0VKuNOaCIQ==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "Apache-2.0 OR MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tauri-apps/cli-win32-arm64-msvc": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.10.1.tgz", - "integrity": "sha512-iSt5B86jHYAPJa/IlYw++SXtFPGnWtFJriHn7X0NFBVunF6zu9+/zOn8OgqIWSl8RgzhLGXQEEtGBdR4wzpVgg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0 OR MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tauri-apps/cli-win32-ia32-msvc": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.10.1.tgz", - "integrity": "sha512-gXyxgEzsFegmnWywYU5pEBURkcFN/Oo45EAwvZrHMh+zUSEAvO5E8TXsgPADYm31d1u7OQU3O3HsYfVBf2moHw==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "Apache-2.0 OR MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tauri-apps/cli-win32-x64-msvc": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.10.1.tgz", - "integrity": "sha512-6Cn7YpPFwzChy0ERz6djKEmUehWrYlM+xTaNzGPgZocw3BD7OfwfWHKVWxXzdjEW2KfKkHddfdxK1XXTYqBRLg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0 OR MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@tauri-apps/plugin-dialog": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-dialog/-/plugin-dialog-2.6.0.tgz", @@ -13081,7 +12849,239 @@ "version": "0.14.0", "license": "MIT", "devDependencies": { - "@tauri-apps/cli": "^2.10.1" + "@tauri-apps/cli": "^2.9.4" + } + }, + "packages/tauri-app/node_modules/@tauri-apps/cli": { + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.9.4.tgz", + "integrity": "sha512-pvylWC9QckrOS9ATWXIXcgu7g2hKK5xTL5ZQyZU/U0n9l88SEFGcWgLQNa8WZmd+wWIOWhkxOFcOl3i6ubDNNw==", + "dev": true, + "license": "Apache-2.0 OR MIT", + "bin": { + "tauri": "tauri.js" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/tauri" + }, + "optionalDependencies": { + "@tauri-apps/cli-darwin-arm64": "2.9.4", + "@tauri-apps/cli-darwin-x64": "2.9.4", + "@tauri-apps/cli-linux-arm-gnueabihf": "2.9.4", + "@tauri-apps/cli-linux-arm64-gnu": "2.9.4", + "@tauri-apps/cli-linux-arm64-musl": "2.9.4", + "@tauri-apps/cli-linux-riscv64-gnu": "2.9.4", + "@tauri-apps/cli-linux-x64-gnu": "2.9.4", + "@tauri-apps/cli-linux-x64-musl": "2.9.4", + "@tauri-apps/cli-win32-arm64-msvc": "2.9.4", + "@tauri-apps/cli-win32-ia32-msvc": "2.9.4", + "@tauri-apps/cli-win32-x64-msvc": "2.9.4" + } + }, + "packages/tauri-app/node_modules/@tauri-apps/cli-darwin-arm64": { + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.9.4.tgz", + "integrity": "sha512-9rHkMVtbMhe0AliVbrGpzMahOBg3rwV46JYRELxR9SN6iu1dvPOaMaiC4cP6M/aD1424ziXnnMdYU06RAH8oIw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "packages/tauri-app/node_modules/@tauri-apps/cli-darwin-x64": { + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.9.4.tgz", + "integrity": "sha512-VT9ymNuT06f5TLjCZW2hfSxbVtZDhORk7CDUDYiq5TiSYQdxkl8MVBy0CCFFcOk4QAkUmqmVUA9r3YZ/N/vPRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "packages/tauri-app/node_modules/@tauri-apps/cli-linux-arm-gnueabihf": { + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.9.4.tgz", + "integrity": "sha512-tTWkEPig+2z3Rk0zqZYfjUYcgD+aSm72wdrIhdYobxbQZOBw0zfn50YtWv+av7bm0SHvv75f0l7JuwgZM1HFow==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "packages/tauri-app/node_modules/@tauri-apps/cli-linux-arm64-gnu": { + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.9.4.tgz", + "integrity": "sha512-ql6vJ611qoqRYHxkKPnb2vHa27U+YRKRmIpLMMBeZnfFtZ938eao7402AQCH1mO2+/8ioUhbpy9R/ZcLTXVmkg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "packages/tauri-app/node_modules/@tauri-apps/cli-linux-arm64-musl": { + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.9.4.tgz", + "integrity": "sha512-vg7yNn7ICTi6hRrcA/6ff2UpZQP7un3xe3SEld5QM0prgridbKAiXGaCKr3BnUBx/rGXegQlD/wiLcWdiiraSw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "packages/tauri-app/node_modules/@tauri-apps/cli-linux-riscv64-gnu": { + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.9.4.tgz", + "integrity": "sha512-l8L+3VxNk6yv5T/Z/gv5ysngmIpsai40B9p6NQQyqYqxImqYX37pqREoEBl1YwG7szGnDibpWhidPrWKR59OJA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "packages/tauri-app/node_modules/@tauri-apps/cli-linux-x64-gnu": { + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.9.4.tgz", + "integrity": "sha512-PepPhCXc/xVvE3foykNho46OmCyx47E/aG676vKTVp+mqin5d+IBqDL6wDKiGNT5OTTxKEyNlCQ81Xs2BQhhqA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "packages/tauri-app/node_modules/@tauri-apps/cli-linux-x64-musl": { + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.9.4.tgz", + "integrity": "sha512-zcd1QVffh5tZs1u1SCKUV/V7RRynebgYUNWHuV0FsIF1MjnULUChEXhAhug7usCDq4GZReMJOoXa6rukEozWIw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "packages/tauri-app/node_modules/@tauri-apps/cli-win32-arm64-msvc": { + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.9.4.tgz", + "integrity": "sha512-/7ZhnP6PY04bEob23q8MH/EoDISdmR1wuNm0k9d5HV7TDMd2GGCDa8dPXA4vJuglJKXIfXqxFmZ4L+J+MO42+w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "packages/tauri-app/node_modules/@tauri-apps/cli-win32-ia32-msvc": { + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.9.4.tgz", + "integrity": "sha512-1LmAfaC4Cq+3O1Ir1ksdhczhdtFSTIV51tbAGtbV/mr348O+M52A/xwCCXQank0OcdBxy5BctqkMtuZnQvA8uQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "packages/tauri-app/node_modules/@tauri-apps/cli-win32-x64-msvc": { + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.9.4.tgz", + "integrity": "sha512-EdYd4c9wGvtPB95kqtEyY+bUR+k4kRw3IA30mAQ1jPH6z57AftT8q84qwv0RDp6kkEqOBKxeInKfqi4BESYuqg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" } }, "packages/ui": { diff --git a/packages/tauri-app/package.json b/packages/tauri-app/package.json index 6f627aefb..c09a70ac8 100644 --- a/packages/tauri-app/package.json +++ b/packages/tauri-app/package.json @@ -14,6 +14,6 @@ "build": "tauri build" }, "devDependencies": { - "@tauri-apps/cli": "^2.10.1" + "@tauri-apps/cli": "^2.9.4" } } From 73a97e64bac916815c3a41d239d85082cdd97954 Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Mon, 20 Apr 2026 23:35:13 +0100 Subject: [PATCH 14/18] fix(tauri): let CI control platform CLI binaries --- package-lock.json | 466 ++++++++++++------------- packages/tauri-app/package.json | 2 +- packages/tauri-app/scripts/prebuild.js | 38 -- 3 files changed, 234 insertions(+), 272 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0821a9281..3e28d769e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3901,6 +3901,238 @@ "url": "https://opencollective.com/tauri" } }, + "node_modules/@tauri-apps/cli": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.10.1.tgz", + "integrity": "sha512-jQNGF/5quwORdZSSLtTluyKQ+o6SMa/AUICfhf4egCGFdMHqWssApVgYSbg+jmrZoc8e1DscNvjTnXtlHLS11g==", + "dev": true, + "license": "Apache-2.0 OR MIT", + "bin": { + "tauri": "tauri.js" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/tauri" + }, + "optionalDependencies": { + "@tauri-apps/cli-darwin-arm64": "2.10.1", + "@tauri-apps/cli-darwin-x64": "2.10.1", + "@tauri-apps/cli-linux-arm-gnueabihf": "2.10.1", + "@tauri-apps/cli-linux-arm64-gnu": "2.10.1", + "@tauri-apps/cli-linux-arm64-musl": "2.10.1", + "@tauri-apps/cli-linux-riscv64-gnu": "2.10.1", + "@tauri-apps/cli-linux-x64-gnu": "2.10.1", + "@tauri-apps/cli-linux-x64-musl": "2.10.1", + "@tauri-apps/cli-win32-arm64-msvc": "2.10.1", + "@tauri-apps/cli-win32-ia32-msvc": "2.10.1", + "@tauri-apps/cli-win32-x64-msvc": "2.10.1" + } + }, + "node_modules/@tauri-apps/cli-darwin-arm64": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.10.1.tgz", + "integrity": "sha512-Z2OjCXiZ+fbYZy7PmP3WRnOpM9+Fy+oonKDEmUE6MwN4IGaYqgceTjwHucc/kEEYZos5GICve35f7ZiizgqEnQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-darwin-x64": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.10.1.tgz", + "integrity": "sha512-V/irQVvjPMGOTQqNj55PnQPVuH4VJP8vZCN7ajnj+ZS8Kom1tEM2hR3qbbIRoS3dBKs5mbG8yg1WC+97dq17Pw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-arm-gnueabihf": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.10.1.tgz", + "integrity": "sha512-Hyzwsb4VnCWKGfTw+wSt15Z2pLw2f0JdFBfq2vHBOBhvg7oi6uhKiF87hmbXOBXUZaGkyRDkCHsdzJcIfoJC2w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-arm64-gnu": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.10.1.tgz", + "integrity": "sha512-OyOYs2t5GkBIvyWjA1+h4CZxTcdz1OZPCWAPz5DYEfB0cnWHERTnQ/SLayQzncrT0kwRoSfSz9KxenkyJoTelA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-arm64-musl": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.10.1.tgz", + "integrity": "sha512-MIj78PDDGjkg3NqGptDOGgfXks7SYJwhiMh8SBoZS+vfdz7yP5jN18bNaLnDhsVIPARcAhE1TlsZe/8Yxo2zqg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-riscv64-gnu": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.10.1.tgz", + "integrity": "sha512-X0lvOVUg8PCVaoEtEAnpxmnkwlE1gcMDTqfhbefICKDnOTJ5Est3qL0SrWxizDackIOKBcvtpejrSiVpuJI1kw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-x64-gnu": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.10.1.tgz", + "integrity": "sha512-2/12bEzsJS9fAKybxgicCDFxYD1WEI9kO+tlDwX5znWG2GwMBaiWcmhGlZ8fi+DMe9CXlcVarMTYc0L3REIRxw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-x64-musl": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.10.1.tgz", + "integrity": "sha512-Y8J0ZzswPz50UcGOFuXGEMrxbjwKSPgXftx5qnkuMs2rmwQB5ssvLb6tn54wDSYxe7S6vlLob9vt0VKuNOaCIQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-win32-arm64-msvc": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.10.1.tgz", + "integrity": "sha512-iSt5B86jHYAPJa/IlYw++SXtFPGnWtFJriHn7X0NFBVunF6zu9+/zOn8OgqIWSl8RgzhLGXQEEtGBdR4wzpVgg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-win32-ia32-msvc": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.10.1.tgz", + "integrity": "sha512-gXyxgEzsFegmnWywYU5pEBURkcFN/Oo45EAwvZrHMh+zUSEAvO5E8TXsgPADYm31d1u7OQU3O3HsYfVBf2moHw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-win32-x64-msvc": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.10.1.tgz", + "integrity": "sha512-6Cn7YpPFwzChy0ERz6djKEmUehWrYlM+xTaNzGPgZocw3BD7OfwfWHKVWxXzdjEW2KfKkHddfdxK1XXTYqBRLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@tauri-apps/plugin-dialog": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-dialog/-/plugin-dialog-2.6.0.tgz", @@ -12849,239 +13081,7 @@ "version": "0.14.0", "license": "MIT", "devDependencies": { - "@tauri-apps/cli": "^2.9.4" - } - }, - "packages/tauri-app/node_modules/@tauri-apps/cli": { - "version": "2.9.4", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.9.4.tgz", - "integrity": "sha512-pvylWC9QckrOS9ATWXIXcgu7g2hKK5xTL5ZQyZU/U0n9l88SEFGcWgLQNa8WZmd+wWIOWhkxOFcOl3i6ubDNNw==", - "dev": true, - "license": "Apache-2.0 OR MIT", - "bin": { - "tauri": "tauri.js" - }, - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/tauri" - }, - "optionalDependencies": { - "@tauri-apps/cli-darwin-arm64": "2.9.4", - "@tauri-apps/cli-darwin-x64": "2.9.4", - "@tauri-apps/cli-linux-arm-gnueabihf": "2.9.4", - "@tauri-apps/cli-linux-arm64-gnu": "2.9.4", - "@tauri-apps/cli-linux-arm64-musl": "2.9.4", - "@tauri-apps/cli-linux-riscv64-gnu": "2.9.4", - "@tauri-apps/cli-linux-x64-gnu": "2.9.4", - "@tauri-apps/cli-linux-x64-musl": "2.9.4", - "@tauri-apps/cli-win32-arm64-msvc": "2.9.4", - "@tauri-apps/cli-win32-ia32-msvc": "2.9.4", - "@tauri-apps/cli-win32-x64-msvc": "2.9.4" - } - }, - "packages/tauri-app/node_modules/@tauri-apps/cli-darwin-arm64": { - "version": "2.9.4", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.9.4.tgz", - "integrity": "sha512-9rHkMVtbMhe0AliVbrGpzMahOBg3rwV46JYRELxR9SN6iu1dvPOaMaiC4cP6M/aD1424ziXnnMdYU06RAH8oIw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0 OR MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "packages/tauri-app/node_modules/@tauri-apps/cli-darwin-x64": { - "version": "2.9.4", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.9.4.tgz", - "integrity": "sha512-VT9ymNuT06f5TLjCZW2hfSxbVtZDhORk7CDUDYiq5TiSYQdxkl8MVBy0CCFFcOk4QAkUmqmVUA9r3YZ/N/vPRQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0 OR MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "packages/tauri-app/node_modules/@tauri-apps/cli-linux-arm-gnueabihf": { - "version": "2.9.4", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.9.4.tgz", - "integrity": "sha512-tTWkEPig+2z3Rk0zqZYfjUYcgD+aSm72wdrIhdYobxbQZOBw0zfn50YtWv+av7bm0SHvv75f0l7JuwgZM1HFow==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "Apache-2.0 OR MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "packages/tauri-app/node_modules/@tauri-apps/cli-linux-arm64-gnu": { - "version": "2.9.4", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.9.4.tgz", - "integrity": "sha512-ql6vJ611qoqRYHxkKPnb2vHa27U+YRKRmIpLMMBeZnfFtZ938eao7402AQCH1mO2+/8ioUhbpy9R/ZcLTXVmkg==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "Apache-2.0 OR MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "packages/tauri-app/node_modules/@tauri-apps/cli-linux-arm64-musl": { - "version": "2.9.4", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.9.4.tgz", - "integrity": "sha512-vg7yNn7ICTi6hRrcA/6ff2UpZQP7un3xe3SEld5QM0prgridbKAiXGaCKr3BnUBx/rGXegQlD/wiLcWdiiraSw==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "Apache-2.0 OR MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "packages/tauri-app/node_modules/@tauri-apps/cli-linux-riscv64-gnu": { - "version": "2.9.4", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.9.4.tgz", - "integrity": "sha512-l8L+3VxNk6yv5T/Z/gv5ysngmIpsai40B9p6NQQyqYqxImqYX37pqREoEBl1YwG7szGnDibpWhidPrWKR59OJA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "Apache-2.0 OR MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "packages/tauri-app/node_modules/@tauri-apps/cli-linux-x64-gnu": { - "version": "2.9.4", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.9.4.tgz", - "integrity": "sha512-PepPhCXc/xVvE3foykNho46OmCyx47E/aG676vKTVp+mqin5d+IBqDL6wDKiGNT5OTTxKEyNlCQ81Xs2BQhhqA==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "Apache-2.0 OR MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "packages/tauri-app/node_modules/@tauri-apps/cli-linux-x64-musl": { - "version": "2.9.4", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.9.4.tgz", - "integrity": "sha512-zcd1QVffh5tZs1u1SCKUV/V7RRynebgYUNWHuV0FsIF1MjnULUChEXhAhug7usCDq4GZReMJOoXa6rukEozWIw==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "Apache-2.0 OR MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "packages/tauri-app/node_modules/@tauri-apps/cli-win32-arm64-msvc": { - "version": "2.9.4", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.9.4.tgz", - "integrity": "sha512-/7ZhnP6PY04bEob23q8MH/EoDISdmR1wuNm0k9d5HV7TDMd2GGCDa8dPXA4vJuglJKXIfXqxFmZ4L+J+MO42+w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0 OR MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "packages/tauri-app/node_modules/@tauri-apps/cli-win32-ia32-msvc": { - "version": "2.9.4", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.9.4.tgz", - "integrity": "sha512-1LmAfaC4Cq+3O1Ir1ksdhczhdtFSTIV51tbAGtbV/mr348O+M52A/xwCCXQank0OcdBxy5BctqkMtuZnQvA8uQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "Apache-2.0 OR MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "packages/tauri-app/node_modules/@tauri-apps/cli-win32-x64-msvc": { - "version": "2.9.4", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.9.4.tgz", - "integrity": "sha512-EdYd4c9wGvtPB95kqtEyY+bUR+k4kRw3IA30mAQ1jPH6z57AftT8q84qwv0RDp6kkEqOBKxeInKfqi4BESYuqg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0 OR MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" + "@tauri-apps/cli": "^2.10.1" } }, "packages/ui": { diff --git a/packages/tauri-app/package.json b/packages/tauri-app/package.json index c09a70ac8..6f627aefb 100644 --- a/packages/tauri-app/package.json +++ b/packages/tauri-app/package.json @@ -14,6 +14,6 @@ "build": "tauri build" }, "devDependencies": { - "@tauri-apps/cli": "^2.9.4" + "@tauri-apps/cli": "^2.10.1" } } diff --git a/packages/tauri-app/scripts/prebuild.js b/packages/tauri-app/scripts/prebuild.js index a22409fba..c50efba0d 100644 --- a/packages/tauri-app/scripts/prebuild.js +++ b/packages/tauri-app/scripts/prebuild.js @@ -248,43 +248,6 @@ function ensureEsbuildPlatformBinary() { }) } -function ensureTauriCliPlatformBinary() { - const platformKey = `${process.platform}-${process.arch}` - const platformPackages = { - "darwin-arm64": "@tauri-apps/cli-darwin-arm64", - "darwin-x64": "@tauri-apps/cli-darwin-x64", - "linux-arm64": "@tauri-apps/cli-linux-arm64-gnu", - "linux-x64": "@tauri-apps/cli-linux-x64-gnu", - "win32-arm64": "@tauri-apps/cli-win32-arm64-msvc", - "win32-x64": "@tauri-apps/cli-win32-x64-msvc", - } - - const pkgName = platformPackages[platformKey] - if (!pkgName) { - return - } - - const platformPackagePath = path.join(workspaceRoot, "node_modules", ...pkgName.split("/")) - if (fs.existsSync(platformPackagePath)) { - return - } - - let cliVersion = "" - try { - cliVersion = require(path.join(root, "node_modules", "@tauri-apps", "cli", "package.json")).version - } catch { - // leave version empty; fallback install will use latest compatible - } - - const packageSpec = cliVersion ? `${pkgName}@${cliVersion}` : pkgName - - console.log("[prebuild] installing tauri CLI platform binary (optional dep workaround)...") - execSync(`npm install ${packageSpec} --no-save --ignore-scripts --fund=false --audit=false`, { - cwd: workspaceRoot, - stdio: "inherit", - }) -} - function copyServerArtifacts() { fs.rmSync(serverDest, { recursive: true, force: true }) fs.mkdirSync(serverDest, { recursive: true }) @@ -377,7 +340,6 @@ function copyUiLoadingAssets() { copyServerArtifacts() stripNodeModuleBins() copyUiLoadingAssets() - ensureTauriCliPlatformBinary() })().catch((err) => { console.error("[prebuild] failed:", err) process.exit(1) From 8173030b1a741e5d9d6934a4e5f11f8f3d2d0b24 Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Tue, 21 Apr 2026 07:35:22 +0100 Subject: [PATCH 15/18] fix(ci): log Linux Tauri bundle diagnostics --- .github/workflows/build-and-upload.yml | 37 +++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-and-upload.yml b/.github/workflows/build-and-upload.yml index 3f8aef8da..683c8106b 100644 --- a/.github/workflows/build-and-upload.yml +++ b/.github/workflows/build-and-upload.yml @@ -651,7 +651,42 @@ jobs: - name: Build Linux bundle (Tauri) working-directory: packages/tauri-app - run: npm exec -- tauri build + shell: bash + env: + RUST_BACKTRACE: full + RUST_LOG: tauri_bundler=trace + TAURI_LOG_LEVEL: debug + run: | + set -uo pipefail + + build_log="$RUNNER_TEMP/tauri-linux-build.log" + status=0 + + npm exec -- tauri build 2>&1 | tee "$build_log" || status=$? + + if [ "$status" -eq 0 ]; then + exit 0 + fi + + echo "Tauri Linux bundle failed with exit code $status" >&2 + + echo "::group::Tauri build log tail" + tail -n 200 "$build_log" || true + echo "::endgroup::" + + echo "::group::Bundle output tree" + find target -maxdepth 6 \( -type d -o -type f \) | sort || true + echo "::endgroup::" + + echo "::group::Likely linuxdeploy temp files" + find /tmp -maxdepth 3 \( -iname '*linuxdeploy*' -o -iname '*appimage*' -o -iname '*.desktop' -o -iname '*.log' \) | sort || true + echo "::endgroup::" + + echo "::group::Bundle metadata files" + find target -maxdepth 8 -type f \( -name '*.desktop' -o -name 'AppRun' -o -name '*.AppImage' -o -name '*.log' -o -name '*.json' \) | sort || true + echo "::endgroup::" + + exit "$status" - name: Package Tauri artifacts (Linux) if: ${{ inputs.upload || inputs.upload_actions_artifacts }} From f56d63d1669f320618753a0c6766bc66d0d3764e Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Tue, 21 Apr 2026 07:51:53 +0100 Subject: [PATCH 16/18] fix(tauri): strip native config addons from bundles --- .../server/scripts/copy-opencode-config.mjs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/packages/server/scripts/copy-opencode-config.mjs b/packages/server/scripts/copy-opencode-config.mjs index 6a9482395..48049d4b7 100644 --- a/packages/server/scripts/copy-opencode-config.mjs +++ b/packages/server/scripts/copy-opencode-config.mjs @@ -51,6 +51,30 @@ function stripNodeModuleBins(rootDir) { return removed } +function stripOptionalNativeAddons(rootDir) { + const nodeModulesRoot = path.join(rootDir, "node_modules") + if (!existsSync(nodeModulesRoot)) { + return 0 + } + + const removablePaths = [ + path.join(nodeModulesRoot, "@msgpackr-extract"), + path.join(nodeModulesRoot, "msgpackr-extract"), + ] + + let removed = 0 + for (const targetPath of removablePaths) { + if (!existsSync(targetPath)) { + continue + } + + rmSync(targetPath, { recursive: true, force: true }) + removed += 1 + } + + return removed +} + if (!existsSync(sourceDir)) { console.error(`[copy-opencode-config] Missing source directory at ${sourceDir}`) process.exit(1) @@ -100,4 +124,9 @@ if (removedBins > 0) { console.log(`[copy-opencode-config] Removed ${removedBins} node_modules/.bin directories`) } +const removedNativeAddons = stripOptionalNativeAddons(targetDir) +if (removedNativeAddons > 0) { + console.log(`[copy-opencode-config] Removed ${removedNativeAddons} optional native addon package paths`) +} + console.log(`[copy-opencode-config] Copied ${sourceDir} -> ${targetDir}`) From 25512e8dc155097183dafad3a99738d23165fafe Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Tue, 21 Apr 2026 07:55:32 +0100 Subject: [PATCH 17/18] fix(ci): install xdg-utils for Linux Tauri bundling --- .github/workflows/build-and-upload.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-and-upload.yml b/.github/workflows/build-and-upload.yml index 683c8106b..65c64515d 100644 --- a/.github/workflows/build-and-upload.yml +++ b/.github/workflows/build-and-upload.yml @@ -614,6 +614,7 @@ jobs: sudo apt-get install -y \ build-essential \ pkg-config \ + xdg-utils \ libgtk-3-dev \ libglib2.0-dev \ libwebkit2gtk-4.1-dev \ From c9eea8c003553c8ad4d5b24a512f5198da698bcf Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Tue, 21 Apr 2026 08:33:53 +0100 Subject: [PATCH 18/18] fix(tauri): require standalone server in desktop bundles --- .github/workflows/build-and-upload.yml | 37 +---------- packages/tauri-app/scripts/prebuild.js | 22 +------ .../tauri-app/src-tauri/src/cli_manager.rs | 64 ++----------------- 3 files changed, 6 insertions(+), 117 deletions(-) diff --git a/.github/workflows/build-and-upload.yml b/.github/workflows/build-and-upload.yml index 65c64515d..dfad1d063 100644 --- a/.github/workflows/build-and-upload.yml +++ b/.github/workflows/build-and-upload.yml @@ -652,42 +652,7 @@ jobs: - name: Build Linux bundle (Tauri) working-directory: packages/tauri-app - shell: bash - env: - RUST_BACKTRACE: full - RUST_LOG: tauri_bundler=trace - TAURI_LOG_LEVEL: debug - run: | - set -uo pipefail - - build_log="$RUNNER_TEMP/tauri-linux-build.log" - status=0 - - npm exec -- tauri build 2>&1 | tee "$build_log" || status=$? - - if [ "$status" -eq 0 ]; then - exit 0 - fi - - echo "Tauri Linux bundle failed with exit code $status" >&2 - - echo "::group::Tauri build log tail" - tail -n 200 "$build_log" || true - echo "::endgroup::" - - echo "::group::Bundle output tree" - find target -maxdepth 6 \( -type d -o -type f \) | sort || true - echo "::endgroup::" - - echo "::group::Likely linuxdeploy temp files" - find /tmp -maxdepth 3 \( -iname '*linuxdeploy*' -o -iname '*appimage*' -o -iname '*.desktop' -o -iname '*.log' \) | sort || true - echo "::endgroup::" - - echo "::group::Bundle metadata files" - find target -maxdepth 8 -type f \( -name '*.desktop' -o -name 'AppRun' -o -name '*.AppImage' -o -name '*.log' -o -name '*.json' \) | sort || true - echo "::endgroup::" - - exit "$status" + run: npm exec -- tauri build - name: Package Tauri artifacts (Linux) if: ${{ inputs.upload || inputs.upload_actions_artifacts }} diff --git a/packages/tauri-app/scripts/prebuild.js b/packages/tauri-app/scripts/prebuild.js index c50efba0d..879de4963 100644 --- a/packages/tauri-app/scripts/prebuild.js +++ b/packages/tauri-app/scripts/prebuild.js @@ -87,10 +87,6 @@ function ensureStandaloneServerBuild() { }) } -function shouldBuildStandaloneServer() { - return process.platform !== "linux" -} - function ensureUiBuild() { const loadingHtml = path.join(uiDist, "loading.html") if (fs.existsSync(loadingHtml)) { @@ -146,17 +142,6 @@ function ensureServerDependencies() { } } -function removeStaleStandaloneServerBuild() { - const staleNames = ["codenomad-server", "codenomad-server.exe"] - for (const name of staleNames) { - const stalePath = path.join(serverRoot, "dist", name) - if (fs.existsSync(stalePath)) { - fs.rmSync(stalePath, { force: true }) - console.log(`[prebuild] removed stale standalone server artifact ${stalePath}`) - } - } -} - function ensureUiDevDependencies() { if (fs.existsSync(viteBinPath)) { return @@ -328,12 +313,7 @@ function copyUiLoadingAssets() { ensureRollupPlatformBinary() ensureEsbuildPlatformBinary() ensureServerBuild() - if (shouldBuildStandaloneServer()) { - ensureStandaloneServerBuild() - } else { - removeStaleStandaloneServerBuild() - console.log("[prebuild] skipping standalone server executable for Linux packaging; linuxdeploy fails on the bundled ELF") - } + ensureStandaloneServerBuild() ensureServerDependencies() ensureUiBuild() syncServerUiBundle() diff --git a/packages/tauri-app/src-tauri/src/cli_manager.rs b/packages/tauri-app/src-tauri/src/cli_manager.rs index 780e6bac4..1f11a8b7f 100644 --- a/packages/tauri-app/src-tauri/src/cli_manager.rs +++ b/packages/tauri-app/src-tauri/src/cli_manager.rs @@ -635,12 +635,12 @@ impl CliProcessManager { let use_user_shell = supports_user_shell(); - if resolution.runner != Runner::Standalone + if resolution.runner == Runner::Tsx && !use_user_shell && which::which(&resolution.node_binary).is_err() { return Err(anyhow::anyhow!( - "Node binary '{}' not found. CodeNomad desktop currently requires Node.js installed on the system, or set NODE_BINARY to a valid runtime path.", + "Node binary '{}' not found. CodeNomad development mode requires Node.js installed on the system, or set NODE_BINARY to a valid runtime path.", resolution.node_binary )); } @@ -943,7 +943,7 @@ impl CliProcessManager { let mut locked = status.lock(); if locked.error.is_none() { locked.error = Some(format!( - "Node binary '{}' not found in the desktop shell environment. CodeNomad desktop currently requires Node.js installed on the system, or set NODE_BINARY to a valid runtime path.", + "Node binary '{}' not found in the desktop shell environment. CodeNomad development mode requires Node.js installed on the system, or set NODE_BINARY to a valid runtime path.", node_binary.trim() )); } @@ -1066,7 +1066,6 @@ struct CliEntry { #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum Runner { - Node, Standalone, Tsx, } @@ -1097,17 +1096,8 @@ impl CliEntry { }); } - if let Some(entry) = resolve_dist_entry(app) { - return Ok(Self { - entry, - runner: Runner::Node, - runner_path: None, - node_binary, - }); - } - Err(anyhow::anyhow!( - "Unable to locate CodeNomad CLI build. Please run `npm run build --workspace @neuralnomads/codenomad`." + "Unable to locate the packaged CodeNomad standalone server. Please rebuild the desktop bundle." )) } @@ -1275,52 +1265,6 @@ fn resolve_standalone_entry(_app: &AppHandle) -> Option { first_existing(candidates) } -fn resolve_dist_entry(_app: &AppHandle) -> Option { - let base = workspace_root(); - let mut candidates: Vec> = vec![ - base.as_ref().map(|p| p.join("packages/server/dist/bin.js")), - base.as_ref() - .map(|p| p.join("packages/server/dist/index.js")), - base.as_ref().map(|p| p.join("server/dist/bin.js")), - base.as_ref().map(|p| p.join("server/dist/index.js")), - ]; - - if let Ok(exe) = std::env::current_exe() { - if let Some(dir) = exe.parent() { - candidates.push(Some(dir.join("resources/server/dist/bin.js"))); - candidates.push(Some(dir.join("resources/server/dist/index.js"))); - candidates.push(Some(dir.join("resources/server/dist/server/bin.js"))); - candidates.push(Some(dir.join("resources/server/dist/server/index.js"))); - - let resources = dir.join("../Resources"); - candidates.push(Some(resources.join("server/dist/bin.js"))); - candidates.push(Some(resources.join("server/dist/index.js"))); - candidates.push(Some(resources.join("server/dist/server/bin.js"))); - candidates.push(Some(resources.join("server/dist/server/index.js"))); - candidates.push(Some(resources.join("resources/server/dist/bin.js"))); - candidates.push(Some(resources.join("resources/server/dist/index.js"))); - candidates.push(Some(resources.join("resources/server/dist/server/bin.js"))); - candidates.push(Some( - resources.join("resources/server/dist/server/index.js"), - )); - - let linux_resource_roots = [dir.join("../lib/CodeNomad"), dir.join("../lib/codenomad")]; - for root in linux_resource_roots { - candidates.push(Some(root.join("server/dist/bin.js"))); - candidates.push(Some(root.join("server/dist/index.js"))); - candidates.push(Some(root.join("server/dist/server/bin.js"))); - candidates.push(Some(root.join("server/dist/server/index.js"))); - candidates.push(Some(root.join("resources/server/dist/bin.js"))); - candidates.push(Some(root.join("resources/server/dist/index.js"))); - candidates.push(Some(root.join("resources/server/dist/server/bin.js"))); - candidates.push(Some(root.join("resources/server/dist/server/index.js"))); - } - } - } - - first_existing(candidates) -} - fn build_shell_command_string( entry: &CliEntry, cli_args: &[String],