dsh-connect-trae 3.8.1 → 3.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -66,6 +66,50 @@ window.__ModuleLoader__.load({
66
66
  */
67
67
  const TRAE_CHECKIN_PATH = "/plugins/dsh-connect-trae/checkin";
68
68
  /**
69
+ * Plugin-owned account-pool endpoint: one route for the pool's batch actions.
70
+ *
71
+ * 参考:`dsh-connect-workbuddy` 的 `WORKBUDDY_POOL_PATH`。本路由与该实现同形
72
+ * (同一 `?action=` 风格、POST、loopback-only、per-row `accountId`),这样同时
73
+ * 装了两个插件的用户面对的是同一套交互。
74
+ *
75
+ * `?action=checkin` and `?action=test` run over the whole member set; `remove`
76
+ * addresses ONE account (the body carries `accountId`) and is the only way a
77
+ * measurement of an account the user no longer wants can leave the store — a
78
+ * deleted member otherwise keeps its row in the file forever, and a re-added
79
+ * member would silently inherit an old exclusion.
80
+ *
81
+ * POST + loopback-only because both batches SPEND something: check-in sends a
82
+ * claim, and a test is a real chat call. A page that is not the Host's own
83
+ * loopback must not be able to trigger either.
84
+ */
85
+ const TRAE_POOL_PATH = "/plugins/dsh-connect-trae/pool";
86
+ /** Query parameter selecting which pool batch action a request means. */
87
+ const TRAE_POOL_ACTION_PARAM = "action";
88
+ /** Query parameter naming ONE pool account a per-row request addresses. */
89
+ const TRAE_POOL_ACCOUNT_PARAM = "accountId";
90
+ /**
91
+ * Address one panel's pool route with its action.
92
+ *
93
+ * Kept as its own helper (rather than a `withTraeRegion` + hand-appended
94
+ * `&action=`) because a missing action parameter means "no action", not a
95
+ * default one — the typo has to be impossible rather than merely unlikely.
96
+ */
97
+ function withTraePoolAction(panel, action) {
98
+ return `${TRAE_POOL_PATH}?${TRAE_REGION_PARAM}=${panel}&${TRAE_POOL_ACTION_PARAM}=${action}`;
99
+ }
100
+ /**
101
+ * Address one panel's pool route for ONE account, e.g. a row's Test or Remove.
102
+ *
103
+ * Kept beside {@link withTraePoolAction} for the same reason that one exists: the
104
+ * account id is a 24-char hex string, and hand-appending `&accountId=` at a call
105
+ * site is one typo away from a request the route answers 400 to — or worse, from
106
+ * one that addresses the wrong action. Both parameters are therefore spelled by
107
+ * this module and nowhere else.
108
+ */
109
+ function withTraePoolAccount(panel, action, accountId) {
110
+ return `${withTraePoolAction(panel, action)}&${TRAE_POOL_ACCOUNT_PARAM}=${encodeURIComponent(accountId)}`;
111
+ }
112
+ /**
69
113
  * Plugin-owned account transfer endpoint: batch export and import of a region's
70
114
  * stored credentials (issue #37).
71
115
  *
@@ -276,6 +320,48 @@ window.__ModuleLoader__.load({
276
320
  });
277
321
  }
278
322
  /**
323
+ * One panel's saved pool PREFERENCES, as the card's settings block edits them.
324
+ *
325
+ * Read from the settings document rather than from the usage answer for the same
326
+ * reason `regionCreditPositionOf` is: the card must paint its controls correctly
327
+ * on the FIRST frame, before any fetch lands, and a control that starts on a
328
+ * default and jumps when data arrives is the "looks like it did nothing" defect.
329
+ *
330
+ * Absent reads as `{}` — every field of {@link TraePoolPreferences} is optional
331
+ * by design, and a profile written by an older build has no `pool` slot at all.
332
+ * The caller applies the defaults it means (OFF, automatic model, no members);
333
+ * this function never invents them, because `targetModelId: ''` and
334
+ * `targetModelId: undefined` mean the same thing and only the CALLER may decide
335
+ * to collapse them.
336
+ *
337
+ * `value` may be the whole settings section or the `regions` map (see
338
+ * {@link regionsMapOf}).
339
+ */
340
+ function regionPoolOf(value, panel) {
341
+ const unwrapped = unwrapVolatileDeep(regionSlotOf(value, panel)["pool"]);
342
+ return typeof unwrapped === "object" && unwrapped !== null && !Array.isArray(unwrapped) ? unwrapped : {};
343
+ }
344
+ /**
345
+ * Build the next `regions` settings value for one panel's pool preferences.
346
+ *
347
+ * Same merge rule as every sibling region write: only the addressed panel's
348
+ * `pool` slot changes, so saving the pool cannot drop that slot's model
349
+ * directory, selection, budgets, enabled flag or credit seat, and cannot touch
350
+ * the other panels. Panels are independent even where they share credentials
351
+ * (`cn` and `enterprise` bill the same Trae CN account): membership is a choice
352
+ * per panel, not per credential.
353
+ *
354
+ * `value` may be the whole settings section or the `regions` map; the RETURN
355
+ * value is always the `regions` map, i.e. exactly what `settingsScope.set(
356
+ * 'regions', ...)` needs.
357
+ */
358
+ function nextRegionPool(value, panel, pool) {
359
+ return nextRegionSlots(regionsMapOf(value), panel, {
360
+ ...regionSlotOf(value, panel),
361
+ pool
362
+ });
363
+ }
364
+ /**
279
365
  * Whether Trae's plan identity says this account is on a paying plan.
280
366
  *
281
367
  * `has_package` alone is NOT that question — it reports whether a package
@@ -312,9 +398,9 @@ window.__ModuleLoader__.load({
312
398
  .dsm-plugin-card-open{background:var(--dsw-alias-bg-layer-2,#25262b);border-color:var(--dsw-alias-label-dimmed,#777)}
313
399
  .dsm-plugin-card-header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:transparent;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}
314
400
  .dsm-plugin-card-header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#5686fe);outline-offset:-2px}
315
- .dsm-plugin-card-head{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}
401
+ .dsm-plugin-card-head{flex-direction:column;flex:1;gap:6px;min-width:0;display:flex}
316
402
  .dsm-plugin-card-title{color:var(--dsw-alias-label-primary,#e6e6e6);font-size:15px;font-weight:600;line-height:1.4}
317
- .dsm-plugin-card-description{color:var(--dsw-alias-label-tertiary,#999);font-size:13px;line-height:1.5}
403
+ .dsm-plugin-card-description{color:var(--dsw-alias-label-tertiary,#999);font-size:12.5px;line-height:1.55}
318
404
  /* Pure-CSS caret: the host primitives' chevron icon names are not part of any
319
405
  stable contract across DSH releases, so no static import is used 鈥?a border
320
406
  caret in the plugin's own CSS is version-proof. */
@@ -336,18 +422,24 @@ window.__ModuleLoader__.load({
336
422
  .dsm-trae-mainui-switch-label input{margin:0;accent-color:var(--dsw-alias-brand-primary,#5686fe)}
337
423
  .dsm-trae-mainui-switch-label:has(input:disabled){opacity:.6;cursor:default}
338
424
  .dsm-trae-usage{display:flex;flex-direction:column;gap:16px;margin:0;padding:16px 0 4px}
339
- .dsm-trae-tabs{display:flex;gap:6px;padding:4px;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:10px;background:var(--dsw-alias-bg-layer-3,#2a2c33)}
340
- .dsm-trae-tab-cell{display:flex;align-items:center;gap:2px;flex:1;min-width:0}
341
- .dsm-trae-tab{appearance:none;font:inherit;cursor:pointer;flex:1;min-width:0;border:0;border-radius:7px;padding:7px 10px;color:var(--dsw-alias-label-tertiary,#999);font-size:13px;font-weight:500;line-height:18px;background:transparent;transition:color .15s,background .15s;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
425
+ .dsm-trae-tabs{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}
426
+ .dsm-trae-tab-cell{display:flex;align-items:center;gap:4px;min-width:0;padding:8px 12px;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:10px;background:var(--dsw-alias-bg-layer-3,#2a2c33);transition:border-color .15s,background .15s}
427
+ .dsm-trae-tab-cell:hover{border-color:var(--dsw-alias-label-dimmed,#777)}
428
+ /* The selected tab is marked on its CHIP, not on the button inside it: an
429
+ inset ring on an inner button, inside a bordered strip, drew two borders
430
+ a few pixels apart and read as a rendering mistake. The chip carries the
431
+ accent border, so the switch beside the label stays in the same unit. */
432
+ .dsm-trae-tab-cell:has(.dsm-trae-tab-active){border-color:var(--dsw-alias-brand-primary,#5686fe);background:var(--dsw-alias-bg-layer-2,#232529)}
433
+ .dsm-trae-tab{appearance:none;font:inherit;cursor:pointer;flex:1;min-width:0;border:0;border-radius:7px;padding:0;color:var(--dsw-alias-label-tertiary,#999);font-size:13px;font-weight:600;line-height:20px;background:transparent;text-align:left;transition:color .15s;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
342
434
  .dsm-trae-tab:hover:not(:disabled):not(.dsm-trae-tab-active){color:var(--dsw-alias-label-primary,#e6e6e6)}
343
435
  .dsm-trae-tab:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#5686fe);outline-offset:1px}
344
- .dsm-trae-tab-active{color:var(--dsw-alias-label-primary,#e6e6e6);background:var(--dsw-alias-bg-layer-2,#232529);box-shadow:inset 0 0 0 1px var(--dsw-alias-border-l2,#3a3d45)}
436
+ .dsm-trae-tab-active{color:var(--dsw-alias-label-primary,#e6e6e6)}
345
437
  .dsm-trae-tab-off{opacity:.55}
346
438
  /* Same checkbox as the settings block (.dsm-trae-switch); only the padding
347
439
  differs, so it sits inside the tab cell without crowding the label. */
348
- .dsm-trae-tab-switch{flex:none;padding:0 8px 0 2px}
349
- .dsm-trae-tab-dot{display:inline-block;width:7px;height:7px;border-radius:50%;margin-right:6px;vertical-align:baseline}
350
- .dsm-trae-tab-hint{margin:0;color:var(--dsw-alias-label-tertiary,#999);font-size:12px;line-height:18px}
440
+ .dsm-trae-tab-switch{flex:none;margin-left:auto;padding-left:10px;border-left:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,.24))}
441
+ .dsm-trae-tab-dot{display:inline-block;width:7px;height:7px;border-radius:50%;margin-right:7px;vertical-align:baseline}
442
+ .dsm-trae-tab-hint{margin:0;padding:0 2px;color:var(--dsw-alias-label-tertiary,#999);font-size:12px;line-height:18px}
351
443
  .dsm-trae-credits-unavailable{margin:10px 0 0;padding:10px 12px;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:10px;color:var(--dsw-alias-label-secondary,#c6c9d0);font-size:12px;line-height:18px;background:var(--dsw-alias-bg-layer-3,#2a2c33)}
352
444
  .dsm-trae-account-value-muted{color:var(--dsw-alias-label-secondary,#c6c9d0);font-size:12px}
353
445
  .dsm-trae-tab-off-notice{margin:0;padding:10px 12px;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:10px;color:var(--dsw-alias-label-secondary,#c6c9d0);font-size:12px;line-height:18px;background:var(--dsw-alias-bg-layer-3,#2a2c33)}
@@ -373,13 +465,27 @@ window.__ModuleLoader__.load({
373
465
  * 銆屼繚瀛樸€?銆屾斁寮冦€?footer. That is gone with the draft it existed to serve 鈥?this
374
466
  * is the sibling dsh-connect-workbuddy card's shape, where a preference is a
375
467
  * checkbox on a line that commits on the click. */
376
- .dsm-trae-prefs{display:flex;flex-direction:column;gap:8px}
377
- .dsm-trae-pref{display:flex;align-items:flex-start;gap:8px;font-size:13px;line-height:18px;color:var(--dsw-alias-label-secondary,#c6c9d0);cursor:pointer}
378
- .dsm-trae-pref input{flex:none;margin:2px 0 0;cursor:pointer;accent-color:var(--dsw-alias-brand-primary,#5686fe)}
468
+ .dsm-trae-prefs{display:flex;flex-direction:column;gap:14px;padding:2px 2px 0}
469
+ .dsm-trae-pref{display:flex;align-items:flex-start;gap:10px;font-size:13px;line-height:18px;color:var(--dsw-alias-label-secondary,#c6c9d0);cursor:pointer}
470
+ .dsm-trae-pref input{flex:none;cursor:pointer;accent-color:var(--dsw-alias-brand-primary,#5686fe)}
471
+ .dsm-trae-pref-check{inline-size:15px;block-size:15px;margin:3px 0 0}
472
+ /* The label and its explanation used to run together as one wrapped blob
473
+ ("跟随 Trae 当前登录的账号 在 Trae 桌面端切换登录后…"), which is what made
474
+ this block read as a wall of text. They are two lines now: what the switch
475
+ does, then what it means. */
476
+ .dsm-trae-pref-copy{display:flex;flex-direction:column;gap:3px;min-width:0}
477
+ .dsm-trae-pref-label{font-weight:500;color:var(--dsw-alias-label-primary,#e6e6e6)}
478
+ /* The select sits NEXT TO its label ("显示位置 [输入框工具栏 ▾]"), not at the far
479
+ right of the card. Pushing it to the opposite edge split one sentence into
480
+ two objects with half a card of empty space between them, and left the reader
481
+ to guess which label the control belonged to. The row packs from the left;
482
+ nothing in it stretches. */
483
+ .dsm-trae-pref-lead{display:flex;align-items:center;justify-content:flex-start;gap:10px;min-width:0}
484
+ .dsm-trae-pref-lead .dsm-trae-pref-label{flex:none}
379
485
  .dsm-trae-pref input:disabled{cursor:default}
380
486
  .dsm-trae-pref:has(input:disabled){opacity:.6;cursor:default}
381
487
  .dsm-trae-pref b{font-weight:500;color:var(--dsw-alias-label-primary,#e6e6e6)}
382
- .dsm-trae-pref-hint{color:var(--dsw-alias-label-tertiary,#9aa0a8)}
488
+ .dsm-trae-pref-hint{color:var(--dsw-alias-label-tertiary,#9aa0a8);font-size:12px;line-height:17px}
383
489
  .dsm-trae-pref span{min-width:0}
384
490
  /* The credit readout's seat picker (learned from workbuddy #62). Its OWN label
385
491
  class, deliberately not the plain .dsm-trae-pref the two checkboxes use: a
@@ -388,8 +494,13 @@ window.__ModuleLoader__.load({
388
494
  would otherwise start asserting about a <select>.
389
495
  The select keeps its own class for the same reason, and takes the checkbox
390
496
  column's width so it reads as a sibling of the box rather than a stray row. */
391
- .dsm-trae-credit-position-pref{align-items:flex-start}
392
- .dsm-trae-credit-position{flex:none;max-width:150px;margin-top:-1px;padding:2px 6px;font-size:12px}
497
+ /* The select row has no checkbox, so it borrows the checkbox's column as
498
+ padding — its label must start on the same left edge as the two labels
499
+ above it (15px box + 10px gap). */
500
+ .dsm-trae-credit-position-pref{align-items:flex-start;padding-left:25px}
501
+ /* Sized by its own two options, not stretched: the control answers one short
502
+ question and sits beside the words that ask it. */
503
+ .dsm-trae-credit-position{flex:none;max-width:none;margin-top:0;padding:3px 8px;font-size:12px;line-height:16px;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:7px;background:var(--dsw-alias-bg-layer-3,#2a2c33);color:var(--dsw-alias-label-primary,#e6e6e6);cursor:pointer}
393
504
  .dsm-trae-credit-position:disabled{cursor:default}
394
505
  /* The card's checkboxes are the platform's own, styled only with
395
506
  accent-color 鈥?the SAME idiom .dsm-trae-mainui-switch-label above already
@@ -406,6 +517,16 @@ window.__ModuleLoader__.load({
406
517
  .dsm-trae-switch input{margin:0;cursor:pointer;accent-color:var(--dsw-alias-brand-primary,#5686fe)}
407
518
  .dsm-trae-switch input:disabled{cursor:default}
408
519
  .dsm-trae-switch:has(input:disabled){opacity:.6;cursor:default}
520
+ /* A pill, only where a pill is the idiom: the standalone preference rows.
521
+ appearance:none hands the geometry to the stylesheet, so the track and the
522
+ knob are ours. It is scoped rather than global because the tab row's switch
523
+ sits inside a chip — a pill there reads as a second button, which is exactly
524
+ how it looked when this was global. */
525
+ .dsm-trae-switch-pill{display:inline-flex;align-items:center}
526
+ .dsm-trae-switch-pill input{appearance:none;-webkit-appearance:none;position:relative;inline-size:36px;block-size:20px;border:0;border-radius:999px;background:var(--dsw-alias-border-l2,#c4c7cc);transition:background-color .15s ease}
527
+ .dsm-trae-switch-pill input::after{content:"";position:absolute;inset-block-start:2px;inset-inline-start:2px;inline-size:16px;block-size:16px;border-radius:50%;background:var(--dsw-alias-bg-layer-3,#fff);box-shadow:0 1px 2px rgba(0,0,0,.24);transition:transform .15s ease}
528
+ .dsm-trae-switch-pill input:checked{background:var(--dsw-alias-brand-primary,#1b1c1e)}
529
+ .dsm-trae-switch-pill input:checked::after{transform:translateX(16px)}
409
530
  /* The account block is a real table: the question it answers is "which of these
410
531
  accounts, and what is the action on each one" 鈥?a dropdown can only ever show
411
532
  the one already chosen. */
@@ -426,7 +547,7 @@ window.__ModuleLoader__.load({
426
547
  content. That is the whole point 鈥?the previous four-column table let each
427
548
  column grow with its own text, so the columns did not line up from row to row
428
549
  and the table read as noise in a narrow panel. */
429
- .dsm-trae-account-table{display:flex;flex-direction:column;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:12px;overflow:hidden;background:var(--dsw-alias-bg-layer-2,#24262c)}
550
+ .dsm-trae-account-table{display:flex;flex-direction:column;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:var(--dsm-trae-pool-radius);overflow:hidden;background:var(--dsw-alias-bg-layer-2,#24262c)}
430
551
  /* Four tracks where the region has a check-in column, three where it does not
431
552
  (the international region has no check-in at all: the ug/* family answers
432
553
  404 on every ai gateway, so the column is absent rather than empty and
@@ -641,6 +762,160 @@ window.__ModuleLoader__.load({
641
762
  @keyframes dsm-trae-veil-spin{to{transform:rotate(360deg)}}
642
763
  @keyframes dsm-trae-veil-dash{0%{stroke-dasharray:12 150;stroke-dashoffset:0}50%{stroke-dasharray:24 150;stroke-dashoffset:-6}to{stroke-dasharray:12 150;stroke-dashoffset:0}}
643
764
  @media (prefers-reduced-motion: reduce){.dsm-trae-veil-spinner{animation:none}.dsm-trae-veil-spinner-arc{animation:none;stroke-dasharray:18 150;stroke-dashoffset:-3}}
765
+
766
+ /* ------------------------------------------------------------------ *
767
+ * The account pool (3.9.0). *
768
+ * *
769
+ * Five tracks, because the table answers "which accounts, and what is *
770
+ * each one worth right now" — a single picker could only ever show the *
771
+ * one already chosen, and every field here is a reason to include or *
772
+ * exclude a row. The check-in track is CONDITIONAL (only the CN panel *
773
+ * grants a daily reward), so the actions cell is pinned to the LAST *
774
+ * track (grid-column:-2/-1) rather than to the fifth one: otherwise *
775
+ * a four-cell row would drop Test/Remove into the check-in slot and *
776
+ * leave a hole on the right. *
777
+ * ------------------------------------------------------------------ */
778
+ .dsm-trae-pool{--dsm-trae-pool-pad-x:14px;--dsm-trae-pool-pad-y:10px;--dsm-trae-pool-radius:14px;display:flex;flex-direction:column;gap:12px;border-top:1px solid var(--dsw-alias-border-l2,#36373b);padding:14px 0 0}
779
+ .dsm-trae-pool-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}
780
+ .dsm-trae-pool-title{margin:0;font-size:13px;font-weight:600;line-height:18px;color:var(--dsw-alias-label-primary,#e6e6e6)}
781
+ .dsm-trae-pool-region-note{margin:3px 0 0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary,#9aa0a8)}
782
+ .dsm-trae-pool-current{flex:none;padding:2px 8px;border-radius:999px;border:1px solid var(--dsw-alias-border-l2,#3a3d45);font-size:11px;line-height:16px;color:var(--dsw-alias-label-secondary,#c6c9d0);white-space:nowrap}
783
+ .dsm-trae-pool-actions{display:flex;align-items:center;flex-wrap:wrap;gap:8px}
784
+ .dsm-trae-pool-action-checkin{flex:none;white-space:nowrap}
785
+ .dsm-trae-pool-action-test{flex:none;white-space:nowrap}
786
+ /* Removing an account from the pool is destructive (it drops THIS panel's saved
787
+ membership), so the hover state takes the error tone rather than the neutral
788
+ one every other outline button gets. */
789
+ .dsm-trae-pool-remove{color:var(--dsw-alias-label-secondary,#c6c9d0)}
790
+ .dsm-trae-pool-remove:hover:not(:disabled){color:var(--dsw-alias-state-error-primary,#d92d20);border-color:var(--dsw-alias-state-error-primary,#d92d20)}
791
+ .dsm-trae-pool-busy{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary,#9aa0a8)}
792
+ .dsm-trae-pool-card{display:flex;flex-direction:column;min-width:0;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:var(--dsm-trae-pool-radius);background:var(--dsw-alias-bg-layer-2,#24262c);overflow:hidden}
793
+ .dsm-trae-pool-card-head{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:14px var(--dsm-trae-pool-pad-x) 0}
794
+ .dsm-trae-pool-card-title{margin:0;font-size:13px;line-height:18px;font-weight:600;color:var(--dsw-alias-label-primary,#e6e6e6)}
795
+ .dsm-trae-pool-card-tools{display:flex;align-items:center;gap:8px;flex:none}
796
+ .dsm-trae-pool-count{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary,#9aa0a8)}
797
+ .dsm-trae-pool-card-note{margin:0;padding:6px var(--dsm-trae-pool-pad-x) 12px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary,#9aa0a8)}
798
+ .dsm-trae-pool-card .dsm-trae-pool-table{border:0;border-radius:0;border-top:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,.18))}
799
+ .dsm-trae-pool-members{display:flex;flex-direction:column;gap:10px}
800
+ /* Prose, not a box. Every explanation in this section is one line of muted
801
+ text; boxing each one is what made the block look like a stack of cards. */
802
+ .dsm-trae-pool-actions-end{margin-inline-start:auto}
803
+ .dsm-trae-pool-credit-meta{display:block;margin-top:2px;font-size:11px;line-height:16px;white-space:nowrap;color:var(--dsw-alias-label-tertiary,#9aa0a8)}
804
+ .dsm-trae-pool-account-note{grid-column:2;grid-row:3;font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary,#9aa0a8)}
805
+ .dsm-trae-pool-tools-dashed{border:1px dashed var(--dsw-alias-border-l2,#3a3d45);border-radius:var(--dsm-trae-pool-radius);padding:var(--dsm-trae-pool-pad-y) var(--dsm-trae-pool-pad-x)}
806
+ .dsm-trae-pool-tool-label{margin-inline-end:auto;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary,#c6c9d0)}
807
+ .dsm-trae-pool-note{margin:0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary,#9aa0a8)}
808
+ /* A setting row: label + hint on the left, the control on the right, one
809
+ rhythm for the switch, the target select and the manual binding. */
810
+ .dsm-trae-pool-set{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:var(--dsm-trae-pool-pad-y) var(--dsm-trae-pool-pad-x);border-top:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,.18))}
811
+ .dsm-trae-pool-set:first-child{border-top:0}
812
+ .dsm-trae-pool-set-copy{display:flex;flex-direction:column;gap:3px;min-width:0}
813
+ .dsm-trae-pool-set-copy b{font-size:13px;line-height:18px;font-weight:500;color:var(--dsw-alias-label-primary,#e6e6e6)}
814
+ .dsm-trae-pool-set-copy span{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary,#9aa0a8)}
815
+ .dsm-trae-pool-set-ctl{display:flex;align-items:center;gap:8px;flex:none}
816
+ .dsm-trae-pool-set-ctl select{min-width:180px}
817
+ .dsm-trae-pool-bind{border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:var(--dsm-trae-pool-radius);background:var(--dsw-alias-bg-layer-2,#24262c)}
818
+ .dsm-trae-pool-account-select{min-width:180px}
819
+ .dsm-trae-pool-tools{display:flex;flex-direction:column;gap:8px}
820
+ .dsm-trae-pool-tool-row{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
821
+ .dsm-trae-pool-meta{font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary,#9aa0a8);white-space:nowrap}
822
+ .dsm-trae-pool-warn-tag{padding:0 6px;border-radius:999px;font-size:11px;line-height:16px;white-space:nowrap}
823
+ .dsm-trae-pool-warn-tag-soon{color:var(--dsw-alias-state-warn-primary,#b45309)}
824
+ .dsm-trae-pool-warn-tag-lapsed{color:var(--dsw-alias-state-error-primary,#d92d20)}
825
+ .dsm-trae-pool-claim{padding:2px 8px;font-size:12px;line-height:16px}
826
+ .dsm-trae-pool-settings-wrap{display:flex;flex-direction:column;gap:10px}
827
+
828
+ .dsm-trae-pool-settings{display:flex;flex-direction:column;gap:0;padding:0;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:var(--dsm-trae-pool-radius);overflow:hidden;background:var(--dsw-alias-bg-layer-2,#24262c)}
829
+ .dsm-trae-pool-members-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}
830
+ .dsm-trae-pool-settings-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}
831
+ .dsm-trae-pool-subtitle{margin:0;font-size:12px;font-weight:600;line-height:18px;color:var(--dsw-alias-label-primary,#e6e6e6)}
832
+ .dsm-trae-pool-subnote{margin:2px 0 0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary,#9aa0a8)}
833
+ .dsm-trae-pool-select-actions{display:flex;flex:none;gap:6px}
834
+ .dsm-trae-pool-select-actions .dsm-btn,.dsm-trae-pool-row-actions .dsm-btn,.dsm-trae-pool-save-row .dsm-btn{padding:4px 10px;font-size:12px;line-height:1.4}
835
+ .dsm-trae-pool-table{--dsm-trae-pool-cols:minmax(0,1.4fr) 128px minmax(0,1.2fr) 96px 128px;display:flex;flex-direction:column;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:var(--dsm-trae-pool-radius);overflow:hidden;background:var(--dsw-alias-bg-layer-2,#24262c)}
836
+ .dsm-trae-pool-table-no-checkin{--dsm-trae-pool-cols:minmax(0,1.4fr) 128px minmax(0,1.2fr) 128px}
837
+ .dsm-trae-pool-row{display:grid;grid-template-columns:var(--dsm-trae-pool-cols);align-items:start;gap:10px;padding:var(--dsm-trae-pool-pad-y) var(--dsm-trae-pool-pad-x);border-top:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,.18))}
838
+ .dsm-trae-pool-row:first-child{border-top:0}
839
+ /* The header carries labels for the first three tracks only: the check-in and
840
+ action cells are self-describing ("Not checked in", "Test"), and a header word
841
+ for an empty cell would be a promise the table does not keep. */
842
+ .dsm-trae-pool-col-account{padding-inline-start:23px}
843
+ .dsm-trae-pool-row-head{align-items:center;padding:8px var(--dsm-trae-pool-pad-x);border-top:0;background:var(--dsw-alias-bg-layer-3,#2a2c33);font-size:11px;line-height:16px;font-weight:600;letter-spacing:.02em;text-transform:uppercase;color:var(--dsw-alias-label-tertiary,#9aa0a8)}
844
+ /* The row that would bill the next request. Tinted, not bordered: a border here
845
+ reads as a selection control, and the checkbox beside it already is one. */
846
+ .dsm-trae-pool-row-current{background:rgba(86,134,254,.07);box-shadow:inset 3px 0 0 var(--dsw-alias-brand-primary,#5686fe)}
847
+ .dsm-trae-pool-row-ghost{opacity:.7}
848
+ .dsm-trae-pool-row-actions{display:flex;flex-wrap:wrap;gap:6px;justify-content:flex-end}
849
+ .dsm-trae-pool-account{display:grid;grid-template-columns:auto minmax(0,1fr);align-items:center;gap:2px 8px;min-width:0}
850
+ .dsm-trae-pool-check{display:inline-flex;align-items:center;flex:none;grid-column:1;grid-row:1;cursor:pointer}
851
+ .dsm-trae-pool-check input{flex:none;margin:0;cursor:pointer;accent-color:var(--dsw-alias-brand-primary,#5686fe)}
852
+ .dsm-trae-pool-check:has(input:disabled){opacity:.6;cursor:default}
853
+ .dsm-trae-pool-account-name{display:flex;align-items:center;gap:6px;min-width:0;grid-column:2;grid-row:1;font-size:13px;line-height:18px;color:var(--dsw-alias-label-primary,#e6e6e6);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
854
+ .dsm-trae-pool-dot{flex:none;width:7px;height:7px;border-radius:50%;background:var(--dsw-alias-label-dimmed,#777)}
855
+ .dsm-trae-pool-dot-ok{background:var(--dsw-alias-state-success-primary,#12b76a)}
856
+ .dsm-trae-pool-dot-excluded{background:var(--dsw-alias-state-warn-primary,#b45309)}
857
+ .dsm-trae-pool-dot-ghost{background:var(--dsw-alias-label-dimmed,#777)}
858
+ .dsm-trae-pool-account-tags{display:flex;flex-wrap:wrap;align-items:center;gap:6px;grid-column:2;grid-row:2;min-width:0}
859
+ .dsm-trae-pool-tag{padding:0 6px;border-radius:999px;font-size:10px;line-height:16px;white-space:nowrap}
860
+ .dsm-trae-pool-member{font-size:11px;line-height:16px;color:var(--dsw-alias-brand-primary,#5686fe)}
861
+ .dsm-trae-pool-not-member{font-size:11px;line-height:16px;color:var(--dsw-alias-label-dimmed,#9aa0a6)}
862
+ .dsm-trae-pool-tag-current{padding:1px 6px;border-radius:999px;background:rgba(86,134,254,.16);color:var(--dsw-alias-brand-primary,#5686fe);font-size:11px;font-weight:600;line-height:16px}
863
+ .dsm-trae-pool-tag-member{border:1px solid var(--dsw-alias-border-l2,#3a3d45);color:var(--dsw-alias-label-tertiary,#9aa0a8)}
864
+ .dsm-trae-pool-tag-excluded{background:var(--dsw-alias-state-warn-tertiary,#fef3c7);color:var(--dsw-alias-state-warn-primary,#b45309)}
865
+ .dsm-trae-pool-tag-ghost{border:1px dashed var(--dsw-alias-border-l2,#3a3d45);color:var(--dsw-alias-label-tertiary,#9aa0a8)}
866
+ .dsm-trae-pool-credit{display:flex;flex-direction:column;gap:1px;min-width:0}
867
+ .dsm-trae-pool-credit-num{font-size:13px;line-height:18px;color:var(--dsw-alias-label-primary,#e6e6e6);font-variant-numeric:tabular-nums}
868
+ .dsm-trae-pool-credit-label{font-size:10px;line-height:14px;color:var(--dsw-alias-label-tertiary,#9aa0a8)}
869
+ .dsm-trae-pool-probe{min-width:0}
870
+ .dsm-trae-pool-probe-cell{display:flex;flex-direction:column;gap:1px;min-width:0}
871
+ .dsm-trae-pool-probe-outcome{display:flex;align-items:center;gap:5px;font-size:12px;line-height:16px;color:var(--dsw-alias-label-secondary,#c6c9d0)}
872
+ .dsm-trae-pool-probe-ok{color:var(--dsw-alias-state-success-primary,#12b76a)}
873
+ .dsm-trae-pool-probe-warn{color:var(--dsw-alias-state-warn-primary,#b45309)}
874
+ .dsm-trae-pool-probe-meta{font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary,#9aa0a8);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
875
+ .dsm-trae-pool-probe-detail{font-size:11px;line-height:15px;color:var(--dsw-alias-label-tertiary,#9aa0a8);overflow-wrap:anywhere}
876
+ .dsm-trae-pool-probe-none{font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary,#9aa0a8)}
877
+ .dsm-trae-pool-checkin{min-width:0}
878
+ .dsm-trae-pool-checkin-state{font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary,#9aa0a8)}
879
+ .dsm-trae-pool-checkin-ok{color:var(--dsw-alias-state-success-primary,#12b76a)}
880
+ .dsm-trae-pool-checkin-no{color:var(--dsw-alias-state-warn-primary,#b45309)}
881
+ /* "Unknown" and "device already claimed" are facts, not faults: a muted line
882
+ says so without dressing either one up as an error. */
883
+ .dsm-trae-pool-checkin-unknown{color:var(--dsw-alias-label-tertiary,#9aa0a8)}
884
+ .dsm-trae-pool-checkin-device{color:var(--dsw-alias-label-tertiary,#9aa0a8)}
885
+ /* The advisory lines share one shape (a quiet bordered box): each is a fact the
886
+ user may need to act on, none of them is an error, and five different
887
+ rectangles in one section would read as five different severities. */
888
+ .dsm-trae-pool-ghost-note{margin:0;padding:8px var(--dsm-trae-pool-pad-x);border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:10px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary,#c6c9d0);background:var(--dsw-alias-bg-layer-3,#2a2c33)}
889
+ .dsm-trae-pool-unsaved{margin:0;padding:8px var(--dsm-trae-pool-pad-x);border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:10px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary,#c6c9d0);background:var(--dsw-alias-bg-layer-3,#2a2c33)}
890
+ .dsm-trae-pool-off-notice{margin:0;padding:0 var(--dsm-trae-pool-pad-x) 10px;font-size:12px;line-height:18px;color:var(--dsw-alias-state-warn-primary,#b45309)}
891
+ .dsm-trae-pool-target-none{margin:0;padding:0 12px 10px;font-size:12px;line-height:18px;color:var(--dsw-alias-state-warn-primary,#b45309)}
892
+ .dsm-trae-pool-target-stale{display:flex;align-items:center;justify-content:space-between;gap:10px;margin:0 var(--dsm-trae-pool-pad-x);padding-bottom:10px;color:var(--dsw-alias-state-warn-primary,#b45309)}
893
+ .dsm-trae-pool-warning{display:flex;flex-direction:column;gap:2px;padding:8px var(--dsm-trae-pool-pad-x);border:1px solid var(--dsw-alias-state-warn-secondary,#fbbf24);border-radius:10px;background:var(--dsw-alias-state-warn-tertiary,#fef3c7);color:var(--dsw-alias-state-warn-primary,#b45309);font-size:12px;line-height:18px}
894
+ .dsm-trae-pool-warning-title{font-weight:600}
895
+ .dsm-trae-pool-warning-hint{opacity:.85}
896
+ /* "Empty" is an unfinished setup, not a fault: neutral tone. */
897
+ .dsm-trae-pool-warning-empty{border-color:var(--dsw-alias-border-l2,#3a3d45);background:var(--dsw-alias-bg-layer-3,#2a2c33);color:var(--dsw-alias-label-secondary,#c6c9d0)}
898
+ /* "Every member is unusable" is the one case that IS the pool's fault, so it
899
+ keeps the warn tone the base class sets. */
900
+ .dsm-trae-pool-warning-unusable{border-color:var(--dsw-alias-state-warn-secondary,#fbbf24);background:var(--dsw-alias-state-warn-tertiary,#fef3c7);color:var(--dsw-alias-state-warn-primary,#b45309)}
901
+ .dsm-trae-pool-save-row{display:flex;align-items:center;gap:8px}
902
+ .dsm-trae-pool-star{margin-inline-end:auto;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary,#c6c9d0);text-decoration:none}
903
+ .dsm-trae-pool-dirty{padding:0 6px;border-radius:999px;background:var(--dsw-alias-state-warn-tertiary,#fef3c7);color:var(--dsw-alias-state-warn-primary,#b45309);font-size:10px;line-height:16px}
904
+ .dsm-trae-pool-save-error{margin:0;font-size:12px;line-height:18px;color:var(--dsw-alias-state-error-primary,#d92d20)}
905
+ .dsm-trae-pool-target{display:flex;flex-direction:column;gap:6px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary,#c6c9d0)}
906
+ .dsm-trae-pool-target-label{color:var(--dsw-alias-label-secondary,#c6c9d0)}
907
+ .dsm-trae-select{max-width:220px;padding:2px 6px;font:inherit;font-size:12px;color:var(--dsw-alias-label-primary,#e6e6e6);background:var(--dsw-alias-bg-layer-3,#2a2c33);border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:8px}
908
+ .dsm-trae-select:disabled{opacity:.6;cursor:default}
909
+ .dsm-trae-pool-log{display:flex;flex-direction:column;gap:6px;padding-top:2px}
910
+ .dsm-trae-pool-log-head{display:flex;align-items:center;justify-content:space-between;gap:10px}
911
+ .dsm-trae-pool-log-empty{margin:0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary,#9aa0a8)}
912
+ .dsm-trae-pool-log-list{display:flex;flex-direction:column;gap:3px;margin:0;padding:0;list-style:none;max-height:160px;overflow-y:auto}
913
+ .dsm-trae-pool-log-item{padding:0 0 0 8px;border-left:2px solid var(--dsw-alias-border-l2,#3a3d45);font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary,#c6c9d0)}
914
+ .dsm-trae-pool-log-info{border-left-color:var(--dsw-alias-border-l2,#3a3d45)}
915
+ .dsm-trae-pool-log-ok{border-left-color:var(--dsw-alias-state-success-primary,#12b76a)}
916
+ .dsm-trae-pool-log-warn{border-left-color:var(--dsw-alias-state-warn-primary,#b45309)}
917
+ .dsm-trae-pool-log-error{border-left-color:var(--dsw-alias-state-error-primary,#d92d20)}
918
+ .dsm-trae-pool-log-text{overflow-wrap:anywhere}
644
919
  `;
645
920
  /**
646
921
  * Composer-row readout styles, injected by the readout itself.
@@ -860,6 +1135,1122 @@ window.__ModuleLoader__.load({
860
1135
  });
861
1136
  }
862
1137
  //#endregion
1138
+ //#region src/client/AccountPool.tsx
1139
+ /**
1140
+ * The account-pool card UI for Trae, ported from the WorkBuddy sibling.
1141
+ *
1142
+ * Mounted by {@link TraeUsageCard} inside the signed-in branch, below the
1143
+ * account table, rendered only when the usage document carries `pool` (the Host
1144
+ * omits it when the panel is unconfigured).
1145
+ *
1146
+ * This half of the feature is deliberately dumb about rules: every verdict an
1147
+ * account needs — credits, exclusion, the in-pool flag, the serving row — comes
1148
+ * pre-derived by the Host (`TraeWebPoolAccount`), so the card never re-ranks on
1149
+ * its own idea of "which account should bill". It drafts the saved preferences
1150
+ * (`TraePoolPreferences`) and writes them back through the card's verified
1151
+ * regions-write helper.
1152
+ */
1153
+ /**
1154
+ * A finite number or the fallback.
1155
+ *
1156
+ * The usage document is served by a Host that may be OLDER than this bundle (the
1157
+ * card ships in the client bundle and updates on refresh, the Host only on
1158
+ * restart), so a field the Host does not send yet must degrade to a usable default
1159
+ * instead of rendering `undefined` into a select.
1160
+ */
1161
+ function numberOr(value, fallback) {
1162
+ return typeof value === "number" && Number.isFinite(value) ? value : fallback;
1163
+ }
1164
+ /**
1165
+ * Probe sizes the card offers, in tokens. `0` is the minimal one-word probe.
1166
+ *
1167
+ * Deliberately a short, coarse list: the number is a spend decision (a probe at
1168
+ * 50k tokens costs real credit on every account in the batch), and offering a
1169
+ * free-text field invites values the upstream rounds away anyway.
1170
+ */
1171
+ const PROBE_INPUT_TOKEN_CHOICES = [
1172
+ 0,
1173
+ 1e4,
1174
+ 2e4,
1175
+ 3e4,
1176
+ 5e4,
1177
+ 1e5
1178
+ ];
1179
+ /** Compare two arrays of ids order-insensitively. */
1180
+ function sameIds(a, b) {
1181
+ const lhs = [...a ?? []].sort();
1182
+ const rhs = [...b ?? []].sort();
1183
+ return lhs.length === rhs.length && lhs.every((v, i) => v === rhs[i]);
1184
+ }
1185
+ function formatNumber$1(value) {
1186
+ return new Intl.NumberFormat(void 0, { maximumFractionDigits: 1 }).format(value);
1187
+ }
1188
+ /** `MM/DD, HH:mm` — the absolute stamp a row shows under its age. */
1189
+ function shortWhen(atMs) {
1190
+ const at = new Date(atMs);
1191
+ const pad = (value) => value < 10 ? `0${value}` : String(value);
1192
+ return `${pad(at.getMonth() + 1)}/${pad(at.getDate())}, ${pad(at.getHours())}:${pad(at.getMinutes())}`;
1193
+ }
1194
+ /** `MM/DD`, for a line that has room for a date but not a clock time. */
1195
+ function shortDate(atMs) {
1196
+ const at = new Date(atMs);
1197
+ const pad = (value) => value < 10 ? `0${value}` : String(value);
1198
+ return `${pad(at.getMonth() + 1)}/${pad(at.getDate())}`;
1199
+ }
1200
+ function relativeAt(atMs, now, t) {
1201
+ const mins = Math.max(0, Math.round((now - atMs) / 6e4));
1202
+ if (mins < 1) return t("row.poolAgoNow");
1203
+ if (mins < 60) return t("row.poolAgoMin", { n: mins });
1204
+ const hours = Math.round(mins / 60);
1205
+ if (hours < 24) return t("row.poolAgoHour", { n: hours });
1206
+ return t("row.poolAgoDay", { n: Math.round(hours / 24) });
1207
+ }
1208
+ /** A human label for every value of {@link TraeWebPoolOutcome}. */
1209
+ function outcomeLabel(outcome, t) {
1210
+ switch (outcome) {
1211
+ case "ok": return t("row.poolProbeOk");
1212
+ case "rate-limited": return t("row.poolProbeRateLimited");
1213
+ case "model-busy": return t("row.poolProbeModelBusy");
1214
+ case "out-of-credit": return t("row.poolProbeOutOfCredit");
1215
+ case "credential-rejected": return t("row.poolProbeCredentialRejected");
1216
+ case "policy-rejected": return t("row.poolProbePolicyRejected");
1217
+ case "plan-gated": return t("row.poolProbePlanGated");
1218
+ case "unavailable": return t("row.poolProbeUnavailable");
1219
+ case "not-found": return t("row.poolProbeNotFound");
1220
+ case "failed": return t("row.poolProbeFailed");
1221
+ }
1222
+ }
1223
+ /** A human label for every value of {@link TraeWebPoolExclusion}. */
1224
+ function exclusionLabel(exclusion, t) {
1225
+ switch (exclusion) {
1226
+ case "unusable": return t("row.poolExcludedUnusable");
1227
+ case "rate-limited": return t("row.poolExcludedRateLimited");
1228
+ case "model-busy": return t("row.poolExcludedModelBusy");
1229
+ case "out-of-credit": return t("row.poolExcludedOutOfCredit");
1230
+ case "credential-rejected": return t("row.poolExcludedCredentialRejected");
1231
+ case "policy-rejected": return t("row.poolExcludedPolicyRejected");
1232
+ case "plan-gated": return t("row.poolExcludedPlanGated");
1233
+ case "not-found": return t("row.poolExcludedNotFound");
1234
+ case "unavailable": return t("row.poolExcludedUnavailable");
1235
+ case "failed": return t("row.poolExcludedFailed");
1236
+ }
1237
+ }
1238
+ function probeSourceLabel(source, t) {
1239
+ switch (source) {
1240
+ case "test-batch": return t("row.poolProbeSourceTest");
1241
+ case "live-request": return t("row.poolProbeSourceLive");
1242
+ case void 0: return t("row.poolProbeSourceUnknown");
1243
+ }
1244
+ }
1245
+ /** The tone to log a test row result under. */
1246
+ function outcomeTone(outcome) {
1247
+ return outcome === "ok" ? "ok" : "warn";
1248
+ }
1249
+ /** The serving account (Host's answer), for the header badge. */
1250
+ function servingAccount(pool) {
1251
+ return pool.accounts.find((account) => account.current === true);
1252
+ }
1253
+ /**
1254
+ * Render a route refusal into a card-safe string. The error statuses the Host
1255
+ * can answer with are listed in the card doc; every one here must render as
1256
+ * TEXT (never crash). `origin-not-trusted`/`pool-unavailable` carry no body, so
1257
+ * they get a dedicated copy line keyed off the status.
1258
+ */
1259
+ function renderRouteError(t, status, body) {
1260
+ if (status === 403 && body?.reason === "origin-not-trusted") return t("row.poolErrOrigin");
1261
+ if (status === 503) return t("row.poolErrUnavailable");
1262
+ if (status === 405) return t("row.poolErrMethod");
1263
+ if (status === 400) return t("row.poolErrBadRequest");
1264
+ if (status === 409) switch (body?.reason) {
1265
+ case "no-members": return t("row.poolErrNoMembers");
1266
+ case "no-live-members": return t("row.poolErrNoLiveMembers");
1267
+ case "checkin-unsupported": return t("row.poolErrCheckinUnsupported");
1268
+ case "target-model-stale": return t("row.poolErrTargetStale");
1269
+ case "no-free-model": return t("row.poolErrNoFreeModel");
1270
+ default: return t("row.poolErrConflict");
1271
+ }
1272
+ return t("row.poolErrGeneric", { status: String(status) });
1273
+ }
1274
+ /**
1275
+ * Consume a `test` NDJSON answer line-by-line so rows render while the batch
1276
+ * runs. A non-JSON line is skipped; a `{reason:'pool-failed'}` line reports the
1277
+ * failure and counts as "saw something". Returns whether any usable line was
1278
+ * seen (a body with nothing parseable is a route bug the caller should surface).
1279
+ */
1280
+ function consumeTestNdjson(text, t, onRow, onFailure) {
1281
+ let sawAny = false;
1282
+ for (const line of text.split(/\r?\n/)) {
1283
+ if (line.trim() === "") continue;
1284
+ let parsed;
1285
+ try {
1286
+ parsed = JSON.parse(line);
1287
+ } catch {
1288
+ continue;
1289
+ }
1290
+ if (parsed.row !== void 0) {
1291
+ sawAny = true;
1292
+ onRow(parsed.row);
1293
+ } else if (parsed.rows !== void 0) {
1294
+ sawAny = true;
1295
+ for (const r of parsed.rows) onRow(r);
1296
+ } else if (parsed.reason === "pool-failed") {
1297
+ sawAny = true;
1298
+ onFailure(parsed.error ?? t("row.poolLogGeneric"));
1299
+ }
1300
+ }
1301
+ return sawAny;
1302
+ }
1303
+ function AccountPool({ panel, pool, models, t, settingsScope, onChanged, onRefresh, tools, topActions, details, claims, onClaim, deviceClaimed = false, onBind, bindingBusy }) {
1304
+ const [settingsTick, setSettingsTick] = (0, react.useState)(0);
1305
+ const saved = (0, react.useMemo)(() => {
1306
+ const base = regionPoolOf(settingsScope?.getSnapshot().value, panel);
1307
+ return {
1308
+ enabled: base.enabled === true,
1309
+ targetModelId: base.targetModelId ?? pool.targetModelId ?? pool.staleTargetModelId ?? "",
1310
+ memberAccountIds: [...base.memberAccountIds ?? []],
1311
+ probeInputTokens: numberOr(base.probeInputTokens, numberOr(pool.probeInputTokens, 0))
1312
+ };
1313
+ }, [
1314
+ settingsTick,
1315
+ settingsScope,
1316
+ panel,
1317
+ pool.targetModelId,
1318
+ pool.staleTargetModelId,
1319
+ pool.probeInputTokens
1320
+ ]);
1321
+ /**
1322
+ * Whether the running Host knows the probe-size preference at all.
1323
+ *
1324
+ * The card bundle and the Host update on different clocks (a page refresh vs a
1325
+ * restart), so an older Host serves a pool document without this field. Writing
1326
+ * a key that Host's schema does not declare would be dropped on the way in and
1327
+ * then fail the read-back check — a save that reports an error and changes
1328
+ * nothing. Hiding the row is the honest state: this Host cannot do it yet.
1329
+ */
1330
+ const probeSupported = typeof pool.probeInputTokens === "number";
1331
+ const [enabled, setEnabled] = (0, react.useState)(saved.enabled);
1332
+ const [targetModelId, setTargetModelId] = (0, react.useState)(saved.targetModelId ?? "");
1333
+ const [probeInputTokens, setProbeInputTokens] = (0, react.useState)(saved.probeInputTokens);
1334
+ const [selected, setSelected] = (0, react.useState)(() => new Set(saved.memberAccountIds ?? []));
1335
+ const [busy, setBusy] = (0, react.useState)(void 0);
1336
+ const [saveError, setSaveError] = (0, react.useState)(void 0);
1337
+ const [testing, setTesting] = (0, react.useState)(void 0);
1338
+ const [rows, setRows] = (0, react.useState)({});
1339
+ const [removed, setRemoved] = (0, react.useState)({});
1340
+ const [log, setLog] = (0, react.useState)([]);
1341
+ const [now, setNow] = (0, react.useState)(Date.now());
1342
+ const logId = (0, react.useRef)(0);
1343
+ const mountRef = (0, react.useRef)(true);
1344
+ (0, react.useEffect)(() => {
1345
+ mountRef.current = true;
1346
+ return () => {
1347
+ mountRef.current = false;
1348
+ };
1349
+ }, []);
1350
+ (0, react.useEffect)(() => settingsScope?.subscribe(() => {
1351
+ setSettingsTick((tick) => tick + 1);
1352
+ }), [settingsScope]);
1353
+ const pushLog = (tone, text) => {
1354
+ if (!mountRef.current) return;
1355
+ logId.current += 1;
1356
+ const id = logId.current;
1357
+ setLog((prev) => [...prev.slice(-49), {
1358
+ id,
1359
+ tone,
1360
+ text
1361
+ }]);
1362
+ };
1363
+ const knownIds = (0, react.useMemo)(() => new Set(pool.accounts.map((account) => account.accountId)), [pool]);
1364
+ const ghosts = (0, react.useMemo)(() => {
1365
+ const names = pool.memberNames ?? {};
1366
+ const result = [];
1367
+ for (const id of saved.memberAccountIds ?? []) if (!knownIds.has(id)) result.push({
1368
+ accountId: id,
1369
+ accountName: names[id] ?? id
1370
+ });
1371
+ return result;
1372
+ }, [
1373
+ saved,
1374
+ pool,
1375
+ knownIds
1376
+ ]);
1377
+ const dirty = enabled !== saved.enabled || (targetModelId ?? "") !== (saved.targetModelId ?? "") || probeSupported && probeInputTokens !== saved.probeInputTokens || !sameIds([...selected], saved.memberAccountIds);
1378
+ const writable = settingsScope?.getSnapshot().writable === true;
1379
+ const savedKey = (0, react.useMemo)(() => `${panel}:${saved.enabled}:${saved.targetModelId ?? ""}:${saved.probeInputTokens}:${[...saved.memberAccountIds ?? []].sort().join("\0")}`, [panel, saved]);
1380
+ (0, react.useEffect)(() => {
1381
+ setEnabled(saved.enabled);
1382
+ setTargetModelId(saved.targetModelId ?? "");
1383
+ setProbeInputTokens(saved.probeInputTokens);
1384
+ setSelected(new Set(saved.memberAccountIds ?? []));
1385
+ setSaveError(void 0);
1386
+ }, [savedKey]);
1387
+ const toggleMember = (accountId) => {
1388
+ setSelected((prev) => {
1389
+ const next = new Set(prev);
1390
+ if (next.has(accountId)) next.delete(accountId);
1391
+ else next.add(accountId);
1392
+ return next;
1393
+ });
1394
+ };
1395
+ const selectAll = () => {
1396
+ setSelected(/* @__PURE__ */ new Set([...pool.accounts.map((a) => a.accountId), ...saved.memberAccountIds ?? []]));
1397
+ };
1398
+ const selectNone = () => setSelected(/* @__PURE__ */ new Set());
1399
+ const discard = () => {
1400
+ setEnabled(saved.enabled);
1401
+ setTargetModelId(saved.targetModelId ?? "");
1402
+ setProbeInputTokens(saved.probeInputTokens);
1403
+ setSelected(new Set(saved.memberAccountIds ?? []));
1404
+ setSaveError(void 0);
1405
+ pushLog("info", t("row.poolLogDiscarded"));
1406
+ setNow(Date.now());
1407
+ };
1408
+ const save = async () => {
1409
+ if (!writable || busy !== void 0) return;
1410
+ setBusy("save");
1411
+ setSaveError(void 0);
1412
+ try {
1413
+ const next = {
1414
+ enabled,
1415
+ ...targetModelId === "" ? {} : { targetModelId },
1416
+ memberAccountIds: [...selected],
1417
+ ...probeSupported ? { probeInputTokens } : {}
1418
+ };
1419
+ await writeSettingsField(settingsScope, "regions", nextRegionPool(settingsScope.getSnapshot().value, panel, next), (readBack) => {
1420
+ const readPool = regionPoolOf(readBack, panel);
1421
+ return readPool.enabled === (next.enabled ?? false) && sameIds(readPool.memberAccountIds, next.memberAccountIds) && (readPool.targetModelId ?? "") === (next.targetModelId ?? "") && (!probeSupported || (readPool.probeInputTokens ?? 0) === probeInputTokens);
1422
+ });
1423
+ pushLog("ok", t("row.poolLogSaved"));
1424
+ onChanged?.();
1425
+ onRefresh?.();
1426
+ setNow(Date.now());
1427
+ } catch (error) {
1428
+ const message = error instanceof Error ? error.message : String(error);
1429
+ setSaveError(t("row.poolSaveFailed", { message }));
1430
+ pushLog("error", t("row.poolLogSaveFailed"));
1431
+ } finally {
1432
+ if (mountRef.current) setBusy(void 0);
1433
+ }
1434
+ };
1435
+ const runCheckin = async () => {
1436
+ if (busy !== void 0) return;
1437
+ const members = [...selected];
1438
+ if (members.length === 0) {
1439
+ pushLog("warn", t("row.poolLogNoMembers"));
1440
+ return;
1441
+ }
1442
+ setBusy("checkin");
1443
+ setNow(Date.now());
1444
+ pushLog("info", t("row.poolLogCheckinStart", { count: members.length }));
1445
+ try {
1446
+ const response = await fetch(withTraePoolAction(panel, "checkin"), { method: "POST" });
1447
+ const body = await response.json();
1448
+ if (!response.ok) throw new Error(renderRouteError(t, response.status, body));
1449
+ for (const row of body?.rows ?? []) switch (row.status) {
1450
+ case "claimed":
1451
+ pushLog("ok", t("row.poolLogCheckinClaimed", { accountName: row.accountName }));
1452
+ break;
1453
+ case "already":
1454
+ case "device-already":
1455
+ pushLog("info", t("row.poolLogCheckinAlready", { accountName: row.accountName }));
1456
+ break;
1457
+ case "failed":
1458
+ case "disabled": pushLog("error", t("row.poolLogCheckinFailed", {
1459
+ accountName: row.accountName,
1460
+ message: row.message ?? ""
1461
+ }));
1462
+ }
1463
+ pushLog(members.length > 0 && body?.rows ? "ok" : "info", t("row.poolLogCheckinDone"));
1464
+ onRefresh?.();
1465
+ setNow(Date.now());
1466
+ } catch (error) {
1467
+ pushLog("error", t("row.poolLogBatchFailed", { message: error instanceof Error ? error.message : String(error) }));
1468
+ } finally {
1469
+ if (mountRef.current) setBusy(void 0);
1470
+ }
1471
+ };
1472
+ const runTest = async () => {
1473
+ if (busy !== void 0) return;
1474
+ const members = [...selected];
1475
+ if (members.length === 0) {
1476
+ pushLog("warn", t("row.poolLogNoMembers"));
1477
+ return;
1478
+ }
1479
+ setBusy("test");
1480
+ setRows({});
1481
+ setTesting({ done: 0 });
1482
+ setNow(Date.now());
1483
+ const targetLabel = targetModelId === "" ? t("row.poolTargetAutoOption") : models.find((m) => m.id === targetModelId)?.name ?? targetModelId;
1484
+ pushLog("info", t("row.poolLogTestStart", {
1485
+ count: members.length,
1486
+ model: targetLabel
1487
+ }));
1488
+ try {
1489
+ const text = await (await fetch(withTraePoolAction(panel, "test"), { method: "POST" })).text();
1490
+ const seen = /* @__PURE__ */ new Set();
1491
+ if (!consumeTestNdjson(text, t, (r) => {
1492
+ setRows((prev) => ({
1493
+ ...prev,
1494
+ [r.accountId]: r
1495
+ }));
1496
+ seen.add(r.accountId);
1497
+ if (mountRef.current) setTesting({ done: seen.size });
1498
+ pushLog(outcomeTone(r.outcome), t("row.poolLogTestRow", {
1499
+ accountName: r.accountName,
1500
+ outcome: outcomeLabel(r.outcome, t)
1501
+ }));
1502
+ }, (errorText) => {
1503
+ pushLog("error", t("row.poolLogBatchFailed", { message: errorText }));
1504
+ })) throw new Error(t("row.poolLogGeneric"));
1505
+ pushLog("ok", t("row.poolLogTestDone"));
1506
+ onRefresh?.();
1507
+ setNow(Date.now());
1508
+ } catch (error) {
1509
+ pushLog("error", t("row.poolLogBatchFailed", { message: error instanceof Error ? error.message : String(error) }));
1510
+ } finally {
1511
+ if (mountRef.current) {
1512
+ setBusy(void 0);
1513
+ setTesting(void 0);
1514
+ }
1515
+ }
1516
+ };
1517
+ const runSingleTest = async (account) => {
1518
+ if (busy !== void 0) return;
1519
+ setBusy("test");
1520
+ setNow(Date.now());
1521
+ const targetLabel = targetModelId === "" ? t("row.poolTargetAutoOption") : models.find((m) => m.id === targetModelId)?.name ?? targetModelId;
1522
+ pushLog("info", t("row.poolLogRowTesting", { accountName: account.accountName }));
1523
+ try {
1524
+ if (!consumeTestNdjson(await (await fetch(withTraePoolAccount(panel, "test", account.accountId), { method: "POST" })).text(), t, (r) => {
1525
+ setRows((prev) => ({
1526
+ ...prev,
1527
+ [r.accountId]: r
1528
+ }));
1529
+ pushLog(outcomeTone(r.outcome), t("row.poolLogTestRow", {
1530
+ accountName: r.accountName,
1531
+ outcome: outcomeLabel(r.outcome, t),
1532
+ model: targetLabel
1533
+ }));
1534
+ }, (errorText) => {
1535
+ pushLog("error", t("row.poolLogBatchFailed", { message: errorText }));
1536
+ })) throw new Error(t("row.poolLogGeneric"));
1537
+ onRefresh?.();
1538
+ setNow(Date.now());
1539
+ } catch (error) {
1540
+ pushLog("error", t("row.poolLogBatchFailed", { message: error instanceof Error ? error.message : String(error) }));
1541
+ } finally {
1542
+ if (mountRef.current) setBusy(void 0);
1543
+ }
1544
+ };
1545
+ const removeAccount = async (account) => {
1546
+ if (busy !== void 0) return;
1547
+ setBusy("remove");
1548
+ setRemoved((prev) => ({
1549
+ ...prev,
1550
+ [account.accountId]: false
1551
+ }));
1552
+ try {
1553
+ const response = await fetch(withTraePoolAccount(panel, "remove", account.accountId), { method: "POST" });
1554
+ const body = await response.json();
1555
+ if (!response.ok) throw new Error(renderRouteError(t, response.status, body));
1556
+ if (body?.removed !== true) throw new Error(t("row.poolRowRemoveFailed"));
1557
+ if (mountRef.current) setRemoved((prev) => ({
1558
+ ...prev,
1559
+ [account.accountId]: true
1560
+ }));
1561
+ pushLog("ok", t("row.poolRowRemoved", { accountName: account.accountName }));
1562
+ setSelected((prev) => {
1563
+ const next = new Set(prev);
1564
+ next.delete(account.accountId);
1565
+ return next;
1566
+ });
1567
+ onRefresh?.();
1568
+ setNow(Date.now());
1569
+ } catch (error) {
1570
+ pushLog("error", t("row.poolRowRemoveError", { message: error instanceof Error ? error.message : String(error) }));
1571
+ } finally {
1572
+ if (mountRef.current) setBusy(void 0);
1573
+ }
1574
+ };
1575
+ const noCandidate = pool.noCandidate;
1576
+ const showNoCandidate = pool.enabled && noCandidate !== void 0;
1577
+ const serving = servingAccount(pool);
1578
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1579
+ className: "dsm-trae-pool",
1580
+ "aria-label": t("row.poolTitle"),
1581
+ children: [
1582
+ pool.enabled !== true && onBind !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1583
+ className: "dsm-trae-pool-set dsm-trae-pool-bind",
1584
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1585
+ className: "dsm-trae-pool-set-copy",
1586
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: t("row.poolManualAccountLabel") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.poolManualAccountHint") })]
1587
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1588
+ className: "dsm-trae-pool-set-ctl",
1589
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
1590
+ className: "dsm-trae-select dsm-trae-pool-account-select",
1591
+ value: serving?.accountId ?? "",
1592
+ disabled: bindingBusy === true || !writable,
1593
+ onChange: (event) => {
1594
+ const next = event.target.value;
1595
+ if (next !== "") onBind(next);
1596
+ },
1597
+ children: [serving === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1598
+ value: "",
1599
+ disabled: true,
1600
+ children: t("row.poolManualAccountNone")
1601
+ }) : null, pool.accounts.filter((account) => account.reachable !== false).map((account) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1602
+ value: account.accountId,
1603
+ children: account.accountName
1604
+ }, account.accountId))]
1605
+ })
1606
+ })]
1607
+ }) : null,
1608
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1609
+ className: "dsm-trae-pool-actions",
1610
+ children: [
1611
+ pool.checkinSupported === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1612
+ type: "button",
1613
+ className: "dsm-btn dsm-btn-outline dsm-trae-pool-action-checkin",
1614
+ disabled: busy !== void 0 || !writable || selected.size === 0 || deviceClaimed,
1615
+ title: deviceClaimed ? t("row.checkinDeviceSpent") : void 0,
1616
+ onClick: () => {
1617
+ runCheckin();
1618
+ },
1619
+ children: busy === "checkin" ? t("row.poolCheckingIn") : t("row.poolCheckinAll")
1620
+ }) : null,
1621
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1622
+ type: "button",
1623
+ className: "dsm-btn dsm-btn-outline dsm-trae-pool-action-test",
1624
+ disabled: busy !== void 0 || selected.size === 0 || pool.targetModelSource === "none",
1625
+ title: pool.targetModelSource === "none" ? t("row.poolTargetNoneHint") : void 0,
1626
+ onClick: () => {
1627
+ runTest();
1628
+ },
1629
+ children: busy === "test" ? t("row.poolTesting") : t("row.poolTestAll")
1630
+ }),
1631
+ busy === "checkin" || busy === "test" || testing !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1632
+ className: "dsm-trae-pool-busy",
1633
+ role: "status",
1634
+ children: t("row.poolBusyHint")
1635
+ }) : null,
1636
+ topActions === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1637
+ className: "dsm-trae-pool-actions-end",
1638
+ children: topActions
1639
+ })
1640
+ ]
1641
+ }),
1642
+ showNoCandidate ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NoCandidateNotice, {
1643
+ noCandidate,
1644
+ t
1645
+ }) : null,
1646
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1647
+ className: "dsm-trae-pool-card",
1648
+ children: [
1649
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1650
+ className: "dsm-trae-pool-card-head",
1651
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
1652
+ className: "dsm-trae-pool-card-title",
1653
+ children: t("row.poolMembersTitle")
1654
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1655
+ className: "dsm-trae-pool-card-tools",
1656
+ children: [
1657
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1658
+ className: "dsm-trae-pool-count",
1659
+ children: t("row.poolSelectedCount", {
1660
+ count: selected.size,
1661
+ total: pool.accounts.length + ghosts.length
1662
+ })
1663
+ }),
1664
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1665
+ type: "button",
1666
+ className: "dsm-btn dsm-btn-outline",
1667
+ disabled: busy !== void 0,
1668
+ onClick: selectAll,
1669
+ children: t("row.poolSelectAll")
1670
+ }),
1671
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1672
+ type: "button",
1673
+ className: "dsm-btn dsm-btn-outline",
1674
+ disabled: busy !== void 0,
1675
+ onClick: selectNone,
1676
+ children: t("row.poolSelectNone")
1677
+ })
1678
+ ]
1679
+ })]
1680
+ }),
1681
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1682
+ className: "dsm-trae-pool-card-note",
1683
+ children: t("row.poolCardHint")
1684
+ }),
1685
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1686
+ className: pool.checkinSupported ? "dsm-trae-pool-table" : "dsm-trae-pool-table dsm-trae-pool-table-no-checkin",
1687
+ role: "table",
1688
+ "aria-label": t("row.poolMembersTitle"),
1689
+ children: [
1690
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemberRowHeader, {
1691
+ t,
1692
+ checkinSupported: pool.checkinSupported
1693
+ }),
1694
+ pool.accounts.map((account) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemberRow, {
1695
+ account,
1696
+ showCheckin: pool.checkinSupported,
1697
+ deviceClaimed,
1698
+ models,
1699
+ rows,
1700
+ checked: selected.has(account.accountId),
1701
+ detail: details?.[account.accountId],
1702
+ claim: claims?.[account.accountId],
1703
+ onClaim,
1704
+ onToggle: () => {
1705
+ toggleMember(account.accountId);
1706
+ },
1707
+ onTest: () => {
1708
+ runSingleTest(account);
1709
+ },
1710
+ t,
1711
+ disabled: busy !== void 0 || !writable,
1712
+ removing: removed[account.accountId] === true,
1713
+ onRemove: () => {
1714
+ removeAccount(account);
1715
+ },
1716
+ formatNumber: formatNumber$1,
1717
+ relativeAt
1718
+ }, account.accountId)),
1719
+ ghosts.map((ghost) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemberGhostRow, {
1720
+ ghost,
1721
+ showCheckin: pool.checkinSupported,
1722
+ checked: selected.has(ghost.accountId),
1723
+ onToggle: () => {
1724
+ toggleMember(ghost.accountId);
1725
+ },
1726
+ t,
1727
+ disabled: busy !== void 0 || !writable,
1728
+ removing: removed[ghost.accountId] === true,
1729
+ onRemove: () => {
1730
+ removeAccount(ghost);
1731
+ }
1732
+ }, `ghost:${ghost.accountId}`))
1733
+ ]
1734
+ }),
1735
+ ghosts.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1736
+ className: "dsm-trae-pool-ghost-note",
1737
+ role: "status",
1738
+ children: t("row.poolGhostMembers", {
1739
+ count: ghosts.length,
1740
+ names: ghosts.map((g) => g.accountName).join("、")
1741
+ })
1742
+ }) : null,
1743
+ dirty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1744
+ className: "dsm-trae-pool-unsaved",
1745
+ role: "status",
1746
+ children: t("row.poolUnsavedMembers")
1747
+ }) : null
1748
+ ]
1749
+ }),
1750
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1751
+ className: "dsm-trae-pool-settings-wrap",
1752
+ children: [
1753
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1754
+ className: "dsm-trae-pool-head",
1755
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
1756
+ className: "dsm-trae-pool-title",
1757
+ children: t("row.poolSettingsTitle")
1758
+ }), dirty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1759
+ className: "dsm-trae-pool-dirty",
1760
+ role: "status",
1761
+ children: t("row.poolDirty")
1762
+ }) : null]
1763
+ }),
1764
+ tools === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1765
+ className: "dsm-trae-pool-tools dsm-trae-pool-tools-dashed",
1766
+ children: tools
1767
+ }),
1768
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1769
+ className: "dsm-trae-pool-note",
1770
+ children: t("row.poolSettingsNote")
1771
+ }),
1772
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1773
+ className: "dsm-trae-pool-settings",
1774
+ children: [
1775
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1776
+ className: "dsm-trae-pool-set",
1777
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1778
+ className: "dsm-trae-pool-set-copy",
1779
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: t("row.poolEnabled") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.poolEnabledHint") })]
1780
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1781
+ className: "dsm-trae-pool-set-ctl",
1782
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
1783
+ className: "dsm-trae-switch dsm-trae-switch-pill",
1784
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1785
+ type: "checkbox",
1786
+ checked: enabled,
1787
+ disabled: busy !== void 0 || !writable,
1788
+ "aria-label": t("row.poolEnabled"),
1789
+ onChange: (e) => {
1790
+ setEnabled(e.target.checked);
1791
+ }
1792
+ })
1793
+ })
1794
+ })]
1795
+ }),
1796
+ saved.enabled === true && enabled === false ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1797
+ className: "dsm-trae-pool-off-notice",
1798
+ role: "status",
1799
+ children: t("row.poolOffNotice")
1800
+ }) : null,
1801
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1802
+ className: "dsm-trae-pool-set",
1803
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1804
+ className: "dsm-trae-pool-set-copy",
1805
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: t("row.poolTargetLabel") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.poolTargetHint") })]
1806
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1807
+ className: "dsm-trae-pool-set-ctl",
1808
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
1809
+ className: "dsm-trae-select",
1810
+ "aria-label": t("row.poolTargetLabel"),
1811
+ value: targetModelId,
1812
+ disabled: busy !== void 0 || !writable,
1813
+ onChange: (e) => {
1814
+ setTargetModelId(e.target.value);
1815
+ },
1816
+ children: [
1817
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1818
+ value: "",
1819
+ children: t("row.poolTargetAutoOption")
1820
+ }),
1821
+ models.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1822
+ value: model.id,
1823
+ children: model.name
1824
+ }, model.id)),
1825
+ pool.targetModelSource === "stale" && pool.staleTargetModelId !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1826
+ value: pool.staleTargetModelId,
1827
+ children: t("row.poolTargetStaleOption", { model: pool.staleTargetModelId })
1828
+ }) : null
1829
+ ]
1830
+ })
1831
+ })]
1832
+ }),
1833
+ pool.targetModelSource === "stale" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1834
+ className: "dsm-trae-pool-target-stale",
1835
+ role: "status",
1836
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.poolTargetStale", { model: pool.staleTargetModelId ?? "" }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1837
+ type: "button",
1838
+ className: "dsm-btn dsm-btn-outline",
1839
+ disabled: busy !== void 0,
1840
+ onClick: () => {
1841
+ setTargetModelId("");
1842
+ pushLog("info", t("row.poolLogTargetCleared"));
1843
+ },
1844
+ children: t("row.poolTargetStaleClear")
1845
+ })]
1846
+ }) : null,
1847
+ pool.targetModelSource === "none" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
1848
+ className: "dsm-trae-pool-target-none",
1849
+ role: "status",
1850
+ children: [
1851
+ t("row.poolTargetNone"),
1852
+ " — ",
1853
+ t("row.poolTargetNoneHint")
1854
+ ]
1855
+ }) : null,
1856
+ probeSupported ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1857
+ className: "dsm-trae-pool-set",
1858
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1859
+ className: "dsm-trae-pool-set-copy",
1860
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: t("row.poolProbeSizeLabel") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.poolProbeSizeHint") })]
1861
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1862
+ className: "dsm-trae-pool-set-ctl",
1863
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
1864
+ className: "dsm-trae-select",
1865
+ "aria-label": t("row.poolProbeSizeLabel"),
1866
+ value: String(probeInputTokens),
1867
+ disabled: busy !== void 0 || !writable,
1868
+ onChange: (e) => {
1869
+ setProbeInputTokens(Number(e.target.value));
1870
+ },
1871
+ children: PROBE_INPUT_TOKEN_CHOICES.map((choice) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1872
+ value: String(choice),
1873
+ children: choice === 0 ? t("row.poolProbeSizeDefault") : t("row.poolProbeSizeOption", { wan: choice / 1e4 })
1874
+ }, choice))
1875
+ })
1876
+ })]
1877
+ }) : null,
1878
+ saveError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1879
+ className: "dsm-trae-pool-save-error",
1880
+ role: "alert",
1881
+ children: saveError
1882
+ })
1883
+ ]
1884
+ }),
1885
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1886
+ className: "dsm-trae-pool-save-row",
1887
+ children: [
1888
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
1889
+ className: "dsm-trae-pool-star",
1890
+ href: "https://github.com/dingminhua/dsh-connect-trae",
1891
+ target: "_blank",
1892
+ rel: "noreferrer noopener",
1893
+ children: [t("row.poolStar"), " ★"]
1894
+ }),
1895
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1896
+ type: "button",
1897
+ className: "dsm-btn dsm-btn-outline",
1898
+ disabled: !dirty || busy !== void 0,
1899
+ onClick: discard,
1900
+ children: t("row.poolDiscard")
1901
+ }),
1902
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1903
+ type: "button",
1904
+ className: "dsm-btn dsm-btn-primary",
1905
+ disabled: !dirty || busy !== void 0,
1906
+ onClick: () => {
1907
+ save();
1908
+ },
1909
+ children: busy === "save" ? t("row.poolSaving") : t("row.poolSaved")
1910
+ })
1911
+ ]
1912
+ })
1913
+ ]
1914
+ }),
1915
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1916
+ className: "dsm-trae-pool-note",
1917
+ children: t("row.poolRegionNote")
1918
+ }),
1919
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActivityLog, {
1920
+ log,
1921
+ t,
1922
+ now,
1923
+ relativeAt,
1924
+ onClear: () => setLog([])
1925
+ })
1926
+ ]
1927
+ });
1928
+ }
1929
+ function NoCandidateNotice({ noCandidate, t }) {
1930
+ if (noCandidate === "empty") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1931
+ className: "dsm-trae-pool-warning dsm-trae-pool-warning-empty",
1932
+ role: "status",
1933
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1934
+ className: "dsm-trae-pool-warning-title",
1935
+ children: t("row.poolNoCandidateEmpty")
1936
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1937
+ className: "dsm-trae-pool-warning-hint",
1938
+ children: t("row.poolNoCandidateEmptyHint")
1939
+ })]
1940
+ });
1941
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1942
+ className: "dsm-trae-pool-warning dsm-trae-pool-warning-unusable",
1943
+ role: "status",
1944
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1945
+ className: "dsm-trae-pool-warning-title",
1946
+ children: t("row.poolNoCandidateUnusable")
1947
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1948
+ className: "dsm-trae-pool-warning-hint",
1949
+ children: [
1950
+ t("row.poolNoCandidateHint"),
1951
+ " ",
1952
+ exclusionLabel(noCandidate, t)
1953
+ ]
1954
+ })]
1955
+ });
1956
+ }
1957
+ function MemberRowHeader({ t, checkinSupported }) {
1958
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1959
+ className: "dsm-trae-pool-row dsm-trae-pool-row-head",
1960
+ role: "row",
1961
+ children: [
1962
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1963
+ className: "dsm-trae-pool-col-account",
1964
+ children: t("row.poolColumnAccount")
1965
+ }),
1966
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.poolColumnCredits") }),
1967
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.poolColumnProbe") }),
1968
+ checkinSupported ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.poolColumnCheckin") }) : null,
1969
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {})
1970
+ ]
1971
+ });
1972
+ }
1973
+ function MemberRow({ account, models, rows, checked, onToggle, onTest, t, disabled, removing, onRemove, formatNumber, relativeAt, detail, claim, onClaim, showCheckin, deviceClaimed }) {
1974
+ const isCurrent = account.current === true;
1975
+ const credit = account.generalAvailable;
1976
+ const excluded = account.excludedBy;
1977
+ const checkin = account.checkedInToday;
1978
+ const device = account.deviceCheckedIn;
1979
+ const testRow = rows[account.accountId];
1980
+ /**
1981
+ * Whether the probe column already explains WHY this row is excluded.
1982
+ *
1983
+ * The exclusion tag and the probe cell are derived from the same verdict, so a
1984
+ * row that shows both says 「登录凭据被拒」 twice, six centimetres apart. Keep
1985
+ * the tag only for the case the cell cannot cover: no probe to show at all
1986
+ * (e.g. a stored member whose last test predates this session).
1987
+ */
1988
+ const probeOnScreen = testRow !== void 0 || account.probe !== void 0;
1989
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1990
+ className: `dsm-trae-pool-row${isCurrent ? " dsm-trae-pool-row-current" : ""}`,
1991
+ role: "row",
1992
+ "data-account-id": account.accountId,
1993
+ children: [
1994
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1995
+ className: "dsm-trae-pool-account",
1996
+ children: [
1997
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
1998
+ className: "dsm-trae-pool-check",
1999
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2000
+ type: "checkbox",
2001
+ checked,
2002
+ disabled,
2003
+ onChange: onToggle,
2004
+ "aria-label": t("row.poolSelectAria", { accountName: account.accountName })
2005
+ })
2006
+ }),
2007
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2008
+ className: "dsm-trae-pool-account-name",
2009
+ children: [
2010
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2011
+ className: `dsm-trae-pool-dot dsm-trae-pool-dot-${excluded ? "excluded" : "ok"}`,
2012
+ "aria-hidden": "true"
2013
+ }),
2014
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: account.accountName }),
2015
+ detail?.meta === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2016
+ className: "dsm-trae-pool-meta",
2017
+ children: detail.meta
2018
+ }),
2019
+ detail?.warn === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2020
+ className: `dsm-trae-pool-warn-tag dsm-trae-pool-warn-tag-${detail.warnTone ?? "soon"}`,
2021
+ role: "status",
2022
+ children: detail.warn
2023
+ })
2024
+ ]
2025
+ }),
2026
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2027
+ className: "dsm-trae-pool-account-tags",
2028
+ children: [
2029
+ isCurrent ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2030
+ className: "dsm-trae-pool-tag dsm-trae-pool-tag-current",
2031
+ children: t("row.poolCurrentBadge")
2032
+ }) : null,
2033
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2034
+ className: checked ? "dsm-trae-pool-member" : "dsm-trae-pool-not-member",
2035
+ children: checked ? t("row.poolMember") : t("row.poolNotMember")
2036
+ }),
2037
+ excluded && !probeOnScreen ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2038
+ className: "dsm-trae-pool-tag dsm-trae-pool-tag-excluded",
2039
+ children: exclusionLabel(excluded, t)
2040
+ }) : null,
2041
+ account.tokenExpired === true && excluded !== "credential-rejected" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2042
+ className: "dsm-trae-pool-warn-tag dsm-trae-pool-warn-tag-lapsed",
2043
+ children: t("row.poolTokenExpired")
2044
+ }) : null
2045
+ ]
2046
+ }),
2047
+ account.probe?.atMs === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2048
+ className: "dsm-trae-pool-account-note",
2049
+ children: t("row.poolProbeAt", { when: shortWhen(account.probe.atMs) })
2050
+ })
2051
+ ]
2052
+ }),
2053
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2054
+ className: "dsm-trae-pool-credit",
2055
+ title: t("row.poolWorkHint"),
2056
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2057
+ className: "dsm-trae-pool-credit-num",
2058
+ children: credit === void 0 ? t("row.poolCreditNone") : formatNumber(credit)
2059
+ }), credit === void 0 || account.nearestExpiryMs === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2060
+ className: "dsm-trae-pool-credit-meta",
2061
+ children: t("row.poolCreditExpiry", { when: shortDate(account.nearestExpiryMs) })
2062
+ })]
2063
+ }),
2064
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2065
+ className: "dsm-trae-pool-probe",
2066
+ children: testRow !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProbeResult, {
2067
+ outcome: testRow.outcome,
2068
+ modelId: testRow.modelId,
2069
+ atMs: void 0,
2070
+ source: void 0,
2071
+ t,
2072
+ relativeAt,
2073
+ now: Date.now(),
2074
+ detail: testRow.detail
2075
+ }) : account.probe !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProbeResult, {
2076
+ outcome: account.probe.outcome,
2077
+ modelId: account.probe.modelId,
2078
+ atMs: account.probe.atMs,
2079
+ source: account.probe.source,
2080
+ t,
2081
+ relativeAt,
2082
+ now: Date.now(),
2083
+ detail: account.probe.message
2084
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2085
+ className: "dsm-trae-pool-probe-none",
2086
+ children: t("row.poolNeverTested")
2087
+ })
2088
+ }),
2089
+ showCheckin ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2090
+ className: "dsm-trae-pool-checkin",
2091
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2092
+ className: `dsm-trae-pool-checkin-state dsm-trae-pool-checkin-${checkin === void 0 ? "unknown" : checkin ? "ok" : device === true || deviceClaimed ? "device" : "no"}`,
2093
+ children: checkin === void 0 ? t("row.poolCheckinUnknown") : checkin ? t("row.poolCheckedIn") : device === true || deviceClaimed ? t("row.poolDeviceUsed") : t("row.poolNotCheckedIn")
2094
+ }), claim === void 0 || onClaim === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2095
+ type: "button",
2096
+ className: "dsm-btn dsm-btn-outline dsm-trae-pool-claim",
2097
+ disabled: claim.disabled || disabled,
2098
+ title: claim.title,
2099
+ onClick: () => {
2100
+ onClaim(account.accountId);
2101
+ },
2102
+ children: claim.label
2103
+ })]
2104
+ }) : null,
2105
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2106
+ className: "dsm-trae-pool-row-actions",
2107
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2108
+ type: "button",
2109
+ className: "dsm-btn dsm-btn-outline",
2110
+ disabled,
2111
+ onClick: onTest,
2112
+ children: t("row.poolRowTest")
2113
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2114
+ type: "button",
2115
+ className: "dsm-btn dsm-btn-outline dsm-trae-pool-remove",
2116
+ disabled: disabled || removing,
2117
+ "aria-label": t("row.poolRowRemoveAria", { accountName: account.accountName }),
2118
+ onClick: onRemove,
2119
+ children: removing ? t("row.poolRowRemoving") : t("row.poolRowRemove")
2120
+ })]
2121
+ })
2122
+ ]
2123
+ });
2124
+ }
2125
+ function MemberGhostRow({ ghost, checked, onToggle, t, disabled, removing, onRemove, showCheckin }) {
2126
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2127
+ className: "dsm-trae-pool-row dsm-trae-pool-row-ghost",
2128
+ role: "row",
2129
+ "data-account-id": ghost.accountId,
2130
+ children: [
2131
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2132
+ className: "dsm-trae-pool-account",
2133
+ children: [
2134
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
2135
+ className: "dsm-trae-pool-check",
2136
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2137
+ type: "checkbox",
2138
+ checked,
2139
+ disabled,
2140
+ onChange: onToggle,
2141
+ "aria-label": t("row.poolSelectAria", { accountName: ghost.accountName })
2142
+ })
2143
+ }),
2144
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2145
+ className: "dsm-trae-pool-account-name",
2146
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2147
+ className: "dsm-trae-pool-dot dsm-trae-pool-dot-ghost",
2148
+ "aria-hidden": "true"
2149
+ }), ghost.accountName]
2150
+ }),
2151
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2152
+ className: "dsm-trae-pool-account-tags",
2153
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2154
+ className: checked ? "dsm-trae-pool-member" : "dsm-trae-pool-not-member",
2155
+ children: checked ? t("row.poolMember") : t("row.poolNotMember")
2156
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2157
+ className: "dsm-trae-pool-tag dsm-trae-pool-tag-ghost",
2158
+ children: t("row.poolExcludedUnusable")
2159
+ })]
2160
+ })
2161
+ ]
2162
+ }),
2163
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2164
+ className: "dsm-trae-pool-credit",
2165
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2166
+ className: "dsm-trae-pool-credit-num",
2167
+ children: t("row.poolCreditNone")
2168
+ })
2169
+ }),
2170
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2171
+ className: "dsm-trae-pool-probe",
2172
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2173
+ className: "dsm-trae-pool-probe-none",
2174
+ children: t("row.poolNeverTested")
2175
+ })
2176
+ }),
2177
+ showCheckin ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2178
+ className: "dsm-trae-pool-checkin",
2179
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2180
+ className: "dsm-trae-pool-checkin-state dsm-trae-pool-checkin-unknown",
2181
+ children: t("row.poolCheckinUnknown")
2182
+ })
2183
+ }) : null,
2184
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2185
+ className: "dsm-trae-pool-row-actions",
2186
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2187
+ type: "button",
2188
+ className: "dsm-btn dsm-btn-outline dsm-trae-pool-remove",
2189
+ disabled: disabled || removing,
2190
+ "aria-label": t("row.poolGhostRemoveAria", { accountName: ghost.accountName }),
2191
+ onClick: onRemove,
2192
+ children: removing ? t("row.poolRowRemoving") : t("row.poolGhostRemove")
2193
+ })
2194
+ })
2195
+ ]
2196
+ });
2197
+ }
2198
+ function ProbeResult({ outcome, modelId, atMs, source, detail, t, relativeAt, now }) {
2199
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2200
+ className: "dsm-trae-pool-probe-cell",
2201
+ children: [
2202
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2203
+ className: `dsm-trae-pool-probe-outcome dsm-trae-pool-probe-${outcome === "ok" ? "ok" : "warn"}`,
2204
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2205
+ className: "dsm-trae-pool-dot dsm-trae-pool-dot-ok",
2206
+ "aria-hidden": "true"
2207
+ }), outcomeLabel(outcome, t)]
2208
+ }),
2209
+ atMs === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2210
+ className: "dsm-trae-pool-probe-meta",
2211
+ children: t("row.poolProbeMeasured", { when: relativeAt(atMs, now, t) })
2212
+ }),
2213
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2214
+ className: "dsm-trae-pool-probe-meta",
2215
+ children: [modelId !== void 0 ? t("row.poolProbeModel", { model: modelId }) : t("row.poolProbeModelUnknown"), source === void 0 ? "" : ` · ${probeSourceLabel(source, t)}`]
2216
+ }),
2217
+ detail !== void 0 && detail !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2218
+ className: "dsm-trae-pool-probe-detail",
2219
+ children: detail
2220
+ }) : null
2221
+ ]
2222
+ });
2223
+ }
2224
+ function ActivityLog({ log, t, now, relativeAt, onClear }) {
2225
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2226
+ className: "dsm-trae-pool-log",
2227
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2228
+ className: "dsm-trae-pool-log-head",
2229
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
2230
+ className: "dsm-trae-pool-subtitle",
2231
+ children: t("row.poolLogTitle")
2232
+ }), log.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2233
+ type: "button",
2234
+ className: "dsm-btn dsm-btn-outline",
2235
+ onClick: onClear,
2236
+ children: t("row.poolLogClear")
2237
+ }) : null]
2238
+ }), log.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2239
+ className: "dsm-trae-pool-log-empty",
2240
+ children: t("row.poolLogEmpty")
2241
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ol", {
2242
+ className: "dsm-trae-pool-log-list",
2243
+ children: log.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", {
2244
+ className: `dsm-trae-pool-log-item dsm-trae-pool-log-${entry.tone}`,
2245
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2246
+ className: "dsm-trae-pool-log-text",
2247
+ children: entry.text
2248
+ })
2249
+ }, entry.id))
2250
+ })]
2251
+ });
2252
+ }
2253
+ //#endregion
863
2254
  //#region src/client/AccountTransfer.tsx
864
2255
  /**
865
2256
  * The card's account-transfer section: batch export and import of a region's
@@ -2378,23 +3769,52 @@ window.__ModuleLoader__.load({
2378
3769
  * Until the row's status lands the button waits, disabled, so the row keeps
2379
3770
  * its shape instead of reflowing the moment the read returns.
2380
3771
  */
2381
- const checkinControl = (account, state) => {
3772
+ /**
3773
+ * Everything a check-in button needs, derived once. The pool's table replaces
3774
+ * this card's roster table, so the same derivation has to feed both: two
3775
+ * copies would drift the moment one of them learned about a new refusal.
3776
+ */
3777
+ /**
3778
+ * Whether THIS machine has already been paid today, for any account.
3779
+ *
3780
+ * `didCheckedIn` is not an account fact. Trae keys the daily claim to
3781
+ * `x-device-id` and answers a second claim with 9095, so once one account has
3782
+ * claimed from this machine, every other account on it is refused for the
3783
+ * rest of the day. Keeping that flag per account made the card invite a
3784
+ * check-in that could only fail — visible as soon as two accounts sit next to
3785
+ * each other in one table.
3786
+ */
3787
+ const deviceClaimedToday = (() => {
3788
+ for (const state of Object.values(accountStatusByRegion[activeRegion])) if (state?.checkin?.didCheckedIn === true) return true;
3789
+ for (const spent of Object.values(deviceSpentByRegion[activeRegion])) if (spent) return true;
3790
+ return status.status === "signed-in" && status.pool !== void 0 && status.pool.accounts.some((account) => account.deviceCheckedIn === true);
3791
+ })();
3792
+ const claimViewOf = (accountId, state) => {
2382
3793
  const checkin = state?.checkin;
2383
3794
  const reward = checkin === void 0 ? void 0 : formatNumber(checkin.credits);
2384
3795
  const claimed = checkin?.checkedIn === true;
2385
- const deviceSpent = deviceSpentByRegion[activeRegion][account.id] === true || checkin?.didCheckedIn === true;
2386
- const spending = claimingFor === account.id;
3796
+ const deviceSpent = deviceClaimedToday;
3797
+ const spending = claimingFor === accountId;
2387
3798
  const usable = checkin !== void 0 && checkin.enabled;
2388
3799
  const title = reward === void 0 || !usable ? t("row.checkinDisabled") : deviceSpent && !claimed ? t("row.checkinDeviceSpent") : claimed ? t("row.checkinClaimed") : t("row.checkinReward", { reward });
3800
+ return {
3801
+ label: spending ? t("row.checkinClaiming") : claimed ? t("row.checkinClaimed") : deviceSpent ? t("row.checkinDeviceSpentShort") : !usable || reward === void 0 ? t("row.checkinClaim") : t("row.checkinClaimWith", { reward }),
3802
+ title,
3803
+ disabled: !usable || claimed || deviceSpent || spending,
3804
+ busy: spending
3805
+ };
3806
+ };
3807
+ const checkinControl = (account, state) => {
3808
+ const view = claimViewOf(account.id, state);
2389
3809
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2390
3810
  type: "button",
2391
3811
  className: "dsm-trae-account-checkin",
2392
- disabled: !usable || claimed || deviceSpent || spending,
2393
- title,
3812
+ disabled: view.disabled,
3813
+ title: view.title,
2394
3814
  onClick: () => {
2395
3815
  claimFor(account.id);
2396
3816
  },
2397
- children: spending ? t("row.checkinClaiming") : claimed ? t("row.checkinClaimed") : deviceSpent ? t("row.checkinDeviceSpentShort") : !usable || reward === void 0 ? t("row.checkinClaim") : t("row.checkinClaimWith", { reward })
3817
+ children: view.label
2398
3818
  });
2399
3819
  };
2400
3820
  const label = status.status === "signed-in" ? t("row.signedIn", { accountName: status.accountName }) : status.status === "error" ? t("row.requestFailed") : t("row.signedOut");
@@ -2498,6 +3918,7 @@ window.__ModuleLoader__.load({
2498
3918
  title: t("row.followAppLoginHint"),
2499
3919
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2500
3920
  type: "checkbox",
3921
+ className: "dsm-trae-pref-check",
2501
3922
  checked: settingsEdits.followAppLogin,
2502
3923
  disabled: settingsScope?.getSnapshot().writable !== true || savingSettings,
2503
3924
  "aria-describedby": followHintId,
@@ -2505,18 +3926,16 @@ window.__ModuleLoader__.load({
2505
3926
  writePreference("followAppLogin", event.currentTarget.checked);
2506
3927
  }
2507
3928
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2508
- id: followHintId,
2509
- children: [
2510
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", {
2511
- id: followLabelId,
2512
- children: t("row.followAppLogin")
2513
- }),
2514
- " ",
2515
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2516
- className: "dsm-trae-pref-hint",
2517
- children: t("row.followAppLoginHint")
2518
- })
2519
- ]
3929
+ className: "dsm-trae-pref-copy",
3930
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3931
+ className: "dsm-trae-pref-label",
3932
+ id: followLabelId,
3933
+ children: t("row.followAppLogin")
3934
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3935
+ className: "dsm-trae-pref-hint",
3936
+ id: followHintId,
3937
+ children: t("row.followAppLoginHint")
3938
+ })]
2520
3939
  })]
2521
3940
  }),
2522
3941
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
@@ -2524,6 +3943,7 @@ window.__ModuleLoader__.load({
2524
3943
  title: t(isCnPool ? "row.showPointsInMainUiHint" : "row.showPointsInMainUiHintAi"),
2525
3944
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2526
3945
  type: "checkbox",
3946
+ className: "dsm-trae-pref-check",
2527
3947
  checked: settingsEdits.showPointsInMainUi,
2528
3948
  disabled: settingsScope?.getSnapshot().writable !== true || savingSettings,
2529
3949
  "aria-describedby": creditsHintId,
@@ -2531,50 +3951,53 @@ window.__ModuleLoader__.load({
2531
3951
  writePreference("showPointsInMainUi", event.currentTarget.checked);
2532
3952
  }
2533
3953
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2534
- id: creditsHintId,
2535
- children: [
2536
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", {
2537
- id: creditsLabelId,
2538
- children: t("row.showPointsInMainUi")
2539
- }),
2540
- " ",
2541
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2542
- className: "dsm-trae-pref-hint",
2543
- children: t(isCnPool ? "row.showPointsInMainUiHint" : "row.showPointsInMainUiHintAi")
2544
- })
2545
- ]
3954
+ className: "dsm-trae-pref-copy",
3955
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3956
+ className: "dsm-trae-pref-label",
3957
+ id: creditsLabelId,
3958
+ children: t("row.showPointsInMainUi")
3959
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3960
+ className: "dsm-trae-pref-hint",
3961
+ id: creditsHintId,
3962
+ children: t(isCnPool ? "row.showPointsInMainUiHint" : "row.showPointsInMainUiHintAi")
3963
+ })]
2546
3964
  })]
2547
3965
  }),
2548
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
3966
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
2549
3967
  className: "dsm-trae-pref dsm-trae-credit-position-pref",
2550
3968
  title: t("row.creditPositionHint"),
2551
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
2552
- className: "dsm-trae-credit-position",
2553
- value: activeRegionOn ? creditPositionOn(activeRegion) : "composer",
2554
- disabled: !activeRegionOn || settingsScope?.getSnapshot().writable !== true || savingSettings,
2555
- "aria-label": t("row.creditPositionAria", { region: activeRegion === "cn" ? t("row.tabCn") : activeRegion === "enterprise" ? t("row.tabEnterprise") : t("row.tabAi") }),
2556
- onChange: (event) => {
2557
- changeCreditPosition(activeRegion, event.currentTarget.value === "stats" ? "stats" : "composer");
2558
- },
2559
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2560
- value: "composer",
2561
- children: t("row.creditPositionComposer")
2562
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2563
- value: "stats",
2564
- children: t("row.creditPositionStats")
2565
- })]
2566
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
2567
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: t("row.creditPositionLabel") }),
2568
- " ",
2569
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3969
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3970
+ className: "dsm-trae-pref-copy",
3971
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3972
+ className: "dsm-trae-pref-lead",
3973
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3974
+ className: "dsm-trae-pref-label",
3975
+ children: t("row.creditPositionLabel")
3976
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
3977
+ className: "dsm-trae-credit-position",
3978
+ value: activeRegionOn ? creditPositionOn(activeRegion) : "composer",
3979
+ disabled: !activeRegionOn || settingsScope?.getSnapshot().writable !== true || savingSettings,
3980
+ "aria-label": t("row.creditPositionAria", { region: activeRegion === "cn" ? t("row.tabCn") : activeRegion === "enterprise" ? t("row.tabEnterprise") : t("row.tabAi") }),
3981
+ onChange: (event) => {
3982
+ changeCreditPosition(activeRegion, event.currentTarget.value === "stats" ? "stats" : "composer");
3983
+ },
3984
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3985
+ value: "composer",
3986
+ children: t("row.creditPositionComposer")
3987
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3988
+ value: "stats",
3989
+ children: t("row.creditPositionStats")
3990
+ })]
3991
+ })]
3992
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2570
3993
  className: "dsm-trae-pref-hint",
2571
3994
  children: t("row.creditPositionHint")
2572
- })
2573
- ] })]
3995
+ })]
3996
+ })
2574
3997
  })
2575
3998
  ]
2576
3999
  }),
2577
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
4000
+ status.status === "signed-in" && status.pool !== void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2578
4001
  className: "dsm-trae-accounts",
2579
4002
  "aria-label": t("row.accountsTitle"),
2580
4003
  children: [
@@ -2698,62 +4121,6 @@ window.__ModuleLoader__.load({
2698
4121
  }, account.id);
2699
4122
  })]
2700
4123
  }) : null,
2701
- status.status === "signed-in" && status.creditsUnavailable === "enterprise" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2702
- className: "dsm-trae-credits-unavailable",
2703
- role: "note",
2704
- children: t("row.enterpriseCreditsNotice")
2705
- }) : null,
2706
- status.status === "signed-in" && status.alternatives !== void 0 && status.alternatives.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2707
- className: "dsm-trae-usage-alternative",
2708
- role: "status",
2709
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
2710
- className: "dsm-trae-usage-alternative-text",
2711
- children: [
2712
- t("row.creditsExhausted", { account: status.accountName }),
2713
- " ",
2714
- t("row.creditsAlternative", { accounts: status.alternatives.map((alt) => `${alt.accountName}(${t(`row.edition${alt.edition === "cn" ? "Cn" : alt.edition === "sg" ? "Sg" : alt.edition === "solo" ? "Solo" : "SoloSg"}`)}:${formatNumber(alt.workAvailable + alt.generalAvailable)})`).join("、") })
2715
- ]
2716
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2717
- type: "button",
2718
- className: "dsm-trae-usage-switch-button",
2719
- disabled: switchingAccount || settingsScope?.getSnapshot()?.writable !== true,
2720
- onClick: () => {
2721
- const target = status.alternatives?.[0];
2722
- if (target !== void 0) switchAccount(target.id);
2723
- },
2724
- children: t("row.creditsAlternativeSwitch")
2725
- })]
2726
- }) : null,
2727
- status.status === "signed-in" && status.credits?.expiry !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ExpiryNotice, {
2728
- expiry: status.credits.expiry,
2729
- t,
2730
- formatNumber,
2731
- formatDateTime
2732
- }) : null,
2733
- status.status === "signed-in" && status.appLogin !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2734
- className: "dsm-trae-usage-alternative",
2735
- role: "status",
2736
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2737
- className: "dsm-trae-usage-alternative-text",
2738
- children: t("row.appLoginMismatch", {
2739
- account: status.appLogin.accountName,
2740
- bound: status.accountName
2741
- })
2742
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2743
- type: "button",
2744
- className: "dsm-trae-usage-switch-button",
2745
- disabled: switchingAccount || settingsScope?.getSnapshot()?.writable !== true,
2746
- onClick: () => {
2747
- followAppLogin();
2748
- },
2749
- children: t("row.appLoginFollow", { account: status.appLogin.accountName })
2750
- })]
2751
- }) : null,
2752
- rescanNote === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2753
- className: "dsm-trae-usage-note",
2754
- role: "status",
2755
- children: rescanNote
2756
- }),
2757
4124
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountTransfer, {
2758
4125
  t,
2759
4126
  region: activeRegion === "ai" ? "ai" : "cn",
@@ -2771,7 +4138,133 @@ window.__ModuleLoader__.load({
2771
4138
  })
2772
4139
  ]
2773
4140
  }),
4141
+ status.status === "signed-in" && status.creditsUnavailable === "enterprise" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4142
+ className: "dsm-trae-credits-unavailable",
4143
+ role: "note",
4144
+ children: t("row.enterpriseCreditsNotice")
4145
+ }) : null,
4146
+ status.status === "signed-in" && status.alternatives !== void 0 && status.alternatives.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4147
+ className: "dsm-trae-usage-alternative",
4148
+ role: "status",
4149
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
4150
+ className: "dsm-trae-usage-alternative-text",
4151
+ children: [
4152
+ t("row.creditsExhausted", { account: status.accountName }),
4153
+ " ",
4154
+ t("row.creditsAlternative", { accounts: status.alternatives.map((alt) => `${alt.accountName}(${t(`row.edition${alt.edition === "cn" ? "Cn" : alt.edition === "sg" ? "Sg" : alt.edition === "solo" ? "Solo" : "SoloSg"}`)}:${formatNumber(alt.workAvailable + alt.generalAvailable)})`).join("、") })
4155
+ ]
4156
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4157
+ type: "button",
4158
+ className: "dsm-trae-usage-switch-button",
4159
+ disabled: switchingAccount || settingsScope?.getSnapshot()?.writable !== true,
4160
+ onClick: () => {
4161
+ const target = status.alternatives?.[0];
4162
+ if (target !== void 0) switchAccount(target.id);
4163
+ },
4164
+ children: t("row.creditsAlternativeSwitch")
4165
+ })]
4166
+ }) : null,
4167
+ status.status === "signed-in" && status.credits?.expiry !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ExpiryNotice, {
4168
+ expiry: status.credits.expiry,
4169
+ t,
4170
+ formatNumber,
4171
+ formatDateTime
4172
+ }) : null,
4173
+ status.status === "signed-in" && status.appLogin !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4174
+ className: "dsm-trae-usage-alternative",
4175
+ role: "status",
4176
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4177
+ className: "dsm-trae-usage-alternative-text",
4178
+ children: t("row.appLoginMismatch", {
4179
+ account: status.appLogin.accountName,
4180
+ bound: status.accountName
4181
+ })
4182
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4183
+ type: "button",
4184
+ className: "dsm-trae-usage-switch-button",
4185
+ disabled: switchingAccount || settingsScope?.getSnapshot()?.writable !== true,
4186
+ onClick: () => {
4187
+ followAppLogin();
4188
+ },
4189
+ children: t("row.appLoginFollow", { account: status.appLogin.accountName })
4190
+ })]
4191
+ }) : null,
4192
+ rescanNote === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4193
+ className: "dsm-trae-usage-note",
4194
+ role: "status",
4195
+ children: rescanNote
4196
+ }),
2774
4197
  status.status === "signed-in" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
4198
+ status.pool !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountPool, {
4199
+ panel: activeRegion,
4200
+ pool: status.pool,
4201
+ models: visibleModels,
4202
+ t,
4203
+ settingsScope,
4204
+ topActions: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4205
+ type: "button",
4206
+ className: "dsm-btn dsm-btn-outline",
4207
+ disabled: busy,
4208
+ onClick: () => {
4209
+ rescanAccounts();
4210
+ },
4211
+ children: busy ? t("row.accountsScanning") : t("row.poolRescan")
4212
+ }),
4213
+ tools: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4214
+ className: "dsm-trae-pool-tool-row",
4215
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4216
+ className: "dsm-trae-pool-tool-label",
4217
+ children: t("row.poolToolsLabel")
4218
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountTransfer, {
4219
+ t,
4220
+ region: activeRegion === "ai" ? "ai" : "cn",
4221
+ accounts: status.accounts.map((account) => ({
4222
+ id: account.id,
4223
+ accountName: account.accountName
4224
+ })),
4225
+ onChanged: async () => {
4226
+ const fresh = await refreshUsage(activeRegion);
4227
+ if (fresh !== void 0) setStatusByRegion((current) => ({
4228
+ ...current,
4229
+ [activeRegion]: fresh
4230
+ }));
4231
+ }
4232
+ })]
4233
+ }),
4234
+ details: Object.fromEntries(status.accounts.map((account) => {
4235
+ const warn = refreshTokenWarning(account.refreshExpiresAtMs, Date.now(), t, formatDateTime);
4236
+ return [account.id, {
4237
+ meta: `${editionLabel(account.edition)} · ${sourceLabel(account.source)}`,
4238
+ ...warn === void 0 ? {} : {
4239
+ warn: warn.text,
4240
+ warnTone: warn.tone
4241
+ }
4242
+ }];
4243
+ })),
4244
+ claims: status.pool.checkinSupported === true ? Object.fromEntries(status.accounts.map((account) => [account.id, claimViewOf(account.id, accountStatusByRegion[activeRegion][account.id])])) : void 0,
4245
+ onClaim: (accountId) => {
4246
+ claimFor(accountId);
4247
+ },
4248
+ deviceClaimed: deviceClaimedToday,
4249
+ onBind: (accountId) => {
4250
+ switchAccount(accountId);
4251
+ },
4252
+ bindingBusy: switchingAccount,
4253
+ onChanged: async () => {
4254
+ const fresh = await refreshUsage(activeRegion);
4255
+ if (fresh !== void 0) setStatusByRegion((current) => ({
4256
+ ...current,
4257
+ [activeRegion]: fresh
4258
+ }));
4259
+ },
4260
+ onRefresh: async () => {
4261
+ const fresh = await refreshUsage(activeRegion);
4262
+ if (fresh !== void 0) setStatusByRegion((current) => ({
4263
+ ...current,
4264
+ [activeRegion]: fresh
4265
+ }));
4266
+ }
4267
+ }) : null,
2775
4268
  status.checkin !== void 0 && !status.checkin.enabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2776
4269
  className: "dsm-trae-usage-error",
2777
4270
  children: t("row.checkinDisabled")
@@ -3859,6 +5352,151 @@ window.__ModuleLoader__.load({
3859
5352
  "row.modelReasoningSupported": "Reasoning: supported",
3860
5353
  "row.modelUnknown": "Unknown",
3861
5354
  "row.modelCapabilityPending": "Image input follows Trae's own multimodal flag, plus anything you tick yourself.",
5355
+ "row.poolTitle": "Account pool",
5356
+ "row.poolEnabled": "Enable the account pool",
5357
+ "row.poolEnabledHint": "The pool picks the account to bill for this panel's requests, ranking by credits and availability. Off means the manually bound account serves.",
5358
+ "row.poolCurrentHeader": "Billing now: {account}",
5359
+ "row.poolCurrentBadge": "serving",
5360
+ "row.poolMember": "in pool",
5361
+ "row.poolNotMember": "not in pool",
5362
+ "row.poolCheckinAll": "Check in all accounts",
5363
+ "row.poolBusyHint": "Working through accounts one at a time…",
5364
+ "row.poolTestAll": "Test all accounts",
5365
+ "row.poolTesting": "Testing…",
5366
+ "row.poolOffNotice": "The pool is off — the bound account is what serves. Turn the switch below on to let the pool decide.",
5367
+ "row.poolNoCandidate": "No usable pool member can serve this panel right now.",
5368
+ "row.poolNoCandidateEmpty": "No pool members are selected yet.",
5369
+ "row.poolNoCandidateEmptyHint": "Tick accounts in the membership list to bring them into the pool.",
5370
+ "row.poolNoCandidateUnusable": "Every selected member is unavailable right now.",
5371
+ "row.poolNoCandidateHint": "Traffic keeps landing on the saved selection until one recovers.",
5372
+ "row.poolMembersTitle": "Accounts in this pool",
5373
+ "row.poolCardHint": "Only checked accounts are checked in, tested and dispatched. Nothing is checked by default — pick the ones you want, then save.",
5374
+ "row.poolRescan": "Re-detect accounts",
5375
+ "row.poolToolsLabel": "Export / import accounts",
5376
+ "row.poolCreditExpiry": "expires {when}",
5377
+ "row.poolProbeMeasured": "measured {when}",
5378
+ "row.poolProbeAt": "tested {when}",
5379
+ "row.poolSettingsNote": "Preferences below take effect when you save; test results are written by the plugin and are not affected by that button.",
5380
+ "row.poolSelectedCount": "Selected {count} / {total}",
5381
+ "row.poolSelectAll": "All",
5382
+ "row.poolSelectNone": "None",
5383
+ "row.poolColumnAccount": "Account",
5384
+ "row.poolColumnCredits": "Credits",
5385
+ "row.poolColumnProbe": "Target model",
5386
+ "row.poolColumnCheckin": "Check-in",
5387
+ "row.poolGeneralLabel": "General",
5388
+ "row.poolWorkHint": "Work credits are not spendable by DSH — only General powers chat.",
5389
+ "row.poolCreditNone": "—",
5390
+ "row.poolTestedAt": "tested {at}",
5391
+ "row.poolNeverTested": "not tested yet",
5392
+ "row.poolRetryAt": "retry {at}",
5393
+ "row.poolRetryUnknown": "retry time unknown",
5394
+ "row.poolTargetCurrentPreferred": "Current: your choice {model}",
5395
+ "row.poolTargetCurrentFree": "Current: free model {model}",
5396
+ "row.poolTargetStale": "Model {model} is no longer offered by this panel",
5397
+ "row.poolTargetStaleOption": "{model} (no longer offered)",
5398
+ "row.poolTargetStaleClear": "Back to automatic",
5399
+ "row.poolTargetNone": "No free model available for this panel",
5400
+ "row.poolTargetNoneHint": "The pool can only pick a free model; none resolves right now, so tests are disabled.",
5401
+ "row.poolTargetAutoOption": "Automatic (free model)",
5402
+ "row.poolTargetLabel": "Target test model",
5403
+ "row.poolTargetHint": "The model a probe test uses.",
5404
+ "row.poolProbeSizeLabel": "Probe request size",
5405
+ "row.poolProbeSizeHint": "How much text a test sends. A one-word probe only proves the account can answer a one-word request — if the upstream refuses long turns, this is where you find out. Bigger is closer to a real conversation, and costs more credit on every account in the batch.",
5406
+ "row.poolProbeSizeDefault": "Minimal (default)",
5407
+ "row.poolProbeSizeOption": "{wan}0k tokens",
5408
+ "row.poolStar": "Star the repo",
5409
+ "row.poolSettingsTitle": "Pool settings",
5410
+ "row.poolManualAccountLabel": "Manually billed account",
5411
+ "row.poolManualAccountHint": "Used while the pool is off. With it on, the Host picks by credits.",
5412
+ "row.poolManualAccountNone": "No account in effect",
5413
+ "row.poolDirty": "unsaved",
5414
+ "row.poolSaved": "Save",
5415
+ "row.poolSaving": "Saving…",
5416
+ "row.poolDiscard": "Discard",
5417
+ "row.poolSaveFailed": "Save failed: {message}",
5418
+ "row.poolGhostMembers": "{count} saved member(s) no longer resolve to a local sign-in: {names}",
5419
+ "row.poolGhostRemove": "Remove {account}",
5420
+ "row.poolGhostRemoveHint": "Forget this panel's binding to {account}.",
5421
+ "row.poolUnsavedMembers": "Unsaved membership changes — Save below to apply.",
5422
+ "row.poolRowTest": "Test",
5423
+ "row.poolRowRemove": "Remove",
5424
+ "row.poolRowRemoveAria": "Remove {accountName} from this panel",
5425
+ "row.poolRowRemoving": "Removing…",
5426
+ "row.poolRowRemoved": "Removed {accountName} from this panel",
5427
+ "row.poolLogTitle": "Activity",
5428
+ "row.poolLogClear": "Clear",
5429
+ "row.poolLogEmpty": "No pool activity yet.",
5430
+ "row.poolLogCheckinStart": "Check-in started for {count} account(s).",
5431
+ "row.poolLogTestStart": "Test started for {count} account(s) against {model}.",
5432
+ "row.poolLogCheckinClaimed": "{accountName} checked in.",
5433
+ "row.poolLogCheckinAlready": "{accountName} already checked in today.",
5434
+ "row.poolLogCheckinFailed": "{accountName} check-in failed: {message}",
5435
+ "row.poolLogCheckinDone": "Check-in finished.",
5436
+ "row.poolLogTestRow": "{accountName}: {outcome}.",
5437
+ "row.poolLogTestRowMalformed": "{accountName}: malformed result.",
5438
+ "row.poolLogTestDone": "Test finished.",
5439
+ "row.poolLogBatchFailed": "Pool action failed: {message}",
5440
+ "row.poolRegionNote": "Each panel has its own pool; enterprise shares the CN account's sign-ins.",
5441
+ "row.poolSelectAria": "Include {accountName} in the pool",
5442
+ "row.poolCheckedIn": "Checked in",
5443
+ "row.poolNotCheckedIn": "Not checked in",
5444
+ "row.poolCheckinUnknown": "Unknown",
5445
+ "row.poolDeviceUsed": "device used",
5446
+ "row.poolTestingPerRow": "testing…",
5447
+ "row.poolProbeOk": "works",
5448
+ "row.poolProbeRateLimited": "rate limited",
5449
+ "row.poolProbeModelBusy": "model busy",
5450
+ "row.poolProbeOutOfCredit": "out of credit",
5451
+ "row.poolProbeCredentialRejected": "sign-in rejected",
5452
+ "row.poolProbePolicyRejected": "policy rejected",
5453
+ "row.poolProbePlanGated": "needs a higher plan",
5454
+ "row.poolProbeUnavailable": "unavailable",
5455
+ "row.poolProbeNotFound": "not served",
5456
+ "row.poolProbeFailed": "failed",
5457
+ "row.poolProbeSourceUnknown": "reason unknown",
5458
+ "row.poolProbeSourceLive": "from a live request",
5459
+ "row.poolProbeSourceTest": "from a test",
5460
+ "row.poolProbeModel": "model {model}",
5461
+ "row.poolProbeModelUnknown": "model unknown",
5462
+ "row.poolExcludedUnusable": "unusable",
5463
+ "row.poolTokenExpired": "sign-in expired",
5464
+ "row.poolExcludedRateLimited": "rate limited",
5465
+ "row.poolExcludedModelBusy": "model busy",
5466
+ "row.poolExcludedOutOfCredit": "out of credit",
5467
+ "row.poolExcludedCredentialRejected": "sign-in rejected",
5468
+ "row.poolExcludedPolicyRejected": "policy rejected",
5469
+ "row.poolExcludedPlanGated": "needs a higher plan",
5470
+ "row.poolExcludedNotFound": "not served",
5471
+ "row.poolExcludedUnavailable": "unavailable",
5472
+ "row.poolExcludedFailed": "failed",
5473
+ "row.poolAgoNow": "just now",
5474
+ "row.poolAgoMin": "{n} min ago",
5475
+ "row.poolAgoHour": "{n} h ago",
5476
+ "row.poolAgoDay": "{n} d ago",
5477
+ "row.poolErrOrigin": "the page is not the plugin's own page, so the request was refused",
5478
+ "row.poolErrUnavailable": "the pool is unavailable right now",
5479
+ "row.poolErrMethod": "the pool does not accept that request method",
5480
+ "row.poolErrBadRequest": "the pool refused the request",
5481
+ "row.poolErrNoMembers": "the pool has no members to act on",
5482
+ "row.poolErrNoLiveMembers": "every member is unavailable — nothing can serve",
5483
+ "row.poolErrCheckinUnsupported": "this panel does not offer a daily check-in",
5484
+ "row.poolErrTargetStale": "the saved target model is no longer offered",
5485
+ "row.poolErrNoFreeModel": "no free model is available for this panel",
5486
+ "row.poolErrConflict": "the pool refused the request",
5487
+ "row.poolErrGeneric": "pool request failed (HTTP {status})",
5488
+ "row.poolCheckingIn": "Checking in…",
5489
+ "row.poolSettingsHint": "The switch and the target model are preferences — save them. Test, check in and remove act at once.",
5490
+ "row.poolGhostRemoveAria": "Remove the saved but unreachable account {accountName} from this panel",
5491
+ "row.poolLogSaved": "Pool settings saved.",
5492
+ "row.poolLogSaveFailed": "Could not save the pool settings.",
5493
+ "row.poolLogNoMembers": "Nothing to do — no pool members are selected.",
5494
+ "row.poolLogDiscarded": "Discarded the unsaved pool changes.",
5495
+ "row.poolLogGeneric": "the pool answered nothing usable",
5496
+ "row.poolLogRowTesting": "Testing {accountName}…",
5497
+ "row.poolLogTargetCleared": "Returned the test target to automatic.",
5498
+ "row.poolRowRemoveFailed": "the pool did not confirm the removal",
5499
+ "row.poolRowRemoveError": "Could not remove {accountName}: {message}",
3862
5500
  "row.cheer": "Star on GitHub"
3863
5501
  };
3864
5502
  const zh = {
@@ -3869,7 +5507,7 @@ window.__ModuleLoader__.load({
3869
5507
  "row.tabCn": "国内版",
3870
5508
  "row.tabEnterprise": "企业版",
3871
5509
  "row.tabAi": "国际版",
3872
- "row.tabHint": "国内版、企业版、国际版是三个独立供应商,各有账号、用量与模型;三者同时生效,互不影响。",
5510
+ "row.tabHint": "国内版、企业版、国际版各有账号、用量与模型,可同时启用。企业版与国内版共用同一份 Trae CN 登录。",
3873
5511
  "row.tabEnterpriseHint": "企业版用同一个 Trae CN 账号走企业租户池计费,而不是个人积分池。它不会回落:被拒时如实报错,绝不悄悄花掉个人额度。",
3874
5512
  "row.tabSwitchHint": "关闭该供应商:其模型不再提供给 DSH;账号与设置保留,可随时恢复。",
3875
5513
  "row.tabSwitchAria": "是否向 DSH 提供{region}供应商",
@@ -4031,6 +5669,151 @@ window.__ModuleLoader__.load({
4031
5669
  "row.modelReasoningSupported": "推理:支持",
4032
5670
  "row.modelUnknown": "未知",
4033
5671
  "row.modelCapabilityPending": "图片输入跟随 Trae 自身的多模态标注,并叠加你手动勾选的模型。",
5672
+ "row.poolTitle": "账号池",
5673
+ "row.poolEnabled": "启用账号池",
5674
+ "row.poolEnabledHint": "账号池按积分与可用性挑选本面板请求的计费账号;关闭时由手动绑定的账号计费。",
5675
+ "row.poolCurrentHeader": "当前计费:{account}",
5676
+ "row.poolCurrentBadge": "派单中",
5677
+ "row.poolMember": "已在池中",
5678
+ "row.poolNotMember": "未入池",
5679
+ "row.poolCheckinAll": "一键签到所有账号",
5680
+ "row.poolBusyHint": "逐个处理账号中…",
5681
+ "row.poolTestAll": "一键测试所有账号",
5682
+ "row.poolTesting": "测速中…",
5683
+ "row.poolOffNotice": "账号池已关闭——由绑定的账号计费。开启上方开关即可让账号池派单。",
5684
+ "row.poolNoCandidate": "当前没有可用的池成员能为本面板提供请求。",
5685
+ "row.poolNoCandidateEmpty": "尚未勾选任何池成员。",
5686
+ "row.poolNoCandidateEmptyHint": "在成员列表中勾选账号即可加入账号池。",
5687
+ "row.poolNoCandidateUnusable": "所有已选成员当前均不可用。",
5688
+ "row.poolNoCandidateHint": "请求会继续落在已保存的账号上,直到有成员恢复可用。",
5689
+ "row.poolMembersTitle": "本池包含的账号",
5690
+ "row.poolCardHint": "只有勾选的账号才会被签到、测试和轮换。默认一个都不勾 —— 请勾选你要的账号,然后保存。",
5691
+ "row.poolRescan": "重新检测账号",
5692
+ "row.poolToolsLabel": "导出 / 导入账号",
5693
+ "row.poolCreditExpiry": "{when} 过期",
5694
+ "row.poolProbeMeasured": "测得 {when}",
5695
+ "row.poolProbeAt": "测试于 {when}",
5696
+ "row.poolSettingsNote": "下面是偏好设置:点保存才生效;测试结果由插件自动写入,不受这个保存按钮影响。",
5697
+ "row.poolSelectedCount": "已选 {count} / {total}",
5698
+ "row.poolSelectAll": "全部",
5699
+ "row.poolSelectNone": "清空",
5700
+ "row.poolColumnAccount": "账号",
5701
+ "row.poolColumnCredits": "积分",
5702
+ "row.poolColumnProbe": "目标模型",
5703
+ "row.poolColumnCheckin": "签到",
5704
+ "row.poolGeneralLabel": "通用",
5705
+ "row.poolWorkHint": "工作积分不能由 DSH 消费——只有通用积分用于对话。",
5706
+ "row.poolCreditNone": "—",
5707
+ "row.poolTestedAt": "测于 {at}",
5708
+ "row.poolNeverTested": "尚未测速",
5709
+ "row.poolRetryAt": "{at} 后重试",
5710
+ "row.poolRetryUnknown": "重试时间未知",
5711
+ "row.poolTargetCurrentPreferred": "当前:你选定的模型 {model}",
5712
+ "row.poolTargetCurrentFree": "当前:免费模型 {model}",
5713
+ "row.poolTargetStale": "模型 {model} 已不再由本面板提供",
5714
+ "row.poolTargetStaleOption": "{model}(已下架)",
5715
+ "row.poolTargetStaleClear": "改回自动",
5716
+ "row.poolTargetNone": "本面板暂无可用免费模型",
5717
+ "row.poolTargetNoneHint": "账号池只会挑免费模型;当前无法解析出可用模型,测速暂被禁用。",
5718
+ "row.poolTargetAutoOption": "自动(免费模型)",
5719
+ "row.poolTargetLabel": "目标测速模型",
5720
+ "row.poolTargetHint": "探针测速所用的模型。",
5721
+ "row.poolProbeSizeLabel": "测试请求体积",
5722
+ "row.poolProbeSizeHint": "测试时发送多少文本。只发一个词的探针,只能证明「这个账号能回答一个词」——如果上游拒绝长请求,问题只会在你真实对话时才出现。填得越多越接近真实对话,每个账号每次测试消耗的积分也越多。",
5723
+ "row.poolProbeSizeDefault": "最小请求(默认)",
5724
+ "row.poolProbeSizeOption": "{wan} 万 token",
5725
+ "row.poolStar": "鼓励一下",
5726
+ "row.poolSettingsTitle": "账号池设置",
5727
+ "row.poolManualAccountLabel": "手动计费账号",
5728
+ "row.poolManualAccountHint": "账号池关闭时由它计费;开启后由 Host 按积分挑选。",
5729
+ "row.poolManualAccountNone": "当前无生效账号",
5730
+ "row.poolDirty": "未保存",
5731
+ "row.poolSaved": "保存",
5732
+ "row.poolSaving": "保存中…",
5733
+ "row.poolDiscard": "放弃",
5734
+ "row.poolSaveFailed": "保存失败:{message}",
5735
+ "row.poolGhostMembers": "{count} 个已保存成员不再对应本机登录:{names}",
5736
+ "row.poolGhostRemove": "移除 {account}",
5737
+ "row.poolGhostRemoveHint": "忘记本面板对 {account} 的绑定。",
5738
+ "row.poolUnsavedMembers": "成员改动尚未保存——请点击下方保存。",
5739
+ "row.poolRowTest": "测速",
5740
+ "row.poolRowRemove": "移除",
5741
+ "row.poolRowRemoveAria": "从本面板移除 {accountName}",
5742
+ "row.poolRowRemoving": "移除中…",
5743
+ "row.poolRowRemoved": "已从本面板移除 {accountName}",
5744
+ "row.poolLogTitle": "活动记录",
5745
+ "row.poolLogClear": "清空",
5746
+ "row.poolLogEmpty": "暂无账号池活动。",
5747
+ "row.poolLogCheckinStart": "开始为 {count} 个账号签到。",
5748
+ "row.poolLogTestStart": "开始为 {count} 个账号测速({model})。",
5749
+ "row.poolLogCheckinClaimed": "{accountName} 已签到。",
5750
+ "row.poolLogCheckinAlready": "{accountName} 今日已签到。",
5751
+ "row.poolLogCheckinFailed": "{accountName} 签到失败:{message}",
5752
+ "row.poolLogCheckinDone": "签到完成。",
5753
+ "row.poolLogTestRow": "{accountName}:{outcome}。",
5754
+ "row.poolLogTestRowMalformed": "{accountName}:结果异常。",
5755
+ "row.poolLogTestDone": "测速完成。",
5756
+ "row.poolLogBatchFailed": "账号池操作失败:{message}",
5757
+ "row.poolRegionNote": "每个面板各有自己的账号池;企业版与国内版共用同一 Trae CN 账号登录。",
5758
+ "row.poolSelectAria": "把 {accountName} 加入账号池",
5759
+ "row.poolCheckedIn": "已签到",
5760
+ "row.poolNotCheckedIn": "未签到",
5761
+ "row.poolCheckinUnknown": "未知",
5762
+ "row.poolDeviceUsed": "本设备今日已用",
5763
+ "row.poolTestingPerRow": "测速中…",
5764
+ "row.poolProbeOk": "正常",
5765
+ "row.poolProbeRateLimited": "被限流",
5766
+ "row.poolProbeModelBusy": "模型繁忙",
5767
+ "row.poolProbeOutOfCredit": "积分不足",
5768
+ "row.poolProbeCredentialRejected": "登录凭据被拒",
5769
+ "row.poolProbePolicyRejected": "被策略拒绝",
5770
+ "row.poolProbePlanGated": "需要更高套餐",
5771
+ "row.poolProbeUnavailable": "不可用",
5772
+ "row.poolProbeNotFound": "上游不提供",
5773
+ "row.poolProbeFailed": "失败",
5774
+ "row.poolProbeSourceUnknown": "原因未知",
5775
+ "row.poolProbeSourceLive": "来自实时请求",
5776
+ "row.poolProbeSourceTest": "来自测速",
5777
+ "row.poolProbeModel": "模型 {model}",
5778
+ "row.poolProbeModelUnknown": "模型未知",
5779
+ "row.poolExcludedUnusable": "不可用",
5780
+ "row.poolTokenExpired": "登录已失效",
5781
+ "row.poolExcludedRateLimited": "被限流",
5782
+ "row.poolExcludedModelBusy": "模型繁忙",
5783
+ "row.poolExcludedOutOfCredit": "积分不足",
5784
+ "row.poolExcludedCredentialRejected": "登录凭据被拒",
5785
+ "row.poolExcludedPolicyRejected": "被策略拒绝",
5786
+ "row.poolExcludedPlanGated": "需要更高套餐",
5787
+ "row.poolExcludedNotFound": "上游不提供",
5788
+ "row.poolExcludedUnavailable": "不可用",
5789
+ "row.poolExcludedFailed": "失败",
5790
+ "row.poolAgoNow": "刚刚",
5791
+ "row.poolAgoMin": "{n} 分钟前",
5792
+ "row.poolAgoHour": "{n} 小时前",
5793
+ "row.poolAgoDay": "{n} 天前",
5794
+ "row.poolErrOrigin": "页面并非插件自身页面,请求因而不被接受",
5795
+ "row.poolErrUnavailable": "账号池当前不可用",
5796
+ "row.poolErrMethod": "账号池不接受该请求方法",
5797
+ "row.poolErrBadRequest": "账号池拒绝了该请求",
5798
+ "row.poolErrNoMembers": "账号池没有任何成员可处理",
5799
+ "row.poolErrNoLiveMembers": "所有成员均不可用——没有可派单的账号",
5800
+ "row.poolErrCheckinUnsupported": "本面板不提供每日签到",
5801
+ "row.poolErrTargetStale": "已保存的目标模型已下架",
5802
+ "row.poolErrNoFreeModel": "本面板没有可用的免费模型",
5803
+ "row.poolErrConflict": "账号池拒绝了该请求",
5804
+ "row.poolErrGeneric": "账号池请求失败(HTTP {status})",
5805
+ "row.poolCheckingIn": "签到中…",
5806
+ "row.poolSettingsHint": "开关与目标模型是偏好,改完要保存;行内的测速、签到与移除立即生效。",
5807
+ "row.poolGhostRemoveAria": "把已保存但本机无法登录的账号 {accountName} 移出本面板",
5808
+ "row.poolLogSaved": "账号池设置已保存。",
5809
+ "row.poolLogSaveFailed": "无法保存账号池设置。",
5810
+ "row.poolLogNoMembers": "没有可处理的内容——尚未勾选任何池成员。",
5811
+ "row.poolLogDiscarded": "已放弃未保存的账号池修改。",
5812
+ "row.poolLogGeneric": "账号池未返回可用的结果",
5813
+ "row.poolLogRowTesting": "正在测速 {accountName}…",
5814
+ "row.poolLogTargetCleared": "测速目标已改回自动。",
5815
+ "row.poolRowRemoveFailed": "账号池未确认移除",
5816
+ "row.poolRowRemoveError": "无法移除 {accountName}:{message}",
4034
5817
  "row.cheer": "鼓励一下"
4035
5818
  };
4036
5819
  //#endregion