dshmarket 1.73.4 → 1.73.5
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/client/client.js +463 -29
- package/lib/agents.js +31 -0
- package/lib/drain-journal.js +133 -0
- package/lib/drain.js +917 -0
- package/lib/dsh-install.js +54 -1
- package/lib/index.js +32 -0
- package/lib/log.js +6 -0
- package/lib/probe-context.js +20 -0
- package/lib/routes.js +629 -23
- package/lib/types/agents.d.ts +30 -0
- package/lib/types/drain-journal.d.ts +25 -0
- package/lib/types/drain.d.ts +279 -0
- package/lib/types/dsh-install.d.ts +32 -4
- package/lib/types/index.d.ts +19 -0
- package/lib/types/probe-context.d.ts +14 -0
- package/lib/types/routes.d.ts +1 -7
- package/lib/types/update-api-v1.d.ts +17 -0
- package/lib/update-api-v1.js +35 -1
- package/lib/updates.js +6 -0
- package/package.json +1 -1
package/client/client.js
CHANGED
|
@@ -384,6 +384,16 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
384
384
|
agentQueueStaleGone: "排队时它还在已安装列表里,现在已不在,因此跳过(没有卸载)。",
|
|
385
385
|
agentQueueStaleNoUpdate: "排队时它有可用更新,现在没有了,已跳过。",
|
|
386
386
|
queuedBadge: "排队中",
|
|
387
|
+
agentDrainBadge: "等待 Agent",
|
|
388
|
+
agentDrainWaiting: "等待 {0} 个 Agent 到达安全点…",
|
|
389
|
+
agentDrainShowAgents: "查看正在运行的会话",
|
|
390
|
+
agentDrainVerifying: "正在验证结果…",
|
|
391
|
+
agentDrainCancelled: "已取消。",
|
|
392
|
+
agentDrainRestarted: "宿主在这次操作完成前重启了,它已取消。需要的话请重新发起。",
|
|
393
|
+
agentDrainHistoryDone: "上次排队的操作已完成。",
|
|
394
|
+
agentDrainHistoryFailed: "上次排队的操作失败了({0})。请检查「已安装」或导出的日志。",
|
|
395
|
+
agentDrainHistoryFailedShort: "上次排队的操作失败了。请检查「已安装」或导出的日志。",
|
|
396
|
+
agentDrainHistoryCancelled: "上次排队的操作已取消。",
|
|
387
397
|
favoriteFailed: "收藏失败,请稍后重试",
|
|
388
398
|
favoriteUnavailable: "收藏功能暂不可用。请刷新页面或重启 dsh web。",
|
|
389
399
|
favoriteBusy: "插件操作未结束,收藏未保存,请稍后重试",
|
|
@@ -1158,6 +1168,16 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
1158
1168
|
agentQueueStaleGone: "It was installed when you queued this, and is not now — skipped, nothing was uninstalled.",
|
|
1159
1169
|
agentQueueStaleNoUpdate: "It had an update available when you queued this, and does not now — skipped.",
|
|
1160
1170
|
queuedBadge: "Queued",
|
|
1171
|
+
agentDrainBadge: "Waiting for Agents",
|
|
1172
|
+
agentDrainWaiting: "Waiting for {0} Agent(s) to reach a safe point…",
|
|
1173
|
+
agentDrainShowAgents: "Show running sessions",
|
|
1174
|
+
agentDrainVerifying: "Verifying the result…",
|
|
1175
|
+
agentDrainCancelled: "Cancelled.",
|
|
1176
|
+
agentDrainRestarted: "The host restarted before this finished, so the task was cancelled. Start it again if you still need it.",
|
|
1177
|
+
agentDrainHistoryDone: "The last deferred operation completed.",
|
|
1178
|
+
agentDrainHistoryFailed: "The last deferred operation failed ({0}). Check Installed or the exported log.",
|
|
1179
|
+
agentDrainHistoryFailedShort: "The last deferred operation failed. Check Installed or the exported log.",
|
|
1180
|
+
agentDrainHistoryCancelled: "The last deferred operation was cancelled.",
|
|
1161
1181
|
favoriteFailed: "Could not update favorites. Try again.",
|
|
1162
1182
|
favoriteUnavailable: "Favorites are unavailable. Refresh the page or restart dsh web.",
|
|
1163
1183
|
favoriteBusy: "A plugin operation is still running. Favorites were not saved — try again shortly.",
|
|
@@ -1932,6 +1952,16 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
1932
1952
|
agentQueueStaleGone: "キューに入れた時点ではインストール済みでしたが、現在は存在しないためスキップしました(アンインストールはしていません)。",
|
|
1933
1953
|
agentQueueStaleNoUpdate: "キューに入れた時点では利用できる更新がありましたが、現在はないためスキップしました。",
|
|
1934
1954
|
queuedBadge: "キュー待ち",
|
|
1955
|
+
agentDrainBadge: "Agent 待ち",
|
|
1956
|
+
agentDrainWaiting: "{0} 件の Agent が安全な地点に到達するのを待っています…",
|
|
1957
|
+
agentDrainShowAgents: "実行中のセッションを表示",
|
|
1958
|
+
agentDrainVerifying: "結果を確認しています…",
|
|
1959
|
+
agentDrainCancelled: "キャンセルしました。",
|
|
1960
|
+
agentDrainRestarted: "この操作が終わる前にホストが再起動したため、キャンセルされました。必要ならもう一度実行してください。",
|
|
1961
|
+
agentDrainHistoryDone: "前回の保留操作は完了しました。",
|
|
1962
|
+
agentDrainHistoryFailed: "前回の保留操作は失敗しました({0})。「インストール済み」またはエクスポートしたログを確認してください。",
|
|
1963
|
+
agentDrainHistoryFailedShort: "前回の保留操作は失敗しました。「インストール済み」またはエクスポートしたログを確認してください。",
|
|
1964
|
+
agentDrainHistoryCancelled: "前回の保留操作はキャンセルされました。",
|
|
1935
1965
|
favoriteFailed: "お気に入りを更新できませんでした。しばらくしてから再試行してください",
|
|
1936
1966
|
favoriteUnavailable: "お気に入り機能は現在使えません。ページを更新するか、dsh web を再起動してください。",
|
|
1937
1967
|
favoriteBusy: "プラグインの操作が終わっていないため、お気に入りは保存されませんでした。しばらくしてから再試行してください",
|
|
@@ -5006,7 +5036,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
5006
5036
|
* that wrote it can still be waiting for that reload.
|
|
5007
5037
|
*/
|
|
5008
5038
|
function serializeSettled(list) {
|
|
5009
|
-
return list.filter(isSettled).slice(-20).map((record) => ({
|
|
5039
|
+
return list.filter(isSettled).filter((record) => record.hostOperationId === void 0).slice(-20).map((record) => ({
|
|
5010
5040
|
id: record.id,
|
|
5011
5041
|
kind: record.kind,
|
|
5012
5042
|
name: record.name,
|
|
@@ -5056,6 +5086,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
5056
5086
|
function restoreSettledRecord(value) {
|
|
5057
5087
|
if (value === null || typeof value !== "object" || Array.isArray(value)) return null;
|
|
5058
5088
|
const row = value;
|
|
5089
|
+
if (asText(row.hostOperationId) !== void 0) return null;
|
|
5059
5090
|
const id = asText(row.id);
|
|
5060
5091
|
const name = asText(row.name);
|
|
5061
5092
|
const kind = row.kind === "install" || row.kind === "update" || row.kind === "uninstall" ? row.kind : void 0;
|
|
@@ -5284,6 +5315,11 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
5284
5315
|
switch (record.state) {
|
|
5285
5316
|
case "queued": {
|
|
5286
5317
|
const blockers = record.blockedBy?.length ?? 0;
|
|
5318
|
+
if (record.hostOperationId !== void 0) {
|
|
5319
|
+
const elapsed = record.waitSince === void 0 ? null : Math.max(0, Math.floor((Date.now() - record.waitSince) / 1e3));
|
|
5320
|
+
const base = blockers > 0 ? t("agentDrainWaiting").replace("{0}", String(blockers)) : t("agentBusyQueued");
|
|
5321
|
+
return elapsed === null ? base : `${base} (${String(elapsed)}s)`;
|
|
5322
|
+
}
|
|
5287
5323
|
if (blockers > 0) return `${t("opQueued")} · ${t("opQueuedWaitingAgents").replace("{0}", String(blockers))}`;
|
|
5288
5324
|
return ahead === null || ahead === 0 ? t("opQueued") : `${t("opQueued")} · ${t("opQueuedAhead")} ${String(ahead)}`;
|
|
5289
5325
|
}
|
|
@@ -5300,6 +5336,13 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
5300
5336
|
const wrapRef = (0, react.useRef)(null);
|
|
5301
5337
|
const summary = summarize(records);
|
|
5302
5338
|
const busy = summary.running + summary.queued > 0;
|
|
5339
|
+
const waitingOnHost = records.some((record) => record.hostOperationId !== void 0 && record.state === "queued" && record.waitSince !== void 0);
|
|
5340
|
+
const [, setClock] = (0, react.useState)(0);
|
|
5341
|
+
(0, react.useEffect)(() => {
|
|
5342
|
+
if (!waitingOnHost) return void 0;
|
|
5343
|
+
const timer = setInterval(() => setClock((value) => value + 1), 1e3);
|
|
5344
|
+
return () => clearInterval(timer);
|
|
5345
|
+
}, [waitingOnHost]);
|
|
5303
5346
|
(0, react.useEffect)(() => {
|
|
5304
5347
|
if (!open) return void 0;
|
|
5305
5348
|
const onKey = (event) => {
|
|
@@ -5429,6 +5472,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
5429
5472
|
className: bucketOf(record.state) === "attention" ? `${Market_module_css_default.opStatus} ${Market_module_css_default.opStatusBad}` : Market_module_css_default.opStatus,
|
|
5430
5473
|
children: statusLine(t, props.lang, record, ahead)
|
|
5431
5474
|
}),
|
|
5475
|
+
record.hostOperationId !== void 0 && record.state === "queued" && (record.blockedBy?.length ?? 0) > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", { children: t("agentDrainShowAgents") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", { children: record.blockedBy.map((id) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: id }, id)) })] }),
|
|
5432
5476
|
needsUser(record) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConflictChoice, {
|
|
5433
5477
|
t,
|
|
5434
5478
|
record,
|
|
@@ -5448,7 +5492,12 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
5448
5492
|
onClick: () => props.onCancel(record),
|
|
5449
5493
|
children: t("cancelOp")
|
|
5450
5494
|
}),
|
|
5451
|
-
record.state === "queued" &&
|
|
5495
|
+
record.state === "queued" && (record.hostOperationId !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
5496
|
+
variant: "outline",
|
|
5497
|
+
size: "sm",
|
|
5498
|
+
onClick: () => props.onCancel(record),
|
|
5499
|
+
children: t("cancelOp")
|
|
5500
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [props.onRunNow !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
5452
5501
|
variant: "primary",
|
|
5453
5502
|
size: "sm",
|
|
5454
5503
|
onClick: () => props.onRunNow?.(record),
|
|
@@ -5458,7 +5507,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
5458
5507
|
size: "sm",
|
|
5459
5508
|
onClick: () => props.onDismiss(record),
|
|
5460
5509
|
children: t("opDequeue")
|
|
5461
|
-
})] }),
|
|
5510
|
+
})] })),
|
|
5462
5511
|
record.state === "done" && record.needsRefresh === true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
5463
5512
|
variant: "primary",
|
|
5464
5513
|
size: "sm",
|
|
@@ -9597,6 +9646,17 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
9597
9646
|
return names.length === 0 ? void 0 : names;
|
|
9598
9647
|
}
|
|
9599
9648
|
/**
|
|
9649
|
+
* Drop a Host-projected row before the page's own record adopts the same
|
|
9650
|
+
* operation id (#411).
|
|
9651
|
+
*
|
|
9652
|
+
* The status poll can create `host-<id>` while a slow probe is still running;
|
|
9653
|
+
* without this the 202 would leave both rows pointing at one Host operation,
|
|
9654
|
+
* and the panel's first match would keep the synthetic one forever.
|
|
9655
|
+
*/
|
|
9656
|
+
function withoutHostProjection(list, operationId, keepId) {
|
|
9657
|
+
return list.filter((record) => record.id === keepId || record.id !== `host-${operationId}` && record.hostOperationId !== operationId);
|
|
9658
|
+
}
|
|
9659
|
+
/**
|
|
9600
9660
|
* Whether the "compatible with this host" filter starts on, and remembers the
|
|
9601
9661
|
* answer once the user disagrees with the default (#872).
|
|
9602
9662
|
*
|
|
@@ -9913,7 +9973,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
9913
9973
|
(0, react.useEffect)(() => {
|
|
9914
9974
|
if (!queueRestoredRef.current) return;
|
|
9915
9975
|
try {
|
|
9916
|
-
const queued = records.filter((record) => record.state === "queued").map((record) => ({
|
|
9976
|
+
const queued = records.filter((record) => record.state === "queued" && record.hostOperationId === void 0).map((record) => ({
|
|
9917
9977
|
kind: record.kind,
|
|
9918
9978
|
name: record.name,
|
|
9919
9979
|
...record.url === void 0 ? {} : { url: record.url },
|
|
@@ -10438,7 +10498,9 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
10438
10498
|
*/
|
|
10439
10499
|
const statusRef = (0, react.useRef)({
|
|
10440
10500
|
busy: false,
|
|
10441
|
-
runningAgents: []
|
|
10501
|
+
runningAgents: [],
|
|
10502
|
+
agentDrain: false,
|
|
10503
|
+
boot: null
|
|
10442
10504
|
});
|
|
10443
10505
|
/** True while the drain is executing one queued request (no parallel starts). */
|
|
10444
10506
|
const drainingRef = (0, react.useRef)(false);
|
|
@@ -10493,6 +10555,221 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
10493
10555
|
setSkins,
|
|
10494
10556
|
setUpdates
|
|
10495
10557
|
]);
|
|
10558
|
+
/**
|
|
10559
|
+
* Host-owned deferred operations (#411).
|
|
10560
|
+
*
|
|
10561
|
+
* These rows are projections of `/status`, never entries in the client
|
|
10562
|
+
* queue: the Host owns the maintenance holds, and re-running a row here
|
|
10563
|
+
* would start a second mutation outside that barrier. `dshm-drain-v1`
|
|
10564
|
+
* remembers only which operations this page asked for, so a Host restart can
|
|
10565
|
+
* say "that one was cancelled" instead of leaving the user to guess.
|
|
10566
|
+
*/
|
|
10567
|
+
const settledHostRef = (0, react.useRef)(/* @__PURE__ */ new Set());
|
|
10568
|
+
const knownHostIdsRef = (0, react.useRef)(/* @__PURE__ */ new Set());
|
|
10569
|
+
/** Host operations the user cleared; a status poll must not add them back. */
|
|
10570
|
+
const dismissedHostRef = (0, react.useRef)(/* @__PURE__ */ new Set());
|
|
10571
|
+
const drainMarkerRestoredRef = (0, react.useRef)(false);
|
|
10572
|
+
const clearDrainMarker = (0, react.useCallback)((operationId) => {
|
|
10573
|
+
try {
|
|
10574
|
+
const raw = localStorage.getItem("dshm-drain-v1");
|
|
10575
|
+
if (raw === null) return;
|
|
10576
|
+
const parsed = JSON.parse(raw);
|
|
10577
|
+
if (!Array.isArray(parsed)) return;
|
|
10578
|
+
const kept = parsed.filter((entry) => entry?.id !== operationId);
|
|
10579
|
+
if (kept.length === 0) localStorage.removeItem("dshm-drain-v1");
|
|
10580
|
+
else localStorage.setItem("dshm-drain-v1", JSON.stringify(kept));
|
|
10581
|
+
} catch {}
|
|
10582
|
+
}, []);
|
|
10583
|
+
const rememberDrainMarker = (0, react.useCallback)((operation) => {
|
|
10584
|
+
try {
|
|
10585
|
+
const raw = localStorage.getItem("dshm-drain-v1");
|
|
10586
|
+
const parsed = raw === null ? [] : JSON.parse(raw);
|
|
10587
|
+
const list = Array.isArray(parsed) ? parsed : [];
|
|
10588
|
+
if (list.some((entry) => entry?.id === operation.operationId)) return;
|
|
10589
|
+
list.push({
|
|
10590
|
+
id: operation.operationId,
|
|
10591
|
+
kind: operation.kind,
|
|
10592
|
+
name: operation.name,
|
|
10593
|
+
...operation.url === void 0 ? {} : { url: operation.url }
|
|
10594
|
+
});
|
|
10595
|
+
localStorage.setItem("dshm-drain-v1", JSON.stringify(list));
|
|
10596
|
+
} catch {}
|
|
10597
|
+
}, []);
|
|
10598
|
+
/** Mark operations this page started in an earlier boot as cancelled by restart. */
|
|
10599
|
+
const restoreDrainMarkers = (0, react.useCallback)((status) => {
|
|
10600
|
+
if (drainMarkerRestoredRef.current) return;
|
|
10601
|
+
const boot = typeof status.boot === "string" ? status.boot : null;
|
|
10602
|
+
if (boot === null) return;
|
|
10603
|
+
drainMarkerRestoredRef.current = true;
|
|
10604
|
+
let saved = null;
|
|
10605
|
+
try {
|
|
10606
|
+
saved = JSON.parse(localStorage.getItem("dshm-drain-v1") ?? "null");
|
|
10607
|
+
} catch {
|
|
10608
|
+
saved = null;
|
|
10609
|
+
}
|
|
10610
|
+
if (!Array.isArray(saved) || saved.length === 0) return;
|
|
10611
|
+
const entries = saved.filter((entry) => {
|
|
10612
|
+
if (entry === null || typeof entry !== "object") return false;
|
|
10613
|
+
return typeof entry.id === "string";
|
|
10614
|
+
});
|
|
10615
|
+
const stale = entries.filter((entry) => !entry.id.startsWith(`${boot}-`));
|
|
10616
|
+
const current = entries.filter((entry) => entry.id.startsWith(`${boot}-`));
|
|
10617
|
+
if (stale.length > 0) {
|
|
10618
|
+
setRecords((previous) => {
|
|
10619
|
+
let next = previous;
|
|
10620
|
+
for (const entry of stale) {
|
|
10621
|
+
if (dismissedHostRef.current.has(entry.id)) continue;
|
|
10622
|
+
if (next.some((record) => record.hostOperationId === entry.id || record.id === `host-${entry.id}`)) continue;
|
|
10623
|
+
const kind = entry.kind === "install" || entry.kind === "update" || entry.kind === "uninstall" ? entry.kind : "update";
|
|
10624
|
+
next = enqueue(next, {
|
|
10625
|
+
id: `host-${entry.id}`,
|
|
10626
|
+
kind,
|
|
10627
|
+
name: typeof entry.name === "string" ? entry.name : "plugin",
|
|
10628
|
+
...typeof entry.url === "string" ? { url: entry.url } : {},
|
|
10629
|
+
hostOperationId: entry.id,
|
|
10630
|
+
state: "warned",
|
|
10631
|
+
reason: t("agentDrainRestarted")
|
|
10632
|
+
});
|
|
10633
|
+
}
|
|
10634
|
+
return next;
|
|
10635
|
+
});
|
|
10636
|
+
setOperationsOpen(true);
|
|
10637
|
+
}
|
|
10638
|
+
try {
|
|
10639
|
+
if (current.length > 0) localStorage.setItem("dshm-drain-v1", JSON.stringify(current));
|
|
10640
|
+
else localStorage.removeItem("dshm-drain-v1");
|
|
10641
|
+
} catch {}
|
|
10642
|
+
}, [t]);
|
|
10643
|
+
/** Project Host-owned operations into the Tasks panel. */
|
|
10644
|
+
const applyDrainStatus = (0, react.useCallback)((status) => {
|
|
10645
|
+
const operations = Array.isArray(status.drainOperations) ? status.drainOperations : [];
|
|
10646
|
+
const history = Array.isArray(status.drainHistory) ? status.drainHistory : [];
|
|
10647
|
+
if (operations.length === 0 && history.length === 0) return;
|
|
10648
|
+
let opened = false;
|
|
10649
|
+
for (const operation of operations) if (operation.state === "accepted" || operation.state === "draining" || operation.state === "queued-for-mutation" || operation.state === "running" || operation.state === "verifying") {
|
|
10650
|
+
if (!knownHostIdsRef.current.has(operation.operationId)) {
|
|
10651
|
+
knownHostIdsRef.current.add(operation.operationId);
|
|
10652
|
+
opened = true;
|
|
10653
|
+
}
|
|
10654
|
+
}
|
|
10655
|
+
for (const operation of operations) {
|
|
10656
|
+
if (dismissedHostRef.current.has(operation.operationId)) continue;
|
|
10657
|
+
if (operation.state !== "succeeded" && operation.state !== "failed" && operation.state !== "cancelled") continue;
|
|
10658
|
+
clearDrainMarker(operation.operationId);
|
|
10659
|
+
if (settledHostRef.current.has(operation.operationId)) continue;
|
|
10660
|
+
settledHostRef.current.add(operation.operationId);
|
|
10661
|
+
if (operation.state === "succeeded") {
|
|
10662
|
+
refreshInstalled();
|
|
10663
|
+
if (operation.outcome?.restartRequired === true) setRestartNames((names) => names.includes(operation.name) ? names : names.concat(operation.name));
|
|
10664
|
+
if (operation.kind === "update") setUpdatedNames((names) => names.includes(operation.name) ? names : names.concat(operation.name));
|
|
10665
|
+
if (operation.kind === "install" && operation.url !== void 0) setDoneUrls((urls) => urls.includes(operation.url) ? urls : urls.concat(operation.url));
|
|
10666
|
+
} else refreshInstalled();
|
|
10667
|
+
}
|
|
10668
|
+
const hostRecord = (operation) => {
|
|
10669
|
+
const base = {
|
|
10670
|
+
id: `host-${operation.operationId}`,
|
|
10671
|
+
kind: operation.kind,
|
|
10672
|
+
name: operation.name,
|
|
10673
|
+
...operation.url === void 0 ? {} : { url: operation.url },
|
|
10674
|
+
hostOperationId: operation.operationId
|
|
10675
|
+
};
|
|
10676
|
+
if (operation.state === "accepted" || operation.state === "draining" || operation.state === "queued-for-mutation") return {
|
|
10677
|
+
...base,
|
|
10678
|
+
state: "queued",
|
|
10679
|
+
...operation.wait === null ? {} : {
|
|
10680
|
+
blockedBy: operation.wait.activeAgentIds,
|
|
10681
|
+
waitSince: operation.wait.since
|
|
10682
|
+
},
|
|
10683
|
+
reason: operation.wait === null ? t("agentBusyQueued") : t("agentDrainWaiting").replace("{0}", String(operation.wait.activeCount))
|
|
10684
|
+
};
|
|
10685
|
+
if (operation.state === "running") return {
|
|
10686
|
+
...base,
|
|
10687
|
+
state: "running",
|
|
10688
|
+
blockedBy: void 0,
|
|
10689
|
+
waitSince: void 0,
|
|
10690
|
+
reason: void 0
|
|
10691
|
+
};
|
|
10692
|
+
if (operation.state === "verifying") return {
|
|
10693
|
+
...base,
|
|
10694
|
+
state: "running",
|
|
10695
|
+
detail: t("agentDrainVerifying"),
|
|
10696
|
+
blockedBy: void 0,
|
|
10697
|
+
waitSince: void 0,
|
|
10698
|
+
reason: void 0
|
|
10699
|
+
};
|
|
10700
|
+
if (operation.state === "succeeded") return {
|
|
10701
|
+
...base,
|
|
10702
|
+
state: "done",
|
|
10703
|
+
needsRefresh: operation.outcome?.needsRefresh === true,
|
|
10704
|
+
blockedBy: void 0,
|
|
10705
|
+
waitSince: void 0,
|
|
10706
|
+
reason: void 0
|
|
10707
|
+
};
|
|
10708
|
+
if (operation.state === "cancelled") return {
|
|
10709
|
+
...base,
|
|
10710
|
+
state: "warned",
|
|
10711
|
+
reason: t("agentDrainCancelled"),
|
|
10712
|
+
blockedBy: void 0,
|
|
10713
|
+
waitSince: void 0
|
|
10714
|
+
};
|
|
10715
|
+
return {
|
|
10716
|
+
...base,
|
|
10717
|
+
state: "failed",
|
|
10718
|
+
reason: operation.failure?.message ?? t("installFail"),
|
|
10719
|
+
blockedBy: void 0,
|
|
10720
|
+
waitSince: void 0
|
|
10721
|
+
};
|
|
10722
|
+
};
|
|
10723
|
+
setRecords((previous) => {
|
|
10724
|
+
let next = previous;
|
|
10725
|
+
for (const operation of operations) {
|
|
10726
|
+
if (dismissedHostRef.current.has(operation.operationId)) continue;
|
|
10727
|
+
const existing = next.find((record) => record.hostOperationId === operation.operationId);
|
|
10728
|
+
const desired = hostRecord(operation);
|
|
10729
|
+
if (existing === void 0) next = enqueue(next, desired);
|
|
10730
|
+
else {
|
|
10731
|
+
const { id: _id, kind: _kind, name: _name, hostOperationId: _host, ...changes } = desired;
|
|
10732
|
+
next = patch(next, existing.id, changes);
|
|
10733
|
+
}
|
|
10734
|
+
}
|
|
10735
|
+
for (const entry of history) {
|
|
10736
|
+
if (dismissedHostRef.current.has(entry.id)) continue;
|
|
10737
|
+
const id = `host-${entry.id}`;
|
|
10738
|
+
const existing = next.find((record) => record.id === id || record.hostOperationId === entry.id);
|
|
10739
|
+
const changes = entry.state === "succeeded" ? existing?.needsRefresh === true ? {
|
|
10740
|
+
state: "done",
|
|
10741
|
+
blockedBy: void 0,
|
|
10742
|
+
waitSince: void 0,
|
|
10743
|
+
reason: void 0
|
|
10744
|
+
} : {
|
|
10745
|
+
state: "done",
|
|
10746
|
+
needsRefresh: false,
|
|
10747
|
+
blockedBy: void 0,
|
|
10748
|
+
waitSince: void 0,
|
|
10749
|
+
reason: void 0
|
|
10750
|
+
} : {
|
|
10751
|
+
state: entry.state === "cancelled" ? "warned" : "failed",
|
|
10752
|
+
reason: entry.state === "cancelled" ? t("agentDrainHistoryCancelled") : t("agentDrainHistoryFailedShort"),
|
|
10753
|
+
blockedBy: void 0,
|
|
10754
|
+
waitSince: void 0
|
|
10755
|
+
};
|
|
10756
|
+
if (existing !== void 0) next = patch(next, existing.id, changes);
|
|
10757
|
+
else next = enqueue(next, {
|
|
10758
|
+
id,
|
|
10759
|
+
kind: entry.kind,
|
|
10760
|
+
name: entry.name,
|
|
10761
|
+
hostOperationId: entry.id,
|
|
10762
|
+
...changes
|
|
10763
|
+
});
|
|
10764
|
+
}
|
|
10765
|
+
return next;
|
|
10766
|
+
});
|
|
10767
|
+
if (opened) setOperationsOpen(true);
|
|
10768
|
+
}, [
|
|
10769
|
+
clearDrainMarker,
|
|
10770
|
+
refreshInstalled,
|
|
10771
|
+
t
|
|
10772
|
+
]);
|
|
10496
10773
|
/** Active Bundles count as installed in Discover without becoming package-manager targets. */
|
|
10497
10774
|
const catalogInstalled = (0, react.useMemo)(() => installedForCatalog(installed, installedBundles), [installed, installedBundles]);
|
|
10498
10775
|
(0, react.useMemo)(() => new Set(disabledNames), [disabledNames]);
|
|
@@ -10638,8 +10915,12 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
10638
10915
|
if (status === null) return;
|
|
10639
10916
|
statusRef.current = {
|
|
10640
10917
|
busy: status.busy === true,
|
|
10641
|
-
runningAgents: Array.isArray(status.runningAgents) ? status.runningAgents.map(String) : []
|
|
10918
|
+
runningAgents: Array.isArray(status.runningAgents) ? status.runningAgents.map(String) : [],
|
|
10919
|
+
agentDrain: status.agentDrain === true,
|
|
10920
|
+
boot: typeof status.boot === "string" ? status.boot : null
|
|
10642
10921
|
};
|
|
10922
|
+
applyDrainStatus(status);
|
|
10923
|
+
restoreDrainMarkers(status);
|
|
10643
10924
|
setEnvReady(status.pnpm !== false);
|
|
10644
10925
|
applyGithubRouting(status);
|
|
10645
10926
|
if (typeof status.boot === "string") {
|
|
@@ -10657,9 +10938,11 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
10657
10938
|
}).catch(() => {});
|
|
10658
10939
|
refreshInstalled();
|
|
10659
10940
|
}, [
|
|
10941
|
+
applyDrainStatus,
|
|
10660
10942
|
refreshInstalled,
|
|
10661
10943
|
loadCatalog,
|
|
10662
|
-
readMarketStatus
|
|
10944
|
+
readMarketStatus,
|
|
10945
|
+
restoreDrainMarkers
|
|
10663
10946
|
]);
|
|
10664
10947
|
(0, react.useEffect)(() => {
|
|
10665
10948
|
if (bootId === null) return;
|
|
@@ -10768,8 +11051,12 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
10768
11051
|
if (status === null) return;
|
|
10769
11052
|
statusRef.current = {
|
|
10770
11053
|
busy: status.busy === true,
|
|
10771
|
-
runningAgents: Array.isArray(status.runningAgents) ? status.runningAgents.map(String) : []
|
|
11054
|
+
runningAgents: Array.isArray(status.runningAgents) ? status.runningAgents.map(String) : [],
|
|
11055
|
+
agentDrain: status.agentDrain === true,
|
|
11056
|
+
boot: typeof status.boot === "string" ? status.boot : null
|
|
10772
11057
|
};
|
|
11058
|
+
applyDrainStatus(status);
|
|
11059
|
+
restoreDrainMarkers(status);
|
|
10773
11060
|
setHostBusy(status.busy === true);
|
|
10774
11061
|
setDebuggerLatch(typeof status.debugger === "string" ? status.debugger : null);
|
|
10775
11062
|
if (status.active) {
|
|
@@ -10853,6 +11140,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
10853
11140
|
}, 2e3);
|
|
10854
11141
|
return () => clearInterval(timer);
|
|
10855
11142
|
}, [
|
|
11143
|
+
applyDrainStatus,
|
|
10856
11144
|
busyUrl,
|
|
10857
11145
|
updatingName,
|
|
10858
11146
|
data,
|
|
@@ -10861,6 +11149,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
10861
11149
|
repoHints,
|
|
10862
11150
|
refreshInstalled,
|
|
10863
11151
|
lang,
|
|
11152
|
+
restoreDrainMarkers,
|
|
10864
11153
|
t
|
|
10865
11154
|
]);
|
|
10866
11155
|
const scrollToTop = () => {
|
|
@@ -11166,7 +11455,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
11166
11455
|
const rest = entries.length > 1 ? ` (+${entries.length - 1})` : "";
|
|
11167
11456
|
return `${first.name} — ${first.layers.join(" / ")}${rest}`;
|
|
11168
11457
|
};
|
|
11169
|
-
const doInstall = (0, react.useCallback)((plugin, force = false, version) => {
|
|
11458
|
+
const doInstall = (0, react.useCallback)((plugin, force = false, version, preferReject = false) => {
|
|
11170
11459
|
setBuildsSkipped(null);
|
|
11171
11460
|
setConfirming(null);
|
|
11172
11461
|
setInstallError(null);
|
|
@@ -11190,7 +11479,8 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
11190
11479
|
body: JSON.stringify({
|
|
11191
11480
|
url: plugin.url,
|
|
11192
11481
|
...force ? { force: true } : {},
|
|
11193
|
-
...version !== void 0 ? { version } : {}
|
|
11482
|
+
...version !== void 0 ? { version } : {},
|
|
11483
|
+
...statusRef.current.agentDrain && !preferReject ? { busyPolicy: "drain" } : {}
|
|
11194
11484
|
})
|
|
11195
11485
|
}).then((res) => res.json().then((body) => ({
|
|
11196
11486
|
status: res.status,
|
|
@@ -11198,6 +11488,26 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
11198
11488
|
}))).then(({ status, body }) => {
|
|
11199
11489
|
setBusyUrl(null);
|
|
11200
11490
|
sessionStorage.removeItem("dshm-pending");
|
|
11491
|
+
if (status === 202 && typeof body.operationId === "string") {
|
|
11492
|
+
const operation = body.operation;
|
|
11493
|
+
setRecords((list) => patch(withoutHostProjection(list, body.operationId, recordId), recordId, {
|
|
11494
|
+
state: "queued",
|
|
11495
|
+
hostOperationId: body.operationId,
|
|
11496
|
+
...operation?.wait === null || operation?.wait === void 0 ? {} : {
|
|
11497
|
+
blockedBy: operation.wait.activeAgentIds,
|
|
11498
|
+
waitSince: operation.wait.since
|
|
11499
|
+
},
|
|
11500
|
+
reason: operation?.wait === null || operation?.wait === void 0 ? t("agentBusyQueued") : t("agentDrainWaiting").replace("{0}", String(operation.wait.activeCount))
|
|
11501
|
+
}));
|
|
11502
|
+
rememberDrainMarker({
|
|
11503
|
+
operationId: body.operationId,
|
|
11504
|
+
kind: "install",
|
|
11505
|
+
name: plugin.name,
|
|
11506
|
+
url: plugin.url
|
|
11507
|
+
});
|
|
11508
|
+
setOperationsOpen(true);
|
|
11509
|
+
return;
|
|
11510
|
+
}
|
|
11201
11511
|
if (status === 200 && body.ok && body.hot && pluginCategories(plugin).includes("theme")) {
|
|
11202
11512
|
sessionStorage.setItem("dshm-toast", JSON.stringify([plugin.name]));
|
|
11203
11513
|
sessionStorage.setItem("dshm-tab", "themes");
|
|
@@ -11241,6 +11551,11 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
11241
11551
|
}));
|
|
11242
11552
|
refreshInstalled();
|
|
11243
11553
|
} else {
|
|
11554
|
+
if (body.unsupported === true) {
|
|
11555
|
+
setRecords((list) => drop(list, recordId));
|
|
11556
|
+
doInstall(plugin, force, version, true);
|
|
11557
|
+
return;
|
|
11558
|
+
}
|
|
11244
11559
|
if (body.agentsBusy !== true && body.migration && typeof body.migration === "object") {
|
|
11245
11560
|
const { from, target } = body.migration;
|
|
11246
11561
|
if (typeof from === "string" && typeof target === "string") {
|
|
@@ -11256,6 +11571,22 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
11256
11571
|
}
|
|
11257
11572
|
}
|
|
11258
11573
|
if (status === 409) {
|
|
11574
|
+
if (body.failureCode === "AGENT_BARRIER_LOST") {
|
|
11575
|
+
setRecords((list) => patch(list, recordId, {
|
|
11576
|
+
state: "failed",
|
|
11577
|
+
reason: String(body.error ?? t("installFail"))
|
|
11578
|
+
}));
|
|
11579
|
+
setOperationsOpen(true);
|
|
11580
|
+
return;
|
|
11581
|
+
}
|
|
11582
|
+
if (body.targetChanged === true) {
|
|
11583
|
+
setRecords((list) => patch(list, recordId, {
|
|
11584
|
+
state: "failed",
|
|
11585
|
+
reason: String(body.error ?? t("installFail"))
|
|
11586
|
+
}));
|
|
11587
|
+
setOperationsOpen(true);
|
|
11588
|
+
return;
|
|
11589
|
+
}
|
|
11259
11590
|
if (body.agentsBusy === true) {
|
|
11260
11591
|
setRecords((list) => patch(list, recordId, {
|
|
11261
11592
|
state: "queued",
|
|
@@ -11315,6 +11646,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
11315
11646
|
}, [
|
|
11316
11647
|
nextRecordId,
|
|
11317
11648
|
refreshInstalled,
|
|
11649
|
+
rememberDrainMarker,
|
|
11318
11650
|
t,
|
|
11319
11651
|
installed
|
|
11320
11652
|
]);
|
|
@@ -11529,14 +11861,15 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
11529
11861
|
enterRecovery
|
|
11530
11862
|
]);
|
|
11531
11863
|
/** Cancel the running plugin command (#6 by @qichuang321). */
|
|
11532
|
-
const doCancel = (0, react.useCallback)(() => {
|
|
11864
|
+
const doCancel = (0, react.useCallback)((record) => {
|
|
11865
|
+
const body = record?.hostOperationId === void 0 ? {} : { operationId: record.hostOperationId };
|
|
11533
11866
|
fetch(api("/dsh-market/cancel"), {
|
|
11534
11867
|
method: "POST",
|
|
11535
11868
|
headers: { "content-type": "application/json" },
|
|
11536
|
-
body:
|
|
11869
|
+
body: JSON.stringify(body)
|
|
11537
11870
|
}).catch(() => {});
|
|
11538
11871
|
}, []);
|
|
11539
|
-
const doUpdate = (0, react.useCallback)((name, force = false, restore = false, compatVersion, confirmAgentsRunning = false, eligibleVersion) => {
|
|
11872
|
+
const doUpdate = (0, react.useCallback)((name, force = false, restore = false, compatVersion, confirmAgentsRunning = false, eligibleVersion, preferReject = false) => {
|
|
11540
11873
|
const expectedVersion = compatVersion === void 0 && !restore ? eligibleVersion ?? updates[name]?.eligibleVersion : void 0;
|
|
11541
11874
|
const updateOptions = {
|
|
11542
11875
|
force,
|
|
@@ -11569,7 +11902,8 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
11569
11902
|
...restore ? { restore: true } : {},
|
|
11570
11903
|
...compatVersion !== void 0 ? { compatVersion } : {},
|
|
11571
11904
|
...expectedVersion !== void 0 ? { eligibleVersion: expectedVersion } : {},
|
|
11572
|
-
...confirmAgentsRunning ? { confirmAgentsRunning: true } : {}
|
|
11905
|
+
...confirmAgentsRunning ? { confirmAgentsRunning: true } : {},
|
|
11906
|
+
...!restore && statusRef.current.agentDrain && !preferReject ? { busyPolicy: "drain" } : {}
|
|
11573
11907
|
})
|
|
11574
11908
|
}).then((res) => res.json().then((body) => ({
|
|
11575
11909
|
status: res.status,
|
|
@@ -11577,6 +11911,25 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
11577
11911
|
}))).then(({ status, body }) => {
|
|
11578
11912
|
sessionStorage.removeItem("dshm-updating");
|
|
11579
11913
|
setUpdatingName(null);
|
|
11914
|
+
if (status === 202 && typeof body.operationId === "string") {
|
|
11915
|
+
const operation = body.operation;
|
|
11916
|
+
setRecords((list) => patch(withoutHostProjection(list, body.operationId, updateRecordId), updateRecordId, {
|
|
11917
|
+
state: "queued",
|
|
11918
|
+
hostOperationId: body.operationId,
|
|
11919
|
+
...operation?.wait === null || operation?.wait === void 0 ? {} : {
|
|
11920
|
+
blockedBy: operation.wait.activeAgentIds,
|
|
11921
|
+
waitSince: operation.wait.since
|
|
11922
|
+
},
|
|
11923
|
+
reason: operation?.wait === null || operation?.wait === void 0 ? t("agentBusyUpdateQueued") : t("agentDrainWaiting").replace("{0}", String(operation.wait.activeCount))
|
|
11924
|
+
}));
|
|
11925
|
+
rememberDrainMarker({
|
|
11926
|
+
operationId: body.operationId,
|
|
11927
|
+
kind: "update",
|
|
11928
|
+
name
|
|
11929
|
+
});
|
|
11930
|
+
setOperationsOpen(true);
|
|
11931
|
+
return;
|
|
11932
|
+
}
|
|
11580
11933
|
if (body.cancelled === true) {
|
|
11581
11934
|
setRecords((list) => drop(list, updateRecordId));
|
|
11582
11935
|
refreshInstalled();
|
|
@@ -11613,7 +11966,29 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
11613
11966
|
if (body.compatibility?.code === "soft-incompatible") setCompatibilityNotice(body.compatibility);
|
|
11614
11967
|
refreshInstalled();
|
|
11615
11968
|
} else {
|
|
11969
|
+
if (body.unsupported === true) {
|
|
11970
|
+
setRecords((list) => drop(list, updateRecordId));
|
|
11971
|
+
doUpdate(name, force, restore, compatVersion, confirmAgentsRunning, eligibleVersion, true);
|
|
11972
|
+
return;
|
|
11973
|
+
}
|
|
11616
11974
|
if (status === 409) {
|
|
11975
|
+
if (body.failureCode === "AGENT_BARRIER_LOST") {
|
|
11976
|
+
setRecords((list) => patch(list, updateRecordId, {
|
|
11977
|
+
state: "failed",
|
|
11978
|
+
reason: String(body.error ?? "An Agent became active before the update; nothing was changed.")
|
|
11979
|
+
}));
|
|
11980
|
+
setOperationsOpen(true);
|
|
11981
|
+
return;
|
|
11982
|
+
}
|
|
11983
|
+
if (body.targetChanged === true) {
|
|
11984
|
+
setRecords((list) => patch(list, updateRecordId, {
|
|
11985
|
+
state: "failed",
|
|
11986
|
+
reason: String(body.error ?? "Update target changed")
|
|
11987
|
+
}));
|
|
11988
|
+
setInstallError(localizeBilingual(String(body.error ?? "Update target changed"), lang));
|
|
11989
|
+
setOperationsOpen(true);
|
|
11990
|
+
return;
|
|
11991
|
+
}
|
|
11617
11992
|
if (body.agentsBusy === true) {
|
|
11618
11993
|
setRecords((list) => patch(list, updateRecordId, {
|
|
11619
11994
|
state: "queued",
|
|
@@ -11667,6 +12042,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
11667
12042
|
}, [
|
|
11668
12043
|
refreshInstalled,
|
|
11669
12044
|
installed,
|
|
12045
|
+
rememberDrainMarker,
|
|
11670
12046
|
t,
|
|
11671
12047
|
lang,
|
|
11672
12048
|
updates
|
|
@@ -12127,7 +12503,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
12127
12503
|
setHotNames((names) => names.filter((entry) => entry !== name));
|
|
12128
12504
|
setRefreshNames((names) => names.filter((entry) => entry !== name));
|
|
12129
12505
|
}, []);
|
|
12130
|
-
const doUninstall = (0, react.useCallback)((name, confirmAgentsRunning = false) => {
|
|
12506
|
+
const doUninstall = (0, react.useCallback)((name, confirmAgentsRunning = false, preferReject = false) => {
|
|
12131
12507
|
setRemoveConfirm(null);
|
|
12132
12508
|
setInstallError(null);
|
|
12133
12509
|
setActivationWarnings([]);
|
|
@@ -12144,12 +12520,53 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
12144
12520
|
headers: { "content-type": "application/json" },
|
|
12145
12521
|
body: JSON.stringify({
|
|
12146
12522
|
name,
|
|
12147
|
-
...confirmAgentsRunning ? { confirmAgentsRunning: true } : {}
|
|
12523
|
+
...confirmAgentsRunning ? { confirmAgentsRunning: true } : {},
|
|
12524
|
+
...statusRef.current.agentDrain && !preferReject ? { busyPolicy: "drain" } : {}
|
|
12148
12525
|
})
|
|
12149
12526
|
}).then((res) => res.json().then((body) => ({
|
|
12150
12527
|
status: res.status,
|
|
12151
12528
|
body
|
|
12152
12529
|
}))).then(({ status, body }) => {
|
|
12530
|
+
if (status === 202 && typeof body.operationId === "string") {
|
|
12531
|
+
const operation = body.operation;
|
|
12532
|
+
setRecords((list) => patch(withoutHostProjection(list, body.operationId, uninstallRecordId), uninstallRecordId, {
|
|
12533
|
+
state: "queued",
|
|
12534
|
+
hostOperationId: body.operationId,
|
|
12535
|
+
...operation?.wait === null || operation?.wait === void 0 ? {} : {
|
|
12536
|
+
blockedBy: operation.wait.activeAgentIds,
|
|
12537
|
+
waitSince: operation.wait.since
|
|
12538
|
+
},
|
|
12539
|
+
reason: operation?.wait === null || operation?.wait === void 0 ? t("agentBusyUninstallQueued") : t("agentDrainWaiting").replace("{0}", String(operation.wait.activeCount))
|
|
12540
|
+
}));
|
|
12541
|
+
rememberDrainMarker({
|
|
12542
|
+
operationId: body.operationId,
|
|
12543
|
+
kind: "uninstall",
|
|
12544
|
+
name
|
|
12545
|
+
});
|
|
12546
|
+
setOperationsOpen(true);
|
|
12547
|
+
return;
|
|
12548
|
+
}
|
|
12549
|
+
if (body.unsupported === true) {
|
|
12550
|
+
setRecords((list) => drop(list, uninstallRecordId));
|
|
12551
|
+
doUninstall(name, confirmAgentsRunning, true);
|
|
12552
|
+
return;
|
|
12553
|
+
}
|
|
12554
|
+
if (status === 409 && body.failureCode === "AGENT_BARRIER_LOST") {
|
|
12555
|
+
setRecords((list) => patch(list, uninstallRecordId, {
|
|
12556
|
+
state: "failed",
|
|
12557
|
+
reason: String(body.error ?? "An Agent became active before the uninstall; nothing was changed.")
|
|
12558
|
+
}));
|
|
12559
|
+
setOperationsOpen(true);
|
|
12560
|
+
return;
|
|
12561
|
+
}
|
|
12562
|
+
if (status === 409 && body.targetChanged === true) {
|
|
12563
|
+
setRecords((list) => patch(list, uninstallRecordId, {
|
|
12564
|
+
state: "failed",
|
|
12565
|
+
reason: String(body.error ?? t("installFail"))
|
|
12566
|
+
}));
|
|
12567
|
+
setOperationsOpen(true);
|
|
12568
|
+
return;
|
|
12569
|
+
}
|
|
12153
12570
|
if (status === 409 && body.agentsBusy === true) {
|
|
12154
12571
|
setRecords((list) => patch(list, uninstallRecordId, {
|
|
12155
12572
|
state: "queued",
|
|
@@ -12187,6 +12604,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
12187
12604
|
hotNames,
|
|
12188
12605
|
refreshNames,
|
|
12189
12606
|
nextRecordId,
|
|
12607
|
+
rememberDrainMarker,
|
|
12190
12608
|
t
|
|
12191
12609
|
]);
|
|
12192
12610
|
const busyUrlRef = (0, react.useRef)(null);
|
|
@@ -12250,19 +12668,24 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
12250
12668
|
const timer = setInterval(() => {
|
|
12251
12669
|
if (drainingRef.current) return;
|
|
12252
12670
|
if (operationInFlight()) return;
|
|
12253
|
-
if (!recordsRef.current.some((record) => record.state === "queued")) return;
|
|
12671
|
+
if (!recordsRef.current.some((record) => record.hostOperationId === void 0 ? record.state === "queued" : record.state === "queued" || record.state === "running")) return;
|
|
12254
12672
|
fetch(api("/dsh-market/status"), { cache: "no-store" }).then(readMarketStatus).then((status) => {
|
|
12255
12673
|
if (disposed || status === null) return;
|
|
12256
12674
|
const runningAgents = Array.isArray(status.runningAgents) ? status.runningAgents.map(String) : [];
|
|
12257
12675
|
const busy = status.busy === true;
|
|
12258
12676
|
statusRef.current = {
|
|
12259
12677
|
busy,
|
|
12260
|
-
runningAgents
|
|
12678
|
+
runningAgents,
|
|
12679
|
+
agentDrain: status.agentDrain === true,
|
|
12680
|
+
boot: typeof status.boot === "string" ? status.boot : null
|
|
12261
12681
|
};
|
|
12682
|
+
applyDrainStatus(status);
|
|
12683
|
+
restoreDrainMarkers(status);
|
|
12262
12684
|
if (busy) return;
|
|
12685
|
+
if (status.drainActive === true) return;
|
|
12263
12686
|
if (operationInFlight()) return;
|
|
12264
|
-
const task = recordsRef.current.find((record) => record.state === "queued" && (record.kind === "install" || runningAgents.length === 0));
|
|
12265
|
-
if (task === void 0 || recordsRef.current.some((record) => record.state === "running")) return;
|
|
12687
|
+
const task = recordsRef.current.find((record) => record.state === "queued" && record.hostOperationId === void 0 && (record.kind === "install" || runningAgents.length === 0));
|
|
12688
|
+
if (task === void 0 || recordsRef.current.some((record) => record.state === "running" && record.hostOperationId === void 0)) return;
|
|
12266
12689
|
setRecords((list) => list.filter((record) => record.id !== task.id));
|
|
12267
12690
|
drainingRef.current = true;
|
|
12268
12691
|
try {
|
|
@@ -12280,9 +12703,14 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
12280
12703
|
disposed = true;
|
|
12281
12704
|
clearInterval(timer);
|
|
12282
12705
|
};
|
|
12283
|
-
}, [
|
|
12706
|
+
}, [
|
|
12707
|
+
applyDrainStatus,
|
|
12708
|
+
readMarketStatus,
|
|
12709
|
+
restoreDrainMarkers
|
|
12710
|
+
]);
|
|
12284
12711
|
/** A queued record's "run now": retry immediately instead of waiting for idle. */
|
|
12285
12712
|
const runQueuedNow = (0, react.useCallback)((record, confirmAgentsRunning = false) => {
|
|
12713
|
+
if (record.hostOperationId !== void 0) return;
|
|
12286
12714
|
if (!recordsRef.current.some((item) => item.id === record.id && item.state === "queued")) return;
|
|
12287
12715
|
if (record.kind !== "install" && !confirmAgentsRunning && (statusRef.current.runningAgents.length > 0 || (record.blockedBy?.length ?? 0) > 0)) {
|
|
12288
12716
|
setAgentsConfirm({
|
|
@@ -13252,7 +13680,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
13252
13680
|
type: "button",
|
|
13253
13681
|
className: Market_module_css_default.cardBlockedMark,
|
|
13254
13682
|
onClick: openOperations,
|
|
13255
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, { size: 13 }), t("queuedBadge")]
|
|
13683
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, { size: 13 }), t(record?.hostOperationId !== void 0 && (record.blockedBy?.length ?? 0) > 0 ? "agentDrainBadge" : "queuedBadge")]
|
|
13256
13684
|
}) : blocked ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
13257
13685
|
type: "button",
|
|
13258
13686
|
className: Market_module_css_default.cardBlockedMark,
|
|
@@ -13456,7 +13884,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
13456
13884
|
variant: "outline",
|
|
13457
13885
|
size: "sm",
|
|
13458
13886
|
disabled: cancelling,
|
|
13459
|
-
onClick: doCancel,
|
|
13887
|
+
onClick: () => doCancel(),
|
|
13460
13888
|
children: cancelling ? t("cancelling") : t("cancelOp")
|
|
13461
13889
|
}),
|
|
13462
13890
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -13595,7 +14023,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
13595
14023
|
type: "button",
|
|
13596
14024
|
className: Market_module_css_default.cardBlockedMark,
|
|
13597
14025
|
onClick: openOperations,
|
|
13598
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, { size: 13 }), t("queuedBadge")]
|
|
14026
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, { size: 13 }), t(record?.hostOperationId !== void 0 && (record.blockedBy?.length ?? 0) > 0 ? "agentDrainBadge" : "queuedBadge")]
|
|
13599
14027
|
}) : blocked ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
13600
14028
|
type: "button",
|
|
13601
14029
|
className: Market_module_css_default.cardBlockedMark,
|
|
@@ -13652,7 +14080,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
13652
14080
|
variant: "outline",
|
|
13653
14081
|
size: "sm",
|
|
13654
14082
|
disabled: cancelling,
|
|
13655
|
-
onClick: doCancel,
|
|
14083
|
+
onClick: () => doCancel(),
|
|
13656
14084
|
children: cancelling ? t("cancelling") : t("cancelOp")
|
|
13657
14085
|
}),
|
|
13658
14086
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -14037,9 +14465,15 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
14037
14465
|
onOpenChange: setOperationsOpen,
|
|
14038
14466
|
replacing,
|
|
14039
14467
|
envReady,
|
|
14040
|
-
onClearSettled: () =>
|
|
14041
|
-
|
|
14042
|
-
|
|
14468
|
+
onClearSettled: () => {
|
|
14469
|
+
for (const record of recordsRef.current) if (record.hostOperationId !== void 0 && (record.state === "done" || record.state === "warned" || record.state === "failed")) dismissedHostRef.current.add(record.hostOperationId);
|
|
14470
|
+
setRecords((list) => clearSettled(list));
|
|
14471
|
+
},
|
|
14472
|
+
onCancel: (record) => doCancel(record),
|
|
14473
|
+
onDismiss: (record) => {
|
|
14474
|
+
if (record.hostOperationId !== void 0) dismissedHostRef.current.add(record.hostOperationId);
|
|
14475
|
+
setRecords((list) => drop(list, record.id));
|
|
14476
|
+
},
|
|
14043
14477
|
onRefresh: () => location.reload(),
|
|
14044
14478
|
onResolveConflict: resolveConflict,
|
|
14045
14479
|
onForceInstall: (record) => {
|
|
@@ -15653,7 +16087,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
15653
16087
|
variant: "outline",
|
|
15654
16088
|
size: "sm",
|
|
15655
16089
|
disabled: cancelling,
|
|
15656
|
-
onClick: doCancel,
|
|
16090
|
+
onClick: () => doCancel(),
|
|
15657
16091
|
children: cancelling ? t("cancelling") : t("cancelOp")
|
|
15658
16092
|
}),
|
|
15659
16093
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|