@inventec/dsh-copilot-auth 1.2.17 → 1.2.19

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/lib/client.js CHANGED
@@ -34,13 +34,12 @@ var import_react = require("react");
34
34
 
35
35
  // src/shared.mjs
36
36
  var ROUTE_PREFIX = "/copilot-auth";
37
- var PROTOCOL_VERSION = 2;
37
+ var PROTOCOL_VERSION = 3;
38
38
  var routes = () => ({
39
39
  start: `${ROUTE_PREFIX}/start`,
40
40
  state: `${ROUTE_PREFIX}/state`,
41
41
  status: `${ROUTE_PREFIX}/status`,
42
42
  logout: `${ROUTE_PREFIX}/logout`,
43
- cancel: `${ROUTE_PREFIX}/cancel`,
44
43
  refreshPreview: `${ROUTE_PREFIX}/refresh/preview`,
45
44
  refreshApply: `${ROUTE_PREFIX}/refresh/apply`,
46
45
  refreshRetire: `${ROUTE_PREFIX}/refresh/retire`
@@ -134,6 +133,41 @@ function refreshBlocked(flags) {
134
133
  const mode = flags?.catalogMode;
135
134
  return flags?.scopeAvailable === false || mode === "blocked" || mode === "unknown";
136
135
  }
136
+ function selectionFields(lastSelection = {}) {
137
+ const out = {};
138
+ if (lastSelection.selectedIds !== void 0) out.selectedIds = lastSelection.selectedIds;
139
+ if (lastSelection.clearOverrides !== void 0) out.clearOverrides = lastSelection.clearOverrides;
140
+ if (lastSelection.confirmEmpty !== void 0) out.confirmEmpty = lastSelection.confirmEmpty;
141
+ return out;
142
+ }
143
+ function upgradeCandidate(state) {
144
+ const p = state.preview;
145
+ if (!p || state.liveUpgradeUsed) return null;
146
+ if ((p.diff?.removed?.length ?? 0) === 0) return null;
147
+ if (p.evidence?.source !== "cache" || p.evidence?.stale === true) return null;
148
+ if (p.catalogSource !== "latest") return null;
149
+ return {
150
+ type: "preview",
151
+ operation: p.operation,
152
+ catalogSource: p.catalogSource,
153
+ ...selectionFields(state.lastSelection)
154
+ };
155
+ }
156
+ function enterConfirming({ flags, preview, staleNotice, lastSelection }) {
157
+ const state = {
158
+ name: "confirming",
159
+ flags: flags ?? initial.flags,
160
+ preview,
161
+ staleNotice: staleNotice === true,
162
+ materializing: false,
163
+ awaitingUpgrade: false,
164
+ liveUpgradeUsed: false,
165
+ lastSelection: lastSelection ?? {}
166
+ };
167
+ const effect = upgradeCandidate(state);
168
+ if (!effect) return [state, null];
169
+ return [{ ...state, liveUpgradeUsed: true, awaitingUpgrade: true, materializing: true }, effect];
170
+ }
137
171
  function reduce(state, event) {
138
172
  switch (state.name) {
139
173
  case "idle":
@@ -146,14 +180,10 @@ function reduce(state, event) {
146
180
  return [{
147
181
  name: "previewing",
148
182
  flags: state.flags ?? initial.flags,
149
- operation: event.operation === "rebuild" ? "rebuild" : "supplement",
183
+ operation: "manage",
150
184
  catalogSource: event.catalogSource,
151
185
  stale: false
152
- }, {
153
- type: "preview",
154
- operation: event.operation === "rebuild" ? "rebuild" : "supplement",
155
- catalogSource: event.catalogSource
156
- }];
186
+ }, { type: "preview", operation: "manage", catalogSource: event.catalogSource }];
157
187
  }
158
188
  if (event.type === "init" && state.name === "idle") return [state, { type: "status" }];
159
189
  if (event.type === "check" && state.name === "resultUnknown") {
@@ -184,7 +214,7 @@ function reduce(state, event) {
184
214
  }
185
215
  case "previewing": {
186
216
  if (event.type === "preview-ok") {
187
- return [{ name: "confirming", flags: state.flags ?? initial.flags, preview: event.preview, staleNotice: state.stale === true }, null];
217
+ return enterConfirming({ flags: state.flags ?? initial.flags, preview: event.preview, staleNotice: state.stale === true });
188
218
  }
189
219
  if (event.type === "preview-fail") return [{ name: "failed", flags: state.flags ?? initial.flags, error: event.error }, null];
190
220
  return [state, null];
@@ -192,25 +222,63 @@ function reduce(state, event) {
192
222
  case "confirming": {
193
223
  const p = state.preview;
194
224
  if (event.type === "select") {
225
+ const lastSelection = { ...selectionFields({
226
+ selectedIds: event.selectedIds,
227
+ clearOverrides: event.clearOverrides,
228
+ confirmEmpty: event.confirmEmpty
229
+ }) };
195
230
  return [{
196
231
  name: "confirming",
197
232
  flags: state.flags ?? initial.flags,
198
233
  preview: p,
199
234
  materializing: true,
235
+ awaitingUpgrade: false,
236
+ liveUpgradeUsed: state.liveUpgradeUsed ?? false,
237
+ lastSelection,
200
238
  staleNotice: state.staleNotice
201
239
  }, {
202
240
  type: "preview",
203
241
  operation: p.operation,
204
242
  catalogSource: p.catalogSource,
205
243
  basePreviewId: p.previewId,
206
- selectedIds: event.selectedIds ?? [],
207
- confirmEmpty: event.confirmEmpty === true
244
+ ...selectionFields(lastSelection)
208
245
  }];
209
246
  }
210
247
  if (event.type === "preview-ok") {
211
- return [{ name: "confirming", flags: state.flags ?? initial.flags, preview: event.preview, staleNotice: state.stale === true, materializing: false }, null];
248
+ const wasUpgrade = state.awaitingUpgrade === true;
249
+ const landed = {
250
+ name: "confirming",
251
+ flags: state.flags ?? initial.flags,
252
+ preview: event.preview,
253
+ materializing: false,
254
+ awaitingUpgrade: false,
255
+ liveUpgradeUsed: state.liveUpgradeUsed ?? false,
256
+ lastSelection: state.lastSelection ?? {},
257
+ staleNotice: wasUpgrade && event.preview?.evidence?.source !== "live" || state.staleNotice === true
258
+ };
259
+ const effect = upgradeCandidate(landed);
260
+ if (effect) return [{ ...landed, liveUpgradeUsed: true, awaitingUpgrade: true, materializing: true }, effect];
261
+ return [landed, null];
212
262
  }
213
263
  if (event.type === "preview-fail") {
264
+ if (state.awaitingUpgrade === true && p) {
265
+ return [{
266
+ name: "confirming",
267
+ flags: state.flags ?? initial.flags,
268
+ preview: p,
269
+ materializing: true,
270
+ awaitingUpgrade: false,
271
+ liveUpgradeUsed: state.liveUpgradeUsed ?? true,
272
+ lastSelection: state.lastSelection ?? {},
273
+ staleNotice: true
274
+ }, {
275
+ type: "preview",
276
+ operation: p.operation,
277
+ catalogSource: p.catalogSource,
278
+ basePreviewId: p.previewId,
279
+ ...selectionFields(state.lastSelection)
280
+ }];
281
+ }
214
282
  return [{ name: "failed", flags: state.flags ?? initial.flags, error: event.error }, null];
215
283
  }
216
284
  if (event.type === "start") {
@@ -219,19 +287,14 @@ function reduce(state, event) {
219
287
  flags: state.flags ?? initial.flags,
220
288
  preview: p,
221
289
  materializing: true,
290
+ awaitingUpgrade: false,
291
+ liveUpgradeUsed: state.liveUpgradeUsed ?? false,
292
+ lastSelection: state.lastSelection ?? {},
222
293
  staleNotice: state.staleNotice
223
- }, {
224
- type: "preview",
225
- operation: event.operation === "rebuild" ? "rebuild" : p.operation,
226
- catalogSource: event.catalogSource
227
- }];
294
+ }, { type: "preview", operation: p.operation, catalogSource: event.catalogSource }];
228
295
  }
229
296
  if (event.type === "confirm") {
230
297
  if (state.materializing) return [state, null];
231
- const isRebuild = p.operation === "rebuild";
232
- const emptyTarget = isRebuild && (p.diff?.targetView?.models ?? []).length === 0;
233
- if (isRebuild && event.second !== true) return [state, null];
234
- if (emptyTarget && event.empty !== true) return [state, null];
235
298
  if (p.evidence?.stale === true) return [state, null];
236
299
  return [{ name: "applying", flags: state.flags ?? initial.flags, preview: p }, { type: "apply", previewId: p.previewId, operationId: p.operationId }];
237
300
  }
@@ -303,8 +366,10 @@ async function runEffect(effect, fetchImpl) {
303
366
  const res = await post(r.refreshPreview, {
304
367
  operation: effect.operation,
305
368
  ...effect.catalogSource ? { catalogSource: effect.catalogSource } : {},
306
- ...effect.basePreviewId ? { basePreviewId: effect.basePreviewId, selectedIds: effect.selectedIds ?? [] } : {},
307
- ...effect.confirmEmpty ? { confirmEmpty: true } : {}
369
+ ...effect.basePreviewId !== void 0 ? { basePreviewId: effect.basePreviewId } : {},
370
+ ...effect.selectedIds !== void 0 ? { selectedIds: effect.selectedIds } : {},
371
+ ...effect.clearOverrides !== void 0 ? { clearOverrides: effect.clearOverrides } : {},
372
+ ...effect.confirmEmpty !== void 0 ? { confirmEmpty: effect.confirmEmpty } : {}
308
373
  });
309
374
  if (res.ok) return { type: "preview-ok", preview: res.body };
310
375
  return { type: "preview-fail", error: res.error?.details?.errorCode ?? res.error?.messageKey };
@@ -406,7 +471,7 @@ function createAuthFlow({ fetchImpl, clock = defaultClock(), onState = () => {
406
471
  emit({ phase: "failed", error: s?.error ?? "authorization-failed" });
407
472
  return;
408
473
  }
409
- if (s?.status === "withdrawal-pending-unverified" || s?.status === "timed-out-unverified") {
474
+ if (s?.status === "timed-out-unverified") {
410
475
  emit({ phase: "risk", riskKind: s.status });
411
476
  return;
412
477
  }
@@ -486,6 +551,10 @@ function createAuthFlow({ fetchImpl, clock = defaultClock(), onState = () => {
486
551
  emit({ phase: "idle", error: r.error?.messageKey, connectivity: "manual" });
487
552
  return;
488
553
  }
554
+ if (r.body?.status === "authorized" && body.configured !== true) {
555
+ emit({ phase: "idle" });
556
+ return;
557
+ }
489
558
  applySnapshot(gen, r.body);
490
559
  }
491
560
  async function start() {
@@ -521,14 +590,6 @@ function createAuthFlow({ fetchImpl, clock = defaultClock(), onState = () => {
521
590
  }
522
591
  emit({ phase: "failed", error: errorCode ?? r.error?.messageKey });
523
592
  }
524
- async function cancel() {
525
- const r = await requestJson(fetchImpl, "/copilot-auth/cancel", { method: "POST" });
526
- if (r.ok) {
527
- emit({ withdrawalDelivery: r.body?.withdrawalDelivery ?? "unavailable" });
528
- return;
529
- }
530
- emit({ withdrawalDelivery: "failed", error: r.error?.details?.errorCode ?? r.error?.messageKey });
531
- }
532
593
  async function logout() {
533
594
  const gen = ++generation;
534
595
  clearTimer();
@@ -553,7 +614,7 @@ function createAuthFlow({ fetchImpl, clock = defaultClock(), onState = () => {
553
614
  generation += 1;
554
615
  clearTimer();
555
616
  }
556
- return { init, start, cancel, logout, refresh, dispose };
617
+ return { init, start, logout, refresh, dispose };
557
618
  }
558
619
 
559
620
  // src/client.jsx
@@ -572,9 +633,6 @@ var DICTS = {
572
633
  loading: "\u2026",
573
634
  login: "Sign in",
574
635
  logout: "Sign out",
575
- withdrawAuth: "Request withdrawal",
576
- withdrawal: "Withdrawal requested; the outcome remains unverified.",
577
- withdrawalUnavailable: "The withdrawal request could not be sent to the host; the outcome remains unverified.",
578
636
  authUnsafe: "Safe completion of the authorization flow is unconfirmed. Sign-out and a new authorization attempt are unavailable.",
579
637
  attemptTimeout: "Authorization wait timed out; the outcome remains unverified. A new authorization attempt is unavailable. Check the status or verify it manually.",
580
638
  attemptShared: "An authorization attempt is already running in this instance.",
@@ -587,6 +645,10 @@ var DICTS = {
587
645
  copy: "Copy",
588
646
  copied: "Copied \u2713",
589
647
  unknown: "Unknown error",
648
+ netConnect: "Cannot reach GitHub. Check this machine's network or proxy settings.",
649
+ netDns: "Cannot resolve github.com. Check DNS or network settings.",
650
+ netCert: "GitHub certificate verification failed. A corporate proxy or TLS inspection may be intercepting the connection.",
651
+ netGeneric: "The authorization request failed over the network.",
590
652
  statusUnknown: "Status unknown",
591
653
  errNetwork: "Network error: cannot reach the authorization service. Retrying automatically.",
592
654
  errGateway: "Gateway error: the DSH service may be restarting. Retrying automatically.",
@@ -594,28 +656,30 @@ var DICTS = {
594
656
  errHttp: "The authorization request failed. Check status and retry.",
595
657
  logoutScope: "This removes Copilot authorization from this credential store and affects other instances using it. It does not delete your GitHub account.",
596
658
  logoutFailed: "Sign-out did not complete; authorization credentials may remain. Please retry.",
597
- // ---- Model management (protocol v2, design §5) ----
598
- supplement: "Add models",
599
- rebuild: "Rebuild model list",
600
- supplementTitle: "Add models",
601
- rebuildTitle: "Rebuild model list",
602
- candidates: "Models available to add",
659
+ // ---- Model management (protocol v3, design §5 / ADR 0005) ----
660
+ manageModels: "Manage model list",
603
661
  selectAll: "Select all",
604
662
  selectNone: "Deselect all",
605
- supplementRisk: "Existing models and customizations will be kept. Only selected models will be added.",
606
- rebuildRisk: "Rebuilding clears model parameters and overrides. Other provider settings remain unchanged.",
607
- clearRisk: "This clears the Copilot model list and its model customizations. Confirm clearing separately.",
608
- secondConfirm: "This is destructive. Click again to confirm.",
663
+ alignAction: "Align with account & catalog",
664
+ groupAddable: "Available to add",
665
+ groupListed: "In list",
666
+ groupRemoval: "Proposed for removal",
667
+ retain: "Keep: re-check to retain",
668
+ customizedBadge: "Customized",
669
+ clearOverrides: "Also clear all model customizations.",
670
+ inheritedOverrides: "Inherited model customizations cannot be cleared here.",
671
+ clearRisk: "Clearing hides nothing \u2014 all available models stay listed; to trim, keep only the ones you check.",
609
672
  applyChanges: "Apply changes",
610
- addedModels: "Models to add",
611
- removedModels: "Models to be removed",
612
- keptModels: "Kept",
613
- removedAccount: "Not included in this account model list",
673
+ removalRisk: "{count} listed model(s) will be removed; their customizations are removed with them.",
674
+ summaryLine: "After applying: {total} model(s) \u2014 {kept} kept \xB7 {added} added \xB7 {removed} removed",
675
+ catalogNotice: "{count} catalog entries will be added",
676
+ noChanges: "No changes and no new catalog entries.",
677
+ removalNeedsLive: "Removals require live account evidence",
678
+ removedAccount: "Not reported by this account",
614
679
  removedUnresolvable: "Not resolvable by the current catalog",
615
- warnings: "Warnings",
616
680
  skippedModels: "Upstream entries skipped by validation",
617
681
  srcLive: "Account models: live",
618
- srcCache: "Account models: cached at {time}; add-only use",
682
+ srcCache: "Account models: cached at {time}; removals require upgrading to live evidence",
619
683
  srcStale: "The cache has no trusted timestamp, is expired, or belongs to changed authorization; reference only.",
620
684
  srcLatest: "Catalog source: latest pi-ai from npm",
621
685
  srcLocal: "Catalog source: local (npm fetch failed)",
@@ -657,9 +721,6 @@ var DICTS = {
657
721
  loading: "\u2026",
658
722
  login: "\u6388\u6743\u767B\u5F55",
659
723
  logout: "\u9000\u51FA\u767B\u5F55",
660
- withdrawAuth: "\u8BF7\u6C42\u64A4\u56DE",
661
- withdrawal: "\u64A4\u56DE\u8BF7\u6C42\u5DF2\u53D1\u9001\uFF0C\u7ED3\u679C\u4ECD\u5F85\u6838\u5B9E\u3002",
662
- withdrawalUnavailable: "\u672A\u80FD\u5411\u5BBF\u4E3B\u53D1\u9001\u64A4\u56DE\u8BF7\u6C42\uFF0C\u7ED3\u679C\u4ECD\u5F85\u6838\u5B9E\u3002",
663
724
  authUnsafe: "\u5C1A\u4E0D\u80FD\u786E\u8BA4\u6388\u6743\u6D41\u7A0B\u5DF2\u5B89\u5168\u7ED3\u675F\uFF0C\u6682\u4E0D\u80FD\u9000\u51FA\u6216\u53D1\u8D77\u65B0\u7684\u6388\u6743\u5C1D\u8BD5\u3002",
664
725
  attemptTimeout: "\u7B49\u5F85\u6388\u6743\u8D85\u65F6\uFF0C\u7ED3\u679C\u4ECD\u5F85\u6838\u5B9E\u3002\u5F53\u524D\u4E0D\u80FD\u53D1\u8D77\u65B0\u7684\u6388\u6743\u5C1D\u8BD5\uFF0C\u8BF7\u67E5\u8BE2\u72B6\u6001\u6216\u4EBA\u5DE5\u6838\u5B9E\u3002",
665
726
  attemptShared: "\u6B64\u5B9E\u4F8B\u5DF2\u6709\u6388\u6743\u6B63\u5728\u8FDB\u884C\u3002",
@@ -671,6 +732,10 @@ var DICTS = {
671
732
  copy: "\u590D\u5236",
672
733
  copied: "\u5DF2\u590D\u5236 \u2713",
673
734
  unknown: "\u672A\u77E5\u9519\u8BEF",
735
+ netConnect: "\u65E0\u6CD5\u8FDE\u63A5 GitHub\uFF1A\u8BF7\u68C0\u67E5\u672C\u673A\u7F51\u7EDC\u6216\u4EE3\u7406\u8BBE\u7F6E\u3002",
736
+ netDns: "\u65E0\u6CD5\u89E3\u6790 GitHub \u57DF\u540D\uFF1A\u8BF7\u68C0\u67E5 DNS \u6216\u7F51\u7EDC\u8BBE\u7F6E\u3002",
737
+ netCert: "GitHub \u8BC1\u4E66\u6821\u9A8C\u5931\u8D25\uFF1A\u53EF\u80FD\u5B58\u5728\u4F01\u4E1A\u4EE3\u7406\u6216 TLS \u68C0\u67E5\u62E6\u622A\u3002",
738
+ netGeneric: "\u6388\u6743\u8BF7\u6C42\u7F51\u7EDC\u5931\u8D25\u3002",
674
739
  statusUnknown: "\u72B6\u6001\u672A\u77E5",
675
740
  errNetwork: "\u7F51\u7EDC\u5F02\u5E38\uFF1A\u65E0\u6CD5\u8FDE\u63A5\u6388\u6743\u670D\u52A1\uFF0C\u6B63\u5728\u81EA\u52A8\u91CD\u8BD5\u3002",
676
741
  errGateway: "\u7F51\u5173\u9519\u8BEF\uFF1ADSH \u670D\u52A1\u53EF\u80FD\u6B63\u5728\u91CD\u542F\uFF0C\u6B63\u5728\u81EA\u52A8\u91CD\u8BD5\u3002",
@@ -678,28 +743,30 @@ var DICTS = {
678
743
  errHttp: "\u6388\u6743\u8BF7\u6C42\u5931\u8D25\u3002\u8BF7\u67E5\u8BE2\u72B6\u6001\u540E\u91CD\u8BD5\u3002",
679
744
  logoutScope: "\u5C06\u6E05\u9664\u6B64\u51ED\u636E\u5E93\u4E2D\u7684 Copilot \u6388\u6743\uFF0C\u4F7F\u7528\u540C\u4E00\u51ED\u636E\u5E93\u7684\u5176\u4ED6\u5B9E\u4F8B\u4E5F\u4F1A\u53D7\u5230\u5F71\u54CD\u3002\u6B64\u64CD\u4F5C\u4E0D\u6CE8\u9500 GitHub \u8D26\u53F7\u3002",
680
745
  logoutFailed: "\u9000\u51FA\u767B\u5F55\u672A\u5B8C\u6210\uFF0C\u6388\u6743\u51ED\u636E\u4ECD\u53EF\u80FD\u5B58\u5728\u3002\u8BF7\u91CD\u8BD5\u3002",
681
- // ---- 模型管理(协议 v2,设计§5) ----
682
- supplement: "\u8865\u5145\u6A21\u578B",
683
- rebuild: "\u91CD\u5EFA\u6A21\u578B\u5217\u8868",
684
- supplementTitle: "\u8865\u5145\u6A21\u578B",
685
- rebuildTitle: "\u91CD\u5EFA\u6A21\u578B\u5217\u8868",
686
- candidates: "\u53EF\u65B0\u589E\u7684\u6A21\u578B",
746
+ // ---- 模型管理(协议 v3,设计§5 / ADR 0005) ----
747
+ manageModels: "\u7BA1\u7406\u6A21\u578B\u5217\u8868",
687
748
  selectAll: "\u5168\u9009",
688
749
  selectNone: "\u53D6\u6D88\u5168\u9009",
689
- supplementRisk: "\u5C06\u4FDD\u7559\u73B0\u6709\u6A21\u578B\u53CA\u5B9A\u5236\uFF0C\u53EA\u6DFB\u52A0\u4F60\u52FE\u9009\u7684\u6A21\u578B\u3002",
690
- rebuildRisk: "\u91CD\u5EFA\u5C06\u6E05\u9664\u73B0\u6709\u6A21\u578B\u53C2\u6570\u4E0E\u8986\u76D6\u914D\u7F6E\uFF0C\u5176\u4ED6\u63D0\u4F9B\u65B9\u8BBE\u7F6E\u4E0D\u53D8\u3002",
691
- clearRisk: "\u672C\u6B21\u5C06\u6E05\u7A7A Copilot \u6A21\u578B\u5217\u8868\u5E76\u6E05\u9664\u5176\u6A21\u578B\u5B9A\u5236\u3002\u8BF7\u5355\u72EC\u786E\u8BA4\u6E05\u7A7A\u3002",
692
- secondConfirm: "\u6B64\u64CD\u4F5C\u5177\u6709\u7834\u574F\u6027\uFF0C\u8BF7\u518D\u6B21\u70B9\u51FB\u786E\u8BA4\u3002",
750
+ alignAction: "\u5BF9\u9F50\u8D26\u53F7\u4E0E\u76EE\u5F55",
751
+ groupAddable: "\u53EF\u65B0\u589E",
752
+ groupListed: "\u5728\u5217",
753
+ groupRemoval: "\u5F85\u5220\u9664\u5019\u9009",
754
+ retain: "\u633D\u7559\uFF1A\u91CD\u65B0\u52FE\u9009\u4EE5\u4FDD\u7559",
755
+ customizedBadge: "\u6709\u5B9A\u5236",
756
+ clearOverrides: "\u540C\u65F6\u6E05\u9664\u5168\u90E8\u6A21\u578B\u5B9A\u5236\u3002",
757
+ inheritedOverrides: "\u5B58\u5728\u7EE7\u627F\u7684\u6A21\u578B\u5B9A\u5236\uFF0C\u65E0\u6CD5\u5728\u6B64\u6E05\u9664\u3002",
758
+ clearRisk: "\u6E05\u7A7A\u4E0D\u4F1A\u9690\u85CF\u6A21\u578B\u2014\u2014\u4F1A\u8BDD\u5185\u5C06\u663E\u793A\u5168\u90E8\u53EF\u7528\u6A21\u578B\uFF1B\u8981\u7CBE\u7B80\uFF0C\u53EA\u9700\u52FE\u9009\u4FDD\u7559\u9879\u3002",
693
759
  applyChanges: "\u5E94\u7528\u66F4\u6539",
694
- addedModels: "\u5C06\u6DFB\u52A0\u7684\u6A21\u578B",
695
- removedModels: "\u5C06\u79FB\u9664\u7684\u6A21\u578B",
696
- keptModels: "\u4FDD\u7559",
697
- removedAccount: "\u672A\u5305\u542B\u5728\u672C\u6B21\u8D26\u53F7\u6A21\u578B\u5217\u8868",
698
- removedUnresolvable: "\u5F53\u524D\u76EE\u5F55\u65E0\u6CD5\u89E3\u6790",
699
- warnings: "\u8B66\u544A",
760
+ removalRisk: "\u5C06\u79FB\u9664 {count} \u4E2A\u5728\u5217\u6A21\u578B\uFF0C\u5176\u5B9A\u5236\u968F\u4E4B\u6E05\u9664\u3002",
761
+ summaryLine: "\u5E94\u7528\u540E\u5171 {total} \u4E2A\uFF1A\u4FDD\u7559 {kept} \xB7 \u65B0\u589E {added} \xB7 \u79FB\u9664 {removed}",
762
+ catalogNotice: "\u76EE\u5F55\u5C06\u65B0\u589E {count} \u6761\u63CF\u8FF0",
763
+ noChanges: "\u65E0\u9700\u66F4\u6539\uFF0C\u76EE\u5F55\u4E5F\u65E0\u65B0\u6570\u636E\u3002",
764
+ removalNeedsLive: "\u79FB\u9664\u7C7B\u66F4\u6539\u9700\u8981\u5B9E\u65F6\u8D26\u53F7\u8BC1\u636E",
765
+ removedAccount: "\u8D26\u53F7\u672A\u62A5\u544A",
766
+ removedUnresolvable: "\u76EE\u5F55\u65E0\u6CD5\u89E3\u6790",
700
767
  skippedModels: "\u88AB\u6821\u9A8C\u8DF3\u8FC7\u7684\u4E0A\u6E38\u6761\u76EE",
701
768
  srcLive: "\u8D26\u53F7\u6A21\u578B\uFF1A\u5B9E\u65F6\u83B7\u53D6",
702
- srcCache: "\u8D26\u53F7\u6A21\u578B\uFF1A\u7F13\u5B58\uFF0C\u83B7\u53D6\u4E8E {time}\uFF0C\u4EC5\u53EF\u7528\u4E8E\u8865\u5145",
769
+ srcCache: "\u8D26\u53F7\u6A21\u578B\uFF1A\u7F13\u5B58\uFF0C\u83B7\u53D6\u4E8E {time}\uFF1B\u79FB\u9664\u7C7B\u66F4\u6539\u9700\u5347\u7EA7\u4E3A\u5B9E\u65F6\u8BC1\u636E",
703
770
  srcStale: "\u7F13\u5B58\u7F3A\u5C11\u53EF\u4FE1\u65F6\u95F4\u3001\u5DF2\u8FC7\u671F\u6216\u6388\u6743\u5DF2\u53D8\u5316\uFF0C\u4EC5\u4F9B\u53C2\u8003\u3002",
704
771
  srcLatest: "\u76EE\u5F55\u6765\u6E90\uFF1Anpm \u6700\u65B0 pi-ai",
705
772
  srcLocal: "\u76EE\u5F55\u6765\u6E90\uFF1A\u672C\u5730\u76EE\u5F55\uFF08npm \u62C9\u53D6\u5931\u8D25\uFF09",
@@ -864,134 +931,134 @@ function sampleThemeSurface() {
864
931
  }
865
932
  function RefreshModal({ t, flow, onSelect, onConfirm, onCancel, onOverlay }) {
866
933
  const p = flow.preview;
867
- const diff = p.diff ?? {};
868
- const isRebuild = p.operation === "rebuild";
869
- const emptyTarget = isRebuild && (diff.targetView?.models ?? []).length === 0;
870
- const candidates = diff.candidates ?? [];
934
+ const isStale = p.evidence?.stale === true;
935
+ const degradedCatalog = p.catalogSource === "local" || p.catalogSource === "overlay";
936
+ const locked = isStale || degradedCatalog;
937
+ const rows = p.rows ?? [];
938
+ const addableIds = rows.filter((r) => r.status === "addable").map((r) => r.id);
939
+ const rawAllIds = rows.filter((r) => r.status !== "addable").map((r) => r.id);
940
+ const removalRows = rows.filter((r) => r.status === "removal-proposal");
871
941
  const [localSel, setLocalSel] = (0, import_react.useState)(null);
942
+ const [coChecked, setCoChecked] = (0, import_react.useState)(false);
872
943
  (0, import_react.useEffect)(() => {
873
944
  setLocalSel(null);
945
+ setCoChecked(p.clearOverrides === true);
874
946
  }, [p.diff]);
875
- const selectedIds = new Set(localSel ?? (diff.added ?? []));
876
- const emitSel = (ids) => {
877
- setLocalSel(new Set(ids));
878
- onSelect(ids);
947
+ const lockedIds = rows.filter((r) => r.status === "removal-proposal" && locked).map((r) => r.id);
948
+ const selected = (() => {
949
+ const s = new Set(localSel ?? (p.diff?.selectedIds ?? []));
950
+ for (const id of lockedIds) s.add(id);
951
+ return s;
952
+ })();
953
+ const confirmEmptyOf = (set) => set.size === 0 && p.hadModels === true;
954
+ const emitSel = (nextSet) => {
955
+ setLocalSel(nextSet);
956
+ const ce = confirmEmptyOf(nextSet);
957
+ onSelect({ selectedIds: [...nextSet], clearOverrides: coChecked, ...ce ? { confirmEmpty: true } : {} });
958
+ };
959
+ const toggle = (id) => {
960
+ const next = new Set(selected);
961
+ if (next.has(id)) next.delete(id);
962
+ else next.add(id);
963
+ emitSel(next);
964
+ };
965
+ const toggleClearOverrides = (v) => {
966
+ setCoChecked(v);
967
+ const ce = confirmEmptyOf(selected);
968
+ onSelect({ selectedIds: [...selected], clearOverrides: v, ...ce ? { confirmEmpty: true } : {} });
879
969
  };
880
- const [stage, setStage] = (0, import_react.useState)(0);
970
+ const keptCount = rawAllIds.filter((id) => selected.has(id)).length;
971
+ const addedCount = addableIds.filter((id) => selected.has(id)).length;
972
+ const removedCount = rawAllIds.length - keptCount;
973
+ const total = keptCount + addedCount;
974
+ const emptyTarget = confirmEmptyOf(selected);
975
+ const applyBlocked = isStale || removedCount > 0 && (p.evidence?.source !== "live" || degradedCatalog);
881
976
  const cancelRef = (0, import_react.useRef)(null);
882
977
  const surface = (0, import_react.useRef)(null);
883
978
  if (!surface.current) surface.current = sampleThemeSurface();
884
- (0, import_react.useEffect)(() => {
885
- setStage(0);
886
- }, [p.previewId]);
887
- (0, import_react.useEffect)(() => {
888
- if (stage > 0) cancelRef.current?.focus();
889
- }, [stage]);
890
979
  const escCancel = (e) => {
891
980
  if (e.key === "Escape") onCancel();
892
981
  };
893
982
  const confirmClick = () => {
894
- if (p.evidence?.stale === true) return;
895
- if (!isRebuild) {
896
- onConfirm({ second: false, empty: false });
897
- return;
898
- }
899
- if (stage === 0) {
900
- setStage(1);
901
- return;
902
- }
903
- if (emptyTarget && stage === 1) {
904
- setStage(2);
905
- return;
906
- }
907
- onConfirm({ second: true, empty: emptyTarget });
983
+ if (isStale || applyBlocked) return;
984
+ onConfirm({});
908
985
  };
909
986
  const sourceLine = [
910
987
  p.evidence?.source === "live" ? t("srcLive") : p.evidence?.source === "cache" ? t("srcCache").replace("{time}", String(p.evidence?.fetchedAt ?? "")) : null,
911
988
  p.catalogSource === "latest" ? t("srcLatest") : p.catalogSource === "overlay" ? t("srcOverlay") : t("srcLocal")
912
989
  ].filter(Boolean).join(" \xB7 ");
913
- const list = (items, style, render) => items.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { opacity: 0.6 }, children: t("none") }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { style: styles.diffList, children: items.map((x, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { style: { ...style, ...styles.mono }, children: render ? render(x) : x }, x?.id ?? x ?? i)) });
990
+ const groupDefs = [
991
+ { key: "groupAddable", items: rows.filter((r) => r.status === "addable") },
992
+ { key: "groupListed", items: rows.filter((r) => r.status === "listed") },
993
+ { key: "groupRemoval", items: removalRows, retain: true }
994
+ ];
914
995
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.modalMask, role: "dialog", "aria-modal": "true", onKeyDown: escCancel, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { ...styles.modal, background: surface.current.bg, color: surface.current.fg, height: "min(80vh, 680px)", padding: 0, overflow: "hidden" }, children: [
915
996
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { padding: "18px 20px 6px" }, children: [
916
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h4", { style: styles.modalTitle, children: t(isRebuild ? "rebuildTitle" : "supplementTitle") }),
917
- flow.staleNotice && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { style: styles.banner, children: [
997
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h4", { style: styles.modalTitle, children: t("manageModels") }),
998
+ flow.staleNotice && !(!isStale && p.evidence?.source !== "live" && p.catalogSource === "latest") && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { style: styles.banner, children: [
918
999
  "\u26A0 ",
919
1000
  t("previewStale")
920
1001
  ] }),
921
1002
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.modalText, children: sourceLine }),
922
- p.evidence?.stale === true && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { style: styles.banner, children: [
1003
+ isStale && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { style: styles.banner, children: [
923
1004
  "\u26A0 ",
924
1005
  t("srcStale")
925
1006
  ] })
926
1007
  ] }),
927
1008
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { flex: 1, overflowY: "auto", minHeight: 0, scrollbarGutter: "stable", padding: "0 20px" }, children: [
928
- !isRebuild && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
929
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { style: styles.modalText, children: [
930
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: t("candidates") }),
931
- "\uFF08",
932
- candidates.length,
933
- "\uFF09",
934
- candidates.length > 0 && (() => {
935
- const allSelected = candidates.every((id) => selectedIds.has(id));
936
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", style: { ...styles.button, ...styles.secondary, height: 24, marginLeft: 10, fontSize: 12 }, onClick: () => emitSel(allSelected ? [] : candidates), children: allSelected ? t("selectNone") : t("selectAll") });
937
- })()
938
- ] }),
939
- list(candidates, styles.added, (id) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [
940
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "checkbox", checked: selectedIds.has(id), onChange: () => {
941
- const next = new Set(selectedIds);
942
- if (next.has(id)) next.delete(id);
943
- else next.add(id);
944
- emitSel([...next]);
945
- } }),
946
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: id })
947
- ] })),
948
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { style: styles.risk, children: [
949
- "\u26A0 ",
950
- t("supplementRisk")
951
- ] })
952
- ] }),
953
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { style: styles.modalText, children: [
954
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: t("addedModels") }),
955
- "\uFF08",
956
- (diff.added ?? []).length,
957
- "\uFF09"
1009
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap", padding: "6px 0" }, children: [
1010
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1011
+ "button",
1012
+ {
1013
+ type: "button",
1014
+ style: { ...styles.button, ...styles.secondary, height: 24, fontSize: 12 },
1015
+ onClick: () => emitSel(/* @__PURE__ */ new Set([...addableIds, ...rawAllIds])),
1016
+ children: t("selectAll")
1017
+ }
1018
+ ),
1019
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1020
+ "button",
1021
+ {
1022
+ type: "button",
1023
+ style: { ...styles.button, ...styles.secondary, height: 24, fontSize: 12 },
1024
+ onClick: () => emitSel(new Set(lockedIds)),
1025
+ children: t("selectNone")
1026
+ }
1027
+ ),
1028
+ removalRows.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1029
+ "button",
1030
+ {
1031
+ type: "button",
1032
+ style: { ...styles.button, ...styles.secondary, height: 24, fontSize: 12, ...locked ? { opacity: 0.5, cursor: "not-allowed" } : null },
1033
+ disabled: locked,
1034
+ title: locked ? t("removalNeedsLive") : void 0,
1035
+ onClick: () => emitSel(/* @__PURE__ */ new Set([...addableIds, ...rows.filter((r) => r.status === "listed").map((r) => r.id)])),
1036
+ children: t("alignAction")
1037
+ }
1038
+ )
958
1039
  ] }),
