Skip to content

Commit e8e2b8a

Browse files
committed
Bundle Node runtime for ACP desktop
1 parent c872d7c commit e8e2b8a

21 files changed

Lines changed: 812 additions & 14 deletions

File tree

console/src-tauri/capabilities/default.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"core:default",
1313
"desktop-updater-check",
1414
"desktop-updater-install",
15+
"dialog:allow-open",
1516
"dialog:allow-save",
1617
"open-external-link",
1718
"tray"

console/src-tauri/src/backend/command.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,15 @@ pub(super) fn create(app: &tauri::AppHandle) -> Result<Command, String> {
7474
installation will be unavailable"
7575
);
7676
}
77+
if let Some(node_runtime) = packaged_node_runtime(app) {
78+
log::info!("[backend] bundled node runtime: {}", node_runtime.display());
79+
command = command.env(
80+
"QWENPAW_DESKTOP_NODE_RUNTIME",
81+
node_runtime.to_string_lossy().to_string(),
82+
);
83+
} else {
84+
log::warn!("[backend] bundled node runtime not found");
85+
}
7786
Ok(command)
7887
}
7988

@@ -94,6 +103,22 @@ fn packaged_python_runtime(app: &tauri::AppHandle) -> Option<PathBuf> {
94103
candidates.into_iter().find(|path| path.is_file())
95104
}
96105

