dsh-connect-trae 3.9.2 → 3.10.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/CHANGELOG.md +134 -0
- package/lib/client.js +378 -186
- 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}
|
|
@@ -1268,7 +1269,6 @@ window.__ModuleLoader__.load({
|
|
|
1268
1269
|
if (status === 409) switch (body?.reason) {
|
|
1269
1270
|
case "no-members": return t("row.poolErrNoMembers");
|
|
1270
1271
|
case "no-live-members": return t("row.poolErrNoLiveMembers");
|
|
1271
|
-
case "checkin-unsupported": return t("row.poolErrCheckinUnsupported");
|
|
1272
1272
|
case "target-model-stale": return t("row.poolErrTargetStale");
|
|
1273
1273
|
case "no-free-model": return t("row.poolErrNoFreeModel");
|
|
1274
1274
|
default: return t("row.poolErrConflict");
|
|
@@ -1276,34 +1276,74 @@ window.__ModuleLoader__.load({
|
|
|
1276
1276
|
return t("row.poolErrGeneric", { status: String(status) });
|
|
1277
1277
|
}
|
|
1278
1278
|
/**
|
|
1279
|
-
*
|
|
1280
|
-
*
|
|
1281
|
-
*
|
|
1282
|
-
*
|
|
1279
|
+
* Handle ONE NDJSON line of a `test` answer. A non-JSON line is skipped; a
|
|
1280
|
+
* `{reason:'pool-failed'}` line reports the failure and counts as "saw
|
|
1281
|
+
* something". Returns whether the line was usable, so the caller can tell "the
|
|
1282
|
+
* route answered nothing parseable" (a bug to surface) from "not finished yet".
|
|
1283
1283
|
*/
|
|
1284
|
-
function
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1284
|
+
function applyTestLine(line, t, onRow, onFailure) {
|
|
1285
|
+
if (line.trim() === "") return false;
|
|
1286
|
+
let parsed;
|
|
1287
|
+
try {
|
|
1288
|
+
parsed = JSON.parse(line);
|
|
1289
|
+
} catch {
|
|
1290
|
+
return false;
|
|
1291
|
+
}
|
|
1292
|
+
if (parsed.row !== void 0) {
|
|
1293
|
+
onRow(parsed.row);
|
|
1294
|
+
return true;
|
|
1295
|
+
}
|
|
1296
|
+
if (parsed.rows !== void 0) {
|
|
1297
|
+
for (const r of parsed.rows) onRow(r);
|
|
1298
|
+
return true;
|
|
1299
|
+
}
|
|
1300
|
+
if (parsed.reason === "pool-failed") {
|
|
1301
|
+
onFailure(parsed.error ?? t("row.poolLogGeneric"));
|
|
1302
|
+
return true;
|
|
1303
|
+
}
|
|
1304
|
+
return false;
|
|
1305
|
+
}
|
|
1306
|
+
/**
|
|
1307
|
+
* Read the answer line by line AS IT ARRIVES.
|
|
1308
|
+
*
|
|
1309
|
+
* The Host streams one line per account, but `response.text()` waits for the
|
|
1310
|
+
* whole body: with a slow probe the card sat on "逐个处理账号中…" and looked
|
|
1311
|
+
* frozen (a single member could take half a minute). Reading the body stream
|
|
1312
|
+
* makes each finished account appear immediately, and an empty/failed stream
|
|
1313
|
+
* still ends the loop instead of hanging the busy state forever.
|
|
1314
|
+
*/
|
|
1315
|
+
async function* testLinesOf(response) {
|
|
1316
|
+
const body = response.body;
|
|
1317
|
+
if (body === null) {
|
|
1318
|
+
for (const line of (await response.text()).split(/\r?\n/)) yield line;
|
|
1319
|
+
return;
|
|
1320
|
+
}
|
|
1321
|
+
const reader = body.getReader();
|
|
1322
|
+
const decoder = new TextDecoder();
|
|
1323
|
+
let buffer = "";
|
|
1324
|
+
for (;;) {
|
|
1325
|
+
const { done, value } = await reader.read();
|
|
1326
|
+
if (done) break;
|
|
1327
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1328
|
+
let cut = buffer.indexOf("\n");
|
|
1329
|
+
while (cut >= 0) {
|
|
1330
|
+
yield buffer.slice(0, cut);
|
|
1331
|
+
buffer = buffer.slice(cut + 1);
|
|
1332
|
+
cut = buffer.indexOf("\n");
|
|
1303
1333
|
}
|
|
1304
1334
|
}
|
|
1305
|
-
|
|
1335
|
+
buffer += decoder.decode();
|
|
1336
|
+
if (buffer.trim() !== "") yield buffer;
|
|
1306
1337
|
}
|
|
1338
|
+
/**
|
|
1339
|
+
* How long one `test` request may take before the card gives up.
|
|
1340
|
+
*
|
|
1341
|
+
* A probe has its own upstream deadline, but a stalled connection (a restarting
|
|
1342
|
+
* Host, a dropped keep-alive) has none: without this the run button stays
|
|
1343
|
+
* disabled until the page is reloaded, which reads as "测速卡住了". Sized for a
|
|
1344
|
+
* batch: a few seconds per account plus room for the slowest upstream.
|
|
1345
|
+
*/
|
|
1346
|
+
const TEST_TIMEOUT_MS = 18e4;
|
|
1307
1347
|
function AccountPool({ panel, pool, models, t, settingsScope, onChanged, onRefresh, tools, topActions, details, claims, onClaim, deviceClaimed = false, onBind, bindingBusy }) {
|
|
1308
1348
|
const [settingsTick, setSettingsTick] = (0, react.useState)(0);
|
|
1309
1349
|
const saved = (0, react.useMemo)(() => {
|
|
@@ -1340,7 +1380,8 @@ window.__ModuleLoader__.load({
|
|
|
1340
1380
|
const [saveError, setSaveError] = (0, react.useState)(void 0);
|
|
1341
1381
|
const [testing, setTesting] = (0, react.useState)(void 0);
|
|
1342
1382
|
const [rows, setRows] = (0, react.useState)({});
|
|
1343
|
-
const [
|
|
1383
|
+
const [deletingIds, setDeletingIds] = (0, react.useState)(() => /* @__PURE__ */ new Set());
|
|
1384
|
+
const [deleteHint, setDeleteHint] = (0, react.useState)(void 0);
|
|
1344
1385
|
const [log, setLog] = (0, react.useState)([]);
|
|
1345
1386
|
const [now, setNow] = (0, react.useState)(Date.now());
|
|
1346
1387
|
const logId = (0, react.useRef)(0);
|
|
@@ -1368,10 +1409,14 @@ window.__ModuleLoader__.load({
|
|
|
1368
1409
|
const ghosts = (0, react.useMemo)(() => {
|
|
1369
1410
|
const names = pool.memberNames ?? {};
|
|
1370
1411
|
const result = [];
|
|
1371
|
-
for (const id of saved.memberAccountIds ?? [])
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1412
|
+
for (const id of saved.memberAccountIds ?? []) {
|
|
1413
|
+
if (knownIds.has(id)) continue;
|
|
1414
|
+
const name = names[id];
|
|
1415
|
+
result.push({
|
|
1416
|
+
accountId: id,
|
|
1417
|
+
accountName: name === void 0 || name === "" ? id : name
|
|
1418
|
+
});
|
|
1419
|
+
}
|
|
1375
1420
|
return result;
|
|
1376
1421
|
}, [
|
|
1377
1422
|
saved,
|
|
@@ -1380,12 +1425,27 @@ window.__ModuleLoader__.load({
|
|
|
1380
1425
|
]);
|
|
1381
1426
|
const dirty = enabled !== saved.enabled || (targetModelId ?? "") !== (saved.targetModelId ?? "") || probeSupported && probeInputTokens !== saved.probeInputTokens || !sameIds([...selected], saved.memberAccountIds);
|
|
1382
1427
|
const writable = settingsScope?.getSnapshot().writable === true;
|
|
1383
|
-
const
|
|
1428
|
+
const savedMemberKey = (0, react.useMemo)(() => [...saved.memberAccountIds ?? []].sort().join("\0"), [saved]);
|
|
1384
1429
|
(0, react.useEffect)(() => {
|
|
1385
1430
|
setEnabled(saved.enabled);
|
|
1431
|
+
}, [panel, saved.enabled]);
|
|
1432
|
+
(0, react.useEffect)(() => {
|
|
1386
1433
|
setTargetModelId(saved.targetModelId ?? "");
|
|
1434
|
+
}, [panel, saved.targetModelId]);
|
|
1435
|
+
(0, react.useEffect)(() => {
|
|
1387
1436
|
setProbeInputTokens(saved.probeInputTokens);
|
|
1437
|
+
}, [panel, saved.probeInputTokens]);
|
|
1438
|
+
(0, react.useEffect)(() => {
|
|
1388
1439
|
setSelected(new Set(saved.memberAccountIds ?? []));
|
|
1440
|
+
}, [panel, savedMemberKey]);
|
|
1441
|
+
const savedKey = (0, react.useMemo)(() => `${panel}:${saved.enabled}:${saved.targetModelId ?? ""}:${saved.probeInputTokens}:${savedMemberKey}`, [
|
|
1442
|
+
panel,
|
|
1443
|
+
saved.enabled,
|
|
1444
|
+
saved.targetModelId,
|
|
1445
|
+
saved.probeInputTokens,
|
|
1446
|
+
savedMemberKey
|
|
1447
|
+
]);
|
|
1448
|
+
(0, react.useEffect)(() => {
|
|
1389
1449
|
setSaveError(void 0);
|
|
1390
1450
|
}, [savedKey]);
|
|
1391
1451
|
const toggleMember = (accountId) => {
|
|
@@ -1436,43 +1496,6 @@ window.__ModuleLoader__.load({
|
|
|
1436
1496
|
if (mountRef.current) setBusy(void 0);
|
|
1437
1497
|
}
|
|
1438
1498
|
};
|
|
1439
|
-
const runCheckin = async () => {
|
|
1440
|
-
if (busy !== void 0) return;
|
|
1441
|
-
const members = [...selected];
|
|
1442
|
-
if (members.length === 0) {
|
|
1443
|
-
pushLog("warn", t("row.poolLogNoMembers"));
|
|
1444
|
-
return;
|
|
1445
|
-
}
|
|
1446
|
-
setBusy("checkin");
|
|
1447
|
-
setNow(Date.now());
|
|
1448
|
-
pushLog("info", t("row.poolLogCheckinStart", { count: members.length }));
|
|
1449
|
-
try {
|
|
1450
|
-
const response = await fetch(withTraePoolAction(panel, "checkin"), { method: "POST" });
|
|
1451
|
-
const body = await response.json();
|
|
1452
|
-
if (!response.ok) throw new Error(renderRouteError(t, response.status, body));
|
|
1453
|
-
for (const row of body?.rows ?? []) switch (row.status) {
|
|
1454
|
-
case "claimed":
|
|
1455
|
-
pushLog("ok", t("row.poolLogCheckinClaimed", { accountName: row.accountName }));
|
|
1456
|
-
break;
|
|
1457
|
-
case "already":
|
|
1458
|
-
case "device-already":
|
|
1459
|
-
pushLog("info", t("row.poolLogCheckinAlready", { accountName: row.accountName }));
|
|
1460
|
-
break;
|
|
1461
|
-
case "failed":
|
|
1462
|
-
case "disabled": pushLog("error", t("row.poolLogCheckinFailed", {
|
|
1463
|
-
accountName: row.accountName,
|
|
1464
|
-
message: row.message ?? ""
|
|
1465
|
-
}));
|
|
1466
|
-
}
|
|
1467
|
-
pushLog(members.length > 0 && body?.rows ? "ok" : "info", t("row.poolLogCheckinDone"));
|
|
1468
|
-
onRefresh?.();
|
|
1469
|
-
setNow(Date.now());
|
|
1470
|
-
} catch (error) {
|
|
1471
|
-
pushLog("error", t("row.poolLogBatchFailed", { message: error instanceof Error ? error.message : String(error) }));
|
|
1472
|
-
} finally {
|
|
1473
|
-
if (mountRef.current) setBusy(void 0);
|
|
1474
|
-
}
|
|
1475
|
-
};
|
|
1476
1499
|
const runTest = async () => {
|
|
1477
1500
|
if (busy !== void 0) return;
|
|
1478
1501
|
const members = [...selected];
|
|
@@ -1489,14 +1512,24 @@ window.__ModuleLoader__.load({
|
|
|
1489
1512
|
count: members.length,
|
|
1490
1513
|
model: targetLabel
|
|
1491
1514
|
}));
|
|
1515
|
+
const controller = new AbortController();
|
|
1516
|
+
const timer = setTimeout(() => {
|
|
1517
|
+
controller.abort();
|
|
1518
|
+
}, TEST_TIMEOUT_MS);
|
|
1492
1519
|
try {
|
|
1493
|
-
const
|
|
1520
|
+
const response = await fetch(withTraePoolAction(panel, "test"), {
|
|
1521
|
+
method: "POST",
|
|
1522
|
+
signal: controller.signal
|
|
1523
|
+
});
|
|
1494
1524
|
const seen = /* @__PURE__ */ new Set();
|
|
1495
|
-
|
|
1525
|
+
let sawAny = false;
|
|
1526
|
+
for await (const line of testLinesOf(response)) if (applyTestLine(line, t, (r) => {
|
|
1527
|
+
const fresh = !seen.has(r.accountId);
|
|
1496
1528
|
setRows((prev) => ({
|
|
1497
1529
|
...prev,
|
|
1498
1530
|
[r.accountId]: r
|
|
1499
1531
|
}));
|
|
1532
|
+
if (!fresh) return;
|
|
1500
1533
|
seen.add(r.accountId);
|
|
1501
1534
|
if (mountRef.current) setTesting({ done: seen.size });
|
|
1502
1535
|
pushLog(outcomeTone(r.outcome), t("row.poolLogTestRow", {
|
|
@@ -1505,13 +1538,15 @@ window.__ModuleLoader__.load({
|
|
|
1505
1538
|
}));
|
|
1506
1539
|
}, (errorText) => {
|
|
1507
1540
|
pushLog("error", t("row.poolLogBatchFailed", { message: errorText }));
|
|
1508
|
-
}))
|
|
1541
|
+
})) sawAny = true;
|
|
1542
|
+
if (!sawAny) throw new Error(t("row.poolLogGeneric"));
|
|
1509
1543
|
pushLog("ok", t("row.poolLogTestDone"));
|
|
1510
1544
|
onRefresh?.();
|
|
1511
1545
|
setNow(Date.now());
|
|
1512
1546
|
} catch (error) {
|
|
1513
|
-
pushLog("error", t("row.poolLogBatchFailed", { message: error instanceof Error ? error.message : String(error) }));
|
|
1547
|
+
pushLog("error", t(controller.signal.aborted ? "row.poolLogTimeout" : "row.poolLogBatchFailed", { message: error instanceof Error ? error.message : String(error) }));
|
|
1514
1548
|
} finally {
|
|
1549
|
+
clearTimeout(timer);
|
|
1515
1550
|
if (mountRef.current) {
|
|
1516
1551
|
setBusy(void 0);
|
|
1517
1552
|
setTesting(void 0);
|
|
@@ -1524,12 +1559,24 @@ window.__ModuleLoader__.load({
|
|
|
1524
1559
|
setNow(Date.now());
|
|
1525
1560
|
const targetLabel = targetModelId === "" ? t("row.poolTargetAutoOption") : models.find((m) => m.id === targetModelId)?.name ?? targetModelId;
|
|
1526
1561
|
pushLog("info", t("row.poolLogRowTesting", { accountName: account.accountName }));
|
|
1562
|
+
const controller = new AbortController();
|
|
1563
|
+
const timer = setTimeout(() => {
|
|
1564
|
+
controller.abort();
|
|
1565
|
+
}, TEST_TIMEOUT_MS);
|
|
1527
1566
|
try {
|
|
1528
|
-
|
|
1567
|
+
const response = await fetch(withTraePoolAccount(panel, "test", account.accountId), {
|
|
1568
|
+
method: "POST",
|
|
1569
|
+
signal: controller.signal
|
|
1570
|
+
});
|
|
1571
|
+
let sawAny = false;
|
|
1572
|
+
const logged = /* @__PURE__ */ new Set();
|
|
1573
|
+
for await (const line of testLinesOf(response)) if (applyTestLine(line, t, (r) => {
|
|
1529
1574
|
setRows((prev) => ({
|
|
1530
1575
|
...prev,
|
|
1531
1576
|
[r.accountId]: r
|
|
1532
1577
|
}));
|
|
1578
|
+
if (logged.has(r.accountId)) return;
|
|
1579
|
+
logged.add(r.accountId);
|
|
1533
1580
|
pushLog(outcomeTone(r.outcome), t("row.poolLogTestRow", {
|
|
1534
1581
|
accountName: r.accountName,
|
|
1535
1582
|
outcome: outcomeLabel(r.outcome, t),
|
|
@@ -1537,43 +1584,136 @@ window.__ModuleLoader__.load({
|
|
|
1537
1584
|
}));
|
|
1538
1585
|
}, (errorText) => {
|
|
1539
1586
|
pushLog("error", t("row.poolLogBatchFailed", { message: errorText }));
|
|
1540
|
-
}))
|
|
1587
|
+
})) sawAny = true;
|
|
1588
|
+
if (!sawAny) throw new Error(t("row.poolLogGeneric"));
|
|
1541
1589
|
onRefresh?.();
|
|
1542
1590
|
setNow(Date.now());
|
|
1543
1591
|
} catch (error) {
|
|
1544
|
-
pushLog("error", t("row.poolLogBatchFailed", { message: error instanceof Error ? error.message : String(error) }));
|
|
1592
|
+
pushLog("error", t(controller.signal.aborted ? "row.poolLogTimeout" : "row.poolLogBatchFailed", { message: error instanceof Error ? error.message : String(error) }));
|
|
1545
1593
|
} finally {
|
|
1594
|
+
clearTimeout(timer);
|
|
1546
1595
|
if (mountRef.current) setBusy(void 0);
|
|
1547
1596
|
}
|
|
1548
1597
|
};
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1598
|
+
/**
|
|
1599
|
+
* Persist the removal of ONE account out of this panel's membership.
|
|
1600
|
+
*
|
|
1601
|
+
* The slot written is the SAVED slot with ONLY `memberAccountIds` replaced —
|
|
1602
|
+
* never the draft's switch or target model, because the card's own hint
|
|
1603
|
+
* promises only the in-row actions take effect immediately
|
|
1604
|
+
* (「开关与目标模型是偏好,改完要保存」), and never the DOCUMENT's effective
|
|
1605
|
+
* target model either: that falls back to the free model when the user left
|
|
1606
|
+
* the choice automatic, so writing it back would pin a model they never chose.
|
|
1607
|
+
* The membership written is the DRAFT minus the removed id, so a checkbox the
|
|
1608
|
+
* user already toggled on another row is not silently discarded by the
|
|
1609
|
+
* settings re-read this write triggers.
|
|
1610
|
+
*/
|
|
1611
|
+
const persistRemoval = async (accountId) => {
|
|
1612
|
+
const scope = settingsScope;
|
|
1613
|
+
if (scope === void 0) throw new Error(t("row.poolLogSaveFailed"));
|
|
1614
|
+
const base = regionPoolOf(scope.getSnapshot().value, panel);
|
|
1615
|
+
const next = {
|
|
1616
|
+
...base,
|
|
1617
|
+
memberAccountIds: [...selected].filter((id) => id !== accountId)
|
|
1618
|
+
};
|
|
1619
|
+
await writeSettingsField(scope, "regions", nextRegionPool(scope.getSnapshot().value, panel, next), (readBack) => {
|
|
1620
|
+
const readPool = regionPoolOf(readBack, panel);
|
|
1621
|
+
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);
|
|
1622
|
+
});
|
|
1623
|
+
};
|
|
1624
|
+
/** Product names for the desktop apps a delete touched or was blocked by. */
|
|
1625
|
+
const appLabels = (editions) => editions.map((edition) => t(`row.edition${edition === "cn" ? "Cn" : edition === "sg" ? "Sg" : edition === "solo" ? "Solo" : "SoloSg"}`)).join("、");
|
|
1626
|
+
/**
|
|
1627
|
+
* Delete an account from this machine: drop the plugin's own credential copy,
|
|
1628
|
+
* then erase the account's login from Trae's `storage.json`, then clear every
|
|
1629
|
+
* account-holder this card holds (pool probe, settings membership, draft
|
|
1630
|
+
* selection, the serving binding it may have held).
|
|
1631
|
+
*
|
|
1632
|
+
* The app half is what makes the row actually disappear. It has one failure
|
|
1633
|
+
* mode worth its own message: a RUNNING Trae flushes its in-memory state back
|
|
1634
|
+
* over the file, so the Host refuses and names the app, and the card says to
|
|
1635
|
+
* quit it instead of reporting a generic failure the user cannot act on.
|
|
1636
|
+
*/
|
|
1637
|
+
const deleteAccount = async (account) => {
|
|
1638
|
+
if (busy !== void 0 || deletingIds.has(account.accountId)) return;
|
|
1639
|
+
setBusy("delete");
|
|
1640
|
+
setDeletingIds((prev) => new Set(prev).add(account.accountId));
|
|
1641
|
+
setDeleteHint(void 0);
|
|
1556
1642
|
try {
|
|
1557
|
-
const response = await fetch(withTraePoolAccount(panel, "
|
|
1643
|
+
const response = await fetch(withTraePoolAccount(panel, "delete", account.accountId), { method: "POST" });
|
|
1558
1644
|
const body = await response.json();
|
|
1559
1645
|
if (!response.ok) throw new Error(renderRouteError(t, response.status, body));
|
|
1560
|
-
|
|
1561
|
-
if (
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1646
|
+
const blocked = body?.blockedApps ?? [];
|
|
1647
|
+
if (blocked.length > 0 && body?.deleted !== true) {
|
|
1648
|
+
setDeleteHint({
|
|
1649
|
+
kind: "app-running",
|
|
1650
|
+
accountId: account.accountId,
|
|
1651
|
+
accountName: account.accountName,
|
|
1652
|
+
apps: blocked
|
|
1653
|
+
});
|
|
1654
|
+
pushLog("warn", t("row.poolRowDeleteAppRunning", {
|
|
1655
|
+
accountName: account.accountName,
|
|
1656
|
+
apps: appLabels(blocked)
|
|
1657
|
+
}));
|
|
1658
|
+
setNow(Date.now());
|
|
1659
|
+
return;
|
|
1660
|
+
}
|
|
1661
|
+
if (body?.deleted !== true) throw new Error(t("row.poolRowDeleteFailed"));
|
|
1662
|
+
const wasMember = (saved.memberAccountIds ?? []).includes(account.accountId);
|
|
1663
|
+
let saveFailed = false;
|
|
1664
|
+
if (wasMember) {
|
|
1665
|
+
if (!writable) {
|
|
1666
|
+
saveFailed = true;
|
|
1667
|
+
setSaveError(t("row.poolSaveFailed", { message: t("row.poolLogSaveFailed") }));
|
|
1668
|
+
pushLog("error", t("row.poolLogSaveFailed"));
|
|
1669
|
+
} else try {
|
|
1670
|
+
await persistRemoval(account.accountId);
|
|
1671
|
+
} catch (error) {
|
|
1672
|
+
saveFailed = true;
|
|
1673
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1674
|
+
setSaveError(t("row.poolSaveFailed", { message }));
|
|
1675
|
+
pushLog("error", t("row.poolLogSaveFailed"));
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1566
1678
|
setSelected((prev) => {
|
|
1567
1679
|
const next = new Set(prev);
|
|
1568
1680
|
next.delete(account.accountId);
|
|
1569
1681
|
return next;
|
|
1570
1682
|
});
|
|
1571
|
-
|
|
1683
|
+
if (!saveFailed) {
|
|
1684
|
+
if (body.stillSignedIn === true) {
|
|
1685
|
+
const hadCopy = body.hadCopy !== false;
|
|
1686
|
+
setDeleteHint({
|
|
1687
|
+
kind: "still-signed-in",
|
|
1688
|
+
accountId: account.accountId,
|
|
1689
|
+
accountName: account.accountName,
|
|
1690
|
+
hadCopy
|
|
1691
|
+
});
|
|
1692
|
+
pushLog("warn", t(hadCopy ? "row.poolRowDeleteStillSigned" : "row.poolRowDeleteNoCopy", { accountName: account.accountName }));
|
|
1693
|
+
} else {
|
|
1694
|
+
const signedOut = body.signedOutApps ?? [];
|
|
1695
|
+
pushLog("ok", signedOut.length === 0 ? t("row.poolRowDeleted", { accountName: account.accountName }) : t("row.poolRowDeletedSignedOut", {
|
|
1696
|
+
accountName: account.accountName,
|
|
1697
|
+
apps: appLabels(signedOut)
|
|
1698
|
+
}));
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1572
1701
|
setNow(Date.now());
|
|
1702
|
+
if (!saveFailed) await onRefresh?.();
|
|
1573
1703
|
} catch (error) {
|
|
1574
|
-
pushLog("error", t("row.
|
|
1704
|
+
pushLog("error", t("row.poolRowDeleteError", {
|
|
1705
|
+
accountName: account.accountName,
|
|
1706
|
+
message: error instanceof Error ? error.message : String(error)
|
|
1707
|
+
}));
|
|
1575
1708
|
} finally {
|
|
1576
|
-
if (mountRef.current)
|
|
1709
|
+
if (mountRef.current) {
|
|
1710
|
+
setBusy(void 0);
|
|
1711
|
+
setDeletingIds((prev) => {
|
|
1712
|
+
const next = new Set(prev);
|
|
1713
|
+
next.delete(account.accountId);
|
|
1714
|
+
return next;
|
|
1715
|
+
});
|
|
1716
|
+
}
|
|
1577
1717
|
}
|
|
1578
1718
|
};
|
|
1579
1719
|
const noCandidate = pool.noCandidate;
|
|
@@ -1585,7 +1725,6 @@ window.__ModuleLoader__.load({
|
|
|
1585
1725
|
*/
|
|
1586
1726
|
const actionNotes = [];
|
|
1587
1727
|
if (selected.size === 0) actionNotes.push(t("row.poolActionNoteNoMembers"));
|
|
1588
|
-
if (deviceClaimed) actionNotes.push(t("row.poolActionNoteCheckinDevice"));
|
|
1589
1728
|
if (pool.targetModelSource === "none") actionNotes.push(t("row.poolActionNoteTestNone"));
|
|
1590
1729
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
1591
1730
|
className: "dsm-trae-pool",
|
|
@@ -1620,16 +1759,6 @@ window.__ModuleLoader__.load({
|
|
|
1620
1759
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1621
1760
|
className: "dsm-trae-pool-actions",
|
|
1622
1761
|
children: [
|
|
1623
|
-
pool.checkinSupported === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1624
|
-
type: "button",
|
|
1625
|
-
className: "dsm-btn dsm-btn-outline dsm-trae-pool-action-checkin",
|
|
1626
|
-
disabled: busy !== void 0 || !writable || selected.size === 0 || deviceClaimed,
|
|
1627
|
-
title: deviceClaimed ? t("row.checkinDeviceSpent") : void 0,
|
|
1628
|
-
onClick: () => {
|
|
1629
|
-
runCheckin();
|
|
1630
|
-
},
|
|
1631
|
-
children: busy === "checkin" ? t("row.poolCheckingIn") : t("row.poolCheckinAll")
|
|
1632
|
-
}) : null,
|
|
1633
1762
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1634
1763
|
type: "button",
|
|
1635
1764
|
className: "dsm-btn dsm-btn-outline dsm-trae-pool-action-test",
|
|
@@ -1640,7 +1769,7 @@ window.__ModuleLoader__.load({
|
|
|
1640
1769
|
},
|
|
1641
1770
|
children: busy === "test" ? t("row.poolTesting") : t("row.poolTestAll")
|
|
1642
1771
|
}),
|
|
1643
|
-
busy === "
|
|
1772
|
+
busy === "test" || testing !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1644
1773
|
className: "dsm-trae-pool-busy",
|
|
1645
1774
|
role: "status",
|
|
1646
1775
|
children: t("row.poolBusyHint")
|
|
@@ -1710,6 +1839,7 @@ window.__ModuleLoader__.load({
|
|
|
1710
1839
|
models,
|
|
1711
1840
|
rows,
|
|
1712
1841
|
checked: selected.has(account.accountId),
|
|
1842
|
+
poolEnabled: pool.enabled === true,
|
|
1713
1843
|
detail: details?.[account.accountId],
|
|
1714
1844
|
claim: claims?.[account.accountId],
|
|
1715
1845
|
onClaim,
|
|
@@ -1721,10 +1851,11 @@ window.__ModuleLoader__.load({
|
|
|
1721
1851
|
},
|
|
1722
1852
|
t,
|
|
1723
1853
|
disabled: busy !== void 0 || !writable,
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1854
|
+
deleting: deletingIds.has(account.accountId),
|
|
1855
|
+
onDelete: () => {
|
|
1856
|
+
deleteAccount(account);
|
|
1727
1857
|
},
|
|
1858
|
+
canDelete: pool.deleteSupported === true,
|
|
1728
1859
|
formatNumber: formatNumber$1,
|
|
1729
1860
|
relativeAt
|
|
1730
1861
|
}, account.accountId)),
|
|
@@ -1737,10 +1868,11 @@ window.__ModuleLoader__.load({
|
|
|
1737
1868
|
},
|
|
1738
1869
|
t,
|
|
1739
1870
|
disabled: busy !== void 0 || !writable,
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
}
|
|
1871
|
+
deleting: deletingIds.has(ghost.accountId),
|
|
1872
|
+
onDelete: () => {
|
|
1873
|
+
deleteAccount(ghost);
|
|
1874
|
+
},
|
|
1875
|
+
canDelete: pool.deleteSupported === true
|
|
1744
1876
|
}, `ghost:${ghost.accountId}`))
|
|
1745
1877
|
]
|
|
1746
1878
|
}),
|
|
@@ -1752,6 +1884,19 @@ window.__ModuleLoader__.load({
|
|
|
1752
1884
|
names: ghosts.map((g) => g.accountName).join("、")
|
|
1753
1885
|
})
|
|
1754
1886
|
}) : null,
|
|
1887
|
+
deleteHint?.kind === "still-signed-in" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1888
|
+
className: "dsm-trae-pool-delete-hint",
|
|
1889
|
+
role: "status",
|
|
1890
|
+
children: t(deleteHint.hadCopy ? "row.poolDeleteStillSigned" : "row.poolDeleteNoCopy", { accountName: deleteHint.accountName })
|
|
1891
|
+
}) : null,
|
|
1892
|
+
deleteHint?.kind === "app-running" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1893
|
+
className: "dsm-trae-pool-delete-hint dsm-trae-pool-delete-hint-action",
|
|
1894
|
+
role: "status",
|
|
1895
|
+
children: t("row.poolDeleteAppRunning", {
|
|
1896
|
+
accountName: deleteHint.accountName,
|
|
1897
|
+
apps: appLabels(deleteHint.apps)
|
|
1898
|
+
})
|
|
1899
|
+
}) : null,
|
|
1755
1900
|
dirty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1756
1901
|
className: "dsm-trae-pool-unsaved",
|
|
1757
1902
|
role: "status",
|
|
@@ -1777,10 +1922,33 @@ window.__ModuleLoader__.load({
|
|
|
1777
1922
|
className: "dsm-trae-pool-tools dsm-trae-pool-tools-dashed",
|
|
1778
1923
|
children: tools
|
|
1779
1924
|
}),
|
|
1925
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1926
|
+
className: "dsm-trae-pool-warning dsm-trae-pool-warning-workflow",
|
|
1927
|
+
role: "note",
|
|
1928
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1929
|
+
className: "dsm-trae-pool-warning-title",
|
|
1930
|
+
children: t("row.poolMirrorWorkflowTitle")
|
|
1931
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1932
|
+
className: "dsm-trae-pool-warning-hint",
|
|
1933
|
+
children: t("row.poolMirrorWorkflowHint")
|
|
1934
|
+
})]
|
|
1935
|
+
}),
|
|
1936
|
+
pool.copies === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1937
|
+
className: "dsm-trae-pool-note dsm-trae-pool-copyfile",
|
|
1938
|
+
role: "note",
|
|
1939
|
+
children: t("row.poolCopyFile", {
|
|
1940
|
+
path: pool.copies.path,
|
|
1941
|
+
count: pool.copies.count
|
|
1942
|
+
})
|
|
1943
|
+
}),
|
|
1780
1944
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1781
1945
|
className: "dsm-trae-pool-note",
|
|
1782
1946
|
children: t("row.poolSettingsNote")
|
|
1783
1947
|
}),
|
|
1948
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1949
|
+
className: "dsm-trae-pool-note",
|
|
1950
|
+
children: t("row.poolDeleteHint")
|
|
1951
|
+
}),
|
|
1784
1952
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1785
1953
|
className: "dsm-trae-pool-settings",
|
|
1786
1954
|
children: [
|
|
@@ -1981,8 +2149,18 @@ window.__ModuleLoader__.load({
|
|
|
1981
2149
|
]
|
|
1982
2150
|
});
|
|
1983
2151
|
}
|
|
1984
|
-
function MemberRow({ account, models, rows, checked, onToggle, onTest, t, disabled,
|
|
1985
|
-
|
|
2152
|
+
function MemberRow({ account, models, rows, checked, onToggle, onTest, t, disabled, deleting, onDelete, canDelete, formatNumber, relativeAt, detail, claim, onClaim, showCheckin, deviceClaimed, poolEnabled }) {
|
|
2153
|
+
/**
|
|
2154
|
+
* Whether this row is the one billing the next request — and, with the pool on,
|
|
2155
|
+
* whether that claim is the pool's own.
|
|
2156
|
+
*
|
|
2157
|
+
* `account.current` alone is not enough: with an empty (or fully excluded) pool
|
|
2158
|
+
* the Host deliberately falls back to the saved selection, so a removed account
|
|
2159
|
+
* stays `current` and the pill would keep announcing it as the pool's pick.
|
|
2160
|
+
* A row can only be the rotation's answer while it is a member.
|
|
2161
|
+
*/
|
|
2162
|
+
const isCurrent = account.current === true && (poolEnabled !== true || account.member === true);
|
|
2163
|
+
const hasCopy = account.reachable;
|
|
1986
2164
|
const credit = account.generalAvailable;
|
|
1987
2165
|
const excluded = account.excludedBy;
|
|
1988
2166
|
const checkin = account.checkedInToday;
|
|
@@ -2019,10 +2197,10 @@ window.__ModuleLoader__.load({
|
|
|
2019
2197
|
className: "dsm-trae-pool-account-name",
|
|
2020
2198
|
children: [
|
|
2021
2199
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2022
|
-
className: `dsm-trae-pool-dot dsm-trae-pool-dot-${excluded ? "excluded" : "ok"}`,
|
|
2200
|
+
className: `dsm-trae-pool-dot dsm-trae-pool-dot-${account.reachable === false ? "ghost" : excluded ? "excluded" : "ok"}`,
|
|
2023
2201
|
"aria-hidden": "true"
|
|
2024
2202
|
}),
|
|
2025
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: account.accountName }),
|
|
2203
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: account.accountName !== "" ? account.accountName : account.accountId }),
|
|
2026
2204
|
detail?.meta === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2027
2205
|
className: "dsm-trae-pool-meta",
|
|
2028
2206
|
children: detail.meta
|
|
@@ -2045,6 +2223,11 @@ window.__ModuleLoader__.load({
|
|
|
2045
2223
|
className: checked ? "dsm-trae-pool-member" : "dsm-trae-pool-not-member",
|
|
2046
2224
|
children: checked ? t("row.poolMember") : t("row.poolNotMember")
|
|
2047
2225
|
}),
|
|
2226
|
+
account.reachable === false ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2227
|
+
className: "dsm-trae-pool-tag dsm-trae-pool-tag-ghost",
|
|
2228
|
+
role: "status",
|
|
2229
|
+
children: t("row.poolNoCopyTag")
|
|
2230
|
+
}) : null,
|
|
2048
2231
|
excluded && !probeOnScreen ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2049
2232
|
className: "dsm-trae-pool-tag dsm-trae-pool-tag-excluded",
|
|
2050
2233
|
children: exclusionLabel(excluded, t)
|
|
@@ -2118,22 +2301,23 @@ window.__ModuleLoader__.load({
|
|
|
2118
2301
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2119
2302
|
type: "button",
|
|
2120
2303
|
className: "dsm-btn dsm-btn-outline",
|
|
2121
|
-
disabled,
|
|
2304
|
+
disabled: disabled || hasCopy === false,
|
|
2305
|
+
title: hasCopy === false ? t("row.poolNoCopyTestHint") : void 0,
|
|
2122
2306
|
onClick: onTest,
|
|
2123
2307
|
children: t("row.poolRowTest")
|
|
2124
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2308
|
+
}), canDelete ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2125
2309
|
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
|
-
})]
|
|
2310
|
+
className: "dsm-btn dsm-btn-outline-danger dsm-trae-pool-delete",
|
|
2311
|
+
disabled: disabled || deleting,
|
|
2312
|
+
"aria-label": t("row.poolRowDeleteAria", { accountName: account.accountName }),
|
|
2313
|
+
onClick: onDelete,
|
|
2314
|
+
children: deleting ? t("row.poolRowDeleting") : t("row.poolRowDelete")
|
|
2315
|
+
}) : null]
|
|
2132
2316
|
})
|
|
2133
2317
|
]
|
|
2134
2318
|
});
|
|
2135
2319
|
}
|
|
2136
|
-
function MemberGhostRow({ ghost, checked, onToggle, t, disabled,
|
|
2320
|
+
function MemberGhostRow({ ghost, checked, onToggle, t, disabled, deleting, onDelete, canDelete, showCheckin }) {
|
|
2137
2321
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2138
2322
|
className: "dsm-trae-pool-row dsm-trae-pool-row-ghost",
|
|
2139
2323
|
role: "row",
|
|
@@ -2194,14 +2378,14 @@ window.__ModuleLoader__.load({
|
|
|
2194
2378
|
}) : null,
|
|
2195
2379
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2196
2380
|
className: "dsm-trae-pool-row-actions",
|
|
2197
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2381
|
+
children: canDelete ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2198
2382
|
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
|
-
})
|
|
2383
|
+
className: "dsm-btn dsm-btn-outline-danger dsm-trae-pool-delete",
|
|
2384
|
+
disabled: disabled || deleting,
|
|
2385
|
+
"aria-label": t("row.poolGhostDeleteAria", { accountName: ghost.accountName }),
|
|
2386
|
+
onClick: onDelete,
|
|
2387
|
+
children: deleting ? t("row.poolRowDeleting") : t("row.poolRowDelete")
|
|
2388
|
+
}) : null
|
|
2205
2389
|
})
|
|
2206
2390
|
]
|
|
2207
2391
|
});
|
|
@@ -5266,22 +5450,22 @@ window.__ModuleLoader__.load({
|
|
|
5266
5450
|
"row.refreshCredits": "Refresh usage",
|
|
5267
5451
|
"row.refreshing": "Refreshing…",
|
|
5268
5452
|
"row.accountsTitle": "Trae account",
|
|
5269
|
-
"row.accountsHint": "
|
|
5453
|
+
"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
5454
|
"row.transferTitle": "Import & export accounts",
|
|
5271
5455
|
"row.transferExport": "Export selected",
|
|
5272
5456
|
"row.transferImport": "Import from file",
|
|
5273
5457
|
"row.transferCancel": "Cancel",
|
|
5274
5458
|
"row.transferSelectAll": "Select all",
|
|
5275
|
-
"row.transferExportPick": "Choose which of the {total}
|
|
5459
|
+
"row.transferExportPick": "Choose which of the {total} account copies to export.",
|
|
5276
5460
|
"row.transferExportConfirm": "Export {count}",
|
|
5277
|
-
"row.transferExported": "Exported {count} account
|
|
5461
|
+
"row.transferExported": "Exported {count} account copy/copies to {fileName}.",
|
|
5278
5462
|
"row.transferExporting": "Preparing the export…",
|
|
5279
|
-
"row.transferImportPick": "This file holds {total} record(s).
|
|
5463
|
+
"row.transferImportPick": "This file holds {total} record(s). Ticked records are added as plugin-owned copies; the Trae app is not touched.",
|
|
5280
5464
|
"row.transferImportConfirm": "Import {count}",
|
|
5281
|
-
"row.transferImported": "Imported {imported} account
|
|
5465
|
+
"row.transferImported": "Imported {imported} account copy/copies, skipped {skipped}.",
|
|
5282
5466
|
"row.transferImporting": "Importing…",
|
|
5283
5467
|
"row.transferReading": "Reading the file…",
|
|
5284
|
-
"row.transferHint": "The exported file
|
|
5468
|
+
"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
5469
|
"row.transferError": "Import/export failed: {message}",
|
|
5286
5470
|
"row.transferVeilHint": "The account list refreshes when this finishes.",
|
|
5287
5471
|
"row.transferUnnamed": "Unnamed account",
|
|
@@ -5382,7 +5566,6 @@ window.__ModuleLoader__.load({
|
|
|
5382
5566
|
"row.poolCurrentBadge": "serving",
|
|
5383
5567
|
"row.poolMember": "in pool",
|
|
5384
5568
|
"row.poolNotMember": "not in pool",
|
|
5385
|
-
"row.poolCheckinAll": "Check in all accounts",
|
|
5386
5569
|
"row.poolBusyHint": "Working through accounts one at a time…",
|
|
5387
5570
|
"row.poolTestAll": "Test all accounts",
|
|
5388
5571
|
"row.poolTesting": "Testing…",
|
|
@@ -5398,8 +5581,13 @@ window.__ModuleLoader__.load({
|
|
|
5398
5581
|
"row.poolCreditExpiry": "expires {when}",
|
|
5399
5582
|
"row.poolProbeMeasured": "measured {when}",
|
|
5400
5583
|
"row.poolProbeAt": "tested {when}",
|
|
5584
|
+
"row.poolMirrorWorkflowTitle": "Sign in one account, then refresh",
|
|
5585
|
+
"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
5586
|
"row.poolSettingsNote": "Preferences below take effect when you save; test results are written by the plugin and are not affected by that button.",
|
|
5402
5587
|
"row.poolSelectedCount": "Selected {count} / {total}",
|
|
5588
|
+
"row.poolNoCopyTag": "no copy here",
|
|
5589
|
+
"row.poolCopyFile": "Copies live in {path} — {count} right now. Switching accounts in Trae adds a copy; it never overwrites an existing one.",
|
|
5590
|
+
"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
5591
|
"row.poolSelectAll": "All",
|
|
5404
5592
|
"row.poolSelectNone": "None",
|
|
5405
5593
|
"row.poolColumnAccount": "Account",
|
|
@@ -5420,7 +5608,6 @@ window.__ModuleLoader__.load({
|
|
|
5420
5608
|
"row.poolTargetStaleClear": "Back to automatic",
|
|
5421
5609
|
"row.poolTargetNone": "No free model available for this panel",
|
|
5422
5610
|
"row.poolTargetNoneHint": "The pool can only pick a free model; none resolves right now, so tests are disabled.",
|
|
5423
|
-
"row.poolActionNoteCheckinDevice": "This machine already used today's check-in (Trae allows one per device per day) — check-in is unavailable until tomorrow.",
|
|
5424
5611
|
"row.poolActionNoteTestNone": "No free model resolves in this panel — pick a target model before testing.",
|
|
5425
5612
|
"row.poolActionNoteNoMembers": "Tick the accounts you want in the pool and save first.",
|
|
5426
5613
|
"row.poolTargetAutoOption": "Automatic (free model)",
|
|
@@ -5440,27 +5627,17 @@ window.__ModuleLoader__.load({
|
|
|
5440
5627
|
"row.poolDiscard": "Discard",
|
|
5441
5628
|
"row.poolSaveFailed": "Save failed: {message}",
|
|
5442
5629
|
"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
5630
|
"row.poolUnsavedMembers": "Unsaved membership changes — Save below to apply.",
|
|
5446
5631
|
"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
5632
|
"row.poolLogTitle": "Activity",
|
|
5452
5633
|
"row.poolLogClear": "Clear",
|
|
5453
5634
|
"row.poolLogEmpty": "No pool activity yet.",
|
|
5454
|
-
"row.poolLogCheckinStart": "Check-in started for {count} account(s).",
|
|
5455
5635
|
"row.poolLogTestStart": "Test started for {count} account(s) against {model}.",
|
|
5456
|
-
"row.poolLogCheckinClaimed": "{accountName} checked in.",
|
|
5457
|
-
"row.poolLogCheckinAlready": "{accountName} already checked in today.",
|
|
5458
|
-
"row.poolLogCheckinFailed": "{accountName} check-in failed: {message}",
|
|
5459
|
-
"row.poolLogCheckinDone": "Check-in finished.",
|
|
5460
5636
|
"row.poolLogTestRow": "{accountName}: {outcome}.",
|
|
5461
5637
|
"row.poolLogTestRowMalformed": "{accountName}: malformed result.",
|
|
5462
5638
|
"row.poolLogTestDone": "Test finished.",
|
|
5463
5639
|
"row.poolLogBatchFailed": "Pool action failed: {message}",
|
|
5640
|
+
"row.poolLogTimeout": "The test did not answer in time — it was cancelled, so the button works again. Check the network and retry: {message}",
|
|
5464
5641
|
"row.poolSelectAria": "Include {accountName} in the pool",
|
|
5465
5642
|
"row.poolCheckedIn": "Checked in",
|
|
5466
5643
|
"row.poolNotCheckedIn": "Not checked in",
|
|
@@ -5508,9 +5685,7 @@ window.__ModuleLoader__.load({
|
|
|
5508
5685
|
"row.poolErrNoFreeModel": "no free model is available for this panel",
|
|
5509
5686
|
"row.poolErrConflict": "the pool refused the request",
|
|
5510
5687
|
"row.poolErrGeneric": "pool request failed (HTTP {status})",
|
|
5511
|
-
"row.
|
|
5512
|
-
"row.poolSettingsHint": "The switch and the target model are preferences — save them. Test, check in and remove act at once.",
|
|
5513
|
-
"row.poolGhostRemoveAria": "Remove the saved but unreachable account {accountName} from this panel",
|
|
5688
|
+
"row.poolSettingsHint": "The switch and the target model are preferences — save them. Test, check in and delete act at once.",
|
|
5514
5689
|
"row.poolLogSaved": "Pool settings saved.",
|
|
5515
5690
|
"row.poolLogSaveFailed": "Could not save the pool settings.",
|
|
5516
5691
|
"row.poolLogNoMembers": "Nothing to do — no pool members are selected.",
|
|
@@ -5518,8 +5693,21 @@ window.__ModuleLoader__.load({
|
|
|
5518
5693
|
"row.poolLogGeneric": "the pool answered nothing usable",
|
|
5519
5694
|
"row.poolLogRowTesting": "Testing {accountName}…",
|
|
5520
5695
|
"row.poolLogTargetCleared": "Returned the test target to automatic.",
|
|
5521
|
-
"row.
|
|
5522
|
-
"row.
|
|
5696
|
+
"row.poolRowDelete": "Delete",
|
|
5697
|
+
"row.poolRowDeleteAria": "Delete {accountName} from this machine: the plugin's copy and its Trae app login (this will sign it out of Trae)",
|
|
5698
|
+
"row.poolRowDeleting": "Deleting…",
|
|
5699
|
+
"row.poolGhostDeleteAria": "Delete the saved but unreachable account {accountName} from this machine",
|
|
5700
|
+
"row.poolRowDeleteError": "Could not delete {accountName}: {message}",
|
|
5701
|
+
"row.poolRowDeleteFailed": "the deletion was not confirmed",
|
|
5702
|
+
"row.poolRowDeleted": "Deleted the credential copy of {accountName}.",
|
|
5703
|
+
"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.",
|
|
5704
|
+
"row.poolRowDeletedSignedOut": "Deleted {accountName} and signed it out of {apps}.",
|
|
5705
|
+
"row.poolRowDeleteAppRunning": "Could not delete {accountName}: {apps} is running and would restore the login. Quit {apps} completely, then delete again.",
|
|
5706
|
+
"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.",
|
|
5707
|
+
"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.",
|
|
5708
|
+
"row.poolRowDeleteStillSigned": "removed this copy, but {accountName} is still signed in to Trae",
|
|
5709
|
+
"row.poolRowDeleteNoCopy": "{accountName} had no plugin copy — only its Trae app login",
|
|
5710
|
+
"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
5711
|
"row.cheer": "Star on GitHub"
|
|
5524
5712
|
};
|
|
5525
5713
|
const zh = {
|
|
@@ -5583,22 +5771,22 @@ window.__ModuleLoader__.load({
|
|
|
5583
5771
|
"row.refreshCredits": "刷新用量",
|
|
5584
5772
|
"row.refreshing": "正在刷新…",
|
|
5585
5773
|
"row.accountsTitle": "Trae 账号",
|
|
5586
|
-
"row.accountsHint": "
|
|
5774
|
+
"row.accountsHint": "本插件保管的账号副本:本机每个 Trae 登录都会在刷新时补建一份副本。登录新号后要刷新一次,否则它会被 Trae 的下一次登录覆盖。Token 不会显示。",
|
|
5587
5775
|
"row.transferTitle": "导入 / 导出账号",
|
|
5588
5776
|
"row.transferExport": "导出所选",
|
|
5589
5777
|
"row.transferImport": "从文件导入",
|
|
5590
5778
|
"row.transferCancel": "取消",
|
|
5591
5779
|
"row.transferSelectAll": "全选",
|
|
5592
|
-
"row.transferExportPick": "从这 {total}
|
|
5780
|
+
"row.transferExportPick": "从这 {total} 个账号副本中选择要导出的。",
|
|
5593
5781
|
"row.transferExportConfirm": "导出 {count} 个",
|
|
5594
|
-
"row.transferExported": "已导出 {count}
|
|
5782
|
+
"row.transferExported": "已导出 {count} 个账号副本到 {fileName}。",
|
|
5595
5783
|
"row.transferExporting": "正在准备导出…",
|
|
5596
|
-
"row.transferImportPick": "该文件包含 {total}
|
|
5784
|
+
"row.transferImportPick": "该文件包含 {total} 条记录;勾选的会作为插件副本导入,不会写入 Trae 应用。",
|
|
5597
5785
|
"row.transferImportConfirm": "导入 {count} 个",
|
|
5598
|
-
"row.transferImported": "已导入 {imported}
|
|
5786
|
+
"row.transferImported": "已导入 {imported} 个账号副本,跳过 {skipped} 个。",
|
|
5599
5787
|
"row.transferImporting": "正在导入…",
|
|
5600
5788
|
"row.transferReading": "正在读取文件…",
|
|
5601
|
-
"row.transferHint": "
|
|
5789
|
+
"row.transferHint": "导出的文件里是插件保管的账号副本(含有效 Token),请当作密码保管。属于另一区域的记录会被跳过。",
|
|
5602
5790
|
"row.transferError": "导入/导出失败:{message}",
|
|
5603
5791
|
"row.transferVeilHint": "完成后账号列表会自动刷新。",
|
|
5604
5792
|
"row.transferUnnamed": "未命名账号",
|
|
@@ -5699,7 +5887,6 @@ window.__ModuleLoader__.load({
|
|
|
5699
5887
|
"row.poolCurrentBadge": "派单中",
|
|
5700
5888
|
"row.poolMember": "已在池中",
|
|
5701
5889
|
"row.poolNotMember": "未入池",
|
|
5702
|
-
"row.poolCheckinAll": "一键签到所有账号",
|
|
5703
5890
|
"row.poolBusyHint": "逐个处理账号中…",
|
|
5704
5891
|
"row.poolTestAll": "一键测试所有账号",
|
|
5705
5892
|
"row.poolTesting": "测速中…",
|
|
@@ -5715,8 +5902,13 @@ window.__ModuleLoader__.load({
|
|
|
5715
5902
|
"row.poolCreditExpiry": "{when} 过期",
|
|
5716
5903
|
"row.poolProbeMeasured": "测得 {when}",
|
|
5717
5904
|
"row.poolProbeAt": "测试于 {when}",
|
|
5905
|
+
"row.poolMirrorWorkflowTitle": "每登录一个账号,就刷新一次",
|
|
5906
|
+
"row.poolMirrorWorkflowHint": "Trae 应用只有一个登录位:每登录一个账号,就回到这里刷新一次(打开卡片或点「重新检测账号」),插件会把这次登录保存成一份自己的副本。没刷新就换号,上一个账号不会被保存——它会被 Trae 里的新登录覆盖。",
|
|
5718
5907
|
"row.poolSettingsNote": "下面是偏好设置:点保存才生效;测试结果由插件自动写入,不受这个保存按钮影响。",
|
|
5719
5908
|
"row.poolSelectedCount": "已选 {count} / {total}",
|
|
5909
|
+
"row.poolNoCopyTag": "本机无副本",
|
|
5910
|
+
"row.poolCopyFile": "副本就存在 {path},当前 {count} 份。在 Trae 里换号只会新增一份,不会覆盖已有的副本。",
|
|
5911
|
+
"row.poolNoCopyTestHint": "本机没有这个账号的登录,无法测速。需要它请在 Trae 里重新登录以重建副本,或删除这一行。",
|
|
5720
5912
|
"row.poolSelectAll": "全部",
|
|
5721
5913
|
"row.poolSelectNone": "清空",
|
|
5722
5914
|
"row.poolColumnAccount": "账号",
|
|
@@ -5737,7 +5929,6 @@ window.__ModuleLoader__.load({
|
|
|
5737
5929
|
"row.poolTargetStaleClear": "改回自动",
|
|
5738
5930
|
"row.poolTargetNone": "本面板暂无可用免费模型",
|
|
5739
5931
|
"row.poolTargetNoneHint": "账号池只会挑免费模型;当前无法解析出可用模型,测速暂被禁用。",
|
|
5740
|
-
"row.poolActionNoteCheckinDevice": "本机今日的签到已用掉(Trae 按每台设备每天一次限制),明天才能再签。",
|
|
5741
5932
|
"row.poolActionNoteTestNone": "本面板没有解析出免费模型——先指定一个目标模型再测速。",
|
|
5742
5933
|
"row.poolActionNoteNoMembers": "先勾选要入池的账号并保存。",
|
|
5743
5934
|
"row.poolTargetAutoOption": "自动(免费模型)",
|
|
@@ -5757,27 +5948,17 @@ window.__ModuleLoader__.load({
|
|
|
5757
5948
|
"row.poolDiscard": "放弃",
|
|
5758
5949
|
"row.poolSaveFailed": "保存失败:{message}",
|
|
5759
5950
|
"row.poolGhostMembers": "{count} 个已保存成员不再对应本机登录:{names}",
|
|
5760
|
-
"row.poolGhostRemove": "移除 {account}",
|
|
5761
|
-
"row.poolGhostRemoveHint": "忘记本面板对 {account} 的绑定。",
|
|
5762
5951
|
"row.poolUnsavedMembers": "成员改动尚未保存——请点击下方保存。",
|
|
5763
5952
|
"row.poolRowTest": "测速",
|
|
5764
|
-
"row.poolRowRemove": "移除",
|
|
5765
|
-
"row.poolRowRemoveAria": "从本面板移除 {accountName}",
|
|
5766
|
-
"row.poolRowRemoving": "移除中…",
|
|
5767
|
-
"row.poolRowRemoved": "已从本面板移除 {accountName}",
|
|
5768
5953
|
"row.poolLogTitle": "活动记录",
|
|
5769
5954
|
"row.poolLogClear": "清空",
|
|
5770
5955
|
"row.poolLogEmpty": "暂无账号池活动。",
|
|
5771
|
-
"row.poolLogCheckinStart": "开始为 {count} 个账号签到。",
|
|
5772
5956
|
"row.poolLogTestStart": "开始为 {count} 个账号测速({model})。",
|
|
5773
|
-
"row.poolLogCheckinClaimed": "{accountName} 已签到。",
|
|
5774
|
-
"row.poolLogCheckinAlready": "{accountName} 今日已签到。",
|
|
5775
|
-
"row.poolLogCheckinFailed": "{accountName} 签到失败:{message}",
|
|
5776
|
-
"row.poolLogCheckinDone": "签到完成。",
|
|
5777
5957
|
"row.poolLogTestRow": "{accountName}:{outcome}。",
|
|
5778
5958
|
"row.poolLogTestRowMalformed": "{accountName}:结果异常。",
|
|
5779
5959
|
"row.poolLogTestDone": "测速完成。",
|
|
5780
5960
|
"row.poolLogBatchFailed": "账号池操作失败:{message}",
|
|
5961
|
+
"row.poolLogTimeout": "测速超时,已自动取消(按钮已恢复可用)。请检查网络后重试:{message}",
|
|
5781
5962
|
"row.poolSelectAria": "把 {accountName} 加入账号池",
|
|
5782
5963
|
"row.poolCheckedIn": "已签到",
|
|
5783
5964
|
"row.poolNotCheckedIn": "未签到",
|
|
@@ -5825,9 +6006,7 @@ window.__ModuleLoader__.load({
|
|
|
5825
6006
|
"row.poolErrNoFreeModel": "本面板没有可用的免费模型",
|
|
5826
6007
|
"row.poolErrConflict": "账号池拒绝了该请求",
|
|
5827
6008
|
"row.poolErrGeneric": "账号池请求失败(HTTP {status})",
|
|
5828
|
-
"row.
|
|
5829
|
-
"row.poolSettingsHint": "开关与目标模型是偏好,改完要保存;行内的测速、签到与移除立即生效。",
|
|
5830
|
-
"row.poolGhostRemoveAria": "把已保存但本机无法登录的账号 {accountName} 移出本面板",
|
|
6009
|
+
"row.poolSettingsHint": "开关与目标模型是偏好,改完要保存;行内的测速、签到与删除立即生效。",
|
|
5831
6010
|
"row.poolLogSaved": "账号池设置已保存。",
|
|
5832
6011
|
"row.poolLogSaveFailed": "无法保存账号池设置。",
|
|
5833
6012
|
"row.poolLogNoMembers": "没有可处理的内容——尚未勾选任何池成员。",
|
|
@@ -5835,8 +6014,21 @@ window.__ModuleLoader__.load({
|
|
|
5835
6014
|
"row.poolLogGeneric": "账号池未返回可用的结果",
|
|
5836
6015
|
"row.poolLogRowTesting": "正在测速 {accountName}…",
|
|
5837
6016
|
"row.poolLogTargetCleared": "测速目标已改回自动。",
|
|
5838
|
-
"row.
|
|
5839
|
-
"row.
|
|
6017
|
+
"row.poolRowDelete": "删除",
|
|
6018
|
+
"row.poolRowDeleteAria": "从本机删除 {accountName}:插件的副本连同它在 Trae 应用里的登录(会把它从 Trae 退出登录)",
|
|
6019
|
+
"row.poolRowDeleting": "删除中…",
|
|
6020
|
+
"row.poolGhostDeleteAria": "从本机删除已保存但无法登录的账号 {accountName}",
|
|
6021
|
+
"row.poolRowDeleteError": "无法删除 {accountName}:{message}",
|
|
6022
|
+
"row.poolRowDeleteFailed": "删除未得到确认",
|
|
6023
|
+
"row.poolRowDeleted": "已删除 {accountName} 的凭据副本。",
|
|
6024
|
+
"row.poolDeleteStillSigned": "已删除本插件对 {accountName} 的副本,但它仍登录在 Trae 里,所以账号本身还在。要彻底从本机移除,请在 Trae 内退出登录。",
|
|
6025
|
+
"row.poolRowDeletedSignedOut": "已删除 {accountName},并将它从 {apps} 中退出登录。",
|
|
6026
|
+
"row.poolRowDeleteAppRunning": "无法删除 {accountName}:{apps} 正在运行,退出时会把登录写回去。请先完全退出 {apps},再重新删除。",
|
|
6027
|
+
"row.poolDeleteAppRunning": "这次没有改动任何文件:{apps} 正在运行,退出时会把自己的状态回写到文件、覆盖这次修改。请先完全退出 {apps},再点「删除」。",
|
|
6028
|
+
"row.poolDeleteNoCopy": "{accountName} 本来就没有插件副本可删——它只存在于 Trae 应用登录里,而这次退出登录没有生效。",
|
|
6029
|
+
"row.poolRowDeleteStillSigned": "已删除副本,但 {accountName} 仍登录在 Trae 中",
|
|
6030
|
+
"row.poolRowDeleteNoCopy": "{accountName} 没有插件副本——它只存在于 Trae 应用登录里",
|
|
6031
|
+
"row.poolDeleteHint": "本插件会为本机每个 Trae 登录各创建一份自己的副本(刷新时自动创建,已有副本则不动),卡片只显示、也只使用这些副本。「删除」会同时删掉副本并把该账号从 Trae 应用中退出登录,改写的每个文件都留一份备份;只要该账号还登录在 Trae 里,就不会被重新收录。Trae 正在运行时会把自己的状态回写到文件,所以要先完全退出——卡片会点名是哪个应用。",
|
|
5840
6032
|
"row.cheer": "鼓励一下"
|
|
5841
6033
|
};
|
|
5842
6034
|
//#endregion
|