dsh-connect-trae 2.0.6 → 2.2.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 +62 -0
- package/README.en.md +25 -3
- package/README.md +25 -3
- package/lib/client.js +293 -38
- package/lib/index.d.ts +147 -5
- package/lib/index.js +225 -41
- package/package.json +3 -3
package/lib/client.js
CHANGED
|
@@ -5,7 +5,6 @@ window.__ModuleLoader__.load({
|
|
|
5
5
|
var exports = module.exports;
|
|
6
6
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
7
|
let react = require("react");
|
|
8
|
-
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
9
8
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
10
9
|
//#region src/status-paths.ts
|
|
11
10
|
/** Plugin-owned usage endpoint consumed by its browser half. */
|
|
@@ -14,6 +13,11 @@ window.__ModuleLoader__.load({
|
|
|
14
13
|
const TRAE_MODELS_REFRESH_PATH = "/plugins/dsh-connect-trae/models/refresh";
|
|
15
14
|
/** Plugin-owned local account rescan endpoint. */
|
|
16
15
|
const TRAE_ACCOUNTS_REFRESH_PATH = "/plugins/dsh-connect-trae/accounts/refresh";
|
|
16
|
+
/**
|
|
17
|
+
* Plugin-owned daily check-in claim endpoint. POST, loopback-only, and the only
|
|
18
|
+
* route in this plugin that changes upstream account state.
|
|
19
|
+
*/
|
|
20
|
+
const TRAE_CHECKIN_PATH = "/plugins/dsh-connect-trae/checkin";
|
|
17
21
|
/** Query parameter naming the region a card request addresses. */
|
|
18
22
|
const TRAE_REGION_PARAM = "region";
|
|
19
23
|
/** Every region, in card tab order. */
|
|
@@ -26,6 +30,42 @@ window.__ModuleLoader__.load({
|
|
|
26
30
|
return `${path}?${TRAE_REGION_PARAM}=${region}`;
|
|
27
31
|
}
|
|
28
32
|
/**
|
|
33
|
+
* Peel ONE `{get(): T}` live reference, the shape DSH 0.1.7 delivers a
|
|
34
|
+
* volatile-marked settings field as (see {@link unwrapVolatileDeep}).
|
|
35
|
+
*/
|
|
36
|
+
function unwrapVolatile(value) {
|
|
37
|
+
if (value !== null && typeof value === "object" && typeof value.get === "function") return value.get();
|
|
38
|
+
return value;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Deep copy of a value with every `{get(): T}` live reference replaced by the
|
|
42
|
+
* value it resolves to.
|
|
43
|
+
*
|
|
44
|
+
* DSH 0.1.7 hands volatile-marked fields back as live references, and a live
|
|
45
|
+
* reference is still `typeof === 'object'` — so it passes a naive object check
|
|
46
|
+
* and every lookup on it is `undefined`. Worse, spreading one (`{ ...ref }`)
|
|
47
|
+
* does NOT read the field: it produces `{ get: <function> }`. Any merge that
|
|
48
|
+
* spreads a resolved field to preserve its siblings — the card's "write one
|
|
49
|
+
* region, keep the other" write, or the account map's spread — would otherwise
|
|
50
|
+
* DROP every sibling and leak a function into the document.
|
|
51
|
+
*
|
|
52
|
+
* Both halves therefore unwrap before touching a resolved field. This lives in
|
|
53
|
+
* the node-free host↔client bridge because the browser half needs it too.
|
|
54
|
+
*
|
|
55
|
+
* Non-reference values are recursed into so a nested volatile field is caught
|
|
56
|
+
* as well; arrays and objects are rebuilt rather than mutated, so the caller's
|
|
57
|
+
* value is never touched.
|
|
58
|
+
*/
|
|
59
|
+
function unwrapVolatileDeep(value) {
|
|
60
|
+
if (value === null || typeof value !== "object") return value;
|
|
61
|
+
if (typeof value.get === "function") return unwrapVolatileDeep(value.get());
|
|
62
|
+
if (Array.isArray(value)) return value.map((entry) => unwrapVolatileDeep(entry));
|
|
63
|
+
const source = value;
|
|
64
|
+
const out = {};
|
|
65
|
+
for (const key of Object.keys(source)) out[key] = unwrapVolatileDeep(source[key]);
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
29
69
|
* Build the next `regions` settings value for the card's save. The write
|
|
30
70
|
* targets ONLY the signed-in account's region slot; every other region's slot
|
|
31
71
|
* is carried over untouched, so switching accounts never clobbers the other
|
|
@@ -33,8 +73,9 @@ window.__ModuleLoader__.load({
|
|
|
33
73
|
* from an empty document.
|
|
34
74
|
*/
|
|
35
75
|
function nextRegionSlots(regions, region, slot) {
|
|
76
|
+
const unwrapped = unwrapVolatileDeep(regions);
|
|
36
77
|
return {
|
|
37
|
-
...typeof
|
|
78
|
+
...typeof unwrapped === "object" && unwrapped !== null && !Array.isArray(unwrapped) ? unwrapped : {},
|
|
38
79
|
[region]: slot
|
|
39
80
|
};
|
|
40
81
|
}
|
|
@@ -45,17 +86,23 @@ window.__ModuleLoader__.load({
|
|
|
45
86
|
* where the map was expected was a real shipped bug — the lookup then read
|
|
46
87
|
* `section['cn']` (absent), so the card's checkbox reported `true` forever and
|
|
47
88
|
* clicking it appeared to do nothing even though the write succeeded.
|
|
89
|
+
*
|
|
90
|
+
* Unwraps live references first: on DSH 0.1.7 the `regions` field arrives as a
|
|
91
|
+
* `{get(): T}` reference, which passes the `typeof === 'object'` check below
|
|
92
|
+
* and would otherwise be returned as if it were the map — making every region
|
|
93
|
+
* read back as absent (and the enabled flag read as "on" forever).
|
|
48
94
|
*/
|
|
49
95
|
function regionsMapOf(value) {
|
|
50
|
-
|
|
51
|
-
|
|
96
|
+
const unwrapped = unwrapVolatileDeep(value);
|
|
97
|
+
if (typeof unwrapped !== "object" || unwrapped === null || Array.isArray(unwrapped)) return {};
|
|
98
|
+
const record = unwrapped;
|
|
52
99
|
const nested = record["regions"];
|
|
53
100
|
if (typeof nested === "object" && nested !== null && !Array.isArray(nested)) return nested;
|
|
54
101
|
return record;
|
|
55
102
|
}
|
|
56
103
|
/** One region's stored slot as a plain object; any other shape reads as empty. */
|
|
57
104
|
function regionSlotOf(value, region) {
|
|
58
|
-
const slot = regionsMapOf(value)[region];
|
|
105
|
+
const slot = unwrapVolatile(regionsMapOf(value)[region]);
|
|
59
106
|
return typeof slot === "object" && slot !== null && !Array.isArray(slot) ? slot : {};
|
|
60
107
|
}
|
|
61
108
|
/**
|
|
@@ -118,7 +165,13 @@ window.__ModuleLoader__.load({
|
|
|
118
165
|
.dsm-plugin-card-head{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}
|
|
119
166
|
.dsm-plugin-card-title{color:var(--dsw-alias-label-primary,#e6e6e6);font-size:15px;font-weight:600;line-height:1.4}
|
|
120
167
|
.dsm-plugin-card-description{color:var(--dsw-alias-label-tertiary,#999);font-size:13px;line-height:1.5}
|
|
121
|
-
|
|
168
|
+
/* Pure-CSS caret: the host primitives' chevron icon names differ per DSH line
|
|
169
|
+
(0.1.5 IconChevronDownOutline14 vs 0.1.7 IconChevronDownOutlineRegular),
|
|
170
|
+
so no static import can serve both — importing the 0.1.5 name makes the
|
|
171
|
+
client bundle fail to resolve on 0.1.7, taking the whole card down. A border
|
|
172
|
+
caret in the plugin's own CSS is version-proof. */
|
|
173
|
+
.dsm-plugin-card-chevron{color:var(--dsw-alias-label-tertiary,#999);flex:none;width:16px;height:16px;position:relative;transition:transform .16s}
|
|
174
|
+
.dsm-plugin-card-chevron::before{content:"";display:block;position:absolute;left:4px;top:5px;width:7px;height:7px;border-right:1.6px solid currentColor;border-bottom:1.6px solid currentColor;transform:rotate(45deg)}
|
|
122
175
|
.dsm-plugin-card-chevron-open{transform:rotate(180deg)}
|
|
123
176
|
.dsm-plugin-card-body{border-top:1px solid var(--dsw-alias-border-l2,#36373b);margin:0 16px;padding:0 0 8px}
|
|
124
177
|
.dsm-plugin-card-icon{width:32px;height:32px;flex:none;border-radius:7px}
|
|
@@ -209,6 +262,11 @@ window.__ModuleLoader__.load({
|
|
|
209
262
|
.dsm-trae-searched-list code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;line-height:16px;color:var(--dsw-alias-label-secondary,#b8b8b8);word-break:break-all}
|
|
210
263
|
.dsm-trae-searched-reason{color:var(--dsw-alias-label-tertiary,#999);font-size:11px}
|
|
211
264
|
.dsm-trae-usage-cheer:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#5686fe);outline-offset:2px}
|
|
265
|
+
.dsm-trae-checkin{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 16px;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:14px;background:var(--dsw-alias-bg-layer-2,#24262c)}
|
|
266
|
+
.dsm-trae-checkin-copy{display:flex;flex-direction:column;gap:2px;min-width:0}
|
|
267
|
+
.dsm-trae-checkin-label{color:var(--dsw-alias-label-primary,#e6e6e6);font-size:13px;line-height:18px;font-weight:600}
|
|
268
|
+
.dsm-trae-checkin-hint{color:var(--dsw-alias-label-tertiary,#9aa0a8);font-size:12px;line-height:17px}
|
|
269
|
+
.dsm-trae-checkin-button{flex:none}
|
|
212
270
|
`;
|
|
213
271
|
//#endregion
|
|
214
272
|
//#region src/client/TraeUsageCard.tsx
|
|
@@ -220,11 +278,49 @@ window.__ModuleLoader__.load({
|
|
|
220
278
|
*/
|
|
221
279
|
const POLL_INTERVAL_MS = 6e4;
|
|
222
280
|
const TRAE_GITHUB_URL = "https://github.com/dingminhua/dsh-connect-trae";
|
|
223
|
-
/**
|
|
281
|
+
/**
|
|
282
|
+
* Read the per-region account selections out of the settings snapshot.
|
|
283
|
+
*
|
|
284
|
+
* Deep-unwraps first: on DSH 0.1.7 a volatile field arrives as a `{get(): T}`
|
|
285
|
+
* live reference, which passes the `typeof === 'object'` check below and would
|
|
286
|
+
* be returned as if it were the map. Every lookup on it is then `undefined`,
|
|
287
|
+
* and the card's merge (`{ ...configured, [region]: id }`) would spread a
|
|
288
|
+
* reference into `{get: <function>}` — dropping the other region's selection
|
|
289
|
+
* and leaking a function into the settings document.
|
|
290
|
+
*/
|
|
224
291
|
function configuredAccountsOf(configured) {
|
|
225
|
-
const accounts = configured?.accounts;
|
|
292
|
+
const accounts = unwrapVolatileDeep(configured)?.accounts;
|
|
226
293
|
return typeof accounts === "object" && accounts !== null ? accounts : {};
|
|
227
294
|
}
|
|
295
|
+
/**
|
|
296
|
+
* Write one settings field and confirm the value actually landed.
|
|
297
|
+
*
|
|
298
|
+
* `settingsScope.set()` / `configForms.set()` resolving is NOT proof that
|
|
299
|
+
* anything was stored. On DSH 0.1.7 a write can be accepted by the transport
|
|
300
|
+
* and still not materialize — a rejected write reloads Host state and merely
|
|
301
|
+
* RETURNS, so the caller's `await` succeeds while the document is unchanged.
|
|
302
|
+
* The 0.1.7 form also answers with an explicit boolean for exactly this reason.
|
|
303
|
+
*
|
|
304
|
+
* Either way the caller must not report success on an unpersisted write: the
|
|
305
|
+
* user would see the control flip and then silently revert.
|
|
306
|
+
*
|
|
307
|
+
* @throws {TraeSettingsWriteError} when the write was refused or did not land.
|
|
308
|
+
*/
|
|
309
|
+
var TraeSettingsWriteError = class extends Error {
|
|
310
|
+
constructor(field, detail) {
|
|
311
|
+
super(detail === void 0 || detail === "" ? `trae: settings field "${field}" was not persisted by the settings write` : `trae: settings field "${field}" was not persisted by the settings write (${detail})`);
|
|
312
|
+
this.name = "TraeSettingsWriteError";
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
async function writeSettingsField(scope, field, value, landed) {
|
|
316
|
+
if (await scope.set(field, value) === false) throw new TraeSettingsWriteError(field, "the Host refused the write");
|
|
317
|
+
if (!landed(unwrapVolatileDeep(scope.getSnapshot().value))) throw new TraeSettingsWriteError(field);
|
|
318
|
+
}
|
|
319
|
+
/** Read one region's stored `regions` slot out of an unwrapped settings value. */
|
|
320
|
+
function regionSlotsOf(value) {
|
|
321
|
+
const regions = value?.regions;
|
|
322
|
+
return typeof regions === "object" && regions !== null ? regions : {};
|
|
323
|
+
}
|
|
228
324
|
/** Inject the shared card CSS once. */
|
|
229
325
|
if (typeof document !== "undefined") {
|
|
230
326
|
const cssId = "dsh-connect-trae/client.css";
|
|
@@ -261,9 +357,9 @@ window.__ModuleLoader__.load({
|
|
|
261
357
|
return { background: status === "signed-in" ? "var(--dsw-alias-state-success-primary, #22a06b)" : status === "error" ? "var(--dsw-alias-state-error-primary, #d92d20)" : "var(--dsw-alias-label-dimmed, #9aa0a6)" };
|
|
262
358
|
}
|
|
263
359
|
/** Render Trae sign-in state and the total usage summary as one expandable card. */
|
|
264
|
-
function TraeUsageCard({ t, settingsScope }) {
|
|
360
|
+
function TraeUsageCard({ t, settingsScope, view }) {
|
|
265
361
|
if (t === void 0) throw new Error("Trae usage card requires its translation function");
|
|
266
|
-
const [open, setOpen] = (0, react.useState)(
|
|
362
|
+
const [open, setOpen] = (0, react.useState)(view === "page");
|
|
267
363
|
/** The region whose tab is on screen; each tab is its own provider stack. */
|
|
268
364
|
const [activeRegion, setActiveRegion] = (0, react.useState)("cn");
|
|
269
365
|
/** Last-known usage per region, so tab dots survive tab switches. */
|
|
@@ -286,6 +382,20 @@ window.__ModuleLoader__.load({
|
|
|
286
382
|
const [switchingAccount, setSwitchingAccount] = (0, react.useState)(false);
|
|
287
383
|
/** Region whose on/off checkbox write is in flight, so its box can't race. */
|
|
288
384
|
const [togglingRegion, setTogglingRegion] = (0, react.useState)(void 0);
|
|
385
|
+
/**
|
|
386
|
+
* Whether a check-in claim is in flight. A single flag rather than a
|
|
387
|
+
* per-region map: the claim exists only on the CN tab, and one shared flag
|
|
388
|
+
* keeps a tab switch from stranding a second concurrent claim.
|
|
389
|
+
*/
|
|
390
|
+
const [claiming, setClaiming] = (0, react.useState)(false);
|
|
391
|
+
/** Last claim refusal, shown until the next successful refresh. */
|
|
392
|
+
const [claimError, setClaimError] = (0, react.useState)(void 0);
|
|
393
|
+
/**
|
|
394
|
+
* A refused or unpersisted settings write, surfaced instead of silently
|
|
395
|
+
* reverting. On 0.1.7 a rejected write resolves normally, so without this
|
|
396
|
+
* the control would flip and then quietly snap back on the next snapshot.
|
|
397
|
+
*/
|
|
398
|
+
const [writeError, setWriteError] = (0, react.useState)(void 0);
|
|
289
399
|
const mounted = (0, react.useRef)(true);
|
|
290
400
|
(0, react.useEffect)(() => {
|
|
291
401
|
mounted.current = true;
|
|
@@ -354,6 +464,14 @@ window.__ModuleLoader__.load({
|
|
|
354
464
|
status: "signed-out",
|
|
355
465
|
accounts: []
|
|
356
466
|
};
|
|
467
|
+
/**
|
|
468
|
+
* Drop a claim refusal when the tab changes. The error belongs to the region
|
|
469
|
+
* it came from — carrying it onto the other tab would attribute an
|
|
470
|
+
* international account's problem to the CN one (or the reverse).
|
|
471
|
+
*/
|
|
472
|
+
(0, react.useEffect)(() => {
|
|
473
|
+
setClaimError(void 0);
|
|
474
|
+
}, [activeRegion]);
|
|
357
475
|
(0, react.useEffect)(() => {
|
|
358
476
|
if (!open || !activeRegionOn || status.status !== "signed-in") return;
|
|
359
477
|
const controller = new AbortController();
|
|
@@ -373,6 +491,7 @@ window.__ModuleLoader__.load({
|
|
|
373
491
|
]);
|
|
374
492
|
const rescanAccounts = async () => {
|
|
375
493
|
setBusy(true);
|
|
494
|
+
setWriteError(void 0);
|
|
376
495
|
try {
|
|
377
496
|
const response = await fetch(withTraeRegion(TRAE_ACCOUNTS_REFRESH_PATH, activeRegion), {
|
|
378
497
|
method: "POST",
|
|
@@ -383,11 +502,13 @@ window.__ModuleLoader__.load({
|
|
|
383
502
|
if (!response.ok || !Array.isArray(body.accounts)) throw new Error(`HTTP ${response.status}`);
|
|
384
503
|
const selected = body.accounts.find((account) => account.selected)?.id;
|
|
385
504
|
const configured = configuredAccountsOf(settingsScope?.getSnapshot().value);
|
|
386
|
-
if (selected !== void 0 && selected !== configured[activeRegion] && settingsScope
|
|
505
|
+
if (selected !== void 0 && selected !== configured[activeRegion] && settingsScope !== void 0 && settingsScope.getSnapshot().writable === true) await writeSettingsField(settingsScope, "accounts", {
|
|
387
506
|
...configured,
|
|
388
507
|
[activeRegion]: selected
|
|
389
|
-
});
|
|
508
|
+
}, (readBack) => configuredAccountsOf(readBack)[activeRegion] === selected);
|
|
390
509
|
await refreshUsage(activeRegion);
|
|
510
|
+
} catch (error) {
|
|
511
|
+
if (mounted.current) setWriteError(error instanceof Error ? error.message : t("row.requestFailed"));
|
|
391
512
|
} finally {
|
|
392
513
|
if (mounted.current) setBusy(false);
|
|
393
514
|
}
|
|
@@ -395,18 +516,52 @@ window.__ModuleLoader__.load({
|
|
|
395
516
|
const switchAccount = async (accountId) => {
|
|
396
517
|
if (settingsScope === void 0) return;
|
|
397
518
|
setSwitchingAccount(true);
|
|
519
|
+
setWriteError(void 0);
|
|
398
520
|
try {
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
...configured,
|
|
521
|
+
await writeSettingsField(settingsScope, "accounts", {
|
|
522
|
+
...configuredAccountsOf(settingsScope.getSnapshot().value),
|
|
402
523
|
[activeRegion]: accountId
|
|
403
|
-
});
|
|
524
|
+
}, (readBack) => configuredAccountsOf(readBack)[activeRegion] === accountId);
|
|
404
525
|
await refreshUsage(activeRegion);
|
|
526
|
+
} catch (error) {
|
|
527
|
+
if (mounted.current) setWriteError(error instanceof Error ? error.message : t("row.requestFailed"));
|
|
405
528
|
} finally {
|
|
406
529
|
if (mounted.current) setSwitchingAccount(false);
|
|
407
530
|
}
|
|
408
531
|
};
|
|
409
532
|
/**
|
|
533
|
+
* Claim today's check-in reward for the active tab's region.
|
|
534
|
+
*
|
|
535
|
+
* The Host route owns every guard that matters — POST only, loopback origin
|
|
536
|
+
* only, CN only, and a status read before the claim — because a browser-side
|
|
537
|
+
* check is not a guard. The button's own `disabled` is therefore a courtesy
|
|
538
|
+
* (it stops an obviously pointless click), not the protection.
|
|
539
|
+
*
|
|
540
|
+
* A refusal is not thrown away: the route answers `claimed: false` with the
|
|
541
|
+
* upstream's business code and message, and the card shows it. The upstream
|
|
542
|
+
* also reports success for an already-claimed day, so `alreadyCheckedIn`
|
|
543
|
+
* refreshes the state without pretending a new reward landed.
|
|
544
|
+
*/
|
|
545
|
+
const claimCheckin = async () => {
|
|
546
|
+
setClaiming(true);
|
|
547
|
+
setClaimError(void 0);
|
|
548
|
+
try {
|
|
549
|
+
const response = await fetch(withTraeRegion(TRAE_CHECKIN_PATH, activeRegion), {
|
|
550
|
+
method: "POST",
|
|
551
|
+
headers: { accept: "application/json" },
|
|
552
|
+
credentials: "same-origin"
|
|
553
|
+
});
|
|
554
|
+
const body = await response.json().catch(() => void 0);
|
|
555
|
+
if (!response.ok) throw new Error(body?.error ?? `HTTP ${response.status}`);
|
|
556
|
+
if (body?.claimed === false && body.alreadyCheckedIn !== true) throw new Error(body.message === void 0 || body.message === "" ? t("row.checkinRefused", { code: String(body.code ?? "") }) : `${t("row.checkinRefused", { code: String(body.code ?? "") })}: ${body.message}`);
|
|
557
|
+
await refreshUsage(activeRegion);
|
|
558
|
+
} catch (error) {
|
|
559
|
+
if (mounted.current) setClaimError(error instanceof Error ? error.message : t("row.requestFailed"));
|
|
560
|
+
} finally {
|
|
561
|
+
if (mounted.current) setClaiming(false);
|
|
562
|
+
}
|
|
563
|
+
};
|
|
564
|
+
/**
|
|
410
565
|
* Switch one region's provider off or on (issue #11). The write carries the
|
|
411
566
|
* region's whole slot through untouched — only `enabled` changes — so the
|
|
412
567
|
* user's directory, model picks, image opt-ins and budgets survive a
|
|
@@ -416,8 +571,11 @@ window.__ModuleLoader__.load({
|
|
|
416
571
|
const toggleRegion = async (item, enabled) => {
|
|
417
572
|
if (settingsScope === void 0 || settingsScope.getSnapshot().writable !== true) return;
|
|
418
573
|
setTogglingRegion(item);
|
|
574
|
+
setWriteError(void 0);
|
|
419
575
|
try {
|
|
420
|
-
await settingsScope
|
|
576
|
+
await writeSettingsField(settingsScope, "regions", nextRegionEnabled(settingsScope.getSnapshot().value, item, enabled), (readBack) => regionSlotsOf(readBack)[item] !== void 0 && regionSlotsOf(readBack)[item].enabled === enabled);
|
|
577
|
+
} catch (error) {
|
|
578
|
+
if (mounted.current) setWriteError(error instanceof Error ? error.message : t("row.requestFailed"));
|
|
421
579
|
} finally {
|
|
422
580
|
if (mounted.current) setTogglingRegion(void 0);
|
|
423
581
|
}
|
|
@@ -465,7 +623,7 @@ window.__ModuleLoader__.load({
|
|
|
465
623
|
if (mounted.current) setBusy(false);
|
|
466
624
|
}
|
|
467
625
|
};
|
|
468
|
-
const settingsValue = settingsScope?.getSnapshot().value;
|
|
626
|
+
const settingsValue = unwrapVolatileDeep(settingsScope?.getSnapshot().value);
|
|
469
627
|
const region = activeRegion;
|
|
470
628
|
const configuredRegions = typeof settingsValue === "object" && settingsValue !== null && typeof settingsValue.regions === "object" && settingsValue.regions !== null ? settingsValue.regions : {};
|
|
471
629
|
const regionSlot = configuredRegions[region];
|
|
@@ -536,9 +694,10 @@ window.__ModuleLoader__.load({
|
|
|
536
694
|
const saveModels = async () => {
|
|
537
695
|
if (settingsScope === void 0) return;
|
|
538
696
|
setSaving(true);
|
|
697
|
+
setWriteError(void 0);
|
|
539
698
|
try {
|
|
540
699
|
if (status.status !== "signed-in") return;
|
|
541
|
-
|
|
700
|
+
const nextSlot = {
|
|
542
701
|
enabled: regionOn(activeRegion),
|
|
543
702
|
lastCatalog: visibleModels.map((model) => ({
|
|
544
703
|
...model,
|
|
@@ -547,9 +706,15 @@ window.__ModuleLoader__.load({
|
|
|
547
706
|
enabledModelIds: [...activeEnabledIds],
|
|
548
707
|
imageModelIds: [...activeImageIds].filter((id) => activeEnabledIds.has(id)),
|
|
549
708
|
contextBudgets: activeContextBudgets
|
|
550
|
-
}
|
|
709
|
+
};
|
|
710
|
+
await writeSettingsField(settingsScope, "regions", nextRegionSlots(configuredRegions, activeRegion, nextSlot), (readBack) => {
|
|
711
|
+
const ids = regionSlotsOf(readBack)[activeRegion]?.enabledModelIds;
|
|
712
|
+
return Array.isArray(ids) && ids.length === nextSlot.enabledModelIds.length && nextSlot.enabledModelIds.every((id) => ids.includes(id));
|
|
713
|
+
});
|
|
551
714
|
discardModels();
|
|
552
715
|
await refreshUsage(activeRegion);
|
|
716
|
+
} catch (error) {
|
|
717
|
+
if (mounted.current) setWriteError(error instanceof Error ? error.message : t("row.requestFailed"));
|
|
553
718
|
} finally {
|
|
554
719
|
if (mounted.current) setSaving(false);
|
|
555
720
|
}
|
|
@@ -584,8 +749,7 @@ window.__ModuleLoader__.load({
|
|
|
584
749
|
}),
|
|
585
750
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
586
751
|
"aria-hidden": "true",
|
|
587
|
-
className: `dsm-plugin-card-chevron${open ? " dsm-plugin-card-chevron-open" : ""}
|
|
588
|
-
children: (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 14 })
|
|
752
|
+
className: `dsm-plugin-card-chevron${open ? " dsm-plugin-card-chevron-open" : ""}`
|
|
589
753
|
})
|
|
590
754
|
]
|
|
591
755
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -637,6 +801,11 @@ window.__ModuleLoader__.load({
|
|
|
637
801
|
className: "dsm-trae-tab-hint",
|
|
638
802
|
children: t("row.tabHint")
|
|
639
803
|
}),
|
|
804
|
+
writeError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
805
|
+
className: "dsm-trae-usage-error",
|
|
806
|
+
role: "alert",
|
|
807
|
+
children: t("row.settingsWriteFailed", { message: writeError })
|
|
808
|
+
}),
|
|
640
809
|
regionOn(activeRegion) ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
641
810
|
className: "dsm-trae-tab-off-notice",
|
|
642
811
|
role: "status",
|
|
@@ -787,6 +956,45 @@ window.__ModuleLoader__.load({
|
|
|
787
956
|
className: "dsm-trae-usage-error",
|
|
788
957
|
children: t("row.creditsError", { message: status.creditsError })
|
|
789
958
|
}),
|
|
959
|
+
status.checkin === void 0 ? null : (() => {
|
|
960
|
+
const done = status.checkin.checkedIn || status.checkin.didCheckedIn;
|
|
961
|
+
const reward = formatNumber(status.checkin.credits);
|
|
962
|
+
const bonus = status.checkin.extraCredits === void 0 ? "" : ` + ${formatNumber(status.checkin.extraCredits)}`;
|
|
963
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
964
|
+
className: "dsm-trae-checkin",
|
|
965
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
966
|
+
className: "dsm-trae-checkin-copy",
|
|
967
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
968
|
+
className: "dsm-trae-checkin-label",
|
|
969
|
+
children: t("row.checkinLabel")
|
|
970
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
971
|
+
className: "dsm-trae-checkin-hint",
|
|
972
|
+
children: [t("row.checkinReward", { reward: `${reward}${bonus}` }), done ? ` · ${t("row.checkinDoneHint")}` : ""]
|
|
973
|
+
})]
|
|
974
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
975
|
+
type: "button",
|
|
976
|
+
className: "dsm-btn dsm-btn-primary dsm-trae-checkin-button",
|
|
977
|
+
disabled: !status.checkin.enabled || done || claiming,
|
|
978
|
+
onClick: () => {
|
|
979
|
+
claimCheckin();
|
|
980
|
+
},
|
|
981
|
+
children: claiming ? t("row.checkinClaiming") : done ? t("row.checkinClaimed") : t("row.checkinClaim")
|
|
982
|
+
})]
|
|
983
|
+
});
|
|
984
|
+
})(),
|
|
985
|
+
status.checkin !== void 0 && !status.checkin.enabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
986
|
+
className: "dsm-trae-usage-error",
|
|
987
|
+
children: t("row.checkinDisabled")
|
|
988
|
+
}) : null,
|
|
989
|
+
status.checkinError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
990
|
+
className: "dsm-trae-usage-error",
|
|
991
|
+
children: t("row.checkinError", { message: status.checkinError })
|
|
992
|
+
}),
|
|
993
|
+
claimError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
994
|
+
className: "dsm-trae-usage-error",
|
|
995
|
+
role: "alert",
|
|
996
|
+
children: t("row.checkinError", { message: claimError })
|
|
997
|
+
}),
|
|
790
998
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
791
999
|
className: "dsm-trae-models",
|
|
792
1000
|
"aria-label": t("row.modelsTitle"),
|
|
@@ -972,6 +1180,7 @@ window.__ModuleLoader__.load({
|
|
|
972
1180
|
"row.tabSwitchHint": "Uncheck to switch this provider off. Its models disappear from the DSH model picker; its account, usage, and model settings are kept and return when you check it again.",
|
|
973
1181
|
"row.tabSwitchAria": "Offer the {region} provider to DSH",
|
|
974
1182
|
"row.tabOffNotice": "This provider is switched off: none of its models are offered to DSH. Your account, usage, and model settings are kept — check the box above the tab to bring it back.",
|
|
1183
|
+
"row.settingsWriteFailed": "The setting was not saved: {message}",
|
|
975
1184
|
"row.signedOut": "Not signed in",
|
|
976
1185
|
"row.signedOutHint": "Sign in once in the Trae desktop app; this plugin follows that sign-in automatically.",
|
|
977
1186
|
"row.searchedTitle": "Paths checked",
|
|
@@ -995,6 +1204,15 @@ window.__ModuleLoader__.load({
|
|
|
995
1204
|
"row.creditsConsumedLabel": "Consumed",
|
|
996
1205
|
"row.creditsTotalLabel": "Total",
|
|
997
1206
|
"row.creditsError": "Usage unavailable: {message}",
|
|
1207
|
+
"row.checkinLabel": "Daily check-in",
|
|
1208
|
+
"row.checkinReward": "{reward} credits per day",
|
|
1209
|
+
"row.checkinClaim": "Claim today",
|
|
1210
|
+
"row.checkinClaiming": "Claiming…",
|
|
1211
|
+
"row.checkinClaimed": "Claimed today",
|
|
1212
|
+
"row.checkinDoneHint": "come back tomorrow",
|
|
1213
|
+
"row.checkinRefused": "Trae refused the check-in (code {code})",
|
|
1214
|
+
"row.checkinError": "Check-in failed: {message}",
|
|
1215
|
+
"row.checkinDisabled": "This account has no check-in activity available.",
|
|
998
1216
|
"row.regionCn": "CN",
|
|
999
1217
|
"row.regionAi": "International",
|
|
1000
1218
|
"row.subscriptionLabel": "Subscription",
|
|
@@ -1040,6 +1258,7 @@ window.__ModuleLoader__.load({
|
|
|
1040
1258
|
"row.tabSwitchHint": "取消勾选即关闭该供应商:它的模型不再出现在 DSH 模型选择器里;账号、用量与模型设置都会保留,重新勾选即恢复。",
|
|
1041
1259
|
"row.tabSwitchAria": "是否向 DSH 提供{region}供应商",
|
|
1042
1260
|
"row.tabOffNotice": "该供应商已关闭:它的模型不会提供给 DSH。账号、用量与模型设置均已保留——勾选上方复选框即可恢复。",
|
|
1261
|
+
"row.settingsWriteFailed": "设置未能保存:{message}",
|
|
1043
1262
|
"row.signedOut": "未登录",
|
|
1044
1263
|
"row.signedOutHint": "在 Trae 桌面 App 里登录一次即可,插件会自动跟随当前登录的账号。",
|
|
1045
1264
|
"row.searchedTitle": "已检查的路径",
|
|
@@ -1063,6 +1282,15 @@ window.__ModuleLoader__.load({
|
|
|
1063
1282
|
"row.creditsConsumedLabel": "消耗",
|
|
1064
1283
|
"row.creditsTotalLabel": "合计",
|
|
1065
1284
|
"row.creditsError": "用量查询失败:{message}",
|
|
1285
|
+
"row.checkinLabel": "每日签到",
|
|
1286
|
+
"row.checkinReward": "每日 {reward} 积分",
|
|
1287
|
+
"row.checkinClaim": "立即领取",
|
|
1288
|
+
"row.checkinClaiming": "领取中…",
|
|
1289
|
+
"row.checkinClaimed": "今日已领取",
|
|
1290
|
+
"row.checkinDoneHint": "明天再来",
|
|
1291
|
+
"row.checkinRefused": "Trae 拒绝了本次签到(业务码 {code})",
|
|
1292
|
+
"row.checkinError": "签到失败:{message}",
|
|
1293
|
+
"row.checkinDisabled": "该账号当前没有可用的签到活动。",
|
|
1066
1294
|
"row.regionCn": "国内版",
|
|
1067
1295
|
"row.regionAi": "国际版",
|
|
1068
1296
|
"row.subscriptionLabel": "订阅状态",
|
|
@@ -1101,12 +1329,17 @@ window.__ModuleLoader__.load({
|
|
|
1101
1329
|
//#region src/client/index.tsx
|
|
1102
1330
|
/** Stable browser-plugin name. */
|
|
1103
1331
|
const name = "dsh-connect-trae-client";
|
|
1104
|
-
/**
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1332
|
+
/**
|
|
1333
|
+
* Client services required by the Plugin configuration contribution.
|
|
1334
|
+
*
|
|
1335
|
+
* Deliberately only the two services that exist on BOTH host lines. Cordis'
|
|
1336
|
+
* dependency gate is hard: any `inject` entry the running line does not provide
|
|
1337
|
+
* keeps `apply` from ever running at all — which is exactly how this plugin
|
|
1338
|
+
* ended up `pending (waiting for service: settingsScope)` on 0.1.7. Probing the
|
|
1339
|
+
* settings surface through `ctx.get()` (which returns undefined, never throws,
|
|
1340
|
+
* for an absent service) is what lets one build serve both lines.
|
|
1341
|
+
*/
|
|
1342
|
+
const inject = ["slots", "locale"];
|
|
1110
1343
|
/**
|
|
1111
1344
|
* Register card copy and the Trae card under Plugin configuration.
|
|
1112
1345
|
*
|
|
@@ -1119,6 +1352,8 @@ window.__ModuleLoader__.load({
|
|
|
1119
1352
|
* NOTE: the try/catch boundary of this function is mirrored (duplicated) in
|
|
1120
1353
|
* `tests/client-fallback.spec.ts`, because the real client entry imports
|
|
1121
1354
|
* browser-only DSH packages that cannot load in the Node test environment.
|
|
1355
|
+
* If you change the guarded body, the soft probe, or the per-slot
|
|
1356
|
+
* registration here, update that mirror too.
|
|
1122
1357
|
*/
|
|
1123
1358
|
function apply(ctx) {
|
|
1124
1359
|
try {
|
|
@@ -1128,16 +1363,36 @@ window.__ModuleLoader__.load({
|
|
|
1128
1363
|
en
|
|
1129
1364
|
}), "dsh-connect-trae: settings copy");
|
|
1130
1365
|
const t = ctx.locale.bind(namespace);
|
|
1131
|
-
const
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1366
|
+
const softGet = (name) => ctx.get(name);
|
|
1367
|
+
let settingsScope;
|
|
1368
|
+
const forms = softGet("configForms");
|
|
1369
|
+
const legacy = softGet("settingsScope");
|
|
1370
|
+
if (forms !== void 0) {
|
|
1371
|
+
let ns = "trae";
|
|
1372
|
+
try {
|
|
1373
|
+
const served = (forms.describe().getSnapshot().view?.namespaces ?? []).find((entry) => entry.ns === "trae" || /trae/i.test(entry.ns));
|
|
1374
|
+
if (served !== void 0) ns = served.ns;
|
|
1375
|
+
} catch {}
|
|
1376
|
+
settingsScope = forms.get(ns);
|
|
1377
|
+
} else if (legacy !== void 0) settingsScope = legacy.bind({ namespace: "trae" });
|
|
1378
|
+
const registerCard = (slotName, key) => {
|
|
1379
|
+
try {
|
|
1380
|
+
ctx.slots.inject(slotName, () => ctx.slots.register({
|
|
1381
|
+
name: slotName,
|
|
1382
|
+
key,
|
|
1383
|
+
priority: 30,
|
|
1384
|
+
inject: () => settingsScope === void 0 ? { t } : {
|
|
1385
|
+
t,
|
|
1386
|
+
settingsScope
|
|
1387
|
+
}
|
|
1388
|
+
}, TraeUsageCard));
|
|
1389
|
+
} catch (error) {
|
|
1390
|
+
console.error(`[dsh-connect-trae] card slot "${slotName}" failed to register (host provider unaffected):`, error);
|
|
1391
|
+
}
|
|
1392
|
+
};
|
|
1393
|
+
registerCard("plugins.bundle.config", "dsh-connect-trae");
|
|
1394
|
+
registerCard("plugins.row.config", "dsh-connect-trae#dsh-connect-trae");
|
|
1395
|
+
registerCard("settings.plugin.item", "trae");
|
|
1141
1396
|
} catch (error) {
|
|
1142
1397
|
console.error("[dsh-connect-trae] client card failed to load (host provider unaffected):", error);
|
|
1143
1398
|
}
|