dsh-connect-trae 3.9.2 → 3.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +110 -0
- package/lib/client.js +377 -120
- package/lib/index.d.ts +196 -1
- package/lib/index.js +530 -27
- package/package.json +3 -2
package/lib/client.js
CHANGED
|
@@ -20,11 +20,6 @@ window.__ModuleLoader__.load({
|
|
|
20
20
|
}
|
|
21
21
|
//#endregion
|
|
22
22
|
//#region src/status-paths.ts
|
|
23
|
-
/**
|
|
24
|
-
* Node-free constants and types shared by the Host and browser halves.
|
|
25
|
-
* Mirrors the `dsh-workbuddy-connect` status-route pattern so the plugin card
|
|
26
|
-
* stays consistent with that project's external presentation.
|
|
27
|
-
*/
|
|
28
23
|
/** Plugin-owned usage endpoint consumed by its browser half. */
|
|
29
24
|
const TRAE_USAGE_PATH = "/plugins/dsh-connect-trae/usage";
|
|
30
25
|
/** Plugin-owned live model refresh endpoint. */
|
|
@@ -789,8 +784,11 @@ window.__ModuleLoader__.load({
|
|
|
789
784
|
/* Removing an account from the pool is destructive (it drops THIS panel's saved
|
|
790
785
|
membership), so the hover state takes the error tone rather than the neutral
|
|
791
786
|
one every other outline button gets. */
|
|
792
|
-
|
|
793
|
-
.
|
|
787
|
+
/* The destructive Delete is visually heavier than 移除 from the start (error
|
|
788
|
+
colour idle), so the row shows two distinct actions instead of two greys. */
|
|
789
|
+
.dsm-trae-pool-delete{color:var(--dsw-alias-state-error-primary,#d92d20)}
|
|
790
|
+
.dsm-trae-pool-delete:hover:not(:disabled){background:rgba(217,45,32,.08)}
|
|
791
|
+
.dsm-trae-pool-delete-hint{margin:0;padding:8px var(--dsm-trae-pool-pad-x);border:1px solid var(--dsw-alias-state-error-primary,#d92d20);border-radius:10px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary,#c6c9d0);background:var(--dsw-alias-bg-layer-3,#2a2c33)}
|
|
794
792
|
.dsm-trae-pool-busy{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary,#9aa0a8)}
|
|
795
793
|
.dsm-trae-pool-action-note{margin:0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary,#9aa0a8)}
|
|
796
794
|
.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}
|
|
@@ -901,6 +899,9 @@ window.__ModuleLoader__.load({
|
|
|
901
899
|
/* "Every member is unusable" is the one case that IS the pool's fault, so it
|
|
902
900
|
keeps the warn tone the base class sets. */
|
|
903
901
|
.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)}
|
|
902
|
+
/* The sign-in/refresh workflow sits in the settings block: it keeps the warn
|
|
903
|
+
tone (getting it wrong loses an account) but is not a fault of the pool. */
|
|
904
|
+
.dsm-trae-pool-warning-workflow{margin:0 0 2px}
|
|
904
905
|
.dsm-trae-pool-save-row{display:flex;align-items:center;justify-content:space-between;gap:12px;border-top:1px solid var(--dsw-alias-border-l2,#36373b);padding-top:12px}
|
|
905
906
|
.dsm-trae-pool-save-buttons{display:flex;align-items:center;justify-content:flex-end;gap:8px}
|
|
906
907
|
.dsm-trae-pool-save-row .dsm-btn{padding:5px 14px;font-size:13px;line-height:1.5}
|
|
@@ -1276,34 +1277,74 @@ window.__ModuleLoader__.load({
|
|
|
1276
1277
|
return t("row.poolErrGeneric", { status: String(status) });
|
|
1277
1278
|
}
|
|
1278
1279
|
/**
|
|
1279
|
-
*
|
|
1280
|
-
*
|
|
1281
|
-
*
|
|
1282
|
-
*
|
|
1280
|
+
* Handle ONE NDJSON line of a `test` answer. A non-JSON line is skipped; a
|
|
1281
|
+
* `{reason:'pool-failed'}` line reports the failure and counts as "saw
|
|
1282
|
+
* something". Returns whether the line was usable, so the caller can tell "the
|
|
1283
|
+
* route answered nothing parseable" (a bug to surface) from "not finished yet".
|
|
1283
1284
|
*/
|
|
1284
|
-
function
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1285
|
+
function applyTestLine(line, t, onRow, onFailure) {
|
|
1286
|
+
if (line.trim() === "") return false;
|
|
1287
|
+
let parsed;
|
|
1288
|
+
try {
|
|
1289
|
+
parsed = JSON.parse(line);
|
|
1290
|
+
} catch {
|
|
1291
|
+
return false;
|
|
1292
|
+
}
|
|
1293
|
+
if (parsed.row !== void 0) {
|
|
1294
|
+
onRow(parsed.row);
|
|
1295
|
+
return true;
|
|
1296
|
+
}
|
|
1297
|
+
if (parsed.rows !== void 0) {
|
|
1298
|
+
for (const r of parsed.rows) onRow(r);
|
|
1299
|
+
return true;
|
|
1300
|
+
}
|
|
1301
|
+
if (parsed.reason === "pool-failed") {
|
|
1302
|
+
onFailure(parsed.error ?? t("row.poolLogGeneric"));
|
|
1303
|
+
return true;
|
|
1304
|
+
}
|
|
1305
|
+
return false;
|
|
1306
|
+
}
|
|
1307
|
+
/**
|
|
1308
|
+
* Read the answer line by line AS IT ARRIVES.
|
|
1309
|
+
*
|
|
1310
|
+
* The Host streams one line per account, but `response.text()` waits for the
|
|
1311
|
+
* whole body: with a slow probe the card sat on "逐个处理账号中…" and looked
|
|
1312
|
+
* frozen (a single member could take half a minute). Reading the body stream
|
|
1313
|
+
* makes each finished account appear immediately, and an empty/failed stream
|
|
1314
|
+
* still ends the loop instead of hanging the busy state forever.
|
|
1315
|
+
*/
|
|
1316
|
+
async function* testLinesOf(response) {
|
|
1317
|
+
const body = response.body;
|
|
1318
|
+
if (body === null) {
|
|
1319
|
+
for (const line of (await response.text()).split(/\r?\n/)) yield line;
|
|
1320
|
+
return;
|
|
1321
|
+
}
|
|
1322
|
+
const reader = body.getReader();
|
|
1323
|
+
const decoder = new TextDecoder();
|
|
1324
|
+
let buffer = "";
|
|
1325
|
+
for (;;) {
|
|
1326
|
+
const { done, value } = await reader.read();
|
|
1327
|
+
if (done) break;
|
|
1328
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1329
|
+
let cut = buffer.indexOf("\n");
|
|
1330
|
+
while (cut >= 0) {
|
|
1331
|
+
yield buffer.slice(0, cut);
|
|
1332
|
+
buffer = buffer.slice(cut + 1);
|
|
1333
|
+
cut = buffer.indexOf("\n");
|
|
1303
1334
|
}
|
|
1304
1335
|
}
|
|
1305
|
-
|
|
1336
|
+
buffer += decoder.decode();
|
|
1337
|
+
if (buffer.trim() !== "") yield buffer;
|
|
1306
1338
|
}
|
|
1339
|
+
/**
|
|
1340
|
+
* How long one `test` request may take before the card gives up.
|
|
1341
|
+
*
|
|
1342
|
+
* A probe has its own upstream deadline, but a stalled connection (a restarting
|
|
1343
|
+
* Host, a dropped keep-alive) has none: without this the run button stays
|
|
1344
|
+
* disabled until the page is reloaded, which reads as "测速卡住了". Sized for a
|
|
1345
|
+
* batch: a few seconds per account plus room for the slowest upstream.
|
|
1346
|
+
*/
|
|
1347
|
+
const TEST_TIMEOUT_MS = 18e4;
|
|
1307
1348
|
function AccountPool({ panel, pool, models, t, settingsScope, onChanged, onRefresh, tools, topActions, details, claims, onClaim, deviceClaimed = false, onBind, bindingBusy }) {
|
|
1308
1349
|
const [settingsTick, setSettingsTick] = (0, react.useState)(0);
|
|
1309
1350
|
const saved = (0, react.useMemo)(() => {
|
|
@@ -1340,7 +1381,8 @@ window.__ModuleLoader__.load({
|
|
|
1340
1381
|
const [saveError, setSaveError] = (0, react.useState)(void 0);
|
|
1341
1382
|
const [testing, setTesting] = (0, react.useState)(void 0);
|
|
1342
1383
|
const [rows, setRows] = (0, react.useState)({});
|
|
1343
|
-
const [
|
|
1384
|
+
const [deletingIds, setDeletingIds] = (0, react.useState)(() => /* @__PURE__ */ new Set());
|
|
1385
|
+
const [deleteHint, setDeleteHint] = (0, react.useState)(void 0);
|
|
1344
1386
|
const [log, setLog] = (0, react.useState)([]);
|
|
1345
1387
|
const [now, setNow] = (0, react.useState)(Date.now());
|
|
1346
1388
|
const logId = (0, react.useRef)(0);
|
|
@@ -1368,10 +1410,14 @@ window.__ModuleLoader__.load({
|
|
|
1368
1410
|
const ghosts = (0, react.useMemo)(() => {
|
|
1369
1411
|
const names = pool.memberNames ?? {};
|
|
1370
1412
|
const result = [];
|
|
1371
|
-
for (const id of saved.memberAccountIds ?? [])
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1413
|
+
for (const id of saved.memberAccountIds ?? []) {
|
|
1414
|
+
if (knownIds.has(id)) continue;
|
|
1415
|
+
const name = names[id];
|
|
1416
|
+
result.push({
|
|
1417
|
+
accountId: id,
|
|
1418
|
+
accountName: name === void 0 || name === "" ? id : name
|
|
1419
|
+
});
|
|
1420
|
+
}
|
|
1375
1421
|
return result;
|
|
1376
1422
|
}, [
|
|
1377
1423
|
saved,
|
|
@@ -1380,12 +1426,27 @@ window.__ModuleLoader__.load({
|
|
|
1380
1426
|
]);
|
|
1381
1427
|
const dirty = enabled !== saved.enabled || (targetModelId ?? "") !== (saved.targetModelId ?? "") || probeSupported && probeInputTokens !== saved.probeInputTokens || !sameIds([...selected], saved.memberAccountIds);
|
|
1382
1428
|
const writable = settingsScope?.getSnapshot().writable === true;
|
|
1383
|
-
const
|
|
1429
|
+
const savedMemberKey = (0, react.useMemo)(() => [...saved.memberAccountIds ?? []].sort().join("\0"), [saved]);
|
|
1384
1430
|
(0, react.useEffect)(() => {
|
|
1385
1431
|
setEnabled(saved.enabled);
|
|
1432
|
+
}, [panel, saved.enabled]);
|
|
1433
|
+
(0, react.useEffect)(() => {
|
|
1386
1434
|
setTargetModelId(saved.targetModelId ?? "");
|
|
1435
|
+
}, [panel, saved.targetModelId]);
|
|
1436
|
+
(0, react.useEffect)(() => {
|
|
1387
1437
|
setProbeInputTokens(saved.probeInputTokens);
|
|
1438
|
+
}, [panel, saved.probeInputTokens]);
|
|
1439
|
+
(0, react.useEffect)(() => {
|
|
1388
1440
|
setSelected(new Set(saved.memberAccountIds ?? []));
|
|
1441
|
+
}, [panel, savedMemberKey]);
|
|
1442
|
+
const savedKey = (0, react.useMemo)(() => `${panel}:${saved.enabled}:${saved.targetModelId ?? ""}:${saved.probeInputTokens}:${savedMemberKey}`, [
|
|
1443
|
+
panel,
|
|
1444
|
+
saved.enabled,
|
|
1445
|
+
saved.targetModelId,
|
|
1446
|
+
saved.probeInputTokens,
|
|
1447
|
+
savedMemberKey
|
|
1448
|
+
]);
|
|
1449
|
+
(0, react.useEffect)(() => {
|
|
1389
1450
|
setSaveError(void 0);
|
|
1390
1451
|
}, [savedKey]);
|
|
1391
1452
|
const toggleMember = (accountId) => {
|
|
@@ -1489,14 +1550,24 @@ window.__ModuleLoader__.load({
|
|
|
1489
1550
|
count: members.length,
|
|
1490
1551
|
model: targetLabel
|
|
1491
1552
|
}));
|
|
1553
|
+
const controller = new AbortController();
|
|
1554
|
+
const timer = setTimeout(() => {
|
|
1555
|
+
controller.abort();
|
|
1556
|
+
}, TEST_TIMEOUT_MS);
|
|
1492
1557
|
try {
|
|
1493
|
-
const
|
|
1558
|
+
const response = await fetch(withTraePoolAction(panel, "test"), {
|
|
1559
|
+
method: "POST",
|
|
1560
|
+
signal: controller.signal
|
|
1561
|
+
});
|
|
1494
1562
|
const seen = /* @__PURE__ */ new Set();
|
|
1495
|
-
|
|
1563
|
+
let sawAny = false;
|
|
1564
|
+
for await (const line of testLinesOf(response)) if (applyTestLine(line, t, (r) => {
|
|
1565
|
+
const fresh = !seen.has(r.accountId);
|
|
1496
1566
|
setRows((prev) => ({
|
|
1497
1567
|
...prev,
|
|
1498
1568
|
[r.accountId]: r
|
|
1499
1569
|
}));
|
|
1570
|
+
if (!fresh) return;
|
|
1500
1571
|
seen.add(r.accountId);
|
|
1501
1572
|
if (mountRef.current) setTesting({ done: seen.size });
|
|
1502
1573
|
pushLog(outcomeTone(r.outcome), t("row.poolLogTestRow", {
|
|
@@ -1505,13 +1576,15 @@ window.__ModuleLoader__.load({
|
|
|
1505
1576
|
}));
|
|
1506
1577
|
}, (errorText) => {
|
|
1507
1578
|
pushLog("error", t("row.poolLogBatchFailed", { message: errorText }));
|
|
1508
|
-
}))
|
|
1579
|
+
})) sawAny = true;
|
|
1580
|
+
if (!sawAny) throw new Error(t("row.poolLogGeneric"));
|
|
1509
1581
|
pushLog("ok", t("row.poolLogTestDone"));
|
|
1510
1582
|
onRefresh?.();
|
|
1511
1583
|
setNow(Date.now());
|
|
1512
1584
|
} catch (error) {
|
|
1513
|
-
pushLog("error", t("row.poolLogBatchFailed", { message: error instanceof Error ? error.message : String(error) }));
|
|
1585
|
+
pushLog("error", t(controller.signal.aborted ? "row.poolLogTimeout" : "row.poolLogBatchFailed", { message: error instanceof Error ? error.message : String(error) }));
|
|
1514
1586
|
} finally {
|
|
1587
|
+
clearTimeout(timer);
|
|
1515
1588
|
if (mountRef.current) {
|
|
1516
1589
|
setBusy(void 0);
|
|
1517
1590
|
setTesting(void 0);
|
|
@@ -1524,12 +1597,24 @@ window.__ModuleLoader__.load({
|
|
|
1524
1597
|
setNow(Date.now());
|
|
1525
1598
|
const targetLabel = targetModelId === "" ? t("row.poolTargetAutoOption") : models.find((m) => m.id === targetModelId)?.name ?? targetModelId;
|
|
1526
1599
|
pushLog("info", t("row.poolLogRowTesting", { accountName: account.accountName }));
|
|
1600
|
+
const controller = new AbortController();
|
|
1601
|
+
const timer = setTimeout(() => {
|
|
1602
|
+
controller.abort();
|
|
1603
|
+
}, TEST_TIMEOUT_MS);
|
|
1527
1604
|
try {
|
|
1528
|
-
|
|
1605
|
+
const response = await fetch(withTraePoolAccount(panel, "test", account.accountId), {
|
|
1606
|
+
method: "POST",
|
|
1607
|
+
signal: controller.signal
|
|
1608
|
+
});
|
|
1609
|
+
let sawAny = false;
|
|
1610
|
+
const logged = /* @__PURE__ */ new Set();
|
|
1611
|
+
for await (const line of testLinesOf(response)) if (applyTestLine(line, t, (r) => {
|
|
1529
1612
|
setRows((prev) => ({
|
|
1530
1613
|
...prev,
|
|
1531
1614
|
[r.accountId]: r
|
|
1532
1615
|
}));
|
|
1616
|
+
if (logged.has(r.accountId)) return;
|
|
1617
|
+
logged.add(r.accountId);
|
|
1533
1618
|
pushLog(outcomeTone(r.outcome), t("row.poolLogTestRow", {
|
|
1534
1619
|
accountName: r.accountName,
|
|
1535
1620
|
outcome: outcomeLabel(r.outcome, t),
|
|
@@ -1537,43 +1622,136 @@ window.__ModuleLoader__.load({
|
|
|
1537
1622
|
}));
|
|
1538
1623
|
}, (errorText) => {
|
|
1539
1624
|
pushLog("error", t("row.poolLogBatchFailed", { message: errorText }));
|
|
1540
|
-
}))
|
|
1625
|
+
})) sawAny = true;
|
|
1626
|
+
if (!sawAny) throw new Error(t("row.poolLogGeneric"));
|
|
1541
1627
|
onRefresh?.();
|
|
1542
1628
|
setNow(Date.now());
|
|
1543
1629
|
} catch (error) {
|
|
1544
|
-
pushLog("error", t("row.poolLogBatchFailed", { message: error instanceof Error ? error.message : String(error) }));
|
|
1630
|
+
pushLog("error", t(controller.signal.aborted ? "row.poolLogTimeout" : "row.poolLogBatchFailed", { message: error instanceof Error ? error.message : String(error) }));
|
|
1545
1631
|
} finally {
|
|
1632
|
+
clearTimeout(timer);
|
|
1546
1633
|
if (mountRef.current) setBusy(void 0);
|
|
1547
1634
|
}
|
|
1548
1635
|
};
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1636
|
+
/**
|
|
1637
|
+
* Persist the removal of ONE account out of this panel's membership.
|
|
1638
|
+
*
|
|
1639
|
+
* The slot written is the SAVED slot with ONLY `memberAccountIds` replaced —
|
|
1640
|
+
* never the draft's switch or target model, because the card's own hint
|
|
1641
|
+
* promises only the in-row actions take effect immediately
|
|
1642
|
+
* (「开关与目标模型是偏好,改完要保存」), and never the DOCUMENT's effective
|
|
1643
|
+
* target model either: that falls back to the free model when the user left
|
|
1644
|
+
* the choice automatic, so writing it back would pin a model they never chose.
|
|
1645
|
+
* The membership written is the DRAFT minus the removed id, so a checkbox the
|
|
1646
|
+
* user already toggled on another row is not silently discarded by the
|
|
1647
|
+
* settings re-read this write triggers.
|
|
1648
|
+
*/
|
|
1649
|
+
const persistRemoval = async (accountId) => {
|
|
1650
|
+
const scope = settingsScope;
|
|
1651
|
+
if (scope === void 0) throw new Error(t("row.poolLogSaveFailed"));
|
|
1652
|
+
const base = regionPoolOf(scope.getSnapshot().value, panel);
|
|
1653
|
+
const next = {
|
|
1654
|
+
...base,
|
|
1655
|
+
memberAccountIds: [...selected].filter((id) => id !== accountId)
|
|
1656
|
+
};
|
|
1657
|
+
await writeSettingsField(scope, "regions", nextRegionPool(scope.getSnapshot().value, panel, next), (readBack) => {
|
|
1658
|
+
const readPool = regionPoolOf(readBack, panel);
|
|
1659
|
+
return !readPool.memberAccountIds?.includes(accountId) && sameIds(readPool.memberAccountIds, next.memberAccountIds) && (readPool.enabled ?? false) === (base.enabled ?? false) && (readPool.targetModelId ?? "") === (base.targetModelId ?? "") && (readPool.probeInputTokens ?? 0) === (base.probeInputTokens ?? 0);
|
|
1660
|
+
});
|
|
1661
|
+
};
|
|
1662
|
+
/** Product names for the desktop apps a delete touched or was blocked by. */
|
|
1663
|
+
const appLabels = (editions) => editions.map((edition) => t(`row.edition${edition === "cn" ? "Cn" : edition === "sg" ? "Sg" : edition === "solo" ? "Solo" : "SoloSg"}`)).join("、");
|
|
1664
|
+
/**
|
|
1665
|
+
* Delete an account from this machine: drop the plugin's own credential copy,
|
|
1666
|
+
* then erase the account's login from Trae's `storage.json`, then clear every
|
|
1667
|
+
* account-holder this card holds (pool probe, settings membership, draft
|
|
1668
|
+
* selection, the serving binding it may have held).
|
|
1669
|
+
*
|
|
1670
|
+
* The app half is what makes the row actually disappear. It has one failure
|
|
1671
|
+
* mode worth its own message: a RUNNING Trae flushes its in-memory state back
|
|
1672
|
+
* over the file, so the Host refuses and names the app, and the card says to
|
|
1673
|
+
* quit it instead of reporting a generic failure the user cannot act on.
|
|
1674
|
+
*/
|
|
1675
|
+
const deleteAccount = async (account) => {
|
|
1676
|
+
if (busy !== void 0 || deletingIds.has(account.accountId)) return;
|
|
1677
|
+
setBusy("delete");
|
|
1678
|
+
setDeletingIds((prev) => new Set(prev).add(account.accountId));
|
|
1679
|
+
setDeleteHint(void 0);
|
|
1556
1680
|
try {
|
|
1557
|
-
const response = await fetch(withTraePoolAccount(panel, "
|
|
1681
|
+
const response = await fetch(withTraePoolAccount(panel, "delete", account.accountId), { method: "POST" });
|
|
1558
1682
|
const body = await response.json();
|
|
1559
1683
|
if (!response.ok) throw new Error(renderRouteError(t, response.status, body));
|
|
1560
|
-
|
|
1561
|
-
if (
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1684
|
+
const blocked = body?.blockedApps ?? [];
|
|
1685
|
+
if (blocked.length > 0 && body?.deleted !== true) {
|
|
1686
|
+
setDeleteHint({
|
|
1687
|
+
kind: "app-running",
|
|
1688
|
+
accountId: account.accountId,
|
|
1689
|
+
accountName: account.accountName,
|
|
1690
|
+
apps: blocked
|
|
1691
|
+
});
|
|
1692
|
+
pushLog("warn", t("row.poolRowDeleteAppRunning", {
|
|
1693
|
+
accountName: account.accountName,
|
|
1694
|
+
apps: appLabels(blocked)
|
|
1695
|
+
}));
|
|
1696
|
+
setNow(Date.now());
|
|
1697
|
+
return;
|
|
1698
|
+
}
|
|
1699
|
+
if (body?.deleted !== true) throw new Error(t("row.poolRowDeleteFailed"));
|
|
1700
|
+
const wasMember = (saved.memberAccountIds ?? []).includes(account.accountId);
|
|
1701
|
+
let saveFailed = false;
|
|
1702
|
+
if (wasMember) {
|
|
1703
|
+
if (!writable) {
|
|
1704
|
+
saveFailed = true;
|
|
1705
|
+
setSaveError(t("row.poolSaveFailed", { message: t("row.poolLogSaveFailed") }));
|
|
1706
|
+
pushLog("error", t("row.poolLogSaveFailed"));
|
|
1707
|
+
} else try {
|
|
1708
|
+
await persistRemoval(account.accountId);
|
|
1709
|
+
} catch (error) {
|
|
1710
|
+
saveFailed = true;
|
|
1711
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1712
|
+
setSaveError(t("row.poolSaveFailed", { message }));
|
|
1713
|
+
pushLog("error", t("row.poolLogSaveFailed"));
|
|
1714
|
+
}
|
|
1715
|
+
}
|
|
1566
1716
|
setSelected((prev) => {
|
|
1567
1717
|
const next = new Set(prev);
|
|
1568
1718
|
next.delete(account.accountId);
|
|
1569
1719
|
return next;
|
|
1570
1720
|
});
|
|
1571
|
-
|
|
1721
|
+
if (!saveFailed) {
|
|
1722
|
+
if (body.stillSignedIn === true) {
|
|
1723
|
+
const hadCopy = body.hadCopy !== false;
|
|
1724
|
+
setDeleteHint({
|
|
1725
|
+
kind: "still-signed-in",
|
|
1726
|
+
accountId: account.accountId,
|
|
1727
|
+
accountName: account.accountName,
|
|
1728
|
+
hadCopy
|
|
1729
|
+
});
|
|
1730
|
+
pushLog("warn", t(hadCopy ? "row.poolRowDeleteStillSigned" : "row.poolRowDeleteNoCopy", { accountName: account.accountName }));
|
|
1731
|
+
} else {
|
|
1732
|
+
const signedOut = body.signedOutApps ?? [];
|
|
1733
|
+
pushLog("ok", signedOut.length === 0 ? t("row.poolRowDeleted", { accountName: account.accountName }) : t("row.poolRowDeletedSignedOut", {
|
|
1734
|
+
accountName: account.accountName,
|
|
1735
|
+
apps: appLabels(signedOut)
|
|
1736
|
+
}));
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1572
1739
|
setNow(Date.now());
|
|
1740
|
+
if (!saveFailed) await onRefresh?.();
|
|
1573
1741
|
} catch (error) {
|
|
1574
|
-
pushLog("error", t("row.
|
|
1742
|
+
pushLog("error", t("row.poolRowDeleteError", {
|
|
1743
|
+
accountName: account.accountName,
|
|
1744
|
+
message: error instanceof Error ? error.message : String(error)
|
|
1745
|
+
}));
|
|
1575
1746
|
} finally {
|
|
1576
|
-
if (mountRef.current)
|
|
1747
|
+
if (mountRef.current) {
|
|
1748
|
+
setBusy(void 0);
|
|
1749
|
+
setDeletingIds((prev) => {
|
|
1750
|
+
const next = new Set(prev);
|
|
1751
|
+
next.delete(account.accountId);
|
|
1752
|
+
return next;
|
|
1753
|
+
});
|
|
1754
|
+
}
|
|
1577
1755
|
}
|
|
1578
1756
|
};
|
|
1579
1757
|
const noCandidate = pool.noCandidate;
|
|
@@ -1710,6 +1888,7 @@ window.__ModuleLoader__.load({
|
|
|
1710
1888
|
models,
|
|
1711
1889
|
rows,
|
|
1712
1890
|
checked: selected.has(account.accountId),
|
|
1891
|
+
poolEnabled: pool.enabled === true,
|
|
1713
1892
|
detail: details?.[account.accountId],
|
|
1714
1893
|
claim: claims?.[account.accountId],
|
|
1715
1894
|
onClaim,
|
|
@@ -1721,10 +1900,11 @@ window.__ModuleLoader__.load({
|
|
|
1721
1900
|
},
|
|
1722
1901
|
t,
|
|
1723
1902
|
disabled: busy !== void 0 || !writable,
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1903
|
+
deleting: deletingIds.has(account.accountId),
|
|
1904
|
+
onDelete: () => {
|
|
1905
|
+
deleteAccount(account);
|
|
1727
1906
|
},
|
|
1907
|
+
canDelete: pool.deleteSupported === true,
|
|
1728
1908
|
formatNumber: formatNumber$1,
|
|
1729
1909
|
relativeAt
|
|
1730
1910
|
}, account.accountId)),
|
|
@@ -1737,10 +1917,11 @@ window.__ModuleLoader__.load({
|
|
|
1737
1917
|
},
|
|
1738
1918
|
t,
|
|
1739
1919
|
disabled: busy !== void 0 || !writable,
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
}
|
|
1920
|
+
deleting: deletingIds.has(ghost.accountId),
|
|
1921
|
+
onDelete: () => {
|
|
1922
|
+
deleteAccount(ghost);
|
|
1923
|
+
},
|
|
1924
|
+
canDelete: pool.deleteSupported === true
|
|
1744
1925
|
}, `ghost:${ghost.accountId}`))
|
|
1745
1926
|
]
|
|
1746
1927
|
}),
|
|
@@ -1752,6 +1933,19 @@ window.__ModuleLoader__.load({
|
|
|
1752
1933
|
names: ghosts.map((g) => g.accountName).join("、")
|
|
1753
1934
|
})
|
|
1754
1935
|
}) : null,
|
|
1936
|
+
deleteHint?.kind === "still-signed-in" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1937
|
+
className: "dsm-trae-pool-delete-hint",
|
|
1938
|
+
role: "status",
|
|
1939
|
+
children: t(deleteHint.hadCopy ? "row.poolDeleteStillSigned" : "row.poolDeleteNoCopy", { accountName: deleteHint.accountName })
|
|
1940
|
+
}) : null,
|
|
1941
|
+
deleteHint?.kind === "app-running" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1942
|
+
className: "dsm-trae-pool-delete-hint dsm-trae-pool-delete-hint-action",
|
|
1943
|
+
role: "status",
|
|
1944
|
+
children: t("row.poolDeleteAppRunning", {
|
|
1945
|
+
accountName: deleteHint.accountName,
|
|
1946
|
+
apps: appLabels(deleteHint.apps)
|
|
1947
|
+
})
|
|
1948
|
+
}) : null,
|
|
1755
1949
|
dirty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1756
1950
|
className: "dsm-trae-pool-unsaved",
|
|
1757
1951
|
role: "status",
|
|
@@ -1777,10 +1971,33 @@ window.__ModuleLoader__.load({
|
|
|
1777
1971
|
className: "dsm-trae-pool-tools dsm-trae-pool-tools-dashed",
|
|
1778
1972
|
children: tools
|
|
1779
1973
|
}),
|
|
1974
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1975
|
+
className: "dsm-trae-pool-warning dsm-trae-pool-warning-workflow",
|
|
1976
|
+
role: "note",
|
|
1977
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1978
|
+
className: "dsm-trae-pool-warning-title",
|
|
1979
|
+
children: t("row.poolMirrorWorkflowTitle")
|
|
1980
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1981
|
+
className: "dsm-trae-pool-warning-hint",
|
|
1982
|
+
children: t("row.poolMirrorWorkflowHint")
|
|
1983
|
+
})]
|
|
1984
|
+
}),
|
|
1985
|
+
pool.copies === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1986
|
+
className: "dsm-trae-pool-note dsm-trae-pool-copyfile",
|
|
1987
|
+
role: "note",
|
|
1988
|
+
children: t("row.poolCopyFile", {
|
|
1989
|
+
path: pool.copies.path,
|
|
1990
|
+
count: pool.copies.count
|
|
1991
|
+
})
|
|
1992
|
+
}),
|
|
1780
1993
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1781
1994
|
className: "dsm-trae-pool-note",
|
|
1782
1995
|
children: t("row.poolSettingsNote")
|
|
1783
1996
|
}),
|
|
1997
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1998
|
+
className: "dsm-trae-pool-note",
|
|
1999
|
+
children: t("row.poolDeleteHint")
|
|
2000
|
+
}),
|
|
1784
2001
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1785
2002
|
className: "dsm-trae-pool-settings",
|
|
1786
2003
|
children: [
|
|
@@ -1981,8 +2198,18 @@ window.__ModuleLoader__.load({
|
|
|
1981
2198
|
]
|
|
1982
2199
|
});
|
|
1983
2200
|
}
|
|
1984
|
-
function MemberRow({ account, models, rows, checked, onToggle, onTest, t, disabled,
|
|
1985
|
-
|
|
2201
|
+
function MemberRow({ account, models, rows, checked, onToggle, onTest, t, disabled, deleting, onDelete, canDelete, formatNumber, relativeAt, detail, claim, onClaim, showCheckin, deviceClaimed, poolEnabled }) {
|
|
2202
|
+
/**
|
|
2203
|
+
* Whether this row is the one billing the next request — and, with the pool on,
|
|
2204
|
+
* whether that claim is the pool's own.
|
|
2205
|
+
*
|
|
2206
|
+
* `account.current` alone is not enough: with an empty (or fully excluded) pool
|
|
2207
|
+
* the Host deliberately falls back to the saved selection, so a removed account
|
|
2208
|
+
* stays `current` and the pill would keep announcing it as the pool's pick.
|
|
2209
|
+
* A row can only be the rotation's answer while it is a member.
|
|
2210
|
+
*/
|
|
2211
|
+
const isCurrent = account.current === true && (poolEnabled !== true || account.member === true);
|
|
2212
|
+
const hasCopy = account.reachable;
|
|
1986
2213
|
const credit = account.generalAvailable;
|
|
1987
2214
|
const excluded = account.excludedBy;
|
|
1988
2215
|
const checkin = account.checkedInToday;
|
|
@@ -2019,10 +2246,10 @@ window.__ModuleLoader__.load({
|
|
|
2019
2246
|
className: "dsm-trae-pool-account-name",
|
|
2020
2247
|
children: [
|
|
2021
2248
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2022
|
-
className: `dsm-trae-pool-dot dsm-trae-pool-dot-${excluded ? "excluded" : "ok"}`,
|
|
2249
|
+
className: `dsm-trae-pool-dot dsm-trae-pool-dot-${account.reachable === false ? "ghost" : excluded ? "excluded" : "ok"}`,
|
|
2023
2250
|
"aria-hidden": "true"
|
|
2024
2251
|
}),
|
|
2025
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: account.accountName }),
|
|
2252
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: account.accountName !== "" ? account.accountName : account.accountId }),
|
|
2026
2253
|
detail?.meta === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2027
2254
|
className: "dsm-trae-pool-meta",
|
|
2028
2255
|
children: detail.meta
|
|
@@ -2045,6 +2272,11 @@ window.__ModuleLoader__.load({
|
|
|
2045
2272
|
className: checked ? "dsm-trae-pool-member" : "dsm-trae-pool-not-member",
|
|
2046
2273
|
children: checked ? t("row.poolMember") : t("row.poolNotMember")
|
|
2047
2274
|
}),
|
|
2275
|
+
account.reachable === false ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2276
|
+
className: "dsm-trae-pool-tag dsm-trae-pool-tag-ghost",
|
|
2277
|
+
role: "status",
|
|
2278
|
+
children: t("row.poolNoCopyTag")
|
|
2279
|
+
}) : null,
|
|
2048
2280
|
excluded && !probeOnScreen ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2049
2281
|
className: "dsm-trae-pool-tag dsm-trae-pool-tag-excluded",
|
|
2050
2282
|
children: exclusionLabel(excluded, t)
|
|
@@ -2118,22 +2350,23 @@ window.__ModuleLoader__.load({
|
|
|
2118
2350
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2119
2351
|
type: "button",
|
|
2120
2352
|
className: "dsm-btn dsm-btn-outline",
|
|
2121
|
-
disabled,
|
|
2353
|
+
disabled: disabled || hasCopy === false,
|
|
2354
|
+
title: hasCopy === false ? t("row.poolNoCopyTestHint") : void 0,
|
|
2122
2355
|
onClick: onTest,
|
|
2123
2356
|
children: t("row.poolRowTest")
|
|
2124
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2357
|
+
}), canDelete ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2125
2358
|
type: "button",
|
|
2126
|
-
className: "dsm-btn dsm-btn-outline dsm-trae-pool-
|
|
2127
|
-
disabled: disabled ||
|
|
2128
|
-
"aria-label": t("row.
|
|
2129
|
-
onClick:
|
|
2130
|
-
children:
|
|
2131
|
-
})]
|
|
2359
|
+
className: "dsm-btn dsm-btn-outline-danger dsm-trae-pool-delete",
|
|
2360
|
+
disabled: disabled || deleting,
|
|
2361
|
+
"aria-label": t("row.poolRowDeleteAria", { accountName: account.accountName }),
|
|
2362
|
+
onClick: onDelete,
|
|
2363
|
+
children: deleting ? t("row.poolRowDeleting") : t("row.poolRowDelete")
|
|
2364
|
+
}) : null]
|
|
2132
2365
|
})
|
|
2133
2366
|
]
|
|
2134
2367
|
});
|
|
2135
2368
|
}
|
|
2136
|
-
function MemberGhostRow({ ghost, checked, onToggle, t, disabled,
|
|
2369
|
+
function MemberGhostRow({ ghost, checked, onToggle, t, disabled, deleting, onDelete, canDelete, showCheckin }) {
|
|
2137
2370
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2138
2371
|
className: "dsm-trae-pool-row dsm-trae-pool-row-ghost",
|
|
2139
2372
|
role: "row",
|
|
@@ -2194,14 +2427,14 @@ window.__ModuleLoader__.load({
|
|
|
2194
2427
|
}) : null,
|
|
2195
2428
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2196
2429
|
className: "dsm-trae-pool-row-actions",
|
|
2197
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2430
|
+
children: canDelete ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2198
2431
|
type: "button",
|
|
2199
|
-
className: "dsm-btn dsm-btn-outline dsm-trae-pool-
|
|
2200
|
-
disabled: disabled ||
|
|
2201
|
-
"aria-label": t("row.
|
|
2202
|
-
onClick:
|
|
2203
|
-
children:
|
|
2204
|
-
})
|
|
2432
|
+
className: "dsm-btn dsm-btn-outline-danger dsm-trae-pool-delete",
|
|
2433
|
+
disabled: disabled || deleting,
|
|
2434
|
+
"aria-label": t("row.poolGhostDeleteAria", { accountName: ghost.accountName }),
|
|
2435
|
+
onClick: onDelete,
|
|
2436
|
+
children: deleting ? t("row.poolRowDeleting") : t("row.poolRowDelete")
|
|
2437
|
+
}) : null
|
|
2205
2438
|
})
|
|
2206
2439
|
]
|
|
2207
2440
|
});
|
|
@@ -5266,22 +5499,22 @@ window.__ModuleLoader__.load({
|
|
|
5266
5499
|
"row.refreshCredits": "Refresh usage",
|
|
5267
5500
|
"row.refreshing": "Refreshing…",
|
|
5268
5501
|
"row.accountsTitle": "Trae account",
|
|
5269
|
-
"row.accountsHint": "
|
|
5502
|
+
"row.accountsHint": "Copies this plugin keeps of every Trae sign-in on this machine (mirrored on refresh). Refresh after signing in, or Trae's next sign-in overwrites that login. Tokens are never shown.",
|
|
5270
5503
|
"row.transferTitle": "Import & export accounts",
|
|
5271
5504
|
"row.transferExport": "Export selected",
|
|
5272
5505
|
"row.transferImport": "Import from file",
|
|
5273
5506
|
"row.transferCancel": "Cancel",
|
|
5274
5507
|
"row.transferSelectAll": "Select all",
|
|
5275
|
-
"row.transferExportPick": "Choose which of the {total}
|
|
5508
|
+
"row.transferExportPick": "Choose which of the {total} account copies to export.",
|
|
5276
5509
|
"row.transferExportConfirm": "Export {count}",
|
|
5277
|
-
"row.transferExported": "Exported {count} account
|
|
5510
|
+
"row.transferExported": "Exported {count} account copy/copies to {fileName}.",
|
|
5278
5511
|
"row.transferExporting": "Preparing the export…",
|
|
5279
|
-
"row.transferImportPick": "This file holds {total} record(s).
|
|
5512
|
+
"row.transferImportPick": "This file holds {total} record(s). Ticked records are added as plugin-owned copies; the Trae app is not touched.",
|
|
5280
5513
|
"row.transferImportConfirm": "Import {count}",
|
|
5281
|
-
"row.transferImported": "Imported {imported} account
|
|
5514
|
+
"row.transferImported": "Imported {imported} account copy/copies, skipped {skipped}.",
|
|
5282
5515
|
"row.transferImporting": "Importing…",
|
|
5283
5516
|
"row.transferReading": "Reading the file…",
|
|
5284
|
-
"row.transferHint": "The exported file
|
|
5517
|
+
"row.transferHint": "The exported file holds this plugin's own copies, including live tokens — treat it as a password. Records belonging to the other region are skipped.",
|
|
5285
5518
|
"row.transferError": "Import/export failed: {message}",
|
|
5286
5519
|
"row.transferVeilHint": "The account list refreshes when this finishes.",
|
|
5287
5520
|
"row.transferUnnamed": "Unnamed account",
|
|
@@ -5398,8 +5631,13 @@ window.__ModuleLoader__.load({
|
|
|
5398
5631
|
"row.poolCreditExpiry": "expires {when}",
|
|
5399
5632
|
"row.poolProbeMeasured": "measured {when}",
|
|
5400
5633
|
"row.poolProbeAt": "tested {when}",
|
|
5634
|
+
"row.poolMirrorWorkflowTitle": "Sign in one account, then refresh",
|
|
5635
|
+
"row.poolMirrorWorkflowHint": "The Trae app holds only one sign-in. After signing in to an account, refresh this card (or click “Detect accounts again”) so the plugin saves that login as its own copy. Switch accounts without refreshing and the previous login is not saved — the new one overwrites it in Trae.",
|
|
5401
5636
|
"row.poolSettingsNote": "Preferences below take effect when you save; test results are written by the plugin and are not affected by that button.",
|
|
5402
5637
|
"row.poolSelectedCount": "Selected {count} / {total}",
|
|
5638
|
+
"row.poolNoCopyTag": "no copy here",
|
|
5639
|
+
"row.poolCopyFile": "Copies live in {path} — {count} right now. Switching accounts in Trae adds a copy; it never overwrites an existing one.",
|
|
5640
|
+
"row.poolNoCopyTestHint": "This machine holds no sign-in for this account, so it cannot be tested. Sign in to Trae with it again to recreate a copy, or delete the row.",
|
|
5403
5641
|
"row.poolSelectAll": "All",
|
|
5404
5642
|
"row.poolSelectNone": "None",
|
|
5405
5643
|
"row.poolColumnAccount": "Account",
|
|
@@ -5440,14 +5678,8 @@ window.__ModuleLoader__.load({
|
|
|
5440
5678
|
"row.poolDiscard": "Discard",
|
|
5441
5679
|
"row.poolSaveFailed": "Save failed: {message}",
|
|
5442
5680
|
"row.poolGhostMembers": "{count} saved member(s) no longer resolve to a local sign-in: {names}",
|
|
5443
|
-
"row.poolGhostRemove": "Remove {account}",
|
|
5444
|
-
"row.poolGhostRemoveHint": "Forget this panel's binding to {account}.",
|
|
5445
5681
|
"row.poolUnsavedMembers": "Unsaved membership changes — Save below to apply.",
|
|
5446
5682
|
"row.poolRowTest": "Test",
|
|
5447
|
-
"row.poolRowRemove": "Remove",
|
|
5448
|
-
"row.poolRowRemoveAria": "Remove {accountName} from this panel",
|
|
5449
|
-
"row.poolRowRemoving": "Removing…",
|
|
5450
|
-
"row.poolRowRemoved": "Removed {accountName} from this panel",
|
|
5451
5683
|
"row.poolLogTitle": "Activity",
|
|
5452
5684
|
"row.poolLogClear": "Clear",
|
|
5453
5685
|
"row.poolLogEmpty": "No pool activity yet.",
|
|
@@ -5461,6 +5693,7 @@ window.__ModuleLoader__.load({
|
|
|
5461
5693
|
"row.poolLogTestRowMalformed": "{accountName}: malformed result.",
|
|
5462
5694
|
"row.poolLogTestDone": "Test finished.",
|
|
5463
5695
|
"row.poolLogBatchFailed": "Pool action failed: {message}",
|
|
5696
|
+
"row.poolLogTimeout": "The test did not answer in time — it was cancelled, so the button works again. Check the network and retry: {message}",
|
|
5464
5697
|
"row.poolSelectAria": "Include {accountName} in the pool",
|
|
5465
5698
|
"row.poolCheckedIn": "Checked in",
|
|
5466
5699
|
"row.poolNotCheckedIn": "Not checked in",
|
|
@@ -5509,8 +5742,7 @@ window.__ModuleLoader__.load({
|
|
|
5509
5742
|
"row.poolErrConflict": "the pool refused the request",
|
|
5510
5743
|
"row.poolErrGeneric": "pool request failed (HTTP {status})",
|
|
5511
5744
|
"row.poolCheckingIn": "Checking in…",
|
|
5512
|
-
"row.poolSettingsHint": "The switch and the target model are preferences — save them. Test, check in and
|
|
5513
|
-
"row.poolGhostRemoveAria": "Remove the saved but unreachable account {accountName} from this panel",
|
|
5745
|
+
"row.poolSettingsHint": "The switch and the target model are preferences — save them. Test, check in and delete act at once.",
|
|
5514
5746
|
"row.poolLogSaved": "Pool settings saved.",
|
|
5515
5747
|
"row.poolLogSaveFailed": "Could not save the pool settings.",
|
|
5516
5748
|
"row.poolLogNoMembers": "Nothing to do — no pool members are selected.",
|
|
@@ -5518,8 +5750,21 @@ window.__ModuleLoader__.load({
|
|
|
5518
5750
|
"row.poolLogGeneric": "the pool answered nothing usable",
|
|
5519
5751
|
"row.poolLogRowTesting": "Testing {accountName}…",
|
|
5520
5752
|
"row.poolLogTargetCleared": "Returned the test target to automatic.",
|
|
5521
|
-
"row.
|
|
5522
|
-
"row.
|
|
5753
|
+
"row.poolRowDelete": "Delete",
|
|
5754
|
+
"row.poolRowDeleteAria": "Delete {accountName} from this machine: the plugin's copy and its Trae app login (this will sign it out of Trae)",
|
|
5755
|
+
"row.poolRowDeleting": "Deleting…",
|
|
5756
|
+
"row.poolGhostDeleteAria": "Delete the saved but unreachable account {accountName} from this machine",
|
|
5757
|
+
"row.poolRowDeleteError": "Could not delete {accountName}: {message}",
|
|
5758
|
+
"row.poolRowDeleteFailed": "the deletion was not confirmed",
|
|
5759
|
+
"row.poolRowDeleted": "Deleted the credential copy of {accountName}.",
|
|
5760
|
+
"row.poolDeleteStillSigned": "Deleted this plugin's copy of {accountName}, but it is still signed in to Trae, so the account itself is not gone. Sign out inside Trae to remove it from this machine.",
|
|
5761
|
+
"row.poolRowDeletedSignedOut": "Deleted {accountName} and signed it out of {apps}.",
|
|
5762
|
+
"row.poolRowDeleteAppRunning": "Could not delete {accountName}: {apps} is running and would restore the login. Quit {apps} completely, then delete again.",
|
|
5763
|
+
"row.poolDeleteAppRunning": "Nothing was changed: {apps} is running, and it would write the login back over the file on exit. Quit {apps} completely, then delete again.",
|
|
5764
|
+
"row.poolDeleteNoCopy": "There was no plugin copy of {accountName} left to delete — it only exists as a Trae app login, and that sign-out did not take effect.",
|
|
5765
|
+
"row.poolRowDeleteStillSigned": "removed this copy, but {accountName} is still signed in to Trae",
|
|
5766
|
+
"row.poolRowDeleteNoCopy": "{accountName} had no plugin copy — only its Trae app login",
|
|
5767
|
+
"row.poolDeleteHint": "The plugin keeps its own copy of every local Trae login (a copy is created on refresh unless one already exists) and only ever shows and bills those copies. Delete removes the copy AND signs that account out of Trae itself, leaving a backup of every file it rewrites; a deleted account is not re-imported while it stays signed in Trae. A running Trae would flush its own state back over the file, so quit it first — the card names the app it needs closed.",
|
|
5523
5768
|
"row.cheer": "Star on GitHub"
|
|
5524
5769
|
};
|
|
5525
5770
|
const zh = {
|
|
@@ -5583,22 +5828,22 @@ window.__ModuleLoader__.load({
|
|
|
5583
5828
|
"row.refreshCredits": "刷新用量",
|
|
5584
5829
|
"row.refreshing": "正在刷新…",
|
|
5585
5830
|
"row.accountsTitle": "Trae 账号",
|
|
5586
|
-
"row.accountsHint": "
|
|
5831
|
+
"row.accountsHint": "本插件保管的账号副本:本机每个 Trae 登录都会在刷新时补建一份副本。登录新号后要刷新一次,否则它会被 Trae 的下一次登录覆盖。Token 不会显示。",
|
|
5587
5832
|
"row.transferTitle": "导入 / 导出账号",
|
|
5588
5833
|
"row.transferExport": "导出所选",
|
|
5589
5834
|
"row.transferImport": "从文件导入",
|
|
5590
5835
|
"row.transferCancel": "取消",
|
|
5591
5836
|
"row.transferSelectAll": "全选",
|
|
5592
|
-
"row.transferExportPick": "从这 {total}
|
|
5837
|
+
"row.transferExportPick": "从这 {total} 个账号副本中选择要导出的。",
|
|
5593
5838
|
"row.transferExportConfirm": "导出 {count} 个",
|
|
5594
|
-
"row.transferExported": "已导出 {count}
|
|
5839
|
+
"row.transferExported": "已导出 {count} 个账号副本到 {fileName}。",
|
|
5595
5840
|
"row.transferExporting": "正在准备导出…",
|
|
5596
|
-
"row.transferImportPick": "该文件包含 {total}
|
|
5841
|
+
"row.transferImportPick": "该文件包含 {total} 条记录;勾选的会作为插件副本导入,不会写入 Trae 应用。",
|
|
5597
5842
|
"row.transferImportConfirm": "导入 {count} 个",
|
|
5598
|
-
"row.transferImported": "已导入 {imported}
|
|
5843
|
+
"row.transferImported": "已导入 {imported} 个账号副本,跳过 {skipped} 个。",
|
|
5599
5844
|
"row.transferImporting": "正在导入…",
|
|
5600
5845
|
"row.transferReading": "正在读取文件…",
|
|
5601
|
-
"row.transferHint": "
|
|
5846
|
+
"row.transferHint": "导出的文件里是插件保管的账号副本(含有效 Token),请当作密码保管。属于另一区域的记录会被跳过。",
|
|
5602
5847
|
"row.transferError": "导入/导出失败:{message}",
|
|
5603
5848
|
"row.transferVeilHint": "完成后账号列表会自动刷新。",
|
|
5604
5849
|
"row.transferUnnamed": "未命名账号",
|
|
@@ -5715,8 +5960,13 @@ window.__ModuleLoader__.load({
|
|
|
5715
5960
|
"row.poolCreditExpiry": "{when} 过期",
|
|
5716
5961
|
"row.poolProbeMeasured": "测得 {when}",
|
|
5717
5962
|
"row.poolProbeAt": "测试于 {when}",
|
|
5963
|
+
"row.poolMirrorWorkflowTitle": "每登录一个账号,就刷新一次",
|
|
5964
|
+
"row.poolMirrorWorkflowHint": "Trae 应用只有一个登录位:每登录一个账号,就回到这里刷新一次(打开卡片或点「重新检测账号」),插件会把这次登录保存成一份自己的副本。没刷新就换号,上一个账号不会被保存——它会被 Trae 里的新登录覆盖。",
|
|
5718
5965
|
"row.poolSettingsNote": "下面是偏好设置:点保存才生效;测试结果由插件自动写入,不受这个保存按钮影响。",
|
|
5719
5966
|
"row.poolSelectedCount": "已选 {count} / {total}",
|
|
5967
|
+
"row.poolNoCopyTag": "本机无副本",
|
|
5968
|
+
"row.poolCopyFile": "副本就存在 {path},当前 {count} 份。在 Trae 里换号只会新增一份,不会覆盖已有的副本。",
|
|
5969
|
+
"row.poolNoCopyTestHint": "本机没有这个账号的登录,无法测速。需要它请在 Trae 里重新登录以重建副本,或删除这一行。",
|
|
5720
5970
|
"row.poolSelectAll": "全部",
|
|
5721
5971
|
"row.poolSelectNone": "清空",
|
|
5722
5972
|
"row.poolColumnAccount": "账号",
|
|
@@ -5757,14 +6007,8 @@ window.__ModuleLoader__.load({
|
|
|
5757
6007
|
"row.poolDiscard": "放弃",
|
|
5758
6008
|
"row.poolSaveFailed": "保存失败:{message}",
|
|
5759
6009
|
"row.poolGhostMembers": "{count} 个已保存成员不再对应本机登录:{names}",
|
|
5760
|
-
"row.poolGhostRemove": "移除 {account}",
|
|
5761
|
-
"row.poolGhostRemoveHint": "忘记本面板对 {account} 的绑定。",
|
|
5762
6010
|
"row.poolUnsavedMembers": "成员改动尚未保存——请点击下方保存。",
|
|
5763
6011
|
"row.poolRowTest": "测速",
|
|
5764
|
-
"row.poolRowRemove": "移除",
|
|
5765
|
-
"row.poolRowRemoveAria": "从本面板移除 {accountName}",
|
|
5766
|
-
"row.poolRowRemoving": "移除中…",
|
|
5767
|
-
"row.poolRowRemoved": "已从本面板移除 {accountName}",
|
|
5768
6012
|
"row.poolLogTitle": "活动记录",
|
|
5769
6013
|
"row.poolLogClear": "清空",
|
|
5770
6014
|
"row.poolLogEmpty": "暂无账号池活动。",
|
|
@@ -5778,6 +6022,7 @@ window.__ModuleLoader__.load({
|
|
|
5778
6022
|
"row.poolLogTestRowMalformed": "{accountName}:结果异常。",
|
|
5779
6023
|
"row.poolLogTestDone": "测速完成。",
|
|
5780
6024
|
"row.poolLogBatchFailed": "账号池操作失败:{message}",
|
|
6025
|
+
"row.poolLogTimeout": "测速超时,已自动取消(按钮已恢复可用)。请检查网络后重试:{message}",
|
|
5781
6026
|
"row.poolSelectAria": "把 {accountName} 加入账号池",
|
|
5782
6027
|
"row.poolCheckedIn": "已签到",
|
|
5783
6028
|
"row.poolNotCheckedIn": "未签到",
|
|
@@ -5826,8 +6071,7 @@ window.__ModuleLoader__.load({
|
|
|
5826
6071
|
"row.poolErrConflict": "账号池拒绝了该请求",
|
|
5827
6072
|
"row.poolErrGeneric": "账号池请求失败(HTTP {status})",
|
|
5828
6073
|
"row.poolCheckingIn": "签到中…",
|
|
5829
|
-
"row.poolSettingsHint": "
|
|
5830
|
-
"row.poolGhostRemoveAria": "把已保存但本机无法登录的账号 {accountName} 移出本面板",
|
|
6074
|
+
"row.poolSettingsHint": "开关与目标模型是偏好,改完要保存;行内的测速、签到与删除立即生效。",
|
|
5831
6075
|
"row.poolLogSaved": "账号池设置已保存。",
|
|
5832
6076
|
"row.poolLogSaveFailed": "无法保存账号池设置。",
|
|
5833
6077
|
"row.poolLogNoMembers": "没有可处理的内容——尚未勾选任何池成员。",
|
|
@@ -5835,8 +6079,21 @@ window.__ModuleLoader__.load({
|
|
|
5835
6079
|
"row.poolLogGeneric": "账号池未返回可用的结果",
|
|
5836
6080
|
"row.poolLogRowTesting": "正在测速 {accountName}…",
|
|
5837
6081
|
"row.poolLogTargetCleared": "测速目标已改回自动。",
|
|
5838
|
-
"row.
|
|
5839
|
-
"row.
|
|
6082
|
+
"row.poolRowDelete": "删除",
|
|
6083
|
+
"row.poolRowDeleteAria": "从本机删除 {accountName}:插件的副本连同它在 Trae 应用里的登录(会把它从 Trae 退出登录)",
|
|
6084
|
+
"row.poolRowDeleting": "删除中…",
|
|
6085
|
+
"row.poolGhostDeleteAria": "从本机删除已保存但无法登录的账号 {accountName}",
|
|
6086
|
+
"row.poolRowDeleteError": "无法删除 {accountName}:{message}",
|
|
6087
|
+
"row.poolRowDeleteFailed": "删除未得到确认",
|
|
6088
|
+
"row.poolRowDeleted": "已删除 {accountName} 的凭据副本。",
|
|
6089
|
+
"row.poolDeleteStillSigned": "已删除本插件对 {accountName} 的副本,但它仍登录在 Trae 里,所以账号本身还在。要彻底从本机移除,请在 Trae 内退出登录。",
|
|
6090
|
+
"row.poolRowDeletedSignedOut": "已删除 {accountName},并将它从 {apps} 中退出登录。",
|
|
6091
|
+
"row.poolRowDeleteAppRunning": "无法删除 {accountName}:{apps} 正在运行,退出时会把登录写回去。请先完全退出 {apps},再重新删除。",
|
|
6092
|
+
"row.poolDeleteAppRunning": "这次没有改动任何文件:{apps} 正在运行,退出时会把自己的状态回写到文件、覆盖这次修改。请先完全退出 {apps},再点「删除」。",
|
|
6093
|
+
"row.poolDeleteNoCopy": "{accountName} 本来就没有插件副本可删——它只存在于 Trae 应用登录里,而这次退出登录没有生效。",
|
|
6094
|
+
"row.poolRowDeleteStillSigned": "已删除副本,但 {accountName} 仍登录在 Trae 中",
|
|
6095
|
+
"row.poolRowDeleteNoCopy": "{accountName} 没有插件副本——它只存在于 Trae 应用登录里",
|
|
6096
|
+
"row.poolDeleteHint": "本插件会为本机每个 Trae 登录各创建一份自己的副本(刷新时自动创建,已有副本则不动),卡片只显示、也只使用这些副本。「删除」会同时删掉副本并把该账号从 Trae 应用中退出登录,改写的每个文件都留一份备份;只要该账号还登录在 Trae 里,就不会被重新收录。Trae 正在运行时会把自己的状态回写到文件,所以要先完全退出——卡片会点名是哪个应用。",
|
|
5840
6097
|
"row.cheer": "鼓励一下"
|
|
5841
6098
|
};
|
|
5842
6099
|
//#endregion
|