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/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 regions === "object" && regions !== null && !Array.isArray(regions) ? regions : {},
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
- if (typeof value !== "object" || value === null || Array.isArray(value)) return {};
51
- const record = value;
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
- .dsm-plugin-card-chevron{color:var(--dsw-alias-label-tertiary,#999);flex:none;display:inline-flex;transition:transform .16s}
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
- /** Read the per-region account selections out of the settings snapshot. */
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)(false);
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?.getSnapshot().writable === true) await settingsScope.set("accounts", {
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
- const configured = configuredAccountsOf(settingsScope.getSnapshot().value);
400
- await settingsScope.set("accounts", {
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.set("regions", nextRegionEnabled(settingsScope.getSnapshot().value, item, enabled));
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
- await settingsScope.set("regions", nextRegionSlots(configuredRegions, activeRegion, {
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
- /** Client services required by the Plugin configuration contribution. */
1105
- const inject = [
1106
- "slots",
1107
- "locale",
1108
- "settingsScope"
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 settingsScope = ctx.settingsScope.bind({ namespace: "trae" });
1132
- ctx.slots.inject("settings.plugin.item", () => ctx.slots.register({
1133
- name: "settings.plugin.item",
1134
- key: "trae",
1135
- priority: 30,
1136
- inject: () => ({
1137
- t,
1138
- settingsScope
1139
- })
1140
- }, TraeUsageCard));
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
  }