106+
#[cfg(not(debug_assertions))]
107+
fn packaged_node_runtime(app: &tauri::AppHandle) -> Option<PathBuf> {
108+
let root = app
109+
.path()
110+
.resource_dir()
111+
.ok()?
112+
.join("binaries")
113+
.join("node-runtime");
114+
let node = if cfg!(windows) {
115+
root.join("node.exe")
116+
} else {
117+
root.join("bin").join("node")
118+
};
119+
node.is_file().then_some(root)
120+
}
121+
97122
#[cfg(not(debug_assertions))]
98123
fn packaged_backend_executable(app: &tauri::AppHandle) -> Result<PathBuf, String> {
99124
let executable_name = if cfg!(windows) {

console/src-tauri/tauri.conf.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,11 @@
3939
"../../scripts/pack/assets/icon.icns",
4040
"../../scripts/pack/assets/icon.ico"
4141
],
42-
"resources": ["binaries/qwenpaw-backend", "binaries/python-runtime"],
42+
"resources": [
43+
"binaries/qwenpaw-backend",
44+
"binaries/python-runtime",
45+
"binaries/node-runtime"
46+
],
4347
"windows": {
4448
"webviewInstallMode": {
4549
"type": "downloadBootstrapper",

console/src/api/modules/acp.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
import { request } from "../request";
2-
import type { ACPAgentConfig, ACPConfig } from "../types";
2+
import type {
3+
ACPAgentConfig,
4+
ACPConfig,
5+
ACPNodeRuntimeStatus,
6+
ACPNodeRuntimeUpdate,
7+
} from "../types";
38

49
export const acpApi = {
510
getACPConfig: () => request<ACPConfig>("/config/acp"),
611

12+
getACPNodeRuntime: () =>
13+
request<ACPNodeRuntimeStatus>("/config/acp/node-runtime"),
14+
715
getACPAgentConfig: (agentName: string) =>
816
request<ACPAgentConfig>(`/config/acp/${encodeURIComponent(agentName)}`),
917

@@ -13,6 +21,12 @@ export const acpApi = {
1321
body: JSON.stringify(body),
1422
}),
1523

24+
updateACPNodeRuntime: (body: ACPNodeRuntimeUpdate) =>
25+
request<ACPNodeRuntimeStatus>("/config/acp/node-runtime", {
26+
method: "PUT",
27+
body: JSON.stringify(body),
28+
}),
29+
1630
updateACPAgentConfig: (agentName: string, body: ACPAgentConfig) =>
1731
request<ACPAgentConfig>(`/config/acp/${encodeURIComponent(agentName)}`, {
1832
method: "PUT",

console/src/api/types/acp.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,28 @@ export interface ACPAgentConfig {
1414
}
1515

1616
export interface ACPConfig {
17+
node_path?: string;
1718
agents: Record<string, ACPAgentConfig>;
1819
}
20+
21+
export interface ACPNodeRuntimeCandidate {
22+
key: string;
23+
label: string;
24+
node_path: string;
25+
npx_path: string;
26+
node_version: string;
27+
npx_version: string;
28+
available: boolean;
29+
reason_code: string;
30+
reason: string;
31+
}
32+
33+
export interface ACPNodeRuntimeStatus {
34+
node_path: string;
35+
effective_node_path: string;
36+
candidates: ACPNodeRuntimeCandidate[];
37+
}
38+
39+
export interface ACPNodeRuntimeUpdate {
40+
node_path: string;
41+
}

console/src/locales/en.json

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,27 @@
255255
"deleteTitle": "Delete {{name}}",
256256
"deleteConfirm": "Delete this ACP agent? This action cannot be undone.",
257257
"deleteSuccess": "ACP agent deleted",
258-
"deleteFailed": "Failed to delete ACP agent"
258+
"deleteFailed": "Failed to delete ACP agent",
259+
"nodeSettings": "Node Settings",
260+
"nodePath": "Node path",
261+
"chooseOtherNode": "Choose another Node...",
262+
"selectNodePath": "Select Node path",
263+
"nodePathPrompt": "Enter the node executable path",
264+
"nodeSaved": "Node settings saved",
265+
"nodeLoadFailed": "Failed to load Node settings",
266+
"nodeSaveFailed": "Node path is unavailable",
267+
"nodeRuntime": {
268+
"bundled": "Bundled Node",
269+
"system": "System Node",
270+
"custom": "Custom Node"
271+
},
272+
"nodeRuntimeReason": {
273+
"systemNodeMissing": "System Node was not detected",
274+
"nodeMissing": "Node path does not exist",
275+
"npxMissing": "npx was not found",
276+
"versionCheckFailed": "Version check failed",
277+
"unavailable": "Unavailable"
278+
}
259279
},
260280
"voiceTranscription": {
261281
"title": "Voice Transcription",

console/src/locales/id.json

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1171,7 +1171,27 @@
11711171
"deleteTitle": "Hapus {{name}}",
11721172
"deleteConfirm": "Hapus agen ACP ini? Tindakan ini tidak dapat dibatalkan.",
11731173
"deleteSuccess": "Agen ACP berhasil dihapus",
1174-
"deleteFailed": "Gagal menghapus agen ACP"
1174+
"deleteFailed": "Gagal menghapus agen ACP",
1175+
"nodeSettings": "Pengaturan Node",
1176+
"nodePath": "Path Node",
1177+
"chooseOtherNode": "Pilih Node lain...",
1178+
"selectNodePath": "Pilih path Node",
1179+
"nodePathPrompt": "Masukkan path file executable node",
1180+
"nodeSaved": "Pengaturan Node tersimpan",
1181+
"nodeLoadFailed": "Gagal memuat pengaturan Node",
1182+
"nodeSaveFailed": "Path Node tidak tersedia",
1183+
"nodeRuntime": {
1184+
"bundled": "Node bawaan",
1185+
"system": "Node sistem",
1186+
"custom": "Node kustom"
1187+
},
1188+
"nodeRuntimeReason": {
1189+
"systemNodeMissing": "Node sistem tidak terdeteksi",
1190+
"nodeMissing": "Path Node tidak ada",
1191+
"npxMissing": "npx tidak ditemukan",
1192+
"versionCheckFailed": "Pemeriksaan versi gagal",
1193+
"unavailable": "Tidak tersedia"
1194+
}
11751195
},
11761196
"sessions": {
11771197
"title": "Sesi",

console/src/locales/ja.json

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2218,7 +2218,27 @@
22182218
"deleteTitle": "{{name}} を削除",
22192219
"deleteConfirm": "この ACP Agent を削除しますか?この操作は元に戻せません。",
22202220
"deleteSuccess": "ACP Agent を削除しました",
2221-
"deleteFailed": "ACP Agent の削除に失敗しました"
2221+
"deleteFailed": "ACP Agent の削除に失敗しました",
2222+
"nodeSettings": "Node 設定",
2223+
"nodePath": "Node パス",
2224+
"chooseOtherNode": "別の Node を選択...",
2225+
"selectNodePath": "Node パスを選択",
2226+
"nodePathPrompt": "node 実行ファイルのパスを入力してください",
2227+
"nodeSaved": "Node 設定を保存しました",
2228+
"nodeLoadFailed": "Node 設定の読み込みに失敗しました",
2229+
"nodeSaveFailed": "Node パスを利用できません",
2230+
"nodeRuntime": {
2231+
"bundled": "同梱 Node",
2232+
"system": "システム Node",
2233+
"custom": "カスタム Node"
2234+
},
2235+
"nodeRuntimeReason": {
2236+
"systemNodeMissing": "システム Node が検出されませんでした",
2237+
"nodeMissing": "Node パスが存在しません",
2238+
"npxMissing": "npx が見つかりません",
2239+
"versionCheckFailed": "バージョン確認に失敗しました",
2240+
"unavailable": "利用不可"
2241+
}
22222242
},
22232243
"backup": {
22242244
"title": "バックアップ",

console/src/locales/pt-BR.json

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,27 @@
138138
"deleteTitle": "Excluir {{name}}",
139139
"deleteConfirm": "Excluir este agente ACP? Esta acao nao pode ser desfeita.",
140140
"deleteSuccess": "Agente ACP excluido",
141-
"deleteFailed": "Falha ao excluir agente ACP"
141+
"deleteFailed": "Falha ao excluir agente ACP",
142+
"nodeSettings": "Configuracoes do Node",
143+
"nodePath": "Caminho do Node",
144+
"chooseOtherNode": "Escolher outro Node...",
145+
"selectNodePath": "Selecionar caminho do Node",
146+
"nodePathPrompt": "Informe o caminho do executavel node",
147+
"nodeSaved": "Configuracoes do Node salvas",
148+
"nodeLoadFailed": "Falha ao carregar configuracoes do Node",
149+
"nodeSaveFailed": "Caminho do Node indisponivel",
150+
"nodeRuntime": {
151+
"bundled": "Node embutido",
152+
"system": "Node do sistema",
153+
"custom": "Node personalizado"
154+
},
155+
"nodeRuntimeReason": {
156+
"systemNodeMissing": "Node do sistema nao detectado",
157+
"nodeMissing": "Caminho do Node nao existe",
158+
"npxMissing": "npx nao encontrado",
159+
"versionCheckFailed": "Falha na verificacao de versao",
160+
"unavailable": "Indisponivel"
161+
}
142162
},
143163
"voiceTranscription": {
144164
"title": "Transcricao de Voz",

console/src/locales/ru.json

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2233,7 +2233,27 @@
22332233
"deleteTitle": "Удалить {{name}}",
22342234
"deleteConfirm": "Удалить этот ACP агент? Это действие нельзя отменить.",
22352235
"deleteSuccess": "ACP агент удалён",
2236-
"deleteFailed": "Не удалось удалить ACP агент"
2236+
"deleteFailed": "Не удалось удалить ACP агент",
2237+
"nodeSettings": "Настройки Node",
2238+
"nodePath": "Путь к Node",
2239+
"chooseOtherNode": "Выбрать другой Node...",
2240+
"selectNodePath": "Выбрать путь к Node",
2241+
"nodePathPrompt": "Введите путь к исполняемому файлу node",
2242+
"nodeSaved": "Настройки Node сохранены",
2243+
"nodeLoadFailed": "Не удалось загрузить настройки Node",
2244+
"nodeSaveFailed": "Путь к Node недоступен",
2245+
"nodeRuntime": {
2246+
"bundled": "Встроенный Node",
2247+
"system": "Системный Node",
2248+
"custom": "Пользовательский Node"
2249+
},
2250+
"nodeRuntimeReason": {
2251+
"systemNodeMissing": "Системный Node не обнаружен",
2252+
"nodeMissing": "Путь к Node не существует",
2253+
"npxMissing": "npx не найден",
2254+
"versionCheckFailed": "Проверка версии не удалась",
2255+
"unavailable": "Недоступно"
2256+
}
22372257
},
22382258
"backup": {
22392259
"title": "Резервные копии",

0 commit comments

Comments
 (0)