dsh-multi-router 0.5.8 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +49 -8
  3. package/cordis.patch.yml +2 -2
  4. package/lib/accounts.js +6 -4
  5. package/lib/api-service.js +11 -0
  6. package/lib/api-service.js.map +7 -0
  7. package/lib/chunk-4V52V5GO.js +105 -0
  8. package/lib/chunk-4V52V5GO.js.map +7 -0
  9. package/lib/{chunk-OFFVP6YN.js → chunk-JUOESKAK.js} +22 -5
  10. package/lib/chunk-JUOESKAK.js.map +7 -0
  11. package/lib/{chunk-P43AWJ4Q.js → chunk-LLDJVBTQ.js} +3 -3
  12. package/lib/{chunk-U2MU2M5C.js → chunk-NSKCFVHP.js} +13 -111
  13. package/lib/chunk-NSKCFVHP.js.map +7 -0
  14. package/lib/{chunk-ILRW3ELD.js → chunk-OCMOMSB4.js} +293 -77
  15. package/lib/chunk-OCMOMSB4.js.map +7 -0
  16. package/lib/{chunk-FTM7RHQV.js → chunk-OK2T2UAZ.js} +4 -4
  17. package/lib/{chunk-J3XO7RTF.js → chunk-ROZINFTV.js} +3 -2
  18. package/lib/chunk-ROZINFTV.js.map +7 -0
  19. package/lib/chunk-SNEZMYNU.js +332 -0
  20. package/lib/chunk-SNEZMYNU.js.map +7 -0
  21. package/lib/{chunk-MCHV2R25.js → chunk-WVNY4LSL.js} +3 -1
  22. package/lib/{chunk-MCHV2R25.js.map → chunk-WVNY4LSL.js.map} +2 -2
  23. package/lib/cli.js +7 -6
  24. package/lib/cli.js.map +1 -1
  25. package/lib/client.js +743 -315
  26. package/lib/client.js.map +2 -2
  27. package/lib/clients.js +6 -5
  28. package/lib/{host-v0.5.8.js → host-v0.6.0.js} +22 -123
  29. package/lib/host.js +22 -123
  30. package/lib/index.js +22 -123
  31. package/lib/index.js.map +4 -4
  32. package/lib/manager.js +11 -7
  33. package/lib/router.js +2 -2
  34. package/lib/shim.js +3 -3
  35. package/package.json +1 -1
  36. package/lib/chunk-ILRW3ELD.js.map +0 -7
  37. package/lib/chunk-J3XO7RTF.js.map +0 -7
  38. package/lib/chunk-OFFVP6YN.js.map +0 -7
  39. package/lib/chunk-U2MU2M5C.js.map +0 -7
  40. /package/lib/{chunk-P43AWJ4Q.js.map → chunk-LLDJVBTQ.js.map} +0 -0
  41. /package/lib/{chunk-FTM7RHQV.js.map → chunk-OK2T2UAZ.js.map} +0 -0
package/lib/client.js CHANGED
@@ -21,11 +21,16 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var client_exports = {};
22
22
  __export(client_exports, {
23
23
  AccountPool: () => AccountPool,
24
+ ApiServicePanel: () => ApiServicePanel,
24
25
  CheckinPanel: () => CheckinPanel,
25
26
  InitialSetup: () => InitialSetup,
27
+ ModelsPanel: () => ModelsPanel,
26
28
  PluginUpdatesPanel: () => PluginUpdatesPanel,
27
29
  RestartGuidance: () => RestartGuidance,
28
30
  RouterPage: () => RouterPage,
31
+ RoutesTable: () => RoutesTable,
32
+ SidebarUsage: () => SidebarUsage,
33
+ SidebarUsageCard: () => SidebarUsageCard,
29
34
  UpdateReminder: () => UpdateReminder,
30
35
  UsageDetails: () => UsageDetails,
31
36
  apply: () => apply,
@@ -42,7 +47,7 @@ module.exports = __toCommonJS(client_exports);
42
47
  var import_react = require("react");
43
48
 
44
49
  // package.json
45
- var version = "0.5.8";
50
+ var version = "0.6.0";
46
51
 
47
52
  // src/client.jsx
48
53
  var import_jsx_runtime = require("react/jsx-runtime");
@@ -113,12 +118,113 @@ var styles = `
113
118
  .mr .table-shell{padding:0;overflow:hidden}.mr .scroll>.table{min-width:760px}.mr .table{font-size:14px}.mr .table th{background:var(--mr-soft);font-size:12px;letter-spacing:.02em;font-weight:500;white-space:nowrap}.mr .table th,.mr .table td{padding:16px 20px;vertical-align:middle}.mr .table td:last-child{white-space:nowrap}.mr .table tbody tr:last-child td{border-bottom:0}.mr .table td small{display:block;font-size:12px;margin-top:3px}.mr .table td>strong{font-weight:600}.mr .table tr.selected{background:var(--mr-selected)}.mr .table tbody tr:hover{background:var(--mr-soft)}.mr .table .account-name{max-width:230px;overflow-wrap:anywhere}.mr .table .amount{font-size:20px;line-height:1.35;font-variant-numeric:tabular-nums;font-weight:600;white-space:nowrap}.mr .table .cell-actions{white-space:nowrap;text-align:right}.mr .table .account-check{min-width:44px;min-height:44px;justify-content:center;flex-shrink:0}.mr .table .account-cell{display:flex;gap:10px;align-items:center}.mr .table .account-cell .check{margin-left:-12px}.mr .table .account-cell input{accent-color:var(--mr-text)}.mr .table .row{flex-wrap:nowrap}.mr .section-title{margin-bottom:4px}.mr .toolbar{align-items:flex-end;gap:16px}.mr .search{max-width:280px;flex:1;min-width:160px}.mr .table-footer{padding:14px 20px;background:var(--mr-soft)}.mr .hint{font-size:13px;color:var(--mr-muted)}.mr details.help{border:0;padding:0}.mr details.help>div{padding:12px 0;max-width:76ch}.mr .plan-form{align-items:flex-end;padding:16px 0 0;border-top:1px solid var(--mr-border)}.mr .plan-form .filter{min-width:180px}.mr .steps{border:1px solid var(--mr-border);border-radius:10px;background:var(--mr-soft);padding:14px 16px}.mr .route-path span{background:var(--mr-soft);border-color:transparent;font-size:12px}.mr .route-card details{padding-top:10px}.mr .feedback{margin-bottom:16px}.mr .box .feedback{margin-bottom:0}.mr .scroll{max-width:100%}.mr .scroll:focus-visible{outline:2px solid var(--mr-accent);outline-offset:-3px}
114
119
  @media(max-width:850px){.mr .metrics{gap:10px}.mr nav{gap:0}.mr nav button{padding:12px 10px}}@media(max-width:600px){.mr h2{font-size:22px}.mr nav{display:grid;grid-template-columns:repeat(4,minmax(0,1fr))}.mr nav button{padding:10px 4px;font-size:12px;white-space:nowrap}.mr .toolbar>.row{width:100%}.mr .toolbar .search{max-width:none}.mr .metric strong{font-size:24px}.mr .table th,.mr .table td{padding:14px 16px}.mr .filter{max-width:none}.mr .plan-form label{flex:1}.mr .box.table-shell{padding:0}}
115
120
 
121
+
122
+ .mr .metrics{gap:0;border:1px solid var(--mr-border);border-radius:12px;overflow:hidden;background:var(--mr-card)}
123
+ .mr .metrics .metric{border:0;border-radius:0;background:transparent;border-right:1px solid var(--mr-border);padding:16px 20px}.mr .metrics .metric:last-child{border-right:0}.mr .metric strong{font-size:25px;margin:6px 0}.mr .performance{gap:10px}.mr .performance .metrics{background:var(--mr-soft);border-color:transparent}.mr .performance .metric strong{font-size:21px}.mr .performance .metric{padding:12px 20px}.mr .performance h3{font-size:14px}.mr .refresh-control{font-size:12px;color:var(--mr-muted)}
124
+ .mr .section-pad{padding:16px 20px}.mr .table-shell>.section-pad+.section-pad{padding-top:0}.mr .route-table td{max-width:300px}.mr .route-table .route-targets{max-width:300px;white-space:normal;overflow-wrap:anywhere}.mr .route-table small{max-width:320px}.mr .model-table td:first-child{width:38%}.mr .table .cell-actions{width:1%}.mr .install-plugin>div{padding-top:16px}.mr details.help.box{padding:18px 20px}.mr .sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}
125
+ @media(max-width:600px){.mr .metrics .metric{padding:12px 14px}.mr .metrics .metric:nth-child(2){border-right:0}.mr .metrics .metric:nth-child(-n+2){border-bottom:1px solid var(--mr-border)}.mr .section-pad{padding:14px 16px}}
126
+
116
127
  @media(prefers-reduced-motion:reduce){.mr *{transition:none!important}}
117
128
  `;
118
129
  function apply(ctx) {
119
130
  reloadFromHost = () => requestPluginReload(ctx.get?.("remote"));
120
131
  ctx.slots.inject("plugins.bundle.config", () => ctx.slots.register({ name: "plugins.bundle.config", key: "dsh-multi-router" }, RouterPage));
121
132
  ctx.slots.inject("shell.overlay", () => ctx.slots.register({ name: "shell.overlay", id: "dsh-multi-router-updates" }, UpdateReminder));
133
+ ctx.slots.inject("sidebar.footer.action", () => ctx.slots.register({
134
+ name: "sidebar.footer.action",
135
+ id: "dsh-multi-router-usage",
136
+ order: 10,
137
+ inject: () => ({ open: () => {
138
+ const navigation = ctx.get?.("pluginNavigation");
139
+ if (!navigation?.openBundle) throw Error("\u8BF7\u5728\u63D2\u4EF6\u9875\u6253\u5F00 dsh-multi-router\uFF1B\u5F53\u524D DSH \u672A\u63D0\u4F9B\u8BE6\u60C5\u5BFC\u822A\u3002");
140
+ navigation.openBundle("dsh-multi-router");
141
+ } })
142
+ }, SidebarUsage));
143
+ }
144
+ var SUMMARY_EVENT = "dsh-multi-router-summary";
145
+ var compactTokens = (n) => n >= 1e9 ? `${(n / 1e9).toFixed(1)}B` : n >= 1e6 ? `${(n / 1e6).toFixed(1)}M` : n >= 1e3 ? `${(n / 1e3).toFixed(1)}K` : fmt(n);
146
+ function SidebarUsageCard({ data, wide = true, open, error }) {
147
+ const title = `${data.today}\uFF08\u5317\u4EAC\u65F6\u95F4\uFF09
148
+ \u4ECA\u65E5 ${fmt(data.tokens)} Token \xB7 ${fmt(data.requests)} \u6B21\u8BF7\u6C42
149
+ \u7D2F\u8BA1 ${fmt(data.totalTokens)} Token${data.unmetered ? `
150
+ ${fmt(data.unmetered)} \u6B21\u8C03\u7528\u672A\u8FD4\u56DE\u7528\u91CF` : ""}${error ? `
151
+ ${error}` : ""}
152
+ \u70B9\u51FB\u6253\u5F00\u8D26\u53F7\u4E0E\u8DEF\u7531`;
153
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
154
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: `
155
+ [class*="_footArea"] [class*="_footerActions"]:has(.mr-sidebar-usage){flex-direction:column}
156
+ .mr-sidebar-usage{box-sizing:border-box;display:flex;align-items:center;gap:10px;width:100%;min-width:0;min-height:48px;padding:7px 10px;border:1px solid var(--dsw-alias-border-l2,#444);border-radius:8px;background:transparent;color:var(--dsw-alias-label-primary,#eee);font:inherit;text-align:left;cursor:pointer}
157
+ .mr-sidebar-usage:hover{background:var(--dsw-alias-bg-module-platform,#303036)}.mr-sidebar-usage:focus-visible{outline:2px solid var(--dsw-alias-state-active-primary,#3a71db);outline-offset:2px}
158
+ .mr-sidebar-usage svg{flex-shrink:0}.mr-sidebar-usage span{flex:1;min-width:0}.mr-sidebar-usage small{display:block;color:var(--dsw-alias-label-secondary,#aaa);font-size:10px;line-height:15px;white-space:nowrap}.mr-sidebar-usage strong{display:block;font-size:13px;font-weight:600;line-height:18px;font-variant-numeric:tabular-nums;white-space:nowrap}.mr-sidebar-usage[data-wide=false]{width:36px;min-height:36px;padding:8px;justify-content:center}
159
+ ` }),
160
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { className: "mr-sidebar-usage", "data-wide": wide, title, "aria-label": `Token \u7528\u91CF\u6982\u89C8\uFF0C\u4ECA\u65E5 ${fmt(data.tokens)}\uFF0C\u7D2F\u8BA1 ${fmt(data.totalTokens)}\uFF0C\u6253\u5F00\u63D2\u4EF6\u8BE6\u60C5`, onClick: open, children: [
161
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.6", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M4 4v16h17M8 16v-5m5 5V7m5 9v-7" }) }),
162
+ wide && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
163
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
164
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("small", { children: [
165
+ "\u4ECA\u65E5 Token",
166
+ error ? " \xB7 \u672A\u5237\u65B0" : ""
167
+ ] }),
168
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: compactTokens(data.tokens) })
169
+ ] }),
170
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
171
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { children: "\u7D2F\u8BA1 Token" }),
172
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: compactTokens(data.totalTokens) })
173
+ ] })
174
+ ] })
175
+ ] })
176
+ ] });
177
+ }
178
+ function SidebarUsage({ wide = true, open }) {
179
+ const [data, setData] = (0, import_react.useState)(null), [error, setError] = (0, import_react.useState)("");
180
+ (0, import_react.useEffect)(() => {
181
+ let stopped = false, pending = false, again = false;
182
+ const abort = new AbortController();
183
+ async function poll(event) {
184
+ if (document.hidden) return;
185
+ if (pending) {
186
+ if (event?.type === SUMMARY_EVENT) again = true;
187
+ return;
188
+ }
189
+ pending = true;
190
+ try {
191
+ const next = await readManagementResponse(await fetch(`${ENDPOINT}/summary`, { cache: "no-store", signal: AbortSignal.any([abort.signal, AbortSignal.timeout(8e3)]) }));
192
+ if (!stopped) {
193
+ setData(next);
194
+ setError("");
195
+ }
196
+ } catch {
197
+ if (!stopped) setError("\u6682\u65F6\u65E0\u6CD5\u5237\u65B0\u672C\u673A\u7EDF\u8BA1");
198
+ } finally {
199
+ pending = false;
200
+ if (again && !stopped) {
201
+ again = false;
202
+ void poll();
203
+ }
204
+ }
205
+ }
206
+ void poll();
207
+ const timer = setInterval(() => {
208
+ void poll();
209
+ }, 6e4);
210
+ document.addEventListener("visibilitychange", poll);
211
+ window.addEventListener(SUMMARY_EVENT, poll);
212
+ return () => {
213
+ stopped = true;
214
+ abort.abort();
215
+ clearInterval(timer);
216
+ document.removeEventListener("visibilitychange", poll);
217
+ window.removeEventListener(SUMMARY_EVENT, poll);
218
+ };
219
+ }, []);
220
+ if (!data || data.enabled === false) return null;
221
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SidebarUsageCard, { data, wide, error, open: () => {
222
+ try {
223
+ open();
224
+ } catch (e) {
225
+ setError(e.message);
226
+ }
227
+ } });
122
228
  }
123
229
  function Modal({ title, close, children, busy }) {
124
230
  const ref = (0, import_react.useRef)(null);
@@ -270,7 +376,7 @@ function RouterPage({ view = "page" }) {
270
376
  }).finally(() => {
271
377
  polling.current = false;
272
378
  });