959
- list(diff.added ?? [], styles.added),
960
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { style: styles.modalText, children: [
961
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: t("removedModels") }),
962
- "\uFF08",
963
- (diff.removed ?? []).length,
964
- "\uFF09"
965
- ] }),
966
- list(diff.removed ?? [], styles.removed, (x) => `${x.id} \u2014 ${t(x.reason === "unresolvable" ? "removedUnresolvable" : "removedAccount")}`),
967
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { style: styles.modalText, children: [
968
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: t("keptModels") }),
969
- "\uFF08",
970
- (diff.kept ?? []).length,
971
- "\uFF09"
972
- ] }),
973
- list(diff.kept ?? [], styles.muted),
974
- (diff.warnings ?? []).length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
975
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { style: styles.modalText, children: [
976
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: t("warnings") }),
1040
+ groupDefs.map(({ key, items, retain }) => items.length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginBottom: 8 }, children: [
1041
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { style: { ...styles.modalText, fontWeight: 600, margin: "6px 0 2px" }, title: retain ? t("retain") : void 0, children: [
1042
+ t(key),
977
1043
  "\uFF08",
978
- diff.warnings.length,
1044
+ items.length,
979
1045
  "\uFF09"
980
1046
  ] }),
981
- list(diff.warnings, styles.skipped, (w) => `${w.id ?? ""} \u2014 ${w.reason ?? ""}`)
982
- ] }),
983
- isRebuild && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { style: styles.risk, children: [
984
- "\u26A0 ",
985
- t("rebuildRisk")
986
- ] }),
987
- emptyTarget && stage >= 1 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { style: styles.risk, children: [
988
- "\u26A0 ",
989
- t("clearRisk")
990
- ] }),
991
- stage === 1 && !emptyTarget && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { style: styles.risk, children: [
992
- "\u26A0 ",
993
- t("secondConfirm")
994
- ] }),
1047
+ items.map((r) => {
1048
+ const forceKept = r.status === "removal-proposal" && locked;
1049
+ const checked = forceKept || selected.has(r.id);
1050
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { style: { display: "flex", gap: 8, alignItems: "center", padding: "2px 0" }, children: [
1051
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "checkbox", checked, disabled: forceKept, onChange: () => toggle(r.id) }),
1052
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.mono, children: r.id }),
1053
+ r.status === "removal-proposal" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { ...styles.removed, fontSize: 12 }, children: t(r.reason === "unresolvable" ? "removedUnresolvable" : "removedAccount") }),
1054
+ r.customized === true && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { ...styles.muted, fontSize: 12 }, children: t("customizedBadge") })
1055
+ ] }, r.id);
1056
+ })
1057
+ ] }, key)),
1058
+ (p.overridesMeta?.clearable || p.overridesMeta?.inheritedOnly) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { padding: "4px 0" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [
1059
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "checkbox", checked: coChecked, disabled: !p.overridesMeta?.clearable, onChange: (e) => toggleClearOverrides(e.target.checked) }),
1060
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: p.overridesMeta?.clearable ? void 0 : { ...styles.muted, fontSize: 13 }, children: t(p.overridesMeta?.clearable ? "clearOverrides" : "inheritedOverrides") })
1061
+ ] }) }),
995
1062
  (p.skipped ?? []).length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("details", { style: styles.modalText, children: [
996
1063
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("summary", { children: [
997
1064
  t("skippedModels"),
@@ -1005,25 +1072,57 @@ function RefreshModal({ t, flow, onSelect, onConfirm, onCancel, onOverlay }) {
1005
1072
  s.reason
1006
1073
  ] }, s.id)) })
1007
1074
  ] }),
