@amd-gaia/agent-ui 0.20.1 → 0.21.1
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/{browser-Bo3ddRrw.js → browser-DFw6ahKM.js} +1 -1
- package/dist/assets/index-DRnhDSoO.css +1 -0
- package/dist/assets/index-Dj-STgr2.js +72 -0
- package/dist/index.html +2 -2
- package/package.json +1 -1
- package/services/backend-installer.cjs +64 -1
- package/services/tray-manager.cjs +24 -10
- package/dist/assets/index-C6QRsg9k.js +0 -70
- package/dist/assets/index-ZwMUX81-.css +0 -1
package/dist/index.html
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<link rel="icon" type="image/png" href="./favicon.png" />
|
|
7
7
|
<title>GAIA</title>
|
|
8
|
-
<script type="module" crossorigin src="./assets/index-
|
|
9
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
8
|
+
<script type="module" crossorigin src="./assets/index-Dj-STgr2.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="./assets/index-DRnhDSoO.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
12
12
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -62,6 +62,11 @@ const NETWORK_CHECK_HOSTS = Object.freeze([
|
|
|
62
62
|
]);
|
|
63
63
|
const NETWORK_CHECK_TIMEOUT_MS = 5000;
|
|
64
64
|
|
|
65
|
+
// Pip-install resilience: the install stage fetches heavy transitive deps from
|
|
66
|
+
// PyPI, so retry a transient network failure a few times before giving up.
|
|
67
|
+
const INSTALL_MAX_ATTEMPTS = 3;
|
|
68
|
+
const INSTALL_RETRY_BACKOFF_MS = 3000;
|
|
69
|
+
|
|
65
70
|
// ── Bundled `uv` binary ──────────────────────────────────────────────────────
|
|
66
71
|
//
|
|
67
72
|
// Issue #782 / T3: the AppImage now ships a pinned `uv` under
|
|
@@ -478,6 +483,37 @@ function isFileLockedError(output) {
|
|
|
478
483
|
return /os error 32/i.test(output) || /being used by another process/i.test(output);
|
|
479
484
|
}
|
|
480
485
|
|
|
486
|
+
/**
|
|
487
|
+
* Detect a transient network failure in `uv pip install` output so the
|
|
488
|
+
* install stage can retry instead of failing the whole bootstrap. The
|
|
489
|
+
* install stage downloads heavy transitive deps (scipy, numpy, torch) from
|
|
490
|
+
* PyPI; a single mid-stream hiccup ("stream closed because of a broken pipe")
|
|
491
|
+
* otherwise fails the entire backend install — and, in the release pipeline,
|
|
492
|
+
* the whole AppImage smoke test that gates publishing.
|
|
493
|
+
*
|
|
494
|
+
* Matches only network-shaped failures — NOT dependency-resolution errors
|
|
495
|
+
* ("No solution found") or disk-full, which retrying cannot fix.
|
|
496
|
+
*/
|
|
497
|
+
function isTransientNetworkError(output) {
|
|
498
|
+
if (!output) return false;
|
|
499
|
+
return (
|
|
500
|
+
/broken pipe/i.test(output) ||
|
|
501
|
+
/stream closed/i.test(output) ||
|
|
502
|
+
/failed to fetch/i.test(output) ||
|
|
503
|
+
/error sending request/i.test(output) ||
|
|
504
|
+
/connection (?:error|reset|closed|refused|aborted)/i.test(output) ||
|
|
505
|
+
/could not connect/i.test(output) ||
|
|
506
|
+
/(?:request|operation|connection)?\s*tim(?:ed\s*out|eout)/i.test(output) ||
|
|
507
|
+
/temporary failure in name resolution/i.test(output) ||
|
|
508
|
+
/(?:could not resolve|failed to lookup|dns error)/i.test(output) ||
|
|
509
|
+
/network is unreachable/i.test(output)
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
function sleep(ms) {
|
|
514
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
515
|
+
}
|
|
516
|
+
|
|
481
517
|
// ── Pre-checks ───────────────────────────────────────────────────────────────
|
|
482
518
|
|
|
483
519
|
/**
|
|
@@ -1156,7 +1192,33 @@ async function installBackend(opts = {}) {
|
|
|
1156
1192
|
pipArgs.push("--extra-index-url", "https://download.pytorch.org/whl/cpu");
|
|
1157
1193
|
}
|
|
1158
1194
|
|
|
1159
|
-
|
|
1195
|
+
// Retry the install on transient PyPI/network failures. The heavy
|
|
1196
|
+
// transitive deps (scipy, numpy, torch) are fetched live from PyPI even
|
|
1197
|
+
// when the gaia wheel itself is local, so a single broken-pipe mid-download
|
|
1198
|
+
// would otherwise fail the whole bootstrap (and block a release). File-lock
|
|
1199
|
+
// failures (Windows os-error-32) are NOT retried — they need user action.
|
|
1200
|
+
let installResult;
|
|
1201
|
+
for (let attempt = 1; attempt <= INSTALL_MAX_ATTEMPTS; attempt++) {
|
|
1202
|
+
installResult = await runCommand("uv", pipArgs, { stageLabel: "pip" });
|
|
1203
|
+
if (installResult.code === 0) break;
|
|
1204
|
+
const attemptOutput = `${installResult.stdout || ""}\n${installResult.stderr || ""}`;
|
|
1205
|
+
const canRetry =
|
|
1206
|
+
attempt < INSTALL_MAX_ATTEMPTS &&
|
|
1207
|
+
isTransientNetworkError(attemptOutput) &&
|
|
1208
|
+
!isFileLockedError(attemptOutput);
|
|
1209
|
+
if (!canRetry) break;
|
|
1210
|
+
const backoffMs = INSTALL_RETRY_BACKOFF_MS * attempt;
|
|
1211
|
+
log(
|
|
1212
|
+
`Transient network error during install (attempt ${attempt}/${INSTALL_MAX_ATTEMPTS}). ` +
|
|
1213
|
+
`Retrying in ${Math.round(backoffMs / 1000)}s…`
|
|
1214
|
+
);
|
|
1215
|
+
report(
|
|
1216
|
+
STAGES.INSTALL_PACKAGE,
|
|
1217
|
+
0,
|
|
1218
|
+
`Network hiccup — retrying install (attempt ${attempt + 1}/${INSTALL_MAX_ATTEMPTS})`
|
|
1219
|
+
);
|
|
1220
|
+
await sleep(backoffMs);
|
|
1221
|
+
}
|
|
1160
1222
|
if (installResult.code !== 0) {
|
|
1161
1223
|
// Windows: an upgrade can't replace gaia.exe while a previous GAIA
|
|
1162
1224
|
// process still holds it open (issue #1388). The orphan cleanup in
|
|
@@ -1458,6 +1520,7 @@ module.exports = {
|
|
|
1458
1520
|
checkDiskSpace,
|
|
1459
1521
|
checkNetwork,
|
|
1460
1522
|
isFileLockedError,
|
|
1523
|
+
isTransientNetworkError,
|
|
1461
1524
|
|
|
1462
1525
|
// State machine
|
|
1463
1526
|
getState,
|
|
@@ -57,8 +57,7 @@ class TrayManager {
|
|
|
57
57
|
this.config = this._loadConfig();
|
|
58
58
|
|
|
59
59
|
/** @type {Electron.NativeImage} */
|
|
60
|
-
|
|
61
|
-
this._icon = this._loadIcon(trayIconFile);
|
|
60
|
+
this._icon = this._loadTrayIcon();
|
|
62
61
|
|
|
63
62
|
this._registerIpcHandlers();
|
|
64
63
|
}
|
|
@@ -153,18 +152,33 @@ class TrayManager {
|
|
|
153
152
|
|
|
154
153
|
// ── Private: Icon loading ────────────────────────────────────────────
|
|
155
154
|
|
|
155
|
+
/**
|
|
156
|
+
* Pick the platform-appropriate tray icon. The menu bar is ~22pt tall, so
|
|
157
|
+
* each platform gets a purpose-built small asset — never the 4K app icon.
|
|
158
|
+
* @returns {Electron.NativeImage}
|
|
159
|
+
*/
|
|
160
|
+
_loadTrayIcon() {
|
|
161
|
+
if (process.platform === "win32") {
|
|
162
|
+
return this._loadIcon("tray-icon.ico");
|
|
163
|
+
}
|
|
164
|
+
if (process.platform === "darwin") {
|
|
165
|
+
// macOS menu-bar extras must be template images: the OS tints the
|
|
166
|
+
// alpha mask for light/dark mode. Electron auto-discovers the @2x
|
|
167
|
+
// retina variant by name.
|
|
168
|
+
const img = this._loadIcon("tray-iconTemplate.png");
|
|
169
|
+
img.setTemplateImage(true);
|
|
170
|
+
return img;
|
|
171
|
+
}
|
|
172
|
+
return this._loadIcon("tray-icon.png"); // Linux: full-colour PNG
|
|
173
|
+
}
|
|
174
|
+
|
|
156
175
|
_loadIcon(filename) {
|
|
157
176
|
// __dirname is services/, assets/ is one level up alongside main.cjs
|
|
158
177
|
const iconPath = path.join(__dirname, "..", "assets", filename);
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
return nativeImage.createFromPath(iconPath);
|
|
162
|
-
}
|
|
163
|
-
} catch (err) {
|
|
164
|
-
console.warn(`[tray] Could not load icon ${filename}:`, err.message);
|
|
178
|
+
if (!fs.existsSync(iconPath)) {
|
|
179
|
+
throw new Error(`[tray] icon asset missing: ${iconPath}`);
|
|
165
180
|
}
|
|
166
|
-
|
|
167
|
-
return nativeImage.createEmpty();
|
|
181
|
+
return nativeImage.createFromPath(iconPath);
|
|
168
182
|
}
|
|
169
183
|
|
|
170
184
|
// ── Private: Config persistence ──────────────────────────────────────
|