273
- }, 15e3);
379
+ }, 6e4);
274
380
  return () => {
275
381
  stopped = true;
276
382
  abort.abort();
@@ -281,7 +387,7 @@ function RouterPage({ view = "page" }) {
281
387
  (0, import_react.useEffect)(() => {
282
388
  if (data?.startup?.phase !== "discovering" && !["waiting", "reloading"].includes(data?.runtime?.phase) && data?.pluginUpdates?.job?.state !== "running" && !data?.checkins?.running) return;
283
389
  const timer = setInterval(() => {
284
- if (polling.current) return;
390
+ if (document.hidden || polling.current) return;
285
391
  polling.current = true;
286
392
  void load().catch((e) => setError(e.message)).finally(() => {
287
393
  polling.current = false;
@@ -310,9 +416,10 @@ function RouterPage({ view = "page" }) {
310
416
  try {
311
417
  const res = await fetch(ENDPOINT, { method: "POST", headers: { "Content-Type": "application/json", "X-Multi-Router-Key": data.key }, body: JSON.stringify({ revision: data.revision, ...action }) });
312
418
  const next = await readManagementResponse(res);
313
- const { exported, workbuddyLogin, loginResult, ...state } = next;
419
+ const { exported, workbuddyLogin, loginResult, apiKey, ...state } = next;
314
420
  setData(state);
315
- setNotice(action.action === "workbuddy.sms" ? "" : action.action === "workbuddy.login" ? loginResult?.warning || "\u767B\u5F55\u51ED\u8BC1\u5DF2\u4FDD\u5B58\uFF0C\u81EA\u52A8\u7B7E\u5230\u5F00\u5173\u4FDD\u6301\u539F\u8BBE\u7F6E\u3002" : action.action === "checkins.verify" ? "\u767B\u5F55\u9A8C\u8BC1\u901A\u8FC7\uFF0C\u65E7\u7684\u4EE4\u724C\u5931\u6548\u63D0\u793A\u5DF2\u89E3\u9664\uFF1B\u672A\u63D0\u4EA4\u7B7E\u5230\u3002" : action.action === "checkins.settings" ? "\u7B7E\u5230\u8BA1\u5212\u5DF2\u4FDD\u5B58\uFF1B\u4ECA\u65E5\u5DF2\u5904\u7406\u8BB0\u5F55\u4FDD\u6301\u4E0D\u53D8\u3002" : action.action.startsWith("checkins.") || action.action.startsWith("plugins.") || action.action === "setup.dismiss" ? "" : action.action === "accounts.discover" || action.action === "monitor.refresh" ? "\u5DF2\u91CD\u65B0\u68C0\u6D4B\u672C\u673A\u767B\u5F55\u5E76\u5237\u65B0\u7528\u91CF\uFF1B\u68C0\u6D4B\u7ED3\u679C\u89C1\u4E0B\u65B9\u3002" : action.action.includes("refresh") ? "\u5DF2\u5237\u65B0\u3002\u65E0\u6CD5\u83B7\u53D6\u7684\u8D26\u53F7\u4F1A\u4FDD\u7559\u4E0A\u6B21\u6570\u636E\u5E76\u6807\u8BB0\u3002" : action.action === "config.import" ? "\u914D\u7F6E\u5DF2\u5BFC\u5165\u5E76\u751F\u6548\uFF0C\u539F\u914D\u7F6E\u5DF2\u5907\u4EFD\uFF1B\u6A21\u578B\u548C\u7528\u91CF\u6B63\u5728\u540E\u53F0\u66F4\u65B0\u3002" : action.action.startsWith("config.") || action.action === "account.export" ? "" : "\u5DF2\u4FDD\u5B58\u5E76\u751F\u6548\u3002");
421
+ if (action.sidebarUsage !== void 0 || action.action === "config.import") window.dispatchEvent(new Event(SUMMARY_EVENT));
422
+ setNotice(action.action.startsWith("api.") ? next.apiService?.phase === "error" ? "\u914D\u7F6E\u5DF2\u4FDD\u5B58\uFF0C\u4F46\u670D\u52A1\u5C1A\u672A\u542F\u52A8\uFF0C\u8BF7\u67E5\u770B\u4E0B\u65B9\u63D0\u793A\u3002" : action.action === "api.key.reveal" ? "" : "API \u8BBE\u7F6E\u5DF2\u4FDD\u5B58\u3002" : action.action === "workbuddy.sms" ? "" : action.action === "workbuddy.login" ? loginResult?.warning || "\u767B\u5F55\u51ED\u8BC1\u5DF2\u4FDD\u5B58\uFF0C\u81EA\u52A8\u7B7E\u5230\u5F00\u5173\u4FDD\u6301\u539F\u8BBE\u7F6E\u3002" : action.action === "checkins.verify" ? "\u767B\u5F55\u9A8C\u8BC1\u901A\u8FC7\uFF0C\u65E7\u7684\u4EE4\u724C\u5931\u6548\u63D0\u793A\u5DF2\u89E3\u9664\uFF1B\u672A\u63D0\u4EA4\u7B7E\u5230\u3002" : action.action === "checkins.settings" ? "\u7B7E\u5230\u8BA1\u5212\u5DF2\u4FDD\u5B58\uFF1B\u4ECA\u65E5\u5DF2\u5904\u7406\u8BB0\u5F55\u4FDD\u6301\u4E0D\u53D8\u3002" : action.action.startsWith("checkins.") || action.action.startsWith("plugins.") || action.action === "setup.dismiss" ? "" : action.action === "accounts.discover" || action.action === "monitor.refresh" ? "\u5DF2\u91CD\u65B0\u68C0\u6D4B\u672C\u673A\u767B\u5F55\u5E76\u5237\u65B0\u7528\u91CF\uFF1B\u68C0\u6D4B\u7ED3\u679C\u89C1\u4E0B\u65B9\u3002" : action.action.includes("refresh") ? "\u5DF2\u5237\u65B0\u3002\u65E0\u6CD5\u83B7\u53D6\u7684\u8D26\u53F7\u4F1A\u4FDD\u7559\u4E0A\u6B21\u6570\u636E\u5E76\u6807\u8BB0\u3002" : action.action === "config.import" ? "\u914D\u7F6E\u5DF2\u5BFC\u5165\u5E76\u751F\u6548\uFF0C\u539F\u914D\u7F6E\u5DF2\u5907\u4EFD\uFF1B\u6A21\u578B\u548C\u7528\u91CF\u6B63\u5728\u540E\u53F0\u66F4\u65B0\u3002" : action.action.startsWith("config.") || action.action === "account.export" ? "" : "\u5DF2\u4FDD\u5B58\u5E76\u751F\u6548\u3002");
316
423
  return next;
317
424
  } catch (e) {
318
425
  setError(e.message);
@@ -360,16 +467,26 @@ function RouterPage({ view = "page" }) {
360
467
  "v",
361
468
  data?.pluginVersion ?? version
362
469
  ] }),
470
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "check refresh-control", children: [
471
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "checkbox", disabled: busy || !data || backendNeedsRestart(data), checked: data?.options?.sidebarUsage !== false, onChange: (e) => {
472
+ void act({ action: "options.save", sidebarUsage: e.target.checked });
473
+ } }),
474
+ "\u4FA7\u680F\u7528\u91CF"
475
+ ] }),
363
476
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "status", children: [
364
477
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("i", { className: busy || !data ? "warn" : "good", "aria-hidden": "true" }),
365
478
  busy ? "\u6B63\u5728\u5904\u7406\u2026" : !data ? "\u6B63\u5728\u8FDE\u63A5\u2026" : "\u672C\u673A\u8FD0\u884C"
366
479
  ] }),
480
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "check refresh-control", children: [
481
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "checkbox", checked: live, onChange: (e) => setLive(e.target.checked) }),
482
+ "\u6BCF\u5206\u949F\u5237\u65B0"
483
+ ] }),
367
484
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: busy, onClick: () => {
368
485
  void load().catch((e) => setError(e.message));
369
486
  }, children: "\u5237\u65B0\u9762\u677F" })
370
487
  ] })
371
488
  ] }),
372
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("nav", { "aria-label": "\u8DEF\u7531\u63D2\u4EF6\u914D\u7F6E\u9875\u7B7E", children: [["overview", "\u603B\u89C8\u4E0E\u7528\u91CF"], ["usage", "Token \u660E\u7EC6"], ["accounts", "\u8D26\u53F7\u4E0E\u767B\u5F55"], ["checkins", "\u79EF\u5206\u7B7E\u5230"], ["models", "\u6A21\u578B\u7BA1\u7406"], ["routes", "\u8DEF\u7531\u89C4\u5219"], ["plugins", "\u63D2\u4EF6\u66F4\u65B0"], ["migration", "\u914D\u7F6E\u8FC1\u79FB"]].map(([key, label]) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { "aria-pressed": tab === key, onClick: () => {
489
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("nav", { "aria-label": "\u8DEF\u7531\u63D2\u4EF6\u914D\u7F6E\u9875\u7B7E", children: [["overview", "\u603B\u89C8\u4E0E\u7528\u91CF"], ["usage", "Token \u660E\u7EC6"], ["accounts", "\u8D26\u53F7\u4E0E\u767B\u5F55"], ["checkins", "\u79EF\u5206\u7B7E\u5230"], ["models", "\u6A21\u578B\u7BA1\u7406"], ["routes", "\u8DEF\u7531\u89C4\u5219"], ["api", "API \u670D\u52A1"], ["plugins", "\u63D2\u4EF6\u66F4\u65B0"], ["migration", "\u914D\u7F6E\u8FC1\u79FB"]].map(([key, label]) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { "aria-pressed": tab === key, onClick: () => {
373
490
  setTab(key);
374
491
  setError("");
375
492
  setNotice("");
@@ -400,83 +517,28 @@ function RouterPage({ view = "page" }) {
400
517
  }, children: "\u91CD\u65B0\u52A0\u8F7D\u63D2\u4EF6" })
401
518
  ] }),
402
519
  ["overview", "accounts"].includes(tab) && data.setup?.dismissed !== true && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InitialSetup, { data, busy, act, go: (key) => setTab(key) }),
403
- tab === "overview" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Overview, { data, live, setLive, busy, act, go: (key) => setTab(key) }),
520
+ tab === "overview" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Overview, { data, busy, act, go: (key) => setTab(key) }),
521
+ tab === "api" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ApiServicePanel, { data: data.apiService, busy, act }),
404
522
  tab === "plugins" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PluginUpdatesPanel, { data: data.pluginUpdates, busy, act }),
405
523
  tab === "checkins" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CheckinPanel, { data, busy, act }),
406
524
  tab === "usage" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(UsageDetails, { data, busy, act }),
407
525
  tab === "accounts" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AccountPool, { data, provider: accountProvider, providerSelect: accountProviderSelect, busy, act, manage: (a) => openAccount("edit", a), add: () => openAccount("add") }, accountProvider),
408
- tab === "models" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "stack", children: [
409
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "box stack", children: [
410
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bar", children: [
411
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
412
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "\u539F\u59CB\u6A21\u578B\u5C55\u793A" }),
413
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "notice", children: [
414
- "\u5168\u90E8\u6E20\u9053\u5DF2\u5C55\u793A ",
415
- data.providers.reduce((n, p2) => n + p2.models.filter((m) => m.visible).length, 0),
416
- " / ",
417
- data.providers.reduce((n, p2) => n + p2.models.length, 0),
418
- " \u4E2A\u6A21\u578B"
419
- ] })
420
- ] }),
421
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "row", children: [
422
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: busy, onClick: () => {
423
- void act({ action: "models.visibility", visible: true });
424
- }, children: "\u5168\u90E8\u5C55\u793A" }),
425
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: busy, onClick: () => {
426
- void act({ action: "models.visibility", visible: false });
427
- }, children: "\u5168\u90E8\u9690\u85CF\uFF08\u4EC5\u8DEF\u7531\uFF09" })
428
- ] })
429
- ] }),
430
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: "\u4E09\u5BB6\u6240\u6709\u533A\u57DF\u7684\u539F\u59CB\u6A21\u578B\u9ED8\u8BA4\u9690\u85CF\uFF0C\u53EA\u5C55\u793A\u7EDF\u4E00\u8DEF\u7531\u3002\u6279\u91CF\u64CD\u4F5C\u4F5C\u7528\u4E8E\u5168\u90E8\u6E20\u9053\uFF0C\u5E76\u9002\u7528\u4E8E\u540E\u7EED\u53D1\u73B0\u7684\u65B0\u6A21\u578B\uFF1B\u5355\u4E2A\u6A21\u578B\u4ECD\u53EF\u5355\u72EC\u8C03\u6574\u3002\u9690\u85CF\u4E0D\u5F71\u54CD\u8DEF\u7531\u76EE\u6807\u6216\u5DF2\u6709\u4F1A\u8BDD\u3002" })
431
- ] }),
432
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bar", children: [
433
- providerSelect,
434
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: busy, onClick: () => {
435
- void act({ action: "refresh", provider });
436
- }, children: "\u83B7\u53D6\u6A21\u578B\u76EE\u5F55" })
437
- ] }),
438
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
439
- "\u641C\u7D22\u6A21\u578B",
440
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "search", placeholder: "\u8F93\u5165\u540D\u79F0\u6216\u6A21\u578B ID", value: search, onChange: (e) => setSearch(e.target.value) })
441
- ] }),
442
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: "\u5C55\u793A\u5F00\u5173\u51B3\u5B9A\u539F\u59CB\u6A21\u578B\u662F\u5426\u51FA\u73B0\u5728 DSH \u9009\u62E9\u5668\uFF1B\u8DEF\u7531\u5C55\u793A\u5728\u300C\u8DEF\u7531\u89C4\u5219\u300D\u4E2D\u72EC\u7ACB\u8BBE\u7F6E\u3002" }),
443
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "box", children: [
444
- !p?.models.length && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "empty", children: "\u5148\u6DFB\u52A0\u5E76\u542F\u7528\u8D26\u53F7\uFF0C\u518D\u83B7\u53D6\u6A21\u578B\u76EE\u5F55\u3002" }),
445
- p?.models.filter((m) => `${m.id} ${m.name}`.toLowerCase().includes(search.toLowerCase())).map((m) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: "item grid", onSubmit: (e) => {
446
- e.preventDefault();
447
- void act({ action: "model.update", provider, id: m.id, name: new FormData(e.currentTarget).get("name") });
448
- }, children: [
449
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
450
- "\u6A21\u578B\u540D\u79F0",
451
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { name: "name", defaultValue: m.name, required: true, maxLength: 200, disabled: busy }, m.name),
452
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "mono muted", children: m.id })
453
- ] }),
454
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bar", children: [
455
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "check", children: [
456
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "checkbox", disabled: busy, checked: m.visible, onChange: (e) => {
457
- void act({ action: "model.update", provider, id: m.id, visible: e.target.checked });
458
- } }),
459
- "\u5C55\u793A"
460
- ] }),
461
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: busy, type: "submit", children: "\u4FDD\u5B58\u540D\u79F0" }),
462
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "notice", children: [
463
- "\u4E0A\u4E0B\u6587 ",
464
- fmt(m.contextWindow / 1e3),
465
- "K"
466
- ] })
467
- ] })
468
- ] }, `${provider}:${m.id}`))
469
- ] })
470
- ] }),
526
+ tab === "models" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ModelsPanel, { data, provider, providerSelect, search, setSearch, busy, act }),
471
527
  tab === "routes" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "stack", children: [
472
528
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bar", children: [
473
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "muted", children: "\u4E00\u4E2A\u8C03\u7528\u540D\u79F0\uFF0C\u8FDE\u63A5\u591A\u4E2A\u8D26\u53F7\u4E0E\u6A21\u578B\u3002" }),
529
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
530
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "\u6A21\u578B\u8DEF\u7531" }),
531
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "notice", children: [
532
+ data.routes.length,
533
+ " \u6761\u8DEF\u7531 \xB7 \u89C4\u5219\u4E0E\u8D26\u53F7\u5728\u7F16\u8F91\u4E2D\u8C03\u6574"
534
+ ] })
535
+ ] }),
474
536
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "row", children: [
475
537
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: busy, onClick: () => {
476
538
  setError("");
477
539
  setNotice("");
478
540
  setPresetModal(true);
479
- }, children: "\u6DFB\u52A0\u6A21\u578B\u9884\u914D\u7F6E" }),
541
+ }, children: "\u6DFB\u52A0\u9884\u914D\u7F6E" }),
480
542
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "primary", disabled: busy, onClick: () => {
481
543
  setError("");
482
544
  setNotice("");
@@ -484,50 +546,7 @@ function RouterPage({ view = "page" }) {
484
546
  }, children: "\u65B0\u5EFA\u8DEF\u7531" })
485
547
  ] })
486
548
  ] }),
487
- data.routes.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "box empty", children: "\u5148\u6DFB\u52A0\u4E09\u5BB6\u5171\u6709\u6A21\u578B\u9884\u914D\u7F6E\uFF0C\u6216\u521B\u5EFA\u4F60\u81EA\u5DF1\u7684\u8DEF\u7531\u3002" }),
488
- data.routes.map((r) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "box stack route-card", children: [
489
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bar", children: [
490
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: r.name }) }),
491
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "row", children: [
492
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "badge", children: [
493
- r.freeOnly ? "\u4EC5\u514D\u8D39 \xB7 \u81EA\u52A8\u53D1\u73B0" : strategies[r.strategy || "failover"],
494
- r.preferExpiringCredits ? " \xB7 \u4E34\u671F\u4F18\u5148" : ""
495
- ] }),
496
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: busy, onClick: () => editRoute(r), children: "\u7F16\u8F91\u8DEF\u7531" })
497
- ] })
498
- ] }),
499
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "route-path", children: r.targets.map((t, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
500
- data.providers.find((p2) => p2.id === t.provider)?.name,
501
- " \xB7 ",
502
- t.account ? data.accounts.find((a) => a.id === t.account)?.name : "\u5168\u90E8\u542F\u7528\u8D26\u53F7",
503
- r.strategy === "weighted" ? ` \xB7 \u6743\u91CD ${t.weight || 1}` : r.strategy === "tokens" ? ` \xB7 ${fmt(t.tokenBudget)} Token` : r.strategy === "credits" ? ` \xB7 ${fmt(t.creditBudget)} \u79EF\u5206` : ""
504
- ] }, i)) }),
505
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bar", children: [
506
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "notice", children: [
507
- r.targets.length,
508
- " \u4E2A\u76EE\u6807 \xB7 ",
509
- data.telemetry?.rounds[r.id] ? `\u7B2C ${data.telemetry.rounds[r.id].number} \u8F6E \xB7 ` : "",
510
- "4xx \u81EA\u52A8\u5207\u6362"
511
- ] }),
512
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "check", children: [
513
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "checkbox", disabled: busy, checked: r.visible !== false, onChange: (e) => {
514
- const { id, reasoning, supportsImages, expiringStatus, freeStatus, ...route } = r;
515
- void act({ action: "route.save", id, previousId: id, route: { ...route, visible: e.target.checked } });
516
- } }),
517
- r.freeOnly ? "\u5C55\u793A\u52FE\u9009\u7684\u514D\u8D39\u6A21\u578B" : "\u5728\u6A21\u578B\u9009\u62E9\u5668\u5C55\u793A"
518
- ] })
519
- ] }),
520
- r.freeOnly && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FreeStatus, { route: r, busy, act }),
521
- !r.freeOnly && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("details", { children: [
522
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("summary", { children: "\u63A8\u7406\u6A21\u5F0F\u4E0E\u517C\u5BB9\u6027" }),
523
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "notice", children: [
524
- "\u63A8\u7406\u6A21\u5F0F\uFF1A\u5E73\u53F0\u9ED8\u8BA4",
525
- r.reasoning?.supportedEfforts?.length ? `\u3001${r.reasoning.supportedEfforts.map((e) => effortNames[e] || e).join("\u3001")}` : "\uFF08\u5E73\u53F0\u672A\u58F0\u660E\u53EF\u8C03\u6863\u4F4D\uFF09",
526
- "\u3002\u9009\u62E9\u6863\u4F4D\u540E\uFF0C\u53EA\u5728\u652F\u6301\u8BE5\u6863\u4F4D\u7684\u76EE\u6807\u95F4\u5207\u6362\u3002"
527
- ] })
528
- ] }),
529
- r.preferExpiringCredits && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ExpiryStatus, { route: r, act, busy, revision: data.revision })
530
- ] }, r.id)),
549
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RoutesTable, { data, busy, act, edit: editRoute }),
531
550
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("details", { children: [
532
551
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("summary", { children: "\u5168\u5C40\u5207\u6362\u8BBE\u7F6E" }),
533
552
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: "box stack", style: { marginTop: 16 }, onSubmit: (e) => {
@@ -581,6 +600,348 @@ function RouterPage({ view = "page" }) {
581
600
  }, error, notice }) })
582
601
  ] });
583
602
  }
603
+ function ApiServicePanel({ data: a, busy, act }) {
604
+ const [key, setKey] = (0, import_react.useState)(""), [hint, setHint] = (0, import_react.useState)(""), [confirmKey, setConfirmKey] = (0, import_react.useState)(null), [models, setModels] = (0, import_react.useState)(false), [example, setExample] = (0, import_react.useState)("");
605
+ const copyField = (0, import_react.useRef)(null);
606
+ (0, import_react.useEffect)(() => {
607
+ setKey((current) => a?.keyHint && current.endsWith(a.keyHint.slice(-4)) ? current : "");
608
+ }, [a?.keyHint]);
609
+ (0, import_react.useEffect)(() => {
610
+ if (!key) return;
611
+ const timer = setTimeout(() => setKey(""), 6e4);
612
+ return () => clearTimeout(timer);
613
+ }, [key]);
614
+ if (!a) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "steps", children: "\u5F53\u524D\u540E\u53F0\u672A\u63D0\u4F9B API \u670D\u52A1\uFF0C\u8BF7\u66F4\u65B0\u63D2\u4EF6\u3002" });
615
+ const save = (fields) => act({ action: "api.settings", apiRevision: a.revision, ...fields });
616
+ async function keyAction(action) {
617
+ const result = await act({ action, apiRevision: a.revision });
618
+ if (result) {
619
+ setKey(result.apiKey || "");
620
+ setConfirmKey(null);
621
+ }
622
+ }
623
+ async function copy(value, fallback = false) {
624
+ try {
625
+ await navigator.clipboard.writeText(value);
626
+ setHint("\u5DF2\u590D\u5236\u3002");
627
+ } catch {
628
+ setHint("\u5F53\u524D\u73AF\u5883\u65E0\u6CD5\u81EA\u52A8\u590D\u5236\uFF0C\u8BF7\u5728\u6587\u672C\u6846\u4E2D\u5168\u9009\u590D\u5236\u3002");
629
+ if (fallback) copyField.current?.select();
630
+ else setExample(value);
631
+ }
632
+ }
633
+ const model = a.models[0]?.id || "\u4F60\u7684\u8DEF\u7531\u6A21\u578B ID";
634
+ const command = `curl '${a.baseUrl}/chat/completions' \\
635
+ -H 'Authorization: Bearer YOUR_API_KEY' \\
636
+ -H 'Content-Type: application/json' \\
637
+ -d '${JSON.stringify({ model, messages: [{ role: "user", content: "\u4F60\u597D" }], stream: true })}'`;
638
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "stack", children: [
639
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "box stack", children: [
640
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bar", children: [
641
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
642
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "API \u670D\u52A1" }),
643
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: "\u5C06\u8D26\u53F7\u6C60\u4E0E\u7EDF\u4E00\u8DEF\u7531\u63D0\u4F9B\u7ED9\u5176\u4ED6\u5DE5\u5177\u3002\u968F DSH \u8FD0\u884C\uFF0C\u9ED8\u8BA4\u5173\u95ED\u3002" })
644
+ ] }),
645
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "status", role: "status", children: [
646
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("i", { className: a.phase === "running" ? "good" : a.phase === "error" ? "warn" : "", "aria-hidden": "true" }),
647
+ { running: "\u8FD0\u884C\u4E2D", stopped: "\u5DF2\u5173\u95ED", starting: "\u542F\u52A8\u4E2D", error: "\u542F\u52A8\u5931\u8D25" }[a.phase] || a.phase
648
+ ] })
649
+ ] }),
650
+ a.error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "error", role: "alert", children: a.error }),
651
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bar", children: [
652
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: a.phase === "running" ? `${a.active} \u4E2A\u5904\u7406\u4E2D\u8BF7\u6C42 \xB7 ${a.allowLan ? "\u672C\u673A\u4E0E\u7F51\u7EDC\u8BBF\u95EE" : "\u4EC5\u672C\u673A\u8BBF\u95EE"}` : "\u5F00\u542F\u540E\u5373\u53EF\u590D\u5236\u63A5\u5165\u5730\u5740\uFF1B\u5173\u95ED\u4F1A\u7EC8\u6B62\u6B63\u5728\u5904\u7406\u7684 API \u8BF7\u6C42\u3002" }),
653
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "check", children: [
654
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "checkbox", checked: a.enabled, disabled: busy || !a.hasKey, onChange: (e) => {
655
+ void save({ enabled: e.target.checked });
656
+ } }),
657
+ "\u542F\u7528 API \u670D\u52A1"
658
+ ] })
659
+ ] }),
660
+ !a.hasKey && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "steps", children: "\u5148\u751F\u6210 API Key\uFF0C\u518D\u542F\u7528\u670D\u52A1\u3002\u8D26\u53F7\u767B\u5F55\u51ED\u8BC1\u4E0D\u4F1A\u901A\u8FC7\u6B64\u670D\u52A1\u63D0\u4F9B\u3002" })
661
+ ] }),
662
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "metrics", children: [
663
+ metric("\u4ECA\u65E5 API \u8C03\u7528", fmt(a.today.calls), `${a.today.attempts} \u6B21\u4E0A\u6E38\u5C1D\u8BD5\uFF0C\u542B\u6545\u969C\u5207\u6362`),
664
+ metric("\u4ECA\u65E5 API Token", fmt(a.today.tokens), "\u5E73\u53F0\u5B9E\u9645\u8FD4\u56DE\u7528\u91CF"),
665
+ metric("\u5DF2\u5F00\u653E\u6A21\u578B", fmt(a.models.length), a.modelIds === null ? "\u8DDF\u968F\u8DEF\u7531\u5C55\u793A\u8BBE\u7F6E" : "\u81EA\u9009\u6A21\u578B"),
666
+ metric("\u5904\u7406\u4E2D", fmt(a.active), "\u6700\u591A 8 \u4E2A\u5E76\u53D1 API \u8BF7\u6C42")
667
+ ] }),
668
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "box stack", children: [
669
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bar", children: [
670
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
671
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "\u63A5\u5165\u4FE1\u606F" }),
672
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: "\u5BA2\u6237\u7AEF\u9009\u62E9 OpenAI \u517C\u5BB9\u65B9\u5F0F\uFF0C\u586B\u5199\u4E0B\u65B9 Base URL \u4E0E API Key\u3002" })
673
+ ] }),
674
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: busy, onClick: () => setExample(command), children: "\u8C03\u7528\u793A\u4F8B" })
675
+ ] }),
676
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
677
+ "Base URL",
678
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "row", children: [
679
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { className: "mono", style: { flex: 1, minWidth: 180 }, readOnly: true, value: a.baseUrl, onFocus: (e) => e.currentTarget.select() }),
680
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: () => {
681
+ void copy(a.baseUrl);
682
+ }, children: "\u590D\u5236\u5730\u5740" })
683
+ ] })
684
+ ] }),
685
+ a.allowLan && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
686
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: "\u5176\u4ED6\u8BBE\u5907\u53EF\u4F7F\u7528\u4EE5\u4E0B\u672C\u673A\u7F51\u5361\u5730\u5740\uFF08\u9700\u8981\u7F51\u7EDC\u53EF\u8FBE\u53CA\u9632\u706B\u5899\u653E\u884C\uFF09\uFF1A" }),
687
+ a.lanUrls.length ? a.lanUrls.map((url) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "mono", style: { overflowWrap: "anywhere" }, children: url }, url)) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: "\u6682\u65E0\u53EF\u7528 IPv4 \u7F51\u5361\u5730\u5740\u3002" })
688
+ ] }),
689
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
690
+ "API Key",
691
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "row", children: [
692
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { ref: copyField, className: "mono", style: { flex: 1, minWidth: 180 }, readOnly: true, autoComplete: "off", spellCheck: "false", value: key || a.keyHint || "\u5C1A\u672A\u751F\u6210", onFocus: (e) => e.currentTarget.select() }),
693
+ a.hasKey && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
694
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: busy, onClick: () => {
695
+ if (key) setKey("");
696
+ else void keyAction("api.key.reveal");
697
+ }, children: key ? "\u9690\u85CF" : "\u663E\u793A" }),
698
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: busy, onClick: async () => {
699
+ const result = await act({ action: "api.key.reveal", apiRevision: a.revision });
700
+ if (result?.apiKey) {
701
+ setKey(result.apiKey);
702
+ await copy(result.apiKey, true);
703
+ }
704
+ }, children: "\u590D\u5236 Key" })
705
+ ] })
706
+ ] })
707
+ ] }),
708
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "row", children: [
709
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: busy, onClick: () => {
710
+ if (a.hasKey) setConfirmKey("create");
711
+ else void keyAction("api.key.create");
712
+ }, children: a.hasKey ? "\u91CD\u7F6E API Key" : "\u751F\u6210 API Key" }),
713
+ a.hasKey && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: busy, onClick: () => setConfirmKey("revoke"), children: "\u64A4\u9500 Key \u5E76\u5173\u95ED\u670D\u52A1" }),
714
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: "Key \u4EC5\u4FDD\u5B58\u5728\u672C\u673A\u79C1\u6709\u6587\u4EF6\uFF1B\u663E\u793A\u540E 60 \u79D2\u81EA\u52A8\u9690\u85CF\u3002" })
715
+ ] }),
716
+ hint && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", role: "status", children: hint })
717
+ ] }),
718
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "box stack", children: [
719
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "\u670D\u52A1\u8BBE\u7F6E" }),
720
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: "stack", onSubmit: (e) => {
721
+ e.preventDefault();
722
+ const f = new FormData(e.currentTarget);
723
+ void save({ port: Number(f.get("port")), allowLan: f.has("lan") });
724
+ }, children: [
725
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "row", children: [
726
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "filter", children: [
727
+ "\u76D1\u542C\u7AEF\u53E3",
728
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "number", name: "port", min: "1024", max: "65535", required: true, defaultValue: a.port, disabled: busy })
729
+ ] }),
730
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "check", children: [
731
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "checkbox", name: "lan", defaultChecked: a.allowLan, disabled: busy }),
732
+ "\u5141\u8BB8\u5C40\u57DF\u7F51\u8BBF\u95EE"
733
+ ] }),
734
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: busy, children: "\u4FDD\u5B58\u8BBE\u7F6E" })
735
+ ] }),
736
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: "\u9ED8\u8BA4\u76D1\u542C 127.0.0.1\u3002\u5F00\u542F\u7F51\u7EDC\u8BBF\u95EE\u540E\u76D1\u542C\u6240\u6709 IPv4 \u7F51\u5361\uFF0C\u4F7F\u7528 HTTP\uFF0C\u8BF7\u4EC5\u7528\u4E8E\u53EF\u4FE1\u7F51\u7EDC\uFF1B\u5982\u9700\u5916\u7F51\u8BBF\u95EE\uFF0C\u8BF7\u81EA\u884C\u914D\u7F6E HTTPS \u4E0E\u8BBF\u95EE\u9650\u5236\u3002\u4FEE\u6539\u7AEF\u53E3\u6216\u8BBF\u95EE\u8303\u56F4\u4F1A\u4E2D\u65AD\u5F53\u524D API \u8BF7\u6C42\u3002" })
737
+ ] }, a.revision)
738
+ ] }),
739
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "box table-shell", children: [
740
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bar table-footer", children: [
741
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
742
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "\u5F00\u653E\u6A21\u578B" }),
743
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: a.modelIds === null ? "\u81EA\u52A8\u8DDF\u968F\u5DF2\u5C55\u793A\u8DEF\u7531\uFF0C\u5305\u542B\u52A8\u6001\u514D\u8D39\u6A21\u578B\u3002" : "\u4EC5\u5F00\u653E\u624B\u52A8\u9009\u62E9\u7684\u8DEF\u7531\u3002" })
744
+ ] }),
745
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: busy, onClick: () => setModels(true), children: "\u9009\u62E9\u5F00\u653E\u6A21\u578B" })
746
+ ] }),
747
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "scroll", role: "region", "aria-label": "API \u5F00\u653E\u6A21\u578B", tabIndex: "0", children: [
748
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("table", { className: "table", children: [
749
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
750
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u6A21\u578B\u540D\u79F0" }),
751
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u8C03\u7528\u6A21\u578B ID" })
752
+ ] }) }),
753
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: a.models.map((m) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
754
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: m.name }),
755
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: "mono", style: { overflowWrap: "anywhere" }, children: m.id })
756
+ ] }, m.id)) })
757
+ ] }),
758
+ !a.models.length && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "empty", children: "\u6682\u672A\u5F00\u653E\u6A21\u578B\uFF0C\u8BF7\u5C55\u793A\u8DEF\u7531\u6216\u624B\u52A8\u9009\u62E9\u3002" })
759
+ ] })
760
+ ] }),
761
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("details", { children: [
762
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("summary", { children: "\u63A5\u53E3\u4E0E\u7EDF\u8BA1\u8BF4\u660E" }),
763
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: "\u652F\u6301 GET /v1/models\u3001POST /v1/chat/completions\uFF08\u6D41\u5F0F\u4E0E\u666E\u901A JSON\uFF09\uFF0C\u6CBF\u7528\u8DEF\u7531\u7684\u63A8\u7406\u9ED8\u8BA4\u503C\uFF0C\u53EF\u7528 reasoning_effort \u8986\u76D6\u3002\u5DE5\u5177\u8C03\u7528\u4E0E\u56FE\u7247\u80FD\u529B\u53D6\u51B3\u4E8E\u76EE\u6807\u6A21\u578B\uFF1B\u5916\u90E8\u5DE5\u5177\u81EA\u884C\u6267\u884C\u5DE5\u5177\u5E76\u56DE\u4F20\u7ED3\u679C\u3002\u7528\u91CF\u8BB0\u5F55\u5230 Token \u660E\u7EC6\u7684\u300CAPI \u670D\u52A1\u300D\u6765\u6E90\uFF0C\u4E0D\u4FDD\u5B58\u8BF7\u6C42\u5185\u5BB9\u6216 Key\uFF1B\u4E0A\u4E0B\u6587\u7531\u8C03\u7528\u5BA2\u6237\u7AEF\u4F20\u5165\u3002\u6B64\u7248\u672C\u4E0D\u63D0\u4F9B Responses\u3001Embeddings \u6216\u8DE8\u7AD9\u6D4F\u89C8\u5668 CORS \u63A5\u53E3\u3002\u542F\u7528\u72B6\u6001\u4F1A\u4FDD\u5B58\uFF0CDSH \u4E0B\u6B21\u542F\u52A8\u81EA\u52A8\u6062\u590D\uFF1B\u9000\u51FA DSH \u540E\u505C\u6B62\u3002" })
764
+ ] }),
765
+ confirmKey && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Modal, { title: confirmKey === "create" ? "\u91CD\u7F6E API Key" : "\u64A4\u9500 API Key", busy, close: () => setConfirmKey(null), children: [
766
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "modal-body stack", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: confirmKey === "create" ? "\u65E7 Key \u5C06\u7ACB\u5373\u5931\u6548\uFF0C\u5176\u4ED6\u5BA2\u6237\u7AEF\u9700\u8981\u586B\u5199\u65B0 Key\u3002" : "Key \u5C06\u7ACB\u5373\u5931\u6548\uFF0CAPI \u670D\u52A1\u4F1A\u5173\u95ED\uFF0C\u6B63\u5728\u5904\u7406\u7684 API \u8BF7\u6C42\u5C06\u4E2D\u65AD\u3002" }) }),
767
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "modal-foot row", children: [
768
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: busy, onClick: () => setConfirmKey(null), children: "\u53D6\u6D88" }),
769
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { className: "primary", disabled: busy, onClick: () => {
770
+ void keyAction(`api.key.${confirmKey}`);
771
+ }, children: [
772
+ "\u786E\u8BA4",
773
+ confirmKey === "create" ? "\u91CD\u7F6E" : "\u64A4\u9500"
774
+ ] })
775
+ ] })
776
+ ] }),
777
+ models && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Modal, { title: "\u9009\u62E9 API \u5F00\u653E\u6A21\u578B", busy, close: () => setModels(false), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ApiModels, { data: a, busy, save: async (modelIds) => {
778
+ if (await save({ modelIds })) setModels(false);
779
+ } }) }),
780
+ example && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Modal, { title: "\u63A5\u5165\u793A\u4F8B / \u624B\u52A8\u590D\u5236", busy: false, close: () => setExample(""), children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "modal-body stack", children: [
781
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: "\u5C06 YOUR_API_KEY \u66FF\u6362\u4E3A\u81EA\u5DF1\u7684\u670D\u52A1 Key\u3002Base URL \u5DF2\u5305\u542B /v1\uFF1B\u6D4B\u8BD5\u8C03\u7528\u4F1A\u6B63\u5E38\u6D88\u8017\u76EE\u6807\u8D26\u53F7\u7684\u7528\u91CF\u3002" }),
782
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
783
+ "\u53EF\u590D\u5236\u5185\u5BB9",
784
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("textarea", { className: "mono", readOnly: true, rows: 8, value: example, onFocus: (e) => e.currentTarget.select() })
785
+ ] })
786
+ ] }) })
787
+ ] });
788
+ }
789
+ function ApiModels({ data, busy, save }) {
790
+ const [follow, setFollow] = (0, import_react.useState)(data.modelIds === null), [selected, setSelected] = (0, import_react.useState)(data.modelIds ?? data.models.map((m) => m.id));
791
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { onSubmit: (e) => {
792
+ e.preventDefault();
793
+ void save(follow ? null : selected);
794
+ }, children: [
795
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "modal-body stack", children: [
796
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "check", children: [
797
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "checkbox", checked: follow, disabled: busy, onChange: (e) => setFollow(e.target.checked) }),
798
+ "\u8DDF\u968F\u8DEF\u7531\u5C55\u793A\uFF08\u65B0\u53D1\u73B0\u7684\u514D\u8D39\u6A21\u578B\u4E5F\u81EA\u52A8\u5F00\u653E\uFF09"
799
+ ] }),
800
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "row", children: [
801
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", disabled: busy || follow, onClick: () => setSelected(data.choices.map((m) => m.id)), children: "\u5168\u9009" }),
802
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", disabled: busy || follow, onClick: () => setSelected([]), children: "\u5168\u4E0D\u9009" })
803
+ ] }),
804
+ data.choices.map((m) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "check", children: [
805
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "checkbox", disabled: busy || follow, checked: follow ? m.visible : selected.includes(m.id), onChange: (e) => setSelected(e.target.checked ? [...selected, m.id] : selected.filter((id) => id !== m.id)) }),
806
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
807
+ m.name,
808
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { className: "mono muted", style: { overflowWrap: "anywhere" }, children: m.id })
809
+ ] })
810
+ ] }, m.id)),
811
+ !data.choices.length && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: "\u8BF7\u5148\u6DFB\u52A0\u8DEF\u7531\u3002" })
812
+ ] }),
813
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "modal-foot row", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "primary", disabled: busy, children: "\u4FDD\u5B58\u5F00\u653E\u8303\u56F4" }) })
814
+ ] });
815
+ }
816
+ function ModelsPanel({ data, provider, providerSelect, search, setSearch, busy, act }) {
817
+ const [editing, setEditing] = (0, import_react.useState)(null);
818
+ const models = data.providers.find((p) => p.id === provider)?.models ?? [];
819
+ const all = data.providers.flatMap((p) => p.models), shown = models.filter((m) => `${m.id} ${m.name}`.toLowerCase().includes(search.toLowerCase()));
820
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "stack", children: [
821
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bar", children: [
822
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
823
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "\u539F\u59CB\u6A21\u578B\u5C55\u793A" }),
824
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "notice", children: [
825
+ "\u5DF2\u5C55\u793A ",
826
+ all.filter((m) => m.visible).length,
827
+ " / ",
828
+ all.length,
829
+ " \u4E2A \xB7 \u9ED8\u8BA4\u4EC5\u5C55\u793A\u7EDF\u4E00\u8DEF\u7531"
830
+ ] })
831
+ ] }),
832
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "row", children: [
833
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: busy, onClick: () => {
834
+ void act({ action: "models.visibility", visible: true });
835
+ }, children: "\u5168\u90E8\u5C55\u793A" }),
836
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: busy, onClick: () => {
837
+ void act({ action: "models.visibility", visible: false });
838
+ }, children: "\u5168\u90E8\u9690\u85CF\uFF08\u4EC5\u8DEF\u7531\uFF09" })
839
+ ] })
840
+ ] }),
841
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bar toolbar", children: [
842
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "row", children: [
843
+ providerSelect,
844
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "search", children: [
845
+ "\u641C\u7D22\u6A21\u578B",
846
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "search", placeholder: "\u540D\u79F0\u6216\u6A21\u578B ID", value: search, onChange: (e) => setSearch(e.target.value) })
847
+ ] })
848
+ ] }),
849
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: busy, onClick: () => {
850
+ void act({ action: "refresh", provider });
851
+ }, children: "\u83B7\u53D6\u6A21\u578B\u76EE\u5F55" })
852
+ ] }),
853
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "box table-shell", children: [
854
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "scroll", role: "region", "aria-label": "\u6A21\u578B\u5C55\u793A\u7BA1\u7406", tabIndex: "0", children: [
855
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("table", { className: "table model-table", children: [
856
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
857
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { scope: "col", children: "\u6A21\u578B" }),
858
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { scope: "col", children: "\u4E0A\u4E0B\u6587 / \u63A8\u7406" }),
859
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { scope: "col", children: "\u9009\u62E9\u5668\u5C55\u793A" }),
860
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { scope: "col", className: "cell-actions", children: "\u7BA1\u7406" })
861
+ ] }) }),
862
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: shown.map((m) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { className: m.visible ? "selected" : "", children: [
863
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("td", { children: [
864
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: m.name }),
865
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { className: "mono muted", children: m.id })
866
+ ] }),
867
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("td", { children: [
868
+ fmt(m.contextWindow / 1e3),
869
+ "K",
870
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { className: "muted", children: m.reasoning?.supportedEfforts?.length ? m.reasoning.supportedEfforts.map((e) => effortNames[e] || e).join(" / ") : "\u5E73\u53F0\u9ED8\u8BA4" })
871
+ ] }),
872
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "check", children: [
873
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "checkbox", "aria-label": `\u5C55\u793A ${m.name}`, disabled: busy, checked: m.visible, onChange: (e) => {
874
+ void act({ action: "model.update", provider, id: m.id, visible: e.target.checked });
875
+ } }),
876
+ m.visible ? "\u5DF2\u5C55\u793A" : "\u5DF2\u9690\u85CF"
877
+ ] }) }),
878
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: "cell-actions", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: busy, onClick: () => setEditing({ id: m.id, name: m.name, provider }), children: "\u4FEE\u6539\u540D\u79F0" }) })
879
+ ] }, m.id)) })
880
+ ] }),
881
+ !shown.length && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "empty", children: models.length ? "\u6CA1\u6709\u5339\u914D\u7684\u6A21\u578B\u3002" : "\u6DFB\u52A0\u5E76\u542F\u7528\u8D26\u53F7\u540E\uFF0C\u83B7\u53D6\u6A21\u578B\u76EE\u5F55\u3002" })
882
+ ] }),
883
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "table-footer", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: "\u9690\u85CF\u4E0D\u5F71\u54CD\u8DEF\u7531\u6216\u5DF2\u6709\u4F1A\u8BDD\u3002\u6279\u91CF\u8BBE\u7F6E\u4F5C\u7528\u4E8E\u5168\u90E8\u4F9B\u5E94\u5546\u53CA\u540E\u7EED\u53D1\u73B0\u7684\u6A21\u578B\u3002" }) })
884
+ ] }),
885
+ editing && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Modal, { title: "\u4FEE\u6539\u6A21\u578B\u540D\u79F0", busy, close: () => setEditing(null), children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { onSubmit: async (e) => {
886
+ e.preventDefault();
887
+ if (await act({ action: "model.update", ...editing })) setEditing(null);
888
+ }, children: [
889
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "modal-body stack", children: [
890
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
891
+ "\u6A21\u578B\u540D\u79F0",
892
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { autoFocus: true, required: true, maxLength: 200, disabled: busy, value: editing.name, onChange: (e) => setEditing({ ...editing, name: e.target.value }) })
893
+ ] }),
894
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "mono muted", children: editing.id })
895
+ ] }),
896
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "modal-foot row", children: [
897
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", disabled: busy, onClick: () => setEditing(null), children: "\u53D6\u6D88" }),
898
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "primary", disabled: busy, children: "\u4FDD\u5B58\u540D\u79F0" })
899
+ ] })
900
+ ] }) })
901
+ ] });
902
+ }
903
+ function RoutesTable({ data, busy, act, edit }) {
904
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("section", { className: "box table-shell", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "scroll", role: "region", "aria-label": "\u6A21\u578B\u8DEF\u7531\u89C4\u5219", tabIndex: "0", children: [
905
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("table", { className: "table route-table", children: [
906
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
907
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { scope: "col", children: "\u8DEF\u7531 / \u6A21\u578B" }),
908
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { scope: "col", children: "\u8D26\u53F7\u4E0E\u6E20\u9053" }),
909
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { scope: "col", children: "\u5207\u6362\u89C4\u5219" }),
910
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { scope: "col", children: "\u5C55\u793A" }),
911
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { scope: "col", className: "cell-actions", children: "\u7BA1\u7406" })
912
+ ] }) }),
913
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: data.routes.map((r) => {
914
+ const accounts = data.accounts.filter((a) => a.enabled !== false && r.targets.some((t) => t.provider === a.provider && (!t.account || t.account === a.id)));
915
+ const providers = [...new Set(r.targets.map((t) => data.providers.find((p) => p.id === t.provider)?.name || t.provider))];
916
+ const cooling = r.expiringStatus?.cooldownUntil > Date.now(), modes = r.reasoning?.supportedEfforts ?? [];
917
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { className: r.visible !== false ? "selected" : "", children: [
918
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("td", { children: [
919
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: r.name }),
920
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { className: "muted", children: r.freeOnly ? `${r.freeStatus?.modelRoutes?.filter((m) => m.selected).length || 0} \u4E2A\u514D\u8D39\u6A21\u578B\u5DF2\u5C55\u793A` : modes.length ? `\u63A8\u7406\uFF1A${modes.map((e) => effortNames[e] || e).join(" / ")}` : "\u63A8\u7406\uFF1A\u5E73\u53F0\u9ED8\u8BA4" })
921
+ ] }),
922
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("td", { className: "route-targets", children: [
923
+ accounts.length,
924
+ " \u4E2A\u53C2\u4E0E\u8D26\u53F7",
925
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { className: "muted", children: providers.join("\u3001") })
926
+ ] }),
927
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("td", { children: [
928
+ r.freeOnly ? "\u4EC5\u514D\u8D39 \xB7 \u81EA\u52A8\u53D1\u73B0" : strategies[r.strategy || "failover"],
929
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { className: cooling ? "warn" : "muted", children: cooling ? "\u4E34\u671F\u4F18\u5148\u51B7\u5374\u4E2D" : r.preferExpiringCredits ? "\u4E34\u671F\u79EF\u5206\u4F18\u5148" : `${r.targets.length} \u4E2A\u76EE\u6807 \xB7 4xx \u5207\u6362` })
930
+ ] }),
931
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "check", children: [
932
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "checkbox", "aria-label": `\u5C55\u793A\u8DEF\u7531 ${r.name}`, disabled: busy, checked: r.visible !== false, onChange: (e) => {
933
+ const { id, reasoning, supportsImages, expiringStatus, freeStatus, ...route } = r;
934
+ void act({ action: "route.save", id, previousId: id, route: { ...route, visible: e.target.checked } });
935
+ } }),
936
+ r.visible !== false ? "\u5F00\u542F" : "\u5173\u95ED"
937
+ ] }) }),
938
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: "cell-actions", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: busy, onClick: () => edit(r), children: r.freeOnly ? "\u7BA1\u7406\u514D\u8D39\u6A21\u578B" : "\u7F16\u8F91\u8DEF\u7531" }) })
939
+ ] }, r.id);
940
+ }) })
941
+ ] }),
942
+ !data.routes.length && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "empty", children: "\u6DFB\u52A0\u6A21\u578B\u9884\u914D\u7F6E\uFF0C\u6216\u521B\u5EFA\u81EA\u5B9A\u4E49\u8DEF\u7531\u3002" })
943
+ ] }) });
944
+ }
584
945
  var displayName = (a) => (a?.name || a?.id || "\u672A\u77E5\u8D26\u53F7").replace(/(?<!\d)(\+?\d{2,3})\d{3,8}(\d{4})(?!\d)/g, "$1****$2");
585
946
  var resultLabels = { claimed: "\u7B7E\u5230\u6210\u529F", "already-claimed": "\u5DF2\u9886\u53D6", "no-campaign": "\u65E0\u6D3B\u52A8", retryable: "\u67E5\u8BE2\u5931\u8D25", "before-window": "\u7A97\u53E3\u672A\u5F00", "auth-invalid": "\u4EE4\u724C\u5931\u6548", paused: "\u5E73\u53F0\u6682\u505C", uncertain: "\u5F85\u6838\u5BF9", "device-used": "\u8BBE\u5907\u5DF2\u53C2\u4E0E" };
586
947
  function Balance({ account }) {
@@ -853,7 +1214,7 @@ function CheckinPanel({ data, busy, act }) {
853
1214
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: "\u591A\u53F0\u8BBE\u5907\u7684\u672C\u5730\u8BB0\u5F55\u4E0D\u4F1A\u4E92\u76F8\u540C\u6B65\uFF0C\u6BCF\u6B21\u90FD\u4F1A\u67E5\u8BE2\u5E73\u53F0\u9886\u53D6\u72B6\u6001\uFF1B\u5EFA\u8BAE\u4EC5\u5728\u4E00\u53F0\u5E38\u7528\u8BBE\u5907\u5F00\u542F\u81EA\u52A8\u7B7E\u5230\u3002\u540C\u4E00 Trae \u8BBE\u5907\u6BCF\u5929\u6700\u591A\u5904\u7406\u4E00\u4E2A\u9886\u53D6\u8BF7\u6C42\uFF0C\u91CD\u590D\u8D26\u53F7\u526F\u672C\u4F1A\u53BB\u91CD\uFF1B\u4E0D\u4FDD\u8BC1\u5E73\u53F0\u4E00\u5B9A\u53D1\u653E\u5956\u52B1\u3002" })
854
1215
  ] });
855
1216
  }
856
- function Overview({ data, live, setLive, busy, act, go }) {
1217
+ function Overview({ data, busy, act, go }) {
857
1218
  const t = data.telemetry || {}, today = t.days?.[t.today] || {}, totals = data.accounts.reduce((s, a) => ({ requests: s.requests + (a.usage?.requests || 0), tokens: s.tokens + (a.usage?.tokens || 0) }), { requests: 0, tokens: 0 });
858
1219
  const dates = Array.from({ length: 7 }, (_, i) => {
859
1220
  const d = /* @__PURE__ */ new Date();
@@ -873,10 +1234,7 @@ function Overview({ data, live, setLive, busy, act, go }) {
873
1234
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bar", children: [
874
1235
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "\u5E73\u53F0\u989D\u5EA6" }),
875
1236
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "row", children: [
876
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "check", children: [
877
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "checkbox", checked: live, onChange: (e) => setLive(e.target.checked) }),
878
- "15 \u79D2\u5237\u65B0\u9762\u677F"
879
- ] }),
1237
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "notice", children: "\u5E73\u53F0\u7528\u91CF \xB7 \u6BCF 5 \u5206\u949F\u66F4\u65B0" }),
880
1238
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: busy, onClick: () => {
881
1239
  void act({ action: "monitor.refresh" });
882
1240
  }, children: "\u5237\u65B0\u5E73\u53F0\u7528\u91CF" })
@@ -913,7 +1271,7 @@ function Overview({ data, live, setLive, busy, act, go }) {
913
1271
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: busy, onClick: () => go("accounts"), children: "\u7BA1\u7406\u8D26\u53F7\u6C60" })
914
1272
  ] })
915
1273
  ] }),
916
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: "\u5E73\u53F0\u4F59\u989D\u6BCF\u5206\u949F\u67E5\u8BE2\uFF1BToken \u4EC5\u7EDF\u8BA1\u672C\u63D2\u4EF6\u8BF7\u6C42\uFF0C\u4ECE\u5347\u7EA7\u5F00\u59CB\u8BB0\u5F55\u3002\u79EF\u5206\u4E0B\u964D\u53EF\u80FD\u5305\u542B\u5BA2\u6237\u7AEF\u6216\u5176\u4ED6\u5E94\u7528\u7684\u6D88\u8017\uFF0C\u4E09\u5BB6\u79EF\u5206\u5206\u522B\u5C55\u793A\u3002" }),
1274
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: "\u9762\u677F\u8BFB\u53D6\u672C\u673A\u7F13\u5B58\uFF0C\u4E0D\u4F1A\u89E6\u53D1\u5E73\u53F0\u67E5\u8BE2\u3002\u5E73\u53F0\u4F59\u989D\u6BCF 5 \u5206\u949F\u66F4\u65B0\uFF0C\u624B\u52A8\u5237\u65B0\u7ACB\u5373\u67E5\u8BE2\uFF1B\u79EF\u5206\u8F6E\u6362\u4E0E\u4E34\u671F\u4F18\u5148\u6309\u9700\u590D\u6838\u3002Token \u4EC5\u7EDF\u8BA1\u672C\u63D2\u4EF6\u8BF7\u6C42\u3002" }),
917
1275
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "grid", children: [
918
1276
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "box", children: [
919
1277
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "\u8FD1 7 \u65E5 Token" }),
@@ -928,14 +1286,8 @@ function Overview({ data, live, setLive, busy, act, go }) {
928
1286
  ] })
929
1287
  ] }),
930
1288
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "box stack", children: [
931
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "\u5FEB\u901F\u5F00\u59CB" }),
932
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "notice", children: [
933
- "1. \u786E\u8BA4\u81EA\u52A8\u83B7\u53D6\u7684\u8D26\u53F7\uFF0C\u6216\u624B\u52A8\u6DFB\u52A0\u51ED\u8BC1",
934
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("br", {}),
935
- "2. \u4F7F\u7528\u9884\u5236\u8DEF\u7531\uFF0C\u6309\u9700\u7F16\u8F91\u8D26\u53F7\u5207\u6362\u89C4\u5219",
936
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("br", {}),
937
- "3. \u5230 DSH \u6A21\u578B\u9009\u62E9\u5668\u9009\u62E9\u300C\u7EDF\u4E00\u8DEF\u7531\u300D\u4E2D\u7684\u6A21\u578B"
938
- ] }),
1289
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "\u5E38\u7528\u5165\u53E3" }),
1290
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: "\u8FDE\u63A5\u8D26\u53F7\u540E\uFF0C\u5728 DSH \u6A21\u578B\u9009\u62E9\u5668\u4E2D\u4F7F\u7528\u300C\u7EDF\u4E00\u8DEF\u7531\u300D\u3002" }),
939
1291
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "row", children: [
940
1292
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: () => go("accounts"), children: "\u67E5\u770B\u8D26\u53F7" }),
941
1293
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: () => go("routes"), children: "\u914D\u7F6E\u8DEF\u7531" }),
@@ -999,7 +1351,7 @@ function performanceSummary(events) {
999
1351
  }
1000
1352
  function PerformanceMetrics({ events }) {
1001
1353
  const stats = performanceSummary(events);
1002
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "stack", "aria-label": "\u8C03\u7528\u6027\u80FD\u7EDF\u8BA1", children: [
1354
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "stack performance", "aria-label": "\u8C03\u7528\u6027\u80FD\u7EDF\u8BA1", children: [
1003
1355
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bar", children: [
1004
1356
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "\u8C03\u7528\u6027\u80FD" }),
1005
1357
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "notice", children: [
@@ -1021,11 +1373,11 @@ function PerformanceMetrics({ events }) {
1021
1373
  ] });
1022
1374
  }
1023
1375
  function UsageDetails({ data, busy, act }) {
1024
- const [account, setAccount] = (0, import_react.useState)(""), [route, setRoute] = (0, import_react.useState)(""), [outcome, setOutcome] = (0, import_react.useState)(""), [page, setPage] = (0, import_react.useState)(0);
1376
+ const [account, setAccount] = (0, import_react.useState)(""), [route, setRoute] = (0, import_react.useState)(""), [outcome, setOutcome] = (0, import_react.useState)(""), [source, setSource] = (0, import_react.useState)(""), [page, setPage] = (0, import_react.useState)(0);
1025
1377
  const [period, setPeriod] = (0, import_react.useState)("30"), [start, setStart] = (0, import_react.useState)(() => new Date(Date.now() - 29 * 864e5).toLocaleDateString("en-CA", { timeZone: "Asia/Shanghai" })), [end, setEnd] = (0, import_react.useState)(() => (/* @__PURE__ */ new Date()).toLocaleDateString("en-CA", { timeZone: "Asia/Shanghai" }));
1026
1378
  const all = data.telemetry?.events || [], retention = data.options.usageRetentionDays;
1027
1379
  const [from, until] = usageDateBounds(period, start, end), invalidRange = !(from < until);
1028
- const events = all.filter((e) => e.at >= from && e.at < until && (!account || e.account === account) && (!route || e.alias === route) && (!outcome || e.outcome === outcome));
1380
+ const events = all.filter((e) => e.at >= from && e.at < until && (!account || e.account === account) && (!route || e.alias === route) && (!outcome || e.outcome === outcome) && (!source || (e.source || "dsh") === source));
1029
1381
  const pages = Math.max(1, Math.ceil(events.length / 50)), current = Math.min(page, pages - 1);
1030
1382
  const accountName = (id) => data.accounts.find((a) => a.id === id)?.name || id;
1031
1383
  const routeName = (id) => data.routes.find((r) => r.id === id)?.name || id;
@@ -1112,6 +1464,17 @@ function UsageDetails({ data, busy, act }) {
1112
1464
  [...new Set(all.map((e) => e.alias))].map((id) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: id, children: routeName(id) }, id))
1113
1465
  ] })
1114
1466
  ] }),
1467
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "filter", children: [
1468
+ "\u6765\u6E90",
1469
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", { value: source, onChange: (e) => {
1470
+ setSource(e.target.value);
1471
+ setPage(0);
1472
+ }, children: [
1473
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "", children: "\u5168\u90E8\u6765\u6E90" }),
1474
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "dsh", children: "DSH \u4F1A\u8BDD" }),
1475
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "api", children: "API \u670D\u52A1" })
1476
+ ] })
1477
+ ] }),
1115
1478
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "filter", children: [
1116
1479
  "\u7ED3\u679C",
1117
1480
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", { value: outcome, onChange: (e) => {
@@ -1137,7 +1500,7 @@ function UsageDetails({ data, busy, act }) {
1137
1500
  "\u7F13\u5B58\u8F93\u5165\u5305\u542B\u5728\u8F93\u5165\u4E2D\uFF0C\u63A8\u7406\u8F93\u51FA\u5305\u542B\u5728\u8F93\u51FA\u4E2D\uFF0C\u5747\u4E0D\u91CD\u590D\u76F8\u52A0\u3002\u672A\u8FD4\u56DE\u7684\u6570\u636E\u4EE5\u300C\u2014\u300D\u663E\u793A\u3002"
1138
1501
  ] }),
1139
1502
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PerformanceMetrics, { events }),
1140
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "box", children: [
1503
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "box table-shell", children: [
1141
1504
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "scroll", role: "region", "aria-label": "\u8BF7\u6C42\u7528\u91CF\u4E0E\u6027\u80FD\u660E\u7EC6", tabIndex: "0", children: [
1142
1505
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("table", { className: "table", children: [
1143
1506
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
@@ -1151,7 +1514,10 @@ function UsageDetails({ data, busy, act }) {
1151
1514
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u7ED3\u679C" })
1152
1515
  ] }) }),
1153
1516
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: events.slice(current * 50, (current + 1) * 50).map((e, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
1154
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: when(e.at) }),
1517
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("td", { children: [
1518
+ when(e.at),
1519
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { className: "muted", children: e.source === "api" ? "API \u670D\u52A1" : "DSH \u4F1A\u8BDD" })
1520
+ ] }),
1155
1521
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("td", { children: [
1156
1522
  data.providers.find((p) => p.id === e.provider)?.name || e.provider,
1157
1523
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("br", {}),
@@ -1193,7 +1559,7 @@ function UsageDetails({ data, busy, act }) {
1193
1559
  ] }),
1194
1560
  !events.length && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "empty", children: "\u5F53\u524D\u7B5B\u9009\u548C\u4FDD\u7559\u671F\u9650\u5185\u6CA1\u6709\u8BF7\u6C42\u660E\u7EC6\u3002" })
1195
1561
  ] }),
1196
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bar", children: [
1562
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bar table-footer", children: [
1197
1563
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "notice", children: [
1198
1564
  "\u7B2C ",
1199
1565
  current + 1,
@@ -1603,129 +1969,161 @@ function CredentialForm({ modal, provider, data, busy, act, close, error, notice
1603
1969
  ] });
1604
1970
  }
1605
1971
  function MigrationForm({ data, act, busy }) {
1972
+ const [panel, setPanel] = (0, import_react.useState)(null);
1606
1973
  const [include, setInclude] = (0, import_react.useState)(false), [exportPassword, setExportPassword] = (0, import_react.useState)(""), [confirmPassword, setConfirmPassword] = (0, import_react.useState)(""), [exportError, setExportError] = (0, import_react.useState)("");
1607
1974
  const [document2, setDocument] = (0, import_react.useState)(""), [fileName, setFileName] = (0, import_react.useState)(""), [password, setPassword] = (0, import_react.useState)(""), [mode, setMode] = (0, import_react.useState)("merge"), [preview, setPreview] = (0, import_react.useState)(null), [error, setError] = (0, import_react.useState)("");
1608
1975
  const resetPreview = () => {
1609
1976
  setPreview(null);
1610
1977
  setError("");
1611
1978
  };
1612
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "grid", children: [
1613
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: "box stack", onSubmit: async (e) => {
1614
- e.preventDefault();
1615
- setExportError("");
1616
- if (include && exportPassword !== confirmPassword) {
1617
- setExportError("\u4E24\u6B21\u8FC1\u79FB\u5BC6\u7801\u4E0D\u4E00\u81F4");
1618
- return;
1619
- }
1620
- const result = await act({ action: "config.export", includeCredentials: include, password: include ? exportPassword : void 0 });
1621
- if (!result) return;
1622
- downloadJson(result.exported);
1623
- setExportPassword("");
1624
- setConfirmPassword("");
1625
- }, children: [
1626
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "\u5BFC\u51FA\u5230\u5176\u4ED6\u8BBE\u5907" }),
1627
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: "\u5305\u542B\u8D26\u53F7\u540D\u79F0\u548C\u987A\u5E8F\u3001\u6A21\u578B\u5C55\u793A\u8BBE\u7F6E\u3001\u8DEF\u7531\u4E0E\u5207\u6362\u89C4\u5219\u3002\u4E0D\u4F1A\u5BFC\u51FA\u4F1A\u8BDD\u3001\u7EDF\u8BA1\u3001\u6587\u4EF6\u8DEF\u5F84\u6216\u7BA1\u7406\u5BC6\u94A5\u3002" }),
1628
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "check", children: [
1629
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "checkbox", checked: include, disabled: busy, onChange: (e) => {
1630
- setInclude(e.target.checked);
1631
- setExportPassword("");
1632
- setConfirmPassword("");
1633
- } }),
1634
- "\u540C\u65F6\u8FC1\u79FB\u8D26\u53F7\u767B\u5F55\u51ED\u8BC1\uFF08\u5BC6\u7801\u52A0\u5BC6\uFF09"
1979
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "stack", children: [
1980
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1981
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "\u914D\u7F6E\u8FC1\u79FB" }),
1982
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: "\u5728\u8BBE\u5907\u95F4\u8FC1\u79FB\u8D26\u53F7\u3001\u6A21\u578B\u548C\u8DEF\u7531\u8BBE\u7F6E\u3002" })
1983
+ ] }),
1984
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("section", { className: "box table-shell", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("table", { className: "table", children: [
1985
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
1986
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { scope: "col", children: "\u64CD\u4F5C" }),
1987
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { scope: "col", children: "\u5185\u5BB9" }),
1988
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { scope: "col", className: "cell-actions", children: "\u5F00\u59CB" })
1989
+ ] }) }),
1990
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tbody", { children: [
1991
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
1992
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: "\u5BFC\u51FA\u5230\u5176\u4ED6\u8BBE\u5907" }) }),
1993
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("td", { children: [
1994
+ "\u8D26\u53F7\u4E0E\u8DEF\u7531\u8BBE\u7F6E",
1995
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { className: "muted", children: "\u53EF\u9009\u5BC6\u7801\u52A0\u5BC6\u643A\u5E26\u767B\u5F55\u51ED\u8BC1" })
1996
+ ] }),
1997
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: "cell-actions", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: busy, onClick: () => setPanel("export"), children: "\u5BFC\u51FA\u914D\u7F6E" }) })
1998
+ ] }),
1999
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
2000
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: "\u5BFC\u5165\u5DF2\u6709\u914D\u7F6E" }) }),
2001
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("td", { children: [
2002
+ "\u5148\u9884\u89C8\uFF0C\u518D\u786E\u8BA4\u5408\u5E76\u6216\u66FF\u6362",
2003
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { className: "muted", children: "\u5BFC\u5165\u524D\u81EA\u52A8\u5907\u4EFD\u672C\u673A\u914D\u7F6E" })
2004
+ ] }),
2005
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: "cell-actions", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: busy, onClick: () => setPanel("import"), children: "\u5BFC\u5165\u914D\u7F6E" }) })
2006
+ ] })
2007
+ ] })
2008
+ ] }) }),
2009
+ panel && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Modal, { title: panel === "export" ? "\u5BFC\u51FA\u914D\u7F6E" : "\u5BFC\u5165\u914D\u7F6E", busy, close: () => setPanel(null), children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "modal-body stack", children: [
2010
+ panel === "export" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: "box stack", onSubmit: async (e) => {
2011
+ e.preventDefault();
2012
+ setExportError("");
2013
+ if (include && exportPassword !== confirmPassword) {
2014
+ setExportError("\u4E24\u6B21\u8FC1\u79FB\u5BC6\u7801\u4E0D\u4E00\u81F4");
2015
+ return;
2016
+ }
2017
+ const result = await act({ action: "config.export", includeCredentials: include, password: include ? exportPassword : void 0 });
2018
+ if (!result) return;
2019
+ downloadJson(result.exported);
2020
+ setExportPassword("");
2021
+ setConfirmPassword("");
2022
+ }, children: [
2023
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "\u5BFC\u51FA\u5230\u5176\u4ED6\u8BBE\u5907" }),
2024
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: "\u5305\u542B\u8D26\u53F7\u540D\u79F0\u548C\u987A\u5E8F\u3001\u6A21\u578B\u5C55\u793A\u8BBE\u7F6E\u3001\u8DEF\u7531\u4E0E\u5207\u6362\u89C4\u5219\u3002\u4E0D\u4F1A\u5BFC\u51FA\u4F1A\u8BDD\u3001\u7EDF\u8BA1\u3001\u6587\u4EF6\u8DEF\u5F84\u6216\u7BA1\u7406\u5BC6\u94A5\u3002" }),
2025
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "check", children: [
2026
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "checkbox", checked: include, disabled: busy, onChange: (e) => {
2027
+ setInclude(e.target.checked);
2028
+ setExportPassword("");
2029
+ setConfirmPassword("");
2030
+ } }),
2031
+ "\u540C\u65F6\u8FC1\u79FB\u8D26\u53F7\u767B\u5F55\u51ED\u8BC1\uFF08\u5BC6\u7801\u52A0\u5BC6\uFF09"
2032
+ ] }),
2033
+ include ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
2034
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "steps", children: "\u52A0\u5BC6\u6587\u4EF6\u5305\u542B\u767B\u5F55 Token / PAT \u548C\u5FC5\u8981\u8BBE\u5907\u8EAB\u4EFD\uFF0C\u8BF7\u81EA\u884C\u4FDD\u7BA1\u3002\u65B0\u8BBE\u5907\u9700\u8981\u540C\u4E00\u5BC6\u7801\uFF1B\u51ED\u8BC1\u53EF\u80FD\u56E0\u5E73\u53F0\u8BBE\u5907\u9650\u5236\u800C\u9700\u8981\u91CD\u65B0\u767B\u5F55\u3002" }),
2035
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
2036
+ "\u8BBE\u7F6E\u8FC1\u79FB\u5BC6\u7801",
2037
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "password", autoComplete: "new-password", required: true, minLength: "10", maxLength: "1024", value: exportPassword, disabled: busy, onChange: (e) => setExportPassword(e.target.value) })
2038
+ ] }),
2039
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
2040
+ "\u518D\u6B21\u8F93\u5165\u5BC6\u7801",
2041
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "password", autoComplete: "new-password", required: true, value: confirmPassword, disabled: busy, onChange: (e) => setConfirmPassword(e.target.value) })
2042
+ ] })
2043
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "steps", children: "\u4EC5\u914D\u7F6E\u6587\u4EF6\u4E0D\u542B\u767B\u5F55\u51ED\u8BC1\u3002\u65B0\u8BBE\u5907\u4E0A\u7684\u8D26\u53F7\u4F1A\u5148\u505C\u7528\uFF0C\u8865\u5145\u51ED\u8BC1\u540E\u5373\u53EF\u542F\u7528\uFF1B\u5408\u5E76\u5BFC\u5165\u4F1A\u4FDD\u7559\u672C\u673A\u5DF2\u6709\u8D26\u53F7\u51ED\u8BC1\u3002" }),
2044
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Feedback, { error: exportError }),
2045
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "primary", type: "submit", disabled: busy, children: include ? "\u5BFC\u51FA\u52A0\u5BC6\u8FC1\u79FB\u6587\u4EF6" : "\u5BFC\u51FA\u914D\u7F6E JSON" })
1635
2046
  ] }),
1636
- include ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1637
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "steps", children: "\u52A0\u5BC6\u6587\u4EF6\u5305\u542B\u767B\u5F55 Token / PAT \u548C\u5FC5\u8981\u8BBE\u5907\u8EAB\u4EFD\uFF0C\u8BF7\u81EA\u884C\u4FDD\u7BA1\u3002\u65B0\u8BBE\u5907\u9700\u8981\u540C\u4E00\u5BC6\u7801\uFF1B\u51ED\u8BC1\u53EF\u80FD\u56E0\u5E73\u53F0\u8BBE\u5907\u9650\u5236\u800C\u9700\u8981\u91CD\u65B0\u767B\u5F55\u3002" }),
2047
+ panel === "import" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: "box stack", onSubmit: async (e) => {
2048
+ e.preventDefault();
2049
+ setError("");
2050
+ const result = await act({ action: "config.preview", document: document2, password, mode });
2051
+ if (result) setPreview({ ...result.importPreview, revision: result.revision });
2052
+ }, children: [
2053
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "\u5BFC\u5165\u63D2\u4EF6\u914D\u7F6E" }),
1638
2054
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1639
- "\u8BBE\u7F6E\u8FC1\u79FB\u5BC6\u7801",
1640
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "password", autoComplete: "new-password", required: true, minLength: "10", maxLength: "1024", value: exportPassword, disabled: busy, onChange: (e) => setExportPassword(e.target.value) })
2055
+ "\u9009\u62E9\u8FC1\u79FB JSON \u6587\u4EF6",
2056
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "file", accept: ".json,application/json", required: true, disabled: busy, onChange: async (e) => {
2057
+ resetPreview();
2058
+ setPassword("");
2059
+ setDocument("");
2060
+ const file = e.target.files?.[0];
2061
+ if (!file) return;
2062
+ if (file.size > 900 * 1024) {
2063
+ setError("\u8FC1\u79FB\u6587\u4EF6\u4E0D\u80FD\u8D85\u8FC7 900 KB");
2064
+ return;
2065
+ }
2066
+ try {
2067
+ setDocument(await file.text());
2068
+ setFileName(file.name);
2069
+ } catch {
2070
+ setError("\u65E0\u6CD5\u8BFB\u53D6\u6587\u4EF6");
2071
+ }
2072
+ } })
1641
2073
  ] }),
2074
+ fileName && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "mono muted", children: fileName }),
1642
2075
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1643
- "\u518D\u6B21\u8F93\u5165\u5BC6\u7801",
1644
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "password", autoComplete: "new-password", required: true, value: confirmPassword, disabled: busy, onChange: (e) => setConfirmPassword(e.target.value) })
1645
- ] })
1646
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "steps", children: "\u4EC5\u914D\u7F6E\u6587\u4EF6\u4E0D\u542B\u767B\u5F55\u51ED\u8BC1\u3002\u65B0\u8BBE\u5907\u4E0A\u7684\u8D26\u53F7\u4F1A\u5148\u505C\u7528\uFF0C\u8865\u5145\u51ED\u8BC1\u540E\u5373\u53EF\u542F\u7528\uFF1B\u5408\u5E76\u5BFC\u5165\u4F1A\u4FDD\u7559\u672C\u673A\u5DF2\u6709\u8D26\u53F7\u51ED\u8BC1\u3002" }),
1647
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Feedback, { error: exportError }),
1648
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "primary", type: "submit", disabled: busy, children: include ? "\u5BFC\u51FA\u52A0\u5BC6\u8FC1\u79FB\u6587\u4EF6" : "\u5BFC\u51FA\u914D\u7F6E JSON" })
1649
- ] }),
1650
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: "box stack", onSubmit: async (e) => {
1651
- e.preventDefault();
1652
- setError("");
1653
- const result = await act({ action: "config.preview", document: document2, password, mode });
1654
- if (result) setPreview({ ...result.importPreview, revision: result.revision });
1655
- }, children: [
1656
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "\u5BFC\u5165\u63D2\u4EF6\u914D\u7F6E" }),
1657
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1658
- "\u9009\u62E9\u8FC1\u79FB JSON \u6587\u4EF6",
1659
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "file", accept: ".json,application/json", required: true, disabled: busy, onChange: async (e) => {
1660
- resetPreview();
1661
- setPassword("");
1662
- setDocument("");
1663
- const file = e.target.files?.[0];
1664
- if (!file) return;
1665
- if (file.size > 900 * 1024) {
1666
- setError("\u8FC1\u79FB\u6587\u4EF6\u4E0D\u80FD\u8D85\u8FC7 900 KB");
1667
- return;
1668
- }
1669
- try {
1670
- setDocument(await file.text());
1671
- setFileName(file.name);
1672
- } catch {
1673
- setError("\u65E0\u6CD5\u8BFB\u53D6\u6587\u4EF6");
1674
- }
1675
- } })
1676
- ] }),
1677
- fileName && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "mono muted", children: fileName }),
1678
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1679
- "\u8FC1\u79FB\u5BC6\u7801\uFF08\u4EC5\u52A0\u5BC6\u6587\u4EF6\u9700\u8981\uFF09",
1680
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "password", autoComplete: "off", maxLength: "1024", disabled: busy, value: password, onChange: (e) => {
1681
- setPassword(e.target.value);
1682
- resetPreview();
1683
- } })
1684
- ] }),
1685
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1686
- "\u5BFC\u5165\u65B9\u5F0F",
1687
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", { disabled: busy, value: mode, onChange: (e) => {
1688
- setMode(e.target.value);
1689
- resetPreview();
1690
- }, children: [
1691
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "merge", children: "\u5408\u5E76\uFF1A\u4FDD\u7559\u672C\u673A\u989D\u5916\u8D26\u53F7\u548C\u8DEF\u7531" }),
1692
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "replace", children: "\u66FF\u6362\uFF1A\u4F7F\u7528\u5BFC\u5165\u6587\u4EF6\u7684\u5B8C\u6574\u914D\u7F6E" })
1693
- ] })
1694
- ] }),
1695
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: "\u5408\u5E76\u65F6\u540C ID \u7684\u914D\u7F6E\u4F1A\u66F4\u65B0\uFF1B\u5E26\u51ED\u8BC1\u7684\u8D26\u53F7\u4F1A\u66F4\u65B0\u767B\u5F55\u526F\u672C\u3002\u66FF\u6362\u4F1A\u79FB\u51FA\u73B0\u6709\u914D\u7F6E\u4E2D\u7684\u989D\u5916\u8D26\u53F7 / \u8DEF\u7531\uFF0C\u4F46\u4FDD\u7559\u539F\u51ED\u8BC1\u6587\u4EF6\u3002\u4E24\u79CD\u65B9\u5F0F\u90FD\u4F1A\u5148\u5907\u4EFD\u672C\u673A\u914D\u7F6E\u3002" }),
1696
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Feedback, { error }),
1697
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "submit", disabled: busy || !document2, children: "\u68C0\u67E5\u6587\u4EF6\u5E76\u9884\u89C8\u53D8\u66F4" }),
1698
- preview && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "steps stack", children: [
1699
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "\u5BFC\u5165\u9884\u89C8" }),
1700
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { children: [
1701
- preview.accounts,
1702
- " \u4E2A\u8D26\u53F7\uFF08\u65B0\u589E ",
1703
- preview.newAccounts,
1704
- "\uFF09 \xB7 ",
1705
- preview.credentials,
1706
- " \u4EFD\u51ED\u8BC1",
1707
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("br", {}),
1708
- preview.routes,
1709
- " \u6761\u8DEF\u7531 \xB7 \u66F4\u65B0\u540C\u540D\u8DEF\u7531 ",
1710
- preview.overwrittenRoutes,
1711
- " \u6761",
1712
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("br", {}),
1713
- preview.needsLogin,
1714
- " \u4E2A\u65B0\u8D26\u53F7\u9700\u8865\u5145\u767B\u5F55\uFF0C\u5C06\u5148\u505C\u7528"
2076
+ "\u8FC1\u79FB\u5BC6\u7801\uFF08\u4EC5\u52A0\u5BC6\u6587\u4EF6\u9700\u8981\uFF09",
2077
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "password", autoComplete: "off", maxLength: "1024", disabled: busy, value: password, onChange: (e) => {
2078
+ setPassword(e.target.value);
2079
+ resetPreview();
2080
+ } })
1715
2081
  ] }),
1716
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { className: "primary", type: "button", disabled: busy, onClick: async () => {
1717
- if (await act({ action: "config.import", revision: preview.revision, document: document2, password, mode })) {
1718
- setDocument("");
1719
- setPassword("");
1720
- setPreview(null);
1721
- }
1722
- }, children: [
1723
- "\u786E\u8BA4",
1724
- mode === "merge" ? "\u5408\u5E76" : "\u66FF\u6362",
1725
- "\u5E76\u5BFC\u5165"
2082
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
2083
+ "\u5BFC\u5165\u65B9\u5F0F",
2084
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", { disabled: busy, value: mode, onChange: (e) => {
2085
+ setMode(e.target.value);
2086
+ resetPreview();
2087
+ }, children: [
2088
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "merge", children: "\u5408\u5E76\uFF1A\u4FDD\u7559\u672C\u673A\u989D\u5916\u8D26\u53F7\u548C\u8DEF\u7531" }),
2089
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "replace", children: "\u66FF\u6362\uFF1A\u4F7F\u7528\u5BFC\u5165\u6587\u4EF6\u7684\u5B8C\u6574\u914D\u7F6E" })
2090
+ ] })
2091
+ ] }),
2092
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: "\u5408\u5E76\u65F6\u540C ID \u7684\u914D\u7F6E\u4F1A\u66F4\u65B0\uFF1B\u5E26\u51ED\u8BC1\u7684\u8D26\u53F7\u4F1A\u66F4\u65B0\u767B\u5F55\u526F\u672C\u3002\u66FF\u6362\u4F1A\u79FB\u51FA\u73B0\u6709\u914D\u7F6E\u4E2D\u7684\u989D\u5916\u8D26\u53F7 / \u8DEF\u7531\uFF0C\u4F46\u4FDD\u7559\u539F\u51ED\u8BC1\u6587\u4EF6\u3002\u4E24\u79CD\u65B9\u5F0F\u90FD\u4F1A\u5148\u5907\u4EFD\u672C\u673A\u914D\u7F6E\u3002" }),
2093
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Feedback, { error }),
2094
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "submit", disabled: busy || !document2, children: "\u68C0\u67E5\u6587\u4EF6\u5E76\u9884\u89C8\u53D8\u66F4" }),
2095
+ preview && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "steps stack", children: [
2096
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "\u5BFC\u5165\u9884\u89C8" }),
2097
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { children: [
2098
+ preview.accounts,
2099
+ " \u4E2A\u8D26\u53F7\uFF08\u65B0\u589E ",
2100
+ preview.newAccounts,
2101
+ "\uFF09 \xB7 ",
2102
+ preview.credentials,
2103
+ " \u4EFD\u51ED\u8BC1",
2104
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("br", {}),
2105
+ preview.routes,
2106
+ " \u6761\u8DEF\u7531 \xB7 \u66F4\u65B0\u540C\u540D\u8DEF\u7531 ",
2107
+ preview.overwrittenRoutes,
2108
+ " \u6761",
2109
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("br", {}),
2110
+ preview.needsLogin,
2111
+ " \u4E2A\u65B0\u8D26\u53F7\u9700\u8865\u5145\u767B\u5F55\uFF0C\u5C06\u5148\u505C\u7528"
2112
+ ] }),
2113
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { className: "primary", type: "button", disabled: busy, onClick: async () => {
2114
+ if (await act({ action: "config.import", revision: preview.revision, document: document2, password, mode })) {
2115
+ setDocument("");
2116
+ setPassword("");
2117
+ setPreview(null);
2118
+ }
2119
+ }, children: [
2120
+ "\u786E\u8BA4",
2121
+ mode === "merge" ? "\u5408\u5E76" : "\u66FF\u6362",
2122
+ "\u5E76\u5BFC\u5165"
2123
+ ] })
1726
2124
  ] })
1727
2125
  ] })
1728
- ] })
2126
+ ] }) })
1729
2127
  ] });
1730
2128
  }
1731
2129
  function ExpiryStatus({ route, act, busy, revision, onForce }) {
@@ -1761,40 +2159,45 @@ function FreeStatus({ route, busy, act, onUpdate }) {
1761
2159
  }, children: status?.checking ? "\u6B63\u5728\u68C0\u67E5\u2026" : "\u7ACB\u5373\u68C0\u67E5\u514D\u8D39\u6A21\u578B" })
1762
2160
  ] }),
1763
2161
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: "\u6309\u6A21\u578B\u81EA\u52A8\u521B\u5EFA\u8DEF\u7531\uFF0C\u52FE\u9009\u540E\u5206\u522B\u51FA\u73B0\u5728 DSH \u6A21\u578B\u5217\u8868\u4E2D\u3002\u540C\u4E00\u6A21\u578B\u53EA\u5728\u6709\u514D\u8D39\u8D44\u683C\u4E14\u652F\u6301\u6240\u9009\u63A8\u7406\u7B49\u7EA7\u7684\u8D26\u53F7\u95F4\u5207\u6362\uFF0C\u4E0D\u6DF7\u7528\u5176\u4ED6\u6A21\u578B\u3002\u6BCF\u5929\u68C0\u67E5\uFF0C\u8C03\u7528\u524D\u590D\u6838\uFF1B\u6062\u590D\u6536\u8D39\u6216\u68C0\u67E5\u5931\u8D25\u65F6\u81EA\u52A8\u79FB\u51FA\uFF0C\u514D\u8D39\u6062\u590D\u540E\u4FDD\u7559\u4F60\u7684\u52FE\u9009\u548C\u6863\u4F4D\u8BBE\u7F6E\u3002" }),
1764
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "grid", children: models.map((m) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "box stack", children: [
1765
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bar", children: [
1766
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: m.modelName }),
1767
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "check", children: [
1768
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "checkbox", "aria-label": `\u5C55\u793A ${m.modelName}`, disabled: busy, checked: m.selected, onChange: (e) => {
1769
- void update(m, { visible: e.target.checked });
1770
- } }),
1771
- "\u5C55\u793A"
1772
- ] })
1773
- ] }),
1774
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "notice", children: [
1775
- m.name.split(" \xB7 ").at(-1),
1776
- " \xB7 ",
1777
- m.accountNames.join("\u3001"),
1778
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("br", {}),
1779
- "\u4E0A\u4E0B\u6587 ",
1780
- fmt(m.contextWindow / 1e3),
1781
- "K \xB7 \u8F93\u51FA ",
1782
- fmt(m.maxTokens / 1e3),
1783
- "K"
1784
- ] }),
1785
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1786
- "\u9ED8\u8BA4\u63A8\u7406\u7B49\u7EA7 \xB7 ",
1787
- m.modelName,
1788
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", { disabled: busy || !m.reasoning?.supportedEfforts?.length, value: m.effortUnavailable ? "" : m.configuredEffort || "", onChange: (e) => {
1789
- void update(m, { reasoningEffort: e.target.value || null });
1790
- }, children: [
1791
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "", children: "\u5E73\u53F0\u9ED8\u8BA4" }),
1792
- m.reasoning?.supportedEfforts?.map((e) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: e, children: effortNames[e] || e }, e))
2162
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "box table-shell", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "scroll", role: "region", "aria-label": "\u514D\u8D39\u6A21\u578B\u5C55\u793A\u4E0E\u63A8\u7406\u7B49\u7EA7", tabIndex: "0", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("table", { className: "table", children: [
2163
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
2164
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { scope: "col", children: "\u6A21\u578B / \u5C55\u793A" }),
2165
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { scope: "col", children: "\u53EF\u7528\u8D26\u53F7" }),
2166
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { scope: "col", children: "\u63A8\u7406\u7B49\u7EA7" })
2167
+ ] }) }),
2168
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: models.map((m) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { className: m.selected ? "selected" : "", children: [
2169
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("td", { children: [
2170
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "check", children: [
2171
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "checkbox", "aria-label": `\u5C55\u793A ${m.modelName}`, disabled: busy, checked: m.selected, onChange: (e) => {
2172
+ void update(m, { visible: e.target.checked });
2173
+ } }),
2174
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: m.modelName })
2175
+ ] }),
2176
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("small", { className: "muted", children: [
2177
+ m.name.split(" \xB7 ").at(-1),
2178
+ " \xB7 ",
2179
+ fmt(m.contextWindow / 1e3),
2180
+ "K \u4E0A\u4E0B\u6587"
2181
+ ] })
2182
+ ] }),
2183
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: m.accountNames.join("\u3001") || "\u6682\u65E0\u8D26\u53F7" }),
2184
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("td", { children: [
2185
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
2186
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "sr-only", children: [
2187
+ m.modelName,
2188
+ " \u63A8\u7406\u7B49\u7EA7"
2189
+ ] }),
2190
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", { "aria-label": `${m.modelName} \u63A8\u7406\u7B49\u7EA7`, disabled: busy, value: m.reasoningEffort || "", onChange: (e) => {
2191
+ void update(m, { reasoningEffort: e.target.value || null });
2192
+ }, children: [
2193
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "", children: "\u5E73\u53F0\u9ED8\u8BA4" }),
2194
+ m.reasoning?.supportedEfforts?.map((e) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: e, children: effortNames[e] || e }, e))
2195
+ ] })
2196
+ ] }),
2197
+ m.effortUnavailable && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { className: "warn", children: "\u539F\u6863\u4F4D\u6682\u4E0D\u53EF\u7528\uFF0C\u4F7F\u7528\u5E73\u53F0\u9ED8\u8BA4" })
1793
2198
  ] })
1794
- ] }),
1795
- !m.reasoning?.supportedEfforts?.length && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: "\u5E73\u53F0\u672A\u58F0\u660E\u53EF\u8C03\u6863\u4F4D\uFF0C\u4F7F\u7528\u5E73\u53F0\u9ED8\u8BA4\u3002" }),
1796
- m.effortUnavailable && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: "\u4E4B\u524D\u9009\u62E9\u7684\u6863\u4F4D\u6682\u4E0D\u53EF\u7528\uFF0C\u5F53\u524D\u4F7F\u7528\u5E73\u53F0\u9ED8\u8BA4\u3002" })
1797
- ] }, m.id)) }),
2199
+ ] }, m.id)) })
2200
+ ] }) }) }),
1798
2201
  !models.length && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: "\u6682\u65E0\u786E\u8BA4\u514D\u8D39\u7684\u6A21\u578B\u3002\u6DFB\u52A0\u5E76\u542F\u7528\u8D26\u53F7\uFF0C\u6216\u7ACB\u5373\u68C0\u67E5\u76EE\u5F55\u3002" }),
1799
2202
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("details", { children: [
1800
2203
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("summary", { children: "\u5404\u8D26\u53F7\u68C0\u67E5\u72B6\u6001" }),
@@ -2014,6 +2417,7 @@ function InitialSetup({ data, busy, act, go }) {
2014
2417
  var updateNames = { unchecked: "\u5F85\u68C0\u67E5", available: "\u53EF\u66F4\u65B0", current: "\u5DF2\u540C\u6B65", pinned: "\u56FA\u5B9A\u63D0\u4EA4", unsupported: "\u672C\u5730\u6765\u6E90", failed: "\u68C0\u67E5 / \u5B89\u88C5\u5931\u8D25", installed: "\u5DF2\u5B89\u88C5" };
2015
2418
  function PluginUpdatesPanel({ data, busy, act }) {
2016
2419
  const [query, setQuery] = (0, import_react.useState)(""), [selected, setSelected] = (0, import_react.useState)([]), [plan, setPlan] = (0, import_react.useState)(null), [registry, setRegistry] = (0, import_react.useState)(""), [filter, setFilter] = (0, import_react.useState)("all"), [pluginSearch, setPluginSearch] = (0, import_react.useState)("");
2420
+ const [diagnostic, setDiagnostic] = (0, import_react.useState)(null), [copied, setCopied] = (0, import_react.useState)(false);
2017
2421
  const running = data?.job?.state === "running", blocked = busy || running;
2018
2422
  const rows = (data?.rows ?? []).filter((r) => r.source.kind !== "builtin"), available = rows.filter((r) => r.status === "available" || r.status === "failed" && r.target);
2019
2423
  (0, import_react.useEffect)(() => {
@@ -2036,34 +2440,40 @@ function PluginUpdatesPanel({ data, busy, act }) {
2036
2440
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bar", children: [
2037
2441
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
2038
2442
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "\u7528\u6237\u5B89\u88C5\u7684\u63D2\u4EF6\u4E0E\u66F4\u65B0" }),
2039
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: "npm \u6309\u7248\u672C / \u53D1\u5E03\u9891\u9053\u68C0\u67E5\uFF1BGitHub \u6309\u5206\u652F\u63D0\u4EA4\u68C0\u67E5\uFF0C\u7248\u672C\u53F7\u4E0D\u53D8\u4E5F\u80FD\u53D1\u73B0\u66F4\u65B0\u3002" })
2443
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "notice", children: [
2444
+ rows.length,
2445
+ " \u4E2A\u7528\u6237\u5B89\u88C5\u63D2\u4EF6 \xB7 ",
2446
+ available.length,
2447
+ " \u4E2A\u53EF\u66F4\u65B0"
2448
+ ] })
2040
2449
  ] }),
2041
2450
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "primary", disabled: blocked, onClick: () => {
2042
2451
  void act({ action: "plugins.check" });
2043
2452
  }, children: "\u68C0\u67E5\u5168\u90E8\u66F4\u65B0" })
2044
2453
  ] }),
2045
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bar", children: [
2046
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "notice", children: [
2047
- rows.length,
2048
- " \u4E2A\u7528\u6237\u5B89\u88C5\u63D2\u4EF6 \xB7 ",
2049
- available.length,
2050
- " \u4E2A\u53EF\u66F4\u65B0 \xB7 \u4E0A\u6B21\u68C0\u67E5 ",
2051
- when(data.checkedAt)
2052
- ] }),
2053
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "filter", children: [
2054
- "npm \u5B89\u88C5\u6E90",
2055
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", { value: registry, disabled: blocked, onChange: (e) => setRegistry(e.target.value), children: [
2056
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "", children: "DSH \u8BBE\u7F6E\uFF08\u542B\u56DE\u9000\u6765\u6E90\uFF09" }),
2057
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "https://registry.npmjs.org/", children: "npm \u5B98\u65B9" }),
2058
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "https://registry.npmmirror.com/", children: "npmmirror" })
2059
- ] })
2060
- ] })
2454
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "notice", children: [
2455
+ "\u4E0A\u6B21\u68C0\u67E5 ",
2456
+ when(data.checkedAt),
2457
+ " \xB7 \u6BCF\u5929\u81EA\u52A8\u68C0\u67E5\u5E76\u63D0\u9192"
2061
2458
  ] }),
2062
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: "\u66F4\u65B0\u6CBF\u7528\u539F npm / GitHub \u6765\u6E90\uFF0C\u5E76\u4FDD\u7559\u542F\u7528\u72B6\u6001\uFF1Bnpm \u56FA\u5B9A\u7248\u672C\u548C\u8303\u56F4\u5347\u7EA7\u5230\u6240\u5728\u9891\u9053\u7684\u6700\u65B0\u7248\u672C\uFF0CGit \u56FA\u5B9A\u63D0\u4EA4\u4FDD\u6301\u4E0D\u52A8\u3002" })
2459
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("details", { className: "help", children: [
2460
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("summary", { children: "\u5B89\u88C5\u6E90\u4E0E\u68C0\u67E5\u89C4\u5219" }),
2461
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "stack", children: [
2462
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "filter", children: [
2463
+ "npm \u5B89\u88C5\u6E90",
2464
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", { value: registry, disabled: blocked, onChange: (e) => setRegistry(e.target.value), children: [
2465
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "", children: "DSH \u8BBE\u7F6E\uFF08\u542B\u56DE\u9000\u6765\u6E90\uFF09" }),
2466
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "https://registry.npmjs.org/", children: "npm \u5B98\u65B9" }),
2467
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "https://registry.npmmirror.com/", children: "npmmirror" })
2468
+ ] })
2469
+ ] }),
2470
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: "npm \u6309\u7248\u672C / \u53D1\u5E03\u9891\u9053\u68C0\u67E5\uFF0CGitHub \u6309\u5206\u652F\u63D0\u4EA4\u68C0\u67E5\uFF1B\u4FDD\u7559\u542F\u7528\u72B6\u6001\uFF0C\u56FA\u5B9A Git \u63D0\u4EA4\u4FDD\u6301\u4E0D\u52A8\u3002" })
2471
+ ] })
2472
+ ] })
2063
2473
  ] }),
2064
2474
  data.job && !(data.job.kind === "check" && data.job.state === "done") && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "box stack", "aria-live": "polite", children: [
2065
2475
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bar", children: [
2066
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: running ? data.job.kind === "check" ? "\u6B63\u5728\u68C0\u67E5\u66F4\u65B0\u2026" : "\u6B63\u5728\u5B89\u88C5\u63D2\u4EF6\u2026" : data.job.state === "cancelled" ? "\u64CD\u4F5C\u5DF2\u53D6\u6D88" : data.job.state === "failed" ? "\u64CD\u4F5C\u672A\u5B8C\u6210" : "\u64CD\u4F5C\u5DF2\u5B8C\u6210" }),
2476
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: running ? data.job.kind === "check" ? "\u6B63\u5728\u68C0\u67E5\u66F4\u65B0\u2026" : "\u6B63\u5728\u5B89\u88C5\u63D2\u4EF6\u2026" : data.job.state === "cancelled" ? "\u64CD\u4F5C\u5DF2\u53D6\u6D88" : data.job.state === "failed" || data.job.results.some((r) => r.status === "failed") ? "\u6709\u63D2\u4EF6\u672A\u5B8C\u6210\u5B89\u88C5" : "\u64CD\u4F5C\u5DF2\u5B8C\u6210" }),
2067
2477
  running && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: busy, onClick: () => {
2068
2478
  void act({ action: "plugins.cancel" });
2069
2479
  }, children: "\u53D6\u6D88" })
@@ -2079,6 +2489,10 @@ function PluginUpdatesPanel({ data, busy, act }) {
2079
2489
  data.job.results.map((r, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "item", children: [
2080
2490
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: r.name }),
2081
2491
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: r.status === "failed" ? "error" : "notice", children: r.message || updateNames[r.status] || r.status }),
2492
+ r.diagnostic && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: () => {
2493
+ setCopied(false);
2494
+ setDiagnostic(JSON.stringify({ plugin: r.name, targetVersion: r.expectedVersion, ...r.diagnostic }, null, 2));
2495
+ }, children: "\u67E5\u770B\u8131\u654F\u8BCA\u65AD" }),
2082
2496
  r.pendingBuilds?.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "notice", children: [
2083
2497
  "\u5F85\u6388\u6743\u811A\u672C\uFF1A",
2084
2498
  r.pendingBuilds.join("\u3001"),
@@ -2087,8 +2501,8 @@ function PluginUpdatesPanel({ data, busy, act }) {
2087
2501
  ] }, r.name + i)),
2088
2502
  data.job.results.some((r) => r.restartRequired) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "steps", children: "\u66F4\u65B0\u6587\u4EF6\u5DF2\u5B89\u88C5\u3002macOS \u4F7F\u7528 \u2318Q \u5B8C\u5168\u9000\u51FA DSH \u540E\u91CD\u5F00\uFF1BWindows \u9000\u51FA\u6258\u76D8\u5E94\u7528\u540E\u91CD\u5F00\uFF1B\u7F51\u9875\u7248\u91CD\u542F\u670D\u52A1\u3002\u5173\u95ED\u7A97\u53E3\u548C\u300C\u5237\u65B0\u9762\u677F\u300D\u4E0D\u4F1A\u91CD\u8F7D\u540E\u53F0\u3002" })
2089
2503
  ] }),
2090
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "box stack", children: [
2091
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bar", children: [
2504
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "box table-shell", children: [
2505
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bar section-pad", children: [
2092
2506
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "row", children: [
2093
2507
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: blocked || !available.length, onClick: () => setSelected(available.map((r) => r.name)), children: "\u9009\u62E9\u5168\u90E8\u53EF\u66F4\u65B0" }),
2094
2508
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: blocked || !selected.length, onClick: () => setSelected([]), children: "\u6E05\u7A7A\u9009\u62E9" })
@@ -2099,7 +2513,7 @@ function PluginUpdatesPanel({ data, busy, act }) {
2099
2513
  "\uFF09"
2100
2514
  ] })
2101
2515
  ] }),
2102
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "row", children: [
2516
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "row section-pad", children: [
2103
2517
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { style: { flex: 1 }, children: [
2104
2518
  "\u641C\u7D22\u5DF2\u5B89\u88C5\u63D2\u4EF6",
2105
2519
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { value: pluginSearch, onChange: (e) => setPluginSearch(e.target.value), placeholder: "\u6309\u63D2\u4EF6\u540D\u79F0\u7B5B\u9009" })
@@ -2132,7 +2546,7 @@ function PluginUpdatesPanel({ data, busy, act }) {
2132
2546
  ] }),
2133
2547
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("td", { children: [
2134
2548
  r.source.kind === "npm" ? `npm \xB7 ${r.version || "\u672A\u77E5"}${r.latestVersion ? " \u2192 " + r.latestVersion : ""}` : r.source.kind === "github" ? `GitHub \xB7 ${r.version || "\u672A\u77E5\u7248\u672C"}` : "\u672C\u5730\u6587\u4EF6 / \u5F00\u53D1\u94FE\u63A5",
2135
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { className: "muted mono", children: r.source.kind === "npm" ? r.source.tag : r.source.kind === "github" ? `${r.commit?.slice(0, 8) || "\u672A\u77E5\u63D0\u4EA4"}${r.latestCommit ? " \u2192 " + r.latestCommit.slice(0, 8) : ""}` : "" })
2549
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { className: "muted mono", children: r.source.kind === "npm" ? `\u5B89\u88C5\u58F0\u660E ${r.source.source} \xB7 \u68C0\u67E5 ${r.source.tag}` : r.source.kind === "github" ? `${r.commit?.slice(0, 8) || "\u672A\u77E5\u63D0\u4EA4"}${r.latestCommit ? " \u2192 " + r.latestCommit.slice(0, 8) : ""}` : "" })
2136
2550
  ] }),
2137
2551
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("td", { children: [
2138
2552
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: r.status === "failed" ? "error" : r.status === "available" ? "good" : "muted", children: updateNames[r.status] }),
@@ -2144,21 +2558,35 @@ function PluginUpdatesPanel({ data, busy, act }) {
2144
2558
  !visibleRows.length && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "empty", children: running ? "\u6B63\u5728\u8BFB\u53D6\u63D2\u4EF6\u6E05\u5355\u2026" : rows.length ? "\u6CA1\u6709\u5339\u914D\u7684\u5DF2\u5B89\u88C5\u63D2\u4EF6\uFF0C\u8BF7\u8C03\u6574\u7B5B\u9009\u3002" : "\u5F53\u524D\u914D\u7F6E\u5C1A\u672A\u8BFB\u53D6\u63D2\u4EF6\uFF0C\u8BF7\u68C0\u67E5\u66F4\u65B0\u3002" })
2145
2559
  ] })
2146
2560
  ] }),
2147
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "box stack", children: [
2148
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "\u5B89\u88C5 npm / GitHub \u63D2\u4EF6" }),
2149
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: "row", onSubmit: (e) => {
2150
- e.preventDefault();
2151
- const spec = new FormData(e.currentTarget).get("spec").trim();
2152
- if (spec) setPlan({ kind: "install", spec });
2153
- }, children: [
2154
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { style: { flex: 1 }, children: [
2155
- "\u5B89\u88C5\u5730\u5740",
2156
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { name: "spec", value: query, onChange: (e) => setQuery(e.target.value), disabled: blocked, required: true, placeholder: "\u5305\u540D\u3001\u5305\u540D@\u7248\u672C \u6216 github:\u7528\u6237/\u4ED3\u5E93", autoComplete: "off" })
2561
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("details", { className: "box help install-plugin", children: [
2562
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("summary", { children: "\u5B89\u88C5 npm / GitHub \u63D2\u4EF6" }),
2563
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "stack", children: [
2564
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: "row", onSubmit: (e) => {
2565
+ e.preventDefault();
2566
+ const spec = new FormData(e.currentTarget).get("spec").trim();
2567
+ if (spec) setPlan({ kind: "install", spec });
2568
+ }, children: [
2569
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { style: { flex: 1 }, children: [
2570
+ "\u5B89\u88C5\u5730\u5740",
2571
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { name: "spec", value: query, onChange: (e) => setQuery(e.target.value), disabled: blocked, required: true, placeholder: "\u5305\u540D\u3001\u5305\u540D@\u7248\u672C \u6216 github:\u7528\u6237/\u4ED3\u5E93", autoComplete: "off" })
2572
+ ] }),
2573
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: blocked || !query.trim(), children: "\u67E5\u770B\u5E76\u5B89\u88C5" })
2157
2574
  ] }),
2158
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: blocked || !query.trim(), children: "\u67E5\u770B\u5E76\u5B89\u88C5" })
2159
- ] }),
2160
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: "\u4F7F\u7528 DSH \u81EA\u5E26\u5B89\u88C5\u5668\u6267\u884C\u517C\u5BB9\u68C0\u67E5\u3001\u955C\u50CF\u91CD\u8BD5\u548C\u5931\u8D25\u56DE\u6EDA\u3002npm \u955C\u50CF\u53EA\u4F5C\u7528\u4E8E npm \u5305\uFF0CGitHub \u4E0B\u8F7D\u5931\u8D25\u9700\u68C0\u67E5 GitHub \u7F51\u7EDC\u3002\u7F51\u7EDC\u5931\u8D25\u540E\u53EF\u91CD\u65B0\u68C0\u67E5\u6216\u66F4\u6362 npm \u5B89\u88C5\u6E90\u518D\u91CD\u8BD5\u3002" })
2575
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: "\u4F7F\u7528 DSH \u81EA\u5E26\u5B89\u88C5\u5668\u6267\u884C\u517C\u5BB9\u68C0\u67E5\u3001\u955C\u50CF\u91CD\u8BD5\u548C\u5931\u8D25\u56DE\u6EDA\u3002npm \u955C\u50CF\u53EA\u4F5C\u7528\u4E8E npm \u5305\uFF0CGitHub \u4E0B\u8F7D\u5931\u8D25\u9700\u68C0\u67E5 GitHub \u7F51\u7EDC\u3002\u7F51\u7EDC\u5931\u8D25\u540E\u53EF\u91CD\u65B0\u68C0\u67E5\u6216\u66F4\u6362 npm \u5B89\u88C5\u6E90\u518D\u91CD\u8BD5\u3002" })
2576
+ ] })
2161
2577
  ] }),
2578
+ diagnostic && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Modal, { title: "\u63D2\u4EF6\u5B89\u88C5\u8BCA\u65AD", close: () => setDiagnostic(null), children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "modal-body stack", children: [
2579
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: "\u4EE5\u4E0B\u4FE1\u606F\u53EA\u542B\u63D2\u4EF6\u540D\u3001\u7248\u672C\u3001\u7CFB\u7EDF\u548C\u9519\u8BEF\u5206\u7C7B\uFF0C\u53EF\u590D\u5236\u7528\u4E8E\u6392\u67E5\uFF1B\u672A\u5305\u542B\u539F\u59CB\u65E5\u5FD7\u3001\u7528\u6237\u540D\u8DEF\u5F84\u6216\u51ED\u8BC1\u3002\u82E5\u539F\u56E0\u672A\u77E5\uFF0C\u8BF7\u5728 DSH \u539F\u751F\u63D2\u4EF6\u9875\u83B7\u53D6\u5B89\u88C5\u65E5\u5FD7\u3002" }),
2580
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("textarea", { "aria-label": "\u8131\u654F\u5B89\u88C5\u8BCA\u65AD", readOnly: true, rows: "9", value: diagnostic, onFocus: (e) => e.currentTarget.select() }),
2581
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: async () => {
2582
+ try {
2583
+ await navigator.clipboard.writeText(diagnostic);
2584
+ setCopied(true);
2585
+ } catch {
2586
+ setCopied(false);
2587
+ }
2588
+ }, children: copied ? "\u5DF2\u590D\u5236" : "\u590D\u5236\u8BCA\u65AD" })
2589
+ ] }) }),
2162
2590
  plan && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Modal, { title: plan.kind === "install" ? "\u786E\u8BA4\u5B89\u88C5\u63D2\u4EF6" : "\u786E\u8BA4\u66F4\u65B0\u63D2\u4EF6", close: () => setPlan(null), busy: blocked, children: [
2163
2591
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "modal-body stack", children: [
2164
2592
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: "\u8FD9\u4E9B\u64CD\u4F5C\u4F5C\u7528\u4E8E\u5F53\u524D DSH \u914D\u7F6E\u7684\u6240\u6709\u4F1A\u8BDD\u3002\u5B89\u88C5\u540E\u662F\u5426\u9700\u8981\u91CD\u542F\uFF0C\u4F1A\u5728\u7ED3\u679C\u4E2D\u8BF4\u660E\u3002" }),