dsh-qoder-connect 0.2.6 → 0.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -1358,6 +1358,65 @@ window.__ModuleLoader__.load({
1358
1358
  fontSize: 13,
1359
1359
  lineHeight: 1.5
1360
1360
  };
1361
+ /** The saved-PAT select: same field skin, sized so the toolbar stays one row. */
1362
+ const patSelectStyle = {
1363
+ boxSizing: "border-box",
1364
+ flex: "0 1 180px",
1365
+ maxWidth: 180,
1366
+ minWidth: 110,
1367
+ padding: "6px 10px",
1368
+ borderWidth: "1px",
1369
+ borderStyle: "solid",
1370
+ borderColor: "var(--dsw-alias-border-l2)",
1371
+ borderRadius: 8,
1372
+ background: "var(--dsw-alias-bg-layer-2)",
1373
+ color: "var(--dsw-alias-label-primary)",
1374
+ font: "inherit",
1375
+ fontSize: 13,
1376
+ lineHeight: 1.5
1377
+ };
1378
+ /**
1379
+ * One account toolbar: the active-account select and its actions share a
1380
+ * single row, so a signed-in card no longer stacks a status line, a select
1381
+ * line and an action line on top of each other.
1382
+ *
1383
+ * The account's identity is the select itself; a separate "Account / Signed
1384
+ * in" heading above it only repeated what the select already says, so the
1385
+ * heading is gone. The non-signed-in states keep a status row, because there
1386
+ * the status *is* the information.
1387
+ */
1388
+ const accountToolbarStyle = {
1389
+ display: "flex",
1390
+ alignItems: "center",
1391
+ gap: 6,
1392
+ flexWrap: "wrap",
1393
+ paddingTop: 6
1394
+ };
1395
+ /** Provenance line under the toolbar; its refresh action sits at the far end. */
1396
+ const accountMetaRowStyle = {
1397
+ display: "flex",
1398
+ alignItems: "center",
1399
+ gap: 8,
1400
+ flexWrap: "wrap",
1401
+ marginTop: 8
1402
+ };
1403
+ const accountStatusRowStyle = {
1404
+ display: "flex",
1405
+ alignItems: "center",
1406
+ gap: 12,
1407
+ flexWrap: "wrap",
1408
+ paddingTop: 6
1409
+ };
1410
+ /** Inline field label; the select carries the value, so this stays quiet. */
1411
+ const accountFieldLabelStyle = {
1412
+ display: "flex",
1413
+ alignItems: "center",
1414
+ gap: 6,
1415
+ fontSize: 13,
1416
+ lineHeight: 1.5,
1417
+ color: "var(--dsw-alias-label-tertiary)",
1418
+ whiteSpace: "nowrap"
1419
+ };
1361
1420
  /**
1362
1421
  * Tab strip for the card body. Kept visually light — a full pill would compete
1363
1422
  * with the section headings, and the card is already the densest surface the
@@ -2020,8 +2079,12 @@ window.__ModuleLoader__.load({
2020
2079
  const [headerFocused, setHeaderFocused] = (0, react.useState)(false);
2021
2080
  const [busy, setBusy] = (0, react.useState)(false);
2022
2081
  const [patDraft, setPatDraft] = (0, react.useState)("");
2082
+ /** Optional display label for the token being added; local only. */
2083
+ const [patLabelDraft, setPatLabelDraft] = (0, react.useState)("");
2023
2084
  const [replacing, setReplacing] = (0, react.useState)(false);
2024
2085
  const [patBusy, setPatBusy] = (0, react.useState)(false);
2086
+ /** Which PAT write is in flight, so each control shows its own progress copy. */
2087
+ const [patAction, setPatAction] = (0, react.useState)();
2025
2088
  const [patError, setPatError] = (0, react.useState)();
2026
2089
  const [patNotice, setPatNotice] = (0, react.useState)();
2027
2090
  const patInput = (0, react.useRef)(null);
@@ -2066,6 +2129,8 @@ window.__ModuleLoader__.load({
2066
2129
  } : sharedDocument;
2067
2130
  const signedInState = status?.status === "signed-in" ? true : status?.status === "signed-out" ? false : void 0;
2068
2131
  const authKey = status === void 0 || status.status === "error" ? void 0 : status.authKey;
2132
+ /** The saved-token list for the select; an env-only sign-in has none. */
2133
+ const savedPats = status?.status === "signed-in" ? status.patProfiles ?? [] : [];
2069
2134
  /**
2070
2135
  * Force one shared read of the shown variant.
2071
2136
  *
@@ -2114,41 +2179,43 @@ window.__ModuleLoader__.load({
2114
2179
  }
2115
2180
  };
2116
2181
  /**
2117
- * Ask the host to re-read the credential and re-fetch this variant's catalog.
2182
+ * One refresh for the signed-in account block.
2118
2183
  *
2119
- * Shares the probe route's key and guards: it is a write that spends an
2120
- * upstream request, so it does not belong on the read-only status GET. A
2121
- * failure is surfaced through the refreshed document's `catalog.error` rather
2122
- * than thrown away, so the reason survives the round trip.
2184
+ * There used to be two buttons: "refresh" re-read the status document
2185
+ * (quota, provenance) and "refresh model list" re-fetched the upstream
2186
+ * catalog and then re-read the status anyway. The second is a superset of
2187
+ * the first, so the card now offers one action: re-fetch the catalog when a
2188
+ * probe key exists, and always re-read the status — even when the catalog
2189
+ * call fails, because the quota answer must not be held hostage by the
2190
+ * model-list endpoint.
2123
2191
  */
2124
- const refreshModels = (0, react.useCallback)(async () => {
2192
+ const refreshAll = (0, react.useCallback)(async () => {
2125
2193
  const key = status?.status === "signed-in" ? status.probeKey : void 0;
2126
- if (key === void 0) return;
2127
2194
  setBusy(true);
2128
2195
  const controller = trackController();
2129
2196
  try {
2130
- const response = await fetch(currentVariant.probePath, {
2131
- method: "POST",
2132
- headers: {
2133
- "Content-Type": "application/json",
2134
- "X-Qoder-Probe-Key": key
2135
- },
2136
- credentials: "same-origin",
2137
- signal: controller.signal,
2138
- body: JSON.stringify({ action: "refresh" })
2139
- });
2140
- if (!response.ok) throw new Error(`HTTP ${response.status}`);
2197
+ if (key !== void 0) {
2198
+ const response = await fetch(currentVariant.probePath, {
2199
+ method: "POST",
2200
+ headers: {
2201
+ "Content-Type": "application/json",
2202
+ "X-Qoder-Probe-Key": key
2203
+ },
2204
+ credentials: "same-origin",
2205
+ signal: controller.signal,
2206
+ body: JSON.stringify({ action: "refresh" })
2207
+ });
2208
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
2209
+ }
2141
2210
  } catch (error) {
2142
2211
  if (mounted.current && controller.signal.aborted !== true) setWriteFailure(error instanceof Error ? error.message : t("requestFailed"));
2143
- manualControllers.current.delete(controller);
2144
- return;
2145
- } finally {
2146
- if (mounted.current) setBusy(false);
2147
- }
2148
- try {
2149
- await refresh();
2150
2212
  } finally {
2151
- manualControllers.current.delete(controller);
2213
+ try {
2214
+ await refresh();
2215
+ } finally {
2216
+ manualControllers.current.delete(controller);
2217
+ if (mounted.current) setBusy(false);
2218
+ }
2152
2219
  }
2153
2220
  }, [
2154
2221
  currentVariant.probePath,
@@ -2282,18 +2349,19 @@ window.__ModuleLoader__.load({
2282
2349
  trackController
2283
2350
  ]);
2284
2351
  /**
2285
- * Validate and store the pasted PAT (or overwrite the stored one).
2352
+ * Validate and store the pasted PAT, then make it the active account.
2286
2353
  *
2287
- * The PAT route answers both outcomes as 200 with `ok` carrying the verdict —
2288
- * a refused token is an answer, not a transport failure — so the card checks
2289
- * `ok` and translates the stable `qoder_invalid_pat` / `qoder_missing_pat`
2290
- * codes into the re-generate prompt. The route is variant-fixed: the token
2291
- * this card saves can only ever be validated and stored for this product.
2354
+ * Saving is additive: an already-saved token updates its own entry (the id
2355
+ * is derived from the token), a new one is appended and activated, and the
2356
+ * previous tokens remain selectable. Only a token that passed validation is
2357
+ * written, so a mistyped paste can never strand the working credential.
2292
2358
  */
2293
2359
  const savePat = (0, react.useCallback)(async () => {
2294
2360
  const key = authKey;
2295
2361
  const pat = patDraft.trim();
2362
+ const label = patLabelDraft.trim();
2296
2363
  if (key === void 0 || pat === "" || patBusy) return;
2364
+ setPatAction("save");
2297
2365
  setPatBusy(true);
2298
2366
  setPatError(void 0);
2299
2367
  setPatNotice(void 0);
@@ -2309,7 +2377,8 @@ window.__ModuleLoader__.load({
2309
2377
  signal: controller.signal,
2310
2378
  body: JSON.stringify({
2311
2379
  action: "save-pat",
2312
- pat
2380
+ pat,
2381
+ ...label === "" ? {} : { label }
2313
2382
  })
2314
2383
  });
2315
2384
  const value = await response.json().catch(() => void 0);
@@ -2324,6 +2393,7 @@ window.__ModuleLoader__.load({
2324
2393
  return;
2325
2394
  }
2326
2395
  setPatDraft("");
2396
+ setPatLabelDraft("");
2327
2397
  setReplacing(false);
2328
2398
  setPatNotice(t("patSaved"));
2329
2399
  noteQuotaSignIn(currentVariant.id, true);
@@ -2332,25 +2402,36 @@ window.__ModuleLoader__.load({
2332
2402
  if (mounted.current && controller.signal.aborted !== true) setPatError(t("patSaveFailed", { message: error instanceof Error ? error.message : t("requestFailed") }));
2333
2403
  } finally {
2334
2404
  manualControllers.current.delete(controller);
2335
- if (mounted.current) setPatBusy(false);
2405
+ if (mounted.current) {
2406
+ setPatBusy(false);
2407
+ setPatAction(void 0);
2408
+ }
2336
2409
  }
2337
2410
  }, [
2338
2411
  authKey,
2339
2412
  currentVariant.authPath,
2340
2413
  patBusy,
2341
2414
  patDraft,
2415
+ patLabelDraft,
2342
2416
  refresh,
2343
2417
  t,
2344
2418
  trackController
2345
2419
  ]);
2346
- /** Remove the stored PAT; the host answer is `{ ok: true }`, then the card re-reads. */
2347
- const clearPat = (0, react.useCallback)(async () => {
2420
+ /**
2421
+ * One authenticated write against this variant's PAT route.
2422
+ *
2423
+ * `activate`, `remove`, and `clear` answer the same `{ok, status}` shape a
2424
+ * save does; one helper keeps the guards, the stable-error handling, and the
2425
+ * post-write re-read together, so a switch cannot update one surface and
2426
+ * leave another showing the previous account.
2427
+ */
2428
+ const patWrite = (0, react.useCallback)(async (body, failure, action) => {
2348
2429
  const key = authKey;
2349
- if (key === void 0 || patBusy) return;
2430
+ if (key === void 0 || patBusy) return false;
2431
+ setPatAction(action);
2350
2432
  setPatBusy(true);
2351
2433
  setPatError(void 0);
2352
2434
  setPatNotice(void 0);
2353
- setReplacing(false);
2354
2435
  const controller = trackController();
2355
2436
  try {
2356
2437
  const response = await fetch(currentVariant.authPath, {
@@ -2361,44 +2442,80 @@ window.__ModuleLoader__.load({
2361
2442
  },
2362
2443
  credentials: "same-origin",
2363
2444
  signal: controller.signal,
2364
- body: JSON.stringify({ action: "clear" })
2445
+ body: JSON.stringify(body)
2365
2446
  });
2366
2447
  const value = await response.json().catch(() => void 0);
2367
2448
  const record = typeof value === "object" && value !== null ? value : {};
2368
2449
  if (!response.ok || record["ok"] !== true) {
2369
2450
  const detail = typeof record["error"] === "string" ? record["error"] : `HTTP ${response.status}`;
2370
- setPatError(t("patSaveFailed", { message: detail }));
2371
- return;
2451
+ setPatError(failure(detail));
2452
+ return false;
2372
2453
  }
2373
- setPatDraft("");
2374
- noteQuotaStatus(currentVariant.id, {
2454
+ const status = record["status"];
2455
+ if (typeof status === "object" && status !== null && status["state"] === "missing") noteQuotaStatus(currentVariant.id, {
2375
2456
  status: "signed-out",
2376
2457
  authKey: key
2377
2458
  });
2378
2459
  await refresh();
2460
+ return true;
2379
2461
  } catch (error) {
2380
- if (mounted.current && controller.signal.aborted !== true) setPatError(t("patSaveFailed", { message: error instanceof Error ? error.message : t("requestFailed") }));
2462
+ if (mounted.current && controller.signal.aborted !== true) setPatError(failure(error instanceof Error ? error.message : t("requestFailed")));
2463
+ return false;
2381
2464
  } finally {
2382
2465
  manualControllers.current.delete(controller);
2383
- if (mounted.current) setPatBusy(false);
2466
+ if (mounted.current) {
2467
+ setPatBusy(false);
2468
+ setPatAction(void 0);
2469
+ }
2384
2470
  }
2385
2471
  }, [
2386
2472
  authKey,
2387
2473
  currentVariant.authPath,
2474
+ currentVariant.id,
2388
2475
  patBusy,
2389
2476
  refresh,
2390
2477
  t,
2391
2478
  trackController
2392
2479
  ]);
2480
+ /** Remove every saved PAT; the host answer is `{ ok: true }`, then the card re-reads. */
2481
+ const clearPat = (0, react.useCallback)(async () => {
2482
+ const key = authKey;
2483
+ if (!await patWrite({ action: "clear" }, (message) => t("patSaveFailed", { message }), "clear")) return;
2484
+ setPatDraft("");
2485
+ setReplacing(false);
2486
+ if (key !== void 0) noteQuotaStatus(currentVariant.id, {
2487
+ status: "signed-out",
2488
+ authKey: key
2489
+ });
2490
+ }, [
2491
+ authKey,
2492
+ currentVariant.id,
2493
+ patWrite,
2494
+ t
2495
+ ]);
2496
+ /** Make one saved entry active; the model group follows on the refresh. */
2497
+ const switchPat = (0, react.useCallback)(async (id) => {
2498
+ await patWrite({
2499
+ action: "activate",
2500
+ id
2501
+ }, (message) => t("patSwitchFailed", { message }), "switch");
2502
+ }, [patWrite, t]);
2503
+ /** Remove one saved entry; removing the last one signs the variant out. */
2504
+ const removePat = (0, react.useCallback)(async (id) => {
2505
+ await patWrite({
2506
+ action: "remove",
2507
+ id
2508
+ }, (message) => t("patRemoveFailed", { message }), "remove");
2509
+ }, [patWrite, t]);
2393
2510
  /**
2394
- * 更换 PAT: open the entry over the signed-in document, pre-cleared and
2395
- * focused. Unlike the removed device-flow switch this needs no sign-out
2396
- * first — saving validates the new token and overwrites the stored one
2397
- * only on success, so a mistyped paste can never strand the working
2398
- * credential.
2511
+ * Add PAT: open the entry over the signed-in document, pre-cleared and
2512
+ * focused. The entry stays additive — saving validates the new token and
2513
+ * appends it (and makes it active) only on success — so adding a second
2514
+ * account never risks the working one.
2399
2515
  */
2400
- const beginReplace = (0, react.useCallback)(() => {
2516
+ const beginAddPat = (0, react.useCallback)(() => {
2401
2517
  setPatDraft("");
2518
+ setPatLabelDraft("");
2402
2519
  setPatError(void 0);
2403
2520
  setPatNotice(void 0);
2404
2521
  setReplacing(true);
@@ -2454,12 +2571,25 @@ window.__ModuleLoader__.load({
2454
2571
  onClick: () => {
2455
2572
  setReplacing(false);
2456
2573
  setPatDraft("");
2574
+ setPatLabelDraft("");
2457
2575
  setPatError(void 0);
2458
2576
  },
2459
2577
  children: t("cancel")
2460
2578
  }) : null
2461
2579
  ]
2462
2580
  }),
2581
+ replacing ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2582
+ type: "text",
2583
+ value: patLabelDraft,
2584
+ maxLength: 48,
2585
+ placeholder: t("patLabelPlaceholder"),
2586
+ "aria-label": t("patLabelPlaceholder"),
2587
+ disabled: patBusy,
2588
+ onChange: (event) => {
2589
+ setPatLabelDraft(event.target.value);
2590
+ },
2591
+ style: patInputStyle
2592
+ }) : null,
2463
2593
  patError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2464
2594
  style: errorStyle,
2465
2595
  children: patError
@@ -2473,12 +2603,15 @@ window.__ModuleLoader__.load({
2473
2603
  const cardTitle = isUnified ? t("unifiedTitle") : t(currentVariant.titleKey);
2474
2604
  const cardIntro = isUnified ? t("unifiedIntro") : t(currentVariant.introKey);
2475
2605
  const label = status === void 0 ? t("loading") : status.status === "signed-in" ? t("signedIn") : status.status === "error" ? t("requestFailed") : t("signedOut");
2606
+ /**
2607
+ * The token's provenance line.
2608
+ *
2609
+ * The account itself is named by the toolbar above (the select, or the env
2610
+ * token's tail), so this line keeps only the facts the toolbar does not
2611
+ * carry: where the token came from and when it was saved.
2612
+ */
2476
2613
  const patSummaryLine = (pat) => {
2477
- const parts = [
2478
- patSourceText(pat.source, t),
2479
- pat.savedAtMs === void 0 ? null : t("patSavedAt", { time: formatTime(pat.savedAtMs) }),
2480
- pat.patTail === void 0 ? null : t("patTail", { tail: `****${pat.patTail}` })
2481
- ].filter((part) => part !== null);
2614
+ const parts = [patSourceText(pat.source, t), pat.savedAtMs === void 0 ? null : t("patSavedAt", { time: formatTime(pat.savedAtMs) })].filter((part) => part !== null);
2482
2615
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2483
2616
  style: bodyStyle,
2484
2617
  children: parts.join(" · ")
@@ -2573,72 +2706,117 @@ window.__ModuleLoader__.load({
2573
2706
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("variantTabGlobal") })]
2574
2707
  })]
2575
2708
  })] }) : null,
2576
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
2577
- style: quotaTitleStyle,
2578
- children: t("accountHeading")
2579
- }),
2580
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2581
- style: rowStyle,
2582
- children: [
2583
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2584
- style: statusStyle,
2585
- role: "status",
2586
- "aria-busy": status === void 0,
2587
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2588
- "aria-hidden": "true",
2589
- style: dotStyle(status === void 0 ? "loading" : status.status)
2590
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: label })]
2591
- }),
2592
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2593
- type: "button",
2594
- style: buttonStyle$1,
2595
- disabled: busy,
2596
- onClick: () => {
2597
- manualRefresh();
2598
- },
2599
- children: busy ? t("refreshing") : t("refresh")
2600
- }),
2601
- status?.status !== "signed-in" || status.authKey === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2602
- type: "button",
2603
- style: buttonStyle$1,
2604
- disabled: busy || patBusy,
2605
- onClick: beginReplace,
2606
- children: t("patReplace")
2607
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2608
- type: "button",
2609
- style: buttonStyle$1,
2610
- disabled: busy || patBusy,
2611
- onClick: () => {
2612
- clearPat();
2613
- },
2614
- children: patBusy ? t("patClearing") : t("patClear")
2615
- })] })
2616
- ]
2617
- }),
2709
+ status?.status !== "signed-in" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2710
+ style: accountStatusRowStyle,
2711
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2712
+ style: statusStyle,
2713
+ role: "status",
2714
+ "aria-busy": status === void 0,
2715
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2716
+ "aria-hidden": "true",
2717
+ style: dotStyle(status === void 0 ? "loading" : status.status)
2718
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: label })]
2719
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2720
+ type: "button",
2721
+ style: {
2722
+ ...buttonStyle$1,
2723
+ marginLeft: "auto"
2724
+ },
2725
+ disabled: busy,
2726
+ onClick: () => {
2727
+ manualRefresh();
2728
+ },
2729
+ children: busy ? t("refreshing") : t("refresh")
2730
+ })]
2731
+ }) : null,
2618
2732
  readFailure === void 0 && writeFailure === void 0 || signedInState === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2619