1008
- p.catalogError && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("details", { style: styles.modalText, children: [
1075
+ ((p.diff?.warnings ?? []).length > 0 || p.catalogError) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("details", { style: styles.modalText, children: [
1009
1076
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("summary", { children: t("techDetails") }),
1010
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { ...styles.mono, fontSize: 12 }, children: p.catalogError })
1077
+ (p.diff?.warnings ?? []).length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { style: styles.diffList, children: (p.diff?.warnings ?? []).map((w, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { style: styles.skipped, children: [
1078
+ w.id,
1079
+ " \u2014 ",
1080
+ w.reason
1081
+ ] }, w.id ?? i)) }),
1082
+ p.catalogError && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { ...styles.mono, fontSize: 12 }, children: p.catalogError })
1011
1083
  ] })
1012
1084
  ] }),
1013
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { padding: "8px 20px 16px" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.modalActions, children: [
1014
- p.catalogSource === "local" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", style: { ...styles.button, ...styles.secondary, marginRight: "auto" }, onClick: onOverlay, children: t("overlayBtn") }),
1015
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { ref: cancelRef, type: "button", style: { ...styles.button, ...styles.secondary }, onClick: onCancel, children: t("cancel") }),
1016
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1017
- "button",
1018
- {
1019
- type: "button",
1020
- style: { ...styles.button, ...styles.primary, ...p.evidence?.stale === true ? { opacity: 0.5, cursor: "not-allowed" } : null },
1021
- disabled: p.evidence?.stale === true,
1022
- onClick: confirmClick,
1023
- children: t("applyChanges")
1024
- }
1025
- )
1026
- ] }) })
1085
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { padding: "8px 20px 16px" }, children: [
1086
+ (removedCount > 0 || coChecked || emptyTarget) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginBottom: 4 }, children: [
1087
+ removedCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { style: styles.risk, children: [
1088
+ "\u26A0 ",
1089
+ t("removalRisk").replace("{count}", String(removedCount))
1090
+ ] }),
1091
+ coChecked && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { style: styles.risk, children: [
1092
+ "\u26A0 ",
1093
+ t("clearOverrides")
1094
+ ] }),
1095
+ emptyTarget && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { style: styles.risk, children: [
1096
+ "\u26A0 ",
1097
+ t("clearRisk")
1098
+ ] })
1099
+ ] }),
1100
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { ...styles.modalText, margin: 0 }, children: total === 0 && removedCount === 0 && (p.catalogNewEntries ?? 0) === 0 ? t("noChanges") : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1101
+ t("summaryLine").replace("{total}", String(total)).replace("{kept}", String(keptCount)).replace("{added}", String(addedCount)).replace("{removed}", String(removedCount)),
1102
+ (p.catalogNewEntries ?? 0) > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1103
+ " \xB7 ",
1104
+ t("catalogNotice").replace("{count}", String(p.catalogNewEntries))
1105
+ ] })
1106
+ ] }) }),
1107
+ removedCount > 0 && applyBlocked && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { style: styles.banner, children: [
1108
+ "\u26A0 ",
1109
+ t("removalNeedsLive")
1110
+ ] }),
1111
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.modalActions, children: [
1112
+ p.catalogSource === "local" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", style: { ...styles.button, ...styles.secondary, marginRight: "auto" }, onClick: onOverlay, children: t("overlayBtn") }),
1113
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { ref: cancelRef, type: "button", style: { ...styles.button, ...styles.secondary }, onClick: onCancel, children: t("cancel") }),
1114
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1115
+ "button",
1116
+ {
1117
+ type: "button",
1118
+ style: { ...styles.button, ...styles.primary, ...applyBlocked ? { opacity: 0.5, cursor: "not-allowed" } : null },
1119
+ disabled: applyBlocked,
1120
+ onClick: confirmClick,
1121
+ children: t("applyChanges")
1122
+ }
1123
+ )
1124
+ ] })
1125
+ ] })
1027
1126
  ] }) });
