@amd-gaia/agent-ui 0.17.5 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/index-BKq2diFF.js +550 -0
- package/dist/assets/index-dpq7GLUN.css +1 -0
- package/dist/index.html +2 -2
- package/main.cjs +91 -1
- package/package.json +2 -2
- package/services/backend-installer-progress-dialog.cjs +79 -45
- package/services/backend-installer.cjs +138 -17
- package/dist/assets/index-CdffaS1a.js +0 -438
- package/dist/assets/index-CzObDlwu.css +0 -1
|
@@ -345,55 +345,89 @@ async function showFailureDialog(parentWindow, errorInfo = {}) {
|
|
|
345
345
|
.filter(Boolean)
|
|
346
346
|
.join("\n");
|
|
347
347
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
348
|
+
// Loop so that certain actions (Copy/Open log) return control to the
|
|
349
|
+
// same dialog rather than exiting the flow. The dialog includes a
|
|
350
|
+
// one-click "Install uv" action which attempts the packaged rescue
|
|
351
|
+
// installer and then returns 'retry' on success so the caller can
|
|
352
|
+
// re-run the full backend install.
|
|
353
|
+
const buttons = [
|
|
354
|
+
"Install uv (auto)",
|
|
355
|
+
"Retry",
|
|
356
|
+
"Manual install instructions",
|
|
357
|
+
"Copy log path",
|
|
358
|
+
"Open log file",
|
|
359
|
+
"Quit",
|
|
360
|
+
];
|
|
361
|
+
|
|
362
|
+
// Helper to show the dialog and handle the response.
|
|
363
|
+
const show = async () => {
|
|
364
|
+
const result = await dialog.showMessageBox(parentWindow || null, {
|
|
365
|
+
type: "error",
|
|
366
|
+
title: "GAIA install failed",
|
|
367
|
+
message,
|
|
368
|
+
detail,
|
|
369
|
+
buttons,
|
|
370
|
+
defaultId: 0,
|
|
371
|
+
cancelId: buttons.length - 1,
|
|
372
|
+
noLink: true,
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
switch (result.response) {
|
|
376
|
+
case 0: {
|
|
377
|
+
// Run the full packaged install flow (ensureBackend) so the user
|
|
378
|
+
// gets a single-click recovery that attempts the entire backend
|
|
379
|
+
// install rather than only uv provisioning. Show progress UI
|
|
380
|
+
// while the operation runs. On success, tell the caller to retry
|
|
381
|
+
// (which will detect READY and proceed); on failure, re-show the
|
|
382
|
+
// dialog with augmented details.
|
|
383
|
+
const { window, onProgress, close } = createProgressWindow();
|
|
384
|
+
try {
|
|
385
|
+
await installer.ensureBackend({ onProgress, isPackaged: true });
|
|
386
|
+
try { close(); } catch {}
|
|
387
|
+
return "retry";
|
|
388
|
+
} catch (err) {
|
|
389
|
+
try { close(); } catch {}
|
|
390
|
+
const nextInfo = Object.assign({}, errorInfo, {
|
|
391
|
+
message: err.message || String(err),
|
|
392
|
+
stage: err.stage || "ensure-backend",
|
|
393
|
+
suggestion: err.suggestion || errorInfo.suggestion,
|
|
394
|
+
});
|
|
395
|
+
return showFailureDialog(parentWindow, nextInfo);
|
|
396
|
+
}
|
|
373
397
|
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
398
|
+
case 1:
|
|
399
|
+
return "retry";
|
|
400
|
+
case 2: {
|
|
401
|
+
try {
|
|
402
|
+
await shell.openExternal("https://amd-gaia.ai/quickstart#cli-install");
|
|
403
|
+
} catch {
|
|
404
|
+
// ignore
|
|
405
|
+
}
|
|
406
|
+
return "manual";
|
|
381
407
|
}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
408
|
+
case 3: {
|
|
409
|
+
try {
|
|
410
|
+
clipboard.writeText(logPath);
|
|
411
|
+
} catch {
|
|
412
|
+
// ignore
|
|
413
|
+
}
|
|
414
|
+
return showFailureDialog(parentWindow, errorInfo);
|
|
415
|
+
}
|
|
416
|
+
case 4: {
|
|
417
|
+
try {
|
|
418
|
+
await shell.openPath(logPath);
|
|
419
|
+
} catch {
|
|
420
|
+
// ignore
|
|
421
|
+
}
|
|
422
|
+
return showFailureDialog(parentWindow, errorInfo);
|
|
390
423
|
}
|
|
391
|
-
|
|
424
|
+
case 5:
|
|
425
|
+
default:
|
|
426
|
+
return "quit";
|
|
392
427
|
}
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
}
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
return show();
|
|
397
431
|
}
|
|
398
432
|
|
|
399
433
|
// ── Pre-check failure dialogs ───────────────────────────────────────────────
|
|
@@ -77,10 +77,28 @@ const NETWORK_CHECK_TIMEOUT_MS = 5000;
|
|
|
77
77
|
// archive against upstream's published .sha256, then extracts the `uv` binary
|
|
78
78
|
// which is what `ensureUv()` hashes at runtime.
|
|
79
79
|
//
|
|
80
|
-
// Currently pinned: uv v0.5.14 linux-x64.
|
|
80
|
+
// Currently pinned: uv v0.5.14 linux-x64, mac-arm64.
|
|
81
|
+
// (win-x64 deferred to a follow-up issue — its SHA must ship together with an
|
|
82
|
+
// NSIS structural-smoke verifier, not on its own; see the #849 lesson.)
|
|
83
|
+
//
|
|
84
|
+
// IMPORTANT: per-platform SHA origin differs:
|
|
85
|
+
// - linux-x64: raw extracted-from-tarball digest (no post-build modification).
|
|
86
|
+
// - mac-arm64: POST-CODESIGN digest. electron-builder code-signs the bundled
|
|
87
|
+
// uv during packaging, so this hash matches what ensureUv() sees
|
|
88
|
+
// at runtime, NOT the upstream tarball. Bumping this pin means
|
|
89
|
+
// running the CI build, then copying the SHA from the
|
|
90
|
+
// dmg-structural-smoke failure message — never from `shasum`
|
|
91
|
+
// against the freshly downloaded tarball.
|
|
81
92
|
const BUNDLED_UV_VERSION = "0.5.14";
|
|
82
93
|
const BUNDLED_UV_SHA256 = {
|
|
83
94
|
"linux-x64": "0e05d828b5708e8a927724124db3746396afddad6273c47283d7c562dc795bd6",
|
|
95
|
+
// The Windows extracted uv.exe SHA is populated by CI during the
|
|
96
|
+
// build step. The placeholder MUST be replaced in CI before packaging
|
|
97
|
+
// so runtime verification remains strict.
|
|
98
|
+
"win-x64": "055d55eec85a91cfb5e9c8bc7f6463f9883866796c5bcb205fbcdfed9c088c88",
|
|
99
|
+
// mac-arm64: POST-codesign digest. CI should populate this value when
|
|
100
|
+
// packaging the macOS DMG and running the dmg-structural-smoke job.
|
|
101
|
+
"mac-arm64": "6099aa8cd701f0c81227ee30c304777ce151e4d47c53a75ce53cd2243448d8c8",
|
|
84
102
|
};
|
|
85
103
|
|
|
86
104
|
const MANAGED_UV_DIR = path.join(GAIA_HOME, "bin");
|
|
@@ -139,6 +157,10 @@ let logStream = null;
|
|
|
139
157
|
*/
|
|
140
158
|
let logRotatedThisSession = false;
|
|
141
159
|
|
|
160
|
+
function isTruthyEnv(value) {
|
|
161
|
+
return /^(1|true|yes|on)$/i.test(String(value || ""));
|
|
162
|
+
}
|
|
163
|
+
|
|
142
164
|
function ensureGaiaHome() {
|
|
143
165
|
try {
|
|
144
166
|
if (!fs.existsSync(GAIA_HOME)) {
|
|
@@ -697,9 +719,12 @@ function findBundledUvResource() {
|
|
|
697
719
|
*/
|
|
698
720
|
async function installBundledUv(sourcePath, platformKey) {
|
|
699
721
|
const expected = BUNDLED_UV_SHA256[platformKey];
|
|
700
|
-
if (!expected) {
|
|
722
|
+
if (!expected || expected.startsWith("<")) {
|
|
723
|
+
// Enforce strict verification: builds MUST populate the expected SHA
|
|
724
|
+
// for packaged binaries. Failing fast prevents shipping an unverified
|
|
725
|
+
// uv binary which would be a supply-chain regression.
|
|
701
726
|
throw new InstallError(
|
|
702
|
-
`No bundled uv checksum registered for platform ${platformKey}
|
|
727
|
+
`No bundled uv checksum registered for platform ${platformKey}. Build must populate BUNDLED_UV_SHA256.${platformKey}`,
|
|
703
728
|
{ stage: STAGES.ENSURE_UV }
|
|
704
729
|
);
|
|
705
730
|
}
|
|
@@ -741,16 +766,20 @@ async function installBundledUv(sourcePath, platformKey) {
|
|
|
741
766
|
);
|
|
742
767
|
}
|
|
743
768
|
|
|
744
|
-
if (
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
769
|
+
if (expected) {
|
|
770
|
+
if (actual !== expected) {
|
|
771
|
+
try { fs.unlinkSync(tmpPath); } catch { /* ignore */ }
|
|
772
|
+
throw new InstallError(
|
|
773
|
+
`Bundled uv SHA256 mismatch (expected ${expected}, got ${actual}).`,
|
|
774
|
+
{
|
|
775
|
+
stage: STAGES.ENSURE_UV,
|
|
776
|
+
suggestion:
|
|
777
|
+
"The AppImage/installer may be corrupt. Re-download from https://amd-gaia.ai and try again.",
|
|
778
|
+
}
|
|
779
|
+
);
|
|
780
|
+
}
|
|
781
|
+
} else {
|
|
782
|
+
log("No expected SHA registered for bundled uv; installed binary will not be verified locally.");
|
|
754
783
|
}
|
|
755
784
|
|
|
756
785
|
try {
|
|
@@ -836,6 +865,10 @@ async function ensureUv({ onProgress, isPackaged } = {}) {
|
|
|
836
865
|
|
|
837
866
|
// Verify the source resource matches the manifest before copying —
|
|
838
867
|
// catches AppImage corruption before we touch the user's home.
|
|
868
|
+
// Enforce that the packaged build provides an expected SHA for the
|
|
869
|
+
// bundled resource. CI replaces the placeholder with the extracted
|
|
870
|
+
// binary's SHA during the build; missing/placeholder values are a
|
|
871
|
+
// build-time error and are rejected at runtime here.
|
|
839
872
|
const srcHash = await sha256File(bundled);
|
|
840
873
|
if (srcHash !== expectedSha) {
|
|
841
874
|
throw new InstallError(
|
|
@@ -925,6 +958,52 @@ async function ensureUv({ onProgress, isPackaged } = {}) {
|
|
|
925
958
|
return;
|
|
926
959
|
}
|
|
927
960
|
|
|
961
|
+
// Packaged Windows rescue: try the Astral PowerShell installer even when
|
|
962
|
+
// running a packaged build. This provides an automated recovery path for
|
|
963
|
+
// end users on clean machines that don't have uv and where the installer
|
|
964
|
+
// build unexpectedly omitted a bundled binary. It is a last-resort and
|
|
965
|
+
// non-fatal attempt; on failure we fall through to the generic error.
|
|
966
|
+
if (IS_WINDOWS && !isDev) {
|
|
967
|
+
log("Packaged Windows: attempting automated uv installer (rescue)");
|
|
968
|
+
try {
|
|
969
|
+
const rescue = await runCommand(
|
|
970
|
+
"powershell",
|
|
971
|
+
[
|
|
972
|
+
"-ExecutionPolicy",
|
|
973
|
+
"Bypass",
|
|
974
|
+
"-Command",
|
|
975
|
+
"irm https://astral.sh/uv/install.ps1 | iex",
|
|
976
|
+
],
|
|
977
|
+
{ stageLabel: "uv-install-packaged-rescue" }
|
|
978
|
+
);
|
|
979
|
+
if (rescue.code === 0) {
|
|
980
|
+
// Ensure common install locations are present on PATH for this
|
|
981
|
+
// process in case the installer placed uv in a user-local bin.
|
|
982
|
+
const candidates = [
|
|
983
|
+
path.join(os.homedir(), ".local", "bin"),
|
|
984
|
+
path.join(os.homedir(), ".cargo", "bin"),
|
|
985
|
+
];
|
|
986
|
+
for (const uvDir of candidates) {
|
|
987
|
+
if (process.env.PATH && !process.env.PATH.includes(uvDir)) {
|
|
988
|
+
process.env.PATH = `${uvDir}${path.delimiter}${process.env.PATH}`;
|
|
989
|
+
log(`Added ${uvDir} to PATH for this process`);
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
if (commandExists("uv")) {
|
|
993
|
+
log("Packaged Windows: uv installed and found on PATH (rescue succeeded)");
|
|
994
|
+
addManagedBinToPath();
|
|
995
|
+
report(STAGES.ENSURE_UV, 100, "uv ready (system, unverified)");
|
|
996
|
+
return;
|
|
997
|
+
}
|
|
998
|
+
log("Packaged Windows: uv installer ran but uv not found on PATH");
|
|
999
|
+
} else {
|
|
1000
|
+
log(`Packaged Windows: automated uv installer exited ${rescue.code}`);
|
|
1001
|
+
}
|
|
1002
|
+
} catch (rescueErr) {
|
|
1003
|
+
log(`Packaged Windows: rescue installer threw: ${rescueErr.message}`);
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
|
|
928
1007
|
// Packaged build, but we somehow don't have a bundled binary for this
|
|
929
1008
|
// platform AND no system uv. Last-ditch: accept an unverified system uv
|
|
930
1009
|
// if present; otherwise fail with a clear message.
|
|
@@ -937,11 +1016,11 @@ async function ensureUv({ onProgress, isPackaged } = {}) {
|
|
|
937
1016
|
}
|
|
938
1017
|
|
|
939
1018
|
throw new InstallError(
|
|
940
|
-
`
|
|
1019
|
+
`GAIA could not find or install its Python helper (uv) required to provision the backend.`,
|
|
941
1020
|
{
|
|
942
1021
|
stage: STAGES.ENSURE_UV,
|
|
943
1022
|
suggestion:
|
|
944
|
-
"Install uv
|
|
1023
|
+
"GAIA attempted automatic recovery but could not install uv. Please either: (a) click 'Install uv' in the dialog to let GAIA try again, or (b) install uv from https://astral.sh/uv and re-launch GAIA.",
|
|
945
1024
|
}
|
|
946
1025
|
);
|
|
947
1026
|
}
|
|
@@ -995,6 +1074,8 @@ async function installBackend(opts = {}) {
|
|
|
995
1074
|
const pipPackage = localWheel
|
|
996
1075
|
? `${localWheel}[ui]`
|
|
997
1076
|
: `amd-gaia[ui]==${version}`;
|
|
1077
|
+
const skipGaiaInit =
|
|
1078
|
+
Boolean(opts.skipGaiaInit) || isTruthyEnv(process.env.GAIA_SKIP_GAIA_INIT);
|
|
998
1079
|
|
|
999
1080
|
log("================================================");
|
|
1000
1081
|
log(" Installing GAIA backend");
|
|
@@ -1091,7 +1172,7 @@ async function installBackend(opts = {}) {
|
|
|
1091
1172
|
report(STAGES.INSTALL_PACKAGE, 100, "GAIA package installed");
|
|
1092
1173
|
|
|
1093
1174
|
// Stage 4: gaia init
|
|
1094
|
-
if (!
|
|
1175
|
+
if (!skipGaiaInit) {
|
|
1095
1176
|
setState(STATES.INSTALLING, { stage: STAGES.GAIA_INIT, version });
|
|
1096
1177
|
report(
|
|
1097
1178
|
STAGES.GAIA_INIT,
|
|
@@ -1114,7 +1195,7 @@ async function installBackend(opts = {}) {
|
|
|
1114
1195
|
}
|
|
1115
1196
|
report(STAGES.GAIA_INIT, 100, "Lemonade Server setup complete");
|
|
1116
1197
|
} else {
|
|
1117
|
-
log("Skipping gaia init (skipGaiaInit=true)");
|
|
1198
|
+
log("Skipping gaia init (skipGaiaInit=true or GAIA_SKIP_GAIA_INIT set)");
|
|
1118
1199
|
}
|
|
1119
1200
|
|
|
1120
1201
|
// Stage 5: verify
|
|
@@ -1135,6 +1216,46 @@ async function installBackend(opts = {}) {
|
|
|
1135
1216
|
log(`Verified gaia binary: ${verifiedBin} (version=${installedVersion || "unknown"})`);
|
|
1136
1217
|
report(STAGES.VERIFY, 100, "Install verified");
|
|
1137
1218
|
|
|
1219
|
+
// Ensure a user-accessible shim is created so users who installed via
|
|
1220
|
+
// AppImage can run `gaia` from a terminal without manually adding the
|
|
1221
|
+
// venv bin directory to their PATH. Do not overwrite an existing system
|
|
1222
|
+
// `gaia` or an existing shim the user may have created.
|
|
1223
|
+
try {
|
|
1224
|
+
// Only create shims on POSIX-like systems (AppImage target).
|
|
1225
|
+
if (process.platform !== "win32") {
|
|
1226
|
+
const userBin = process.env.XDG_BIN_HOME || path.join(os.homedir(), ".local", "bin");
|
|
1227
|
+
const shimPath = path.join(userBin, "gaia");
|
|
1228
|
+
|
|
1229
|
+
// Only create a shim if there's no `gaia` already on PATH and no shim
|
|
1230
|
+
// at the target location. This avoids clobbering system packages.
|
|
1231
|
+
if (!commandExists("gaia") && !fs.existsSync(shimPath)) {
|
|
1232
|
+
try {
|
|
1233
|
+
// Basic sanity-check on the target path to avoid writing a
|
|
1234
|
+
// wrapper that could execute an arbitrary command. The
|
|
1235
|
+
// verifiedBin is produced by our installer and is expected to be
|
|
1236
|
+
// a normal filesystem path (alphanum, dash, dot, slash, underscore).
|
|
1237
|
+
if (!/^[\w\-./]+$/.test(verifiedBin)) {
|
|
1238
|
+
log(`Refusing to create shim: verified bin path looks suspicious: ${verifiedBin}`);
|
|
1239
|
+
} else {
|
|
1240
|
+
fs.mkdirSync(userBin, { recursive: true });
|
|
1241
|
+
const wrapper = `#!/bin/sh\nexec \"${verifiedBin}\" \"$@\"\n`;
|
|
1242
|
+
fs.writeFileSync(shimPath, wrapper, { mode: 0o755 });
|
|
1243
|
+
log(`Created user shim at ${shimPath} pointing to ${verifiedBin}`);
|
|
1244
|
+
}
|
|
1245
|
+
} catch (err) {
|
|
1246
|
+
log(`Could not create user shim at ${shimPath}: ${err.message}`);
|
|
1247
|
+
}
|
|
1248
|
+
} else if (fs.existsSync(shimPath)) {
|
|
1249
|
+
log(`User shim already exists at ${shimPath}; leaving it intact`);
|
|
1250
|
+
} else {
|
|
1251
|
+
log("A system 'gaia' binary was found on PATH; skipping shim creation");
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
} catch (err) {
|
|
1255
|
+
// Non-fatal: proceed even if shim creation fails.
|
|
1256
|
+
log(`Shim creation check failed: ${err.message}`);
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1138
1259
|
setState(STATES.READY, { stage: null, version, installedVersion });
|
|
1139
1260
|
log("Backend install complete");
|
|
1140
1261
|
}
|