2733
  style: errorStyle,
2620
2734
  children: t("statusRefreshFailed", { message: readFailure !== void 0 ? readFailure.kind === "invalid" ? t("statusResponseInvalid") : readFailure.message : writeFailure })
2621
2735
  }),
2622
2736
  status?.status === "signed-in" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
2623
- status.pat === void 0 ? null : patSummaryLine(status.pat),
2737
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2738
+ style: accountToolbarStyle,
2739
+ children: [
2740
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2741
+ style: accountFieldLabelStyle,
2742
+ ...savedPats.length > 0 ? { htmlFor: `qoder-pat-select-${currentVariant.id}` } : {},
2743
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2744
+ "aria-hidden": "true",
2745
+ style: dotStyle("signed-in")
2746
+ }), t("patAccounts")]
2747
+ }),
2748
+ savedPats.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
2749
+ id: `qoder-pat-select-${currentVariant.id}`,
2750
+ value: status.activePatId ?? "",
2751
+ disabled: busy || patBusy,
2752
+ onChange: (event) => {
2753
+ const id = event.target.value;
2754
+ if (id !== "") switchPat(id);
2755
+ },
2756
+ style: patSelectStyle,
2757
+ children: savedPats.map((profile) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2758
+ value: profile.id,
2759
+ children: profile.label ?? (profile.patTail === void 0 ? profile.id : `****${profile.patTail}`)
2760
+ }, profile.id))
2761
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2762
+ style: bodyStyle,
2763
+ children: status.pat?.patTail === void 0 ? t("signedIn") : `****${status.pat.patTail}`
2764
+ }),
2765
+ status.authKey === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
2766
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2767
+ type: "button",
2768
+ style: buttonStyle$1,
2769
+ disabled: busy || patBusy,
2770
+ onClick: beginAddPat,
2771
+ children: patBusy && patAction === "save" ? t("patSaving") : t("patAdd")
2772
+ }),
2773
+ savedPats.length > 1 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2774
+ type: "button",
2775
+ style: buttonStyle$1,
2776
+ disabled: busy || patBusy || status.activePatId === void 0,
2777
+ onClick: () => {
2778
+ const id = status.activePatId;
2779
+ if (id !== void 0) removePat(id);
2780
+ },
2781
+ children: patBusy && patAction === "remove" ? t("patRemoving") : t("patRemove")
2782
+ }) : null,
2783
+ savedPats.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2784
+ type: "button",
2785
+ style: buttonStyle$1,
2786
+ disabled: busy || patBusy,
2787
+ onClick: () => {
2788
+ clearPat();
2789
+ },
2790
+ children: patBusy && patAction === "clear" ? t("patClearing") : t("patClear")
2791
+ }) : null
2792
+ ] })
2793
+ ]
2794
+ }),
2795
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2796
+ style: accountMetaRowStyle,
2797
+ children: status.pat === void 0 ? null : patSummaryLine(status.pat)
2798
+ }),
2624
2799
  replacing ? patEntry() : null,
2625
2800
  status.jobTokenRefreshedAt === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2626
2801
  style: bodyStyle,
2627
2802
  children: t("jobTokenRefreshed", { time: formatTime(status.jobTokenRefreshedAt) })
2628
2803
  }),
2629
- status.catalog === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2630
- style: rowStyle,
2631
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2804
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2805
+ style: accountMetaRowStyle,
2806
+ children: [status.catalog === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2632
2807
  style: bodyStyle,
2633
2808
  children: status.catalog.source === "live" && status.catalog.fetchedAt !== void 0 ? t("catalogLive", { time: formatTime(status.catalog.fetchedAt) }) : status.catalog.source === "saved" && status.catalog.fetchedAt !== void 0 ? t("catalogSaved", { time: formatTime(status.catalog.fetchedAt) }) : t("catalogFallback")
2634
2809
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2635
2810
  type: "button",
2636
- style: buttonStyle$1,
2811
+ style: {
2812
+ ...buttonStyle$1,
2813
+ marginLeft: "auto"
2814
+ },
2637
2815
  disabled: busy,
2638
2816
  onClick: () => {
2639
- refreshModels();
2817
+ refreshAll();
2640
2818
  },
2641
- children: busy ? t("refreshingModels") : t("refreshModels")
2819
+ children: busy ? t("refreshing") : t("refresh")
2642
2820
  })]
2643
2821
  }),
2644
2822
  status.catalog?.error === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
@@ -3984,9 +4162,15 @@ window.__ModuleLoader__.load({
3984
4162
  patSaved: "PAT saved.",
3985
4163
  patSaveFailed: "Save failed: {message}",
3986
4164
  patInvalid: "That PAT was rejected — generate a new one in your account settings and try again.",
3987
- patReplace: "Replace PAT",
3988
- patClear: "Clear PAT",
3989
- patClearing: "Clearing PAT…",
4165
+ patAdd: "Add PAT",
4166
+ patLabelPlaceholder: "Label for this token (optional, local only)",
4167
+ patAccounts: "Active account",
4168
+ patRemove: "Remove current",
4169
+ patRemoving: "Removing…",
4170
+ patSwitchFailed: "Switch failed: {message}",
4171
+ patRemoveFailed: "Remove failed: {message}",
4172
+ patClear: "Clear all",
4173
+ patClearing: "Clearing…",
3990
4174
  signedIn: "Signed in",
3991
4175
  patSourceCard: "source: saved via this card",
3992
4176
  patSourceEnv: "source: environment variable",
@@ -4022,8 +4206,6 @@ window.__ModuleLoader__.load({
4022
4206
  creditsError: "Credit unavailable: {message}",
4023
4207
  refresh: "Refresh",
4024
4208
  refreshing: "Refreshing…",
4025
- refreshModels: "Refresh model list",
4026
- refreshingModels: "Refreshing models…",
4027
4209
  jobTokenRefreshed: "Job token was auto-refreshed at {time} (upstream rejected the old one; the message recovered).",
4028
4210
  catalogLive: "Model list updated {time}",
4029
4211
  catalogSaved: "Showing the saved model list from {time}",
@@ -4032,7 +4214,6 @@ window.__ModuleLoader__.load({
4032
4214
  requestFailed: "Request failed",
4033
4215
  statusRefreshFailed: "Refresh failed: {message} — showing the last known state",
4034
4216
  statusResponseInvalid: "Qoder returned an unreadable status reply",
4035
- accountHeading: "Account",
4036
4217
  modelsHeading: "Model visibility",
4037
4218
  modelsSubtitle: "Choose which models are exposed to the DeepSeek Harness model selector.",
4038
4219
  modelsSearchPlaceholder: "Search by model name or ID…",
@@ -4159,9 +4340,15 @@ window.__ModuleLoader__.load({
4159
4340
  patSaved: "PAT 已保存。",
4160
4341
  patSaveFailed: "保存失败:{message}",
4161
4342
  patInvalid: "PAT 无效或已过期 — 请到账号设置重新生成后再试。",
4162
- patReplace: "更换 PAT",
4163
- patClear: "清除 PAT",
4164
- patClearing: "正在清除 PAT…",
4343
+ patAdd: "添加 PAT",
4344
+ patLabelPlaceholder: "备注名(可选,仅本机显示)",
4345
+ patAccounts: "当前账号",
4346
+ patRemove: "删除当前",
4347
+ patRemoving: "正在删除…",
4348
+ patSwitchFailed: "切换失败:{message}",
4349
+ patRemoveFailed: "删除失败:{message}",
4350
+ patClear: "清除全部",
4351
+ patClearing: "正在清除…",
4165
4352
  signedIn: "已登录",
4166
4353
  patSourceCard: "来源:卡片保存",
4167
4354
  patSourceEnv: "来源:环境变量",
@@ -4197,9 +4384,7 @@ window.__ModuleLoader__.load({
4197
4384
  creditsError: "额度查询失败:{message}",
4198
4385
  refresh: "刷新",
4199
4386
  refreshing: "正在刷新…",
4200
- refreshModels: "刷新模型列表",
4201
4387
  jobTokenRefreshed: "jobToken 已于 {time} 自动刷新(上游拒绝了旧令牌,消息已自动恢复)。",
4202
- refreshingModels: "正在刷新模型…",
4203
4388
  catalogLive: "模型列表更新于 {time}",
4204
4389
  catalogSaved: "当前显示已保存的模型列表,更新于 {time}",
4205
4390
  catalogFallback: "当前显示内置模型列表(尚未从 Qoder 更新)",
@@ -4207,7 +4392,6 @@ window.__ModuleLoader__.load({
4207
4392
  requestFailed: "请求失败",
4208
4393
  statusRefreshFailed: "刷新失败:{message} — 当前显示的是上次成功获取的状态",
4209
4394
  statusResponseInvalid: "Qoder 返回的状态数据无法识别",
4210
- accountHeading: "账号",
4211
4395
  modelsHeading: "模型开关",
4212
4396
  modelsSubtitle: "控制哪些模型在 DeepSeek Harness 模型选择器中可见并可供调用。",
4213
4397
  modelsSearchPlaceholder: "按模型名称或 ID 搜索…",