1028
1127
  }
1029
1128
  function RetireDialog({ t, onConfirm, onCancel }) {
@@ -1090,9 +1189,6 @@ function CopilotSection({ t = (key) => DICTS.en[key] ?? key }) {
1090
1189
  setCopyFail(false);
1091
1190
  void authFlowRef.current?.start();
1092
1191
  };
1093
- const withdraw = () => {
1094
- void authFlowRef.current?.cancel();
1095
- };
1096
1192
  const checkStatus = () => {
1097
1193
  void authFlowRef.current?.refresh();
1098
1194
  };
@@ -1143,15 +1239,13 @@ function CopilotSection({ t = (key) => DICTS.en[key] ?? key }) {
1143
1239
  ] }),
1144
1240
  codeNotice.url && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { style: styles.link, href: codeNotice.url, target: "_blank", rel: "noreferrer", children: codeNotice.url }),
1145
1241
  copyFail && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.error, children: t("copyFailed") })
1146
- ] }),
1147
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", style: { ...styles.button, ...styles.secondary }, onClick: withdraw, children: t("withdrawAuth") }) })
1242
+ ] })
1148
1243
  ] }),
1149
1244
  phase === "risk" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.card, children: [
1150
1245
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { style: styles.banner, children: [
1151
1246
  "\u26A0 ",
1152
1247
  t("authUnsafe")
1153
1248
  ] }),
1154
- auth.riskKind === "withdrawal-pending-unverified" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.modalText, children: auth.withdrawalDelivery === "invoked" ? t("withdrawal") : t("withdrawalUnavailable") }),
1155
1249
  auth.riskKind === "timed-out-unverified" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.modalText, children: t("attemptTimeout") }),
1156
1250
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", style: { ...styles.button, ...styles.secondary }, onClick: checkStatus, children: t("retryNow") }) })
1157
1251
  ] }),
@@ -1159,7 +1253,16 @@ function CopilotSection({ t = (key) => DICTS.en[key] ?? key }) {
1159
1253
  "\u26A0 ",
1160
1254
  t("unavailable")
1161
1255
  ] }),
1162
- phase === "failed" && attempt?.error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.error, children: attempt.error }),
1256
+ phase === "failed" && attempt?.error && (() => {
1257
+ const m = /^fetch failed \(([A-Za-z0-9_]+)\)$/.exec(attempt.error);
1258
+ if (!m) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.error, children: attempt.error });
1259
+ const code = m[1];
1260
+ const human = code === "ENOTFOUND" ? t("netDns") : code.startsWith("CERT") || code.includes("SIGNATURE") ? t("netCert") : t("netConnect");
1261
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1262
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.error, children: human }),
1263
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { ...styles.modalText, opacity: 0.6 }, children: attempt.error })
1264
+ ] });
1265
+ })(),
1163
1266
  phase === "failed" && !attempt?.error && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1164
1267
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { style: styles.banner, children: [
1165
1268
  "\u26A0 ",
@@ -1187,28 +1290,16 @@ function CopilotSection({ t = (key) => DICTS.en[key] ?? key }) {
1187
1290
  }
1188
1291
  );
1189
1292
  })(),
1190
- ["previewing", "applying", "checking", "retiring"].includes(flow.name) ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", style: { ...styles.button, ...styles.primary, opacity: 0.5 }, disabled: true, children: flow.name === "applying" ? t("applying") : t("refreshing") }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1191
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1192
- "button",
1193
- {
1194
- type: "button",
1195
- style: { ...styles.button, ...styles.primary, ...refreshBlocked(flow.flags) ? { opacity: 0.5, cursor: "not-allowed" } : null },
1196
- disabled: refreshBlocked(flow.flags),
1197
- onClick: () => drive({ type: "start", operation: "supplement" }),
1198
- children: t("supplement")
1199
- }
1200
- ),
1201
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1202
- "button",
1203
- {
1204
- type: "button",
1205
- style: { ...styles.button, ...styles.primary, ...refreshBlocked(flow.flags) ? { opacity: 0.5, cursor: "not-allowed" } : null },
1206
- disabled: refreshBlocked(flow.flags),
1207
- onClick: () => drive({ type: "start", operation: "rebuild" }),
1208
- children: t("rebuild")
1209
- }
1210
- )
1211
- ] })
1293
+ ["previewing", "applying", "checking", "retiring"].includes(flow.name) ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", style: { ...styles.button, ...styles.primary, opacity: 0.5 }, disabled: true, children: flow.name === "applying" ? t("applying") : t("refreshing") }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1294
+ "button",
1295
+ {
1296
+ type: "button",
1297
+ style: { ...styles.button, ...styles.primary, ...refreshBlocked(flow.flags) ? { opacity: 0.5, cursor: "not-allowed" } : null },
1298
+ disabled: refreshBlocked(flow.flags),
1299
+ onClick: () => drive({ type: "start", operation: "manage" }),
1300
+ children: t("manageModels")
1301
+ }
1302
+ )
1212
1303
  ] }),
1213
1304
  auth.logoutError && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { style: styles.error, children: [
1214
1305
  "\u26A0 ",
@@ -1268,7 +1359,7 @@ function CopilotSection({ t = (key) => DICTS.en[key] ?? key }) {
1268
1359
  {
1269
1360
  t,
1270
1361
  flow,
1271
- onSelect: (selectedIds) => drive({ type: "select", selectedIds }),
1362
+ onSelect: ({ selectedIds, clearOverrides, confirmEmpty }) => drive({ type: "select", selectedIds, clearOverrides, confirmEmpty }),
1272
1363
  onConfirm: (opts) => drive({ type: "confirm", ...opts }),
1273
1364
  onCancel: () => drive({ type: "cancel" }),
1274
1365
  onOverlay: () => drive({ type: "start", catalogSource: "overlay" })