@eddyskywalker/dsh-chatgpt-subscription 0.41.1 → 0.42.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
@@ -4631,6 +4631,195 @@ window.__ModuleLoader__.load({
4631
4631
  });
4632
4632
  }
4633
4633
  //#endregion
4634
+ //#region src/client/CompactionRouteCard.tsx
4635
+ /**
4636
+ * Choose the model an automatic compaction summarizes on.
4637
+ *
4638
+ * The route lives in this plugin's preferences and is applied by the host's
4639
+ * `llm/stream` listener, so a saved choice takes effect on the next compaction
4640
+ * without a restart. Both selects are needed together: a model id alone belongs
4641
+ * to no route.
4642
+ *
4643
+ * The picker loads its directory on demand rather than on mount: opening the
4644
+ * settings page must not cost a request to every line (that page is opened for
4645
+ * unrelated reasons all the time), and the card already knows the saved route
4646
+ * from the status it was rendered with.
4647
+ */
4648
+ function CompactionRouteCard({ api, preferences, disabled = false, labels, onSaved }) {
4649
+ const [picking, setPicking] = (0, react.useState)(false);
4650
+ const [candidates, setCandidates] = (0, react.useState)(null);
4651
+ const [loadError, setLoadError] = (0, react.useState)(null);
4652
+ const [provider, setProvider] = (0, react.useState)("");
4653
+ const [model, setModel] = (0, react.useState)("");
4654
+ const [busy, setBusy] = (0, react.useState)(false);
4655
+ const [notice, setNotice] = (0, react.useState)(null);
4656
+ const [error, setError] = (0, react.useState)(null);
4657
+ const load = (0, react.useCallback)(async () => {
4658
+ try {
4659
+ const value = await api.compactionCandidates();
4660
+ setCandidates(value);
4661
+ setLoadError(null);
4662
+ } catch (cause) {
4663
+ setLoadError(cause instanceof Error ? cause.message : String(cause));
4664
+ }
4665
+ }, [api]);
4666
+ (0, react.useEffect)(() => {
4667
+ if (picking && candidates === null) load();
4668
+ }, [
4669
+ picking,
4670
+ candidates,
4671
+ load
4672
+ ]);
4673
+ (0, react.useEffect)(() => {
4674
+ const saved = preferences?.compactionRoute ?? null;
4675
+ if (saved === null) return;
4676
+ setProvider((current) => current === "" ? saved.provider : current);
4677
+ setModel((current) => current === "" ? saved.model : current);
4678
+ }, [preferences?.compactionRoute]);
4679
+ const providers = candidates?.providers ?? [];
4680
+ const models = providers.find((entry) => entry.id === provider)?.models ?? [];
4681
+ const selected = models.find((entry) => entry.id === model);
4682
+ const savedProvider = preferences?.compactionRoute?.provider ?? "";
4683
+ const savedModel = preferences?.compactionRoute?.model ?? "";
4684
+ const canSave = provider !== "" && model !== "" && (provider !== savedProvider || model !== savedModel) && !busy && !disabled;
4685
+ const providerOptions = savedProvider === "" || providers.some((entry) => entry.id === savedProvider) ? providers : [{
4686
+ id: savedProvider,
4687
+ name: savedProvider
4688
+ }, ...providers];
4689
+ const modelOptions = model !== "" && !models.some((entry) => entry.id === model) ? [{
4690
+ id: model,
4691
+ name: model,
4692
+ modalities: []
4693
+ }, ...models] : models;
4694
+ const currentLabel = savedProvider === "" ? labels.follow : `${savedProvider} / ${savedModel}`;
4695
+ const save = async (target) => {
4696
+ setBusy(true);
4697
+ setError(null);
4698
+ setNotice(null);
4699
+ try {
4700
+ onSaved(await api.updatePreferences({ compactionRoute: target }));
4701
+ setNotice(labels.saved);
4702
+ setPicking(false);
4703
+ } catch (cause) {
4704
+ setError(cause instanceof Error ? cause.message : String(cause));
4705
+ } finally {
4706
+ setBusy(false);
4707
+ }
4708
+ };
4709
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
4710
+ className: "dsha-group",
4711
+ children: [
4712
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4713
+ className: "dsha-grouphead",
4714
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: labels.title })
4715
+ }),
4716
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4717
+ className: "dsha-muted",
4718
+ children: labels.hint
4719
+ }),
4720
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4721
+ className: "dsha-pref-row",
4722
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: currentLabel }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4723
+ className: "dsha-btn",
4724
+ type: "button",
4725
+ disabled: busy || disabled,
4726
+ onClick: () => setPicking((current) => !current),
4727
+ children: labels.change
4728
+ })]
4729
+ }),
4730
+ picking ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
4731
+ loadError !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4732
+ className: "dsh-codex-error",
4733
+ role: "alert",
4734
+ children: loadError
4735
+ }) : null,
4736
+ candidates !== null && providers.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4737
+ className: "dsh-codex-empty",
4738
+ children: labels.empty
4739
+ }) : null,
4740
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4741
+ className: "dsha-pref-row",
4742
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: labels.provider }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
4743
+ className: "dsha-select",
4744
+ "aria-label": labels.provider,
4745
+ value: provider,
4746
+ disabled: busy || disabled,
4747
+ onChange: (event) => {
4748
+ setProvider(event.currentTarget.value);
4749
+ setModel("");
4750
+ },
4751
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
4752
+ value: "",
4753
+ children: "—"
4754
+ }), providerOptions.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
4755
+ value: entry.id,
4756
+ children: entry.name
4757
+ }, entry.id))]
4758
+ })]
4759
+ }),
4760
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4761
+ className: "dsha-pref-row",
4762
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: labels.model }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
4763
+ className: "dsha-select",
4764
+ "aria-label": labels.model,
4765
+ value: model,
4766
+ disabled: busy || disabled || provider === "",
4767
+ onChange: (event) => setModel(event.currentTarget.value),
4768
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
4769
+ value: "",
4770
+ children: "—"
4771
+ }), modelOptions.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
4772
+ value: entry.id,
4773
+ children: entry.name
4774
+ }, entry.id))]
4775
+ })]
4776
+ }),
4777
+ selected !== void 0 && selected.modalities.length > 0 && !selected.modalities.includes("video") ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4778
+ className: "dsh-codex-warning",
4779
+ children: labels.noVideo
4780
+ }) : null,
4781
+ selected !== void 0 && selected.modalities.length > 0 && !selected.modalities.includes("image") ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4782
+ className: "dsh-codex-warning",
4783
+ children: labels.noImage
4784
+ }) : null,
4785
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4786
+ className: "dsha-actions",
4787
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4788
+ className: "dsha-btn dsha-btn-primary",
4789
+ type: "button",
4790
+ disabled: !canSave,
4791
+ onClick: () => void save({
4792
+ provider,
4793
+ model
4794
+ }),
4795
+ children: labels.save
4796
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4797
+ className: "dsha-btn",
4798
+ type: "button",
4799
+ disabled: busy || disabled || savedProvider === "",
4800
+ onClick: () => void save(null),
4801
+ children: labels.clear
4802
+ })]
4803
+ })
4804
+ ] }) : null,
4805
+ notice !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4806
+ className: "dsh-codex-success",
4807
+ role: "status",
4808
+ children: notice
4809
+ }) : null,
4810
+ error !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
4811
+ className: "dsh-codex-error",
4812
+ role: "alert",
4813
+ children: [
4814
+ labels.failed,
4815
+ ": ",
4816
+ error
4817
+ ]
4818
+ }) : null
4819
+ ]
4820
+ });
4821
+ }
4822
+ //#endregion
4634
4823
  //#region src/client/kimi-code/locales.ts
4635
4824
  const NS_KIMI_CODE = "dsh-kimi-code";
4636
4825
  const zh$10 = {
@@ -4731,11 +4920,7 @@ window.__ModuleLoader__.load({
4731
4920
  cacheOutput: "输出 tokens",
4732
4921
  cacheRequests: "请求数",
4733
4922
  cacheWrite: "缓存写入",
4734
- cacheWrite5m: "5 分钟档",
4735
- cacheWrite1h: "1 小时档",
4736
- cacheWriteHint: "写入是独立计费项:1h 写入单价约为 5m 的两倍,命中后会按原 TTL 续期。分档显示 0 表示服务端没有回报该档的写入数(响应头缺失就是静默),不等于该档确实没写入。",
4737
4923
  unknownMetric: "暂无",
4738
- cacheHint: "同一会话内:系统提示与工具列表一旦变化会使缓存整体失效,因此保持工具集合稳定、把新增内容追加在末尾,命中率最高。",
4739
4924
  cacheExpired: "本会话已空闲超过缓存有效期,下一轮将重新处理约 {tokens} 个 token。",
4740
4925
  cacheDrift: "最近前缀状态",
4741
4926
  cacheDriftFirst: "首个请求(缓存为空)",
@@ -4786,7 +4971,20 @@ window.__ModuleLoader__.load({
4786
4971
  loading: "加载中...",
4787
4972
  loginFailed: "登录失败",
4788
4973
  requests: "次请求",
4789
- tokens: "tokens"
4974
+ tokens: "tokens",
4975
+ compactionRoute: "压缩模型",
4976
+ compactionRouteHint: "自动压缩时由哪个模型来写摘要(仅对本线路的会话生效)。留空则继续用会话自己的模型,也就继续消耗这条线路的额度。",
4977
+ compactionRouteProvider: "线路",
4978
+ compactionRouteModel: "模型",
4979
+ compactionRouteSave: "保存",
4980
+ compactionRouteClear: "恢复会话模型",
4981
+ compactionRouteChange: "设置",
4982
+ compactionRouteFollow: "跟随会话模型(默认)",
4983
+ compactionRouteSaved: "已保存,下次压缩生效。",
4984
+ compactionRouteFailed: "保存失败",
4985
+ compactionRouteNoVideo: "该模型不接受视频:含视频的会话会回落到会话模型完成压缩。",
4986
+ compactionRouteNoImage: "该模型不接受图片:摘要里的截图会被替换为占位文本。",
4987
+ compactionRouteEmpty: "没有可用的线路——先登录任意一条线路再回来设置。"
4790
4988
  };
4791
4989
  const en$10 = {
4792
4990
  ...accountPoolEn,
@@ -4887,11 +5085,7 @@ window.__ModuleLoader__.load({
4887
5085
  cacheOutput: "Output tokens",
4888
5086
  cacheRequests: "Requests",
4889
5087
  cacheWrite: "Cache writes",
4890
- cacheWrite5m: "5-minute tier",
4891
- cacheWrite1h: "1-hour tier",
4892
- cacheWriteHint: "A write is billed separately: a 1h write costs about twice a 5m one, and a hit renews the entry under its original TTL. A tier showing 0 means the service did not report a write for it (a missing response header is silence), not that nothing was written there.",
4893
5088
  unknownMetric: "n/a",
4894
- cacheHint: "Within one session: any change to the system prompt or the tool list invalidates the whole prefix cache, so keep the tool set stable and append new material at the tail for the best hit rate.",
4895
5089
  cacheDrift: "Last prefix state",
4896
5090
  cacheDriftFirst: "First request (cache cold)",
4897
5091
  cacheDriftStable: "Stable (prefix unchanged)",
@@ -4941,7 +5135,20 @@ window.__ModuleLoader__.load({
4941
5135
  loading: "Loading...",
4942
5136
  loginFailed: "Sign-in failed",
4943
5137
  requests: "requests",
4944
- tokens: "tokens"
5138
+ tokens: "tokens",
5139
+ compactionRoute: "Compaction model",
5140
+ compactionRouteHint: "Which model writes the summary when the context is compacted — it applies to sessions on this line only. Leave it unset to keep the conversation's own model, and this line's quota.",
5141
+ compactionRouteProvider: "Line",
5142
+ compactionRouteModel: "Model",
5143
+ compactionRouteSave: "Save",
5144
+ compactionRouteClear: "Use the conversation model",
5145
+ compactionRouteChange: "Change",
5146
+ compactionRouteFollow: "Follows the conversation model (default)",
5147
+ compactionRouteSaved: "Saved; it applies to the next compaction.",
5148
+ compactionRouteFailed: "Save failed",
5149
+ compactionRouteNoVideo: "This model takes no video: a session that carries video falls back to the conversation model.",
5150
+ compactionRouteNoImage: "This model takes no images: screenshots become placeholder text in the summary.",
5151
+ compactionRouteEmpty: "No route is available — sign in to one first."
4945
5152
  };
4946
5153
  const dictionaries$10 = {
4947
5154
  zh: zh$10,
@@ -5461,6 +5668,8 @@ window.__ModuleLoader__.load({
5461
5668
  const [savingModel, setSavingModel] = (0, react.useState)(null);
5462
5669
  const [copied, setCopied] = (0, react.useState)(false);
5463
5670
  const [connectionNotice, setConnectionNotice] = (0, react.useState)(null);
5671
+ const [subscriptionApi] = (0, react.useState)(() => new SubscriptionApi());
5672
+ const [compactionPreferences, setCompactionPreferences] = (0, react.useState)(null);
5464
5673
  /** Follow-up poll owed while the host refreshes the quota behind an answer. */
5465
5674
  const quotaFollowUp = (0, react.useRef)(null);
5466
5675
  const t = useLineDict(NS_KIMI_CODE, zh$10);
@@ -5484,6 +5693,16 @@ window.__ModuleLoader__.load({
5484
5693
  setLoading(false);
5485
5694
  }
5486
5695
  }, []);
5696
+ (0, react.useEffect)(() => {
5697
+ let live = true;
5698
+ subscriptionApi.status().then((answer) => {
5699
+ const preferences = answer?.preferences;
5700
+ if (live && preferences !== void 0 && preferences !== null) setCompactionPreferences(preferences);
5701
+ }).catch(() => void 0);
5702
+ return () => {
5703
+ live = false;
5704
+ };
5705
+ }, [subscriptionApi]);
5487
5706
  (0, react.useEffect)(() => {
5488
5707
  loadStatus();
5489
5708
  const refreshWhenVisible = () => {
@@ -5980,33 +6199,35 @@ window.__ModuleLoader__.load({
5980
6199
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5981
6200
  className: "dsha-label",
5982
6201
  children: t.cacheWrite
5983
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
6202
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5984
6203
  className: "dsha-value",
5985
- children: [
5986
- tokenCount(status.cache.cacheWriteTokens, t),
5987
- " · ",
5988
- t.cacheWrite5m,
5989
- " ",
5990
- tokenCount(status.cache.cacheWrite5mTokens, t),
5991
- " · ",
5992
- t.cacheWrite1h,
5993
- " ",
5994
- tokenCount(status.cache.cacheWrite1hTokens, t)
5995
- ]
6204
+ children: tokenCount(status.cache.cacheWriteTokens, t)
5996
6205
  })]
5997
- }),
5998
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5999
- className: "dsha-muted",
6000
- children: t.cacheWriteHint
6001
- }),
6002
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6003
- className: "dsha-muted",
6004
- children: t.cacheHint
6005
6206
  })
6006
6207
  ]
6007
6208
  })
6008
6209
  ]
6009
6210
  }),
6211
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CompactionRouteCard, {
6212
+ api: subscriptionApi,
6213
+ preferences: compactionPreferences,
6214
+ labels: {
6215
+ title: t.compactionRoute,
6216
+ hint: t.compactionRouteHint,
6217
+ provider: t.compactionRouteProvider,
6218
+ model: t.compactionRouteModel,
6219
+ save: t.compactionRouteSave,
6220
+ clear: t.compactionRouteClear,
6221
+ change: t.compactionRouteChange,
6222
+ follow: t.compactionRouteFollow,
6223
+ saved: t.compactionRouteSaved,
6224
+ failed: t.compactionRouteFailed,
6225
+ noVideo: t.compactionRouteNoVideo,
6226
+ noImage: t.compactionRouteNoImage,
6227
+ empty: t.compactionRouteEmpty
6228
+ },
6229
+ onSaved: setCompactionPreferences
6230
+ }),
6010
6231
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
6011
6232
  className: "dsha-group",
6012
6233
  children: [
@@ -18900,259 +19121,6 @@ window.__ModuleLoader__.load({
18900
19121
  });
18901
19122
  }
18902
19123
  //#endregion
18903
- //#region src/client/CompactionRouteCard.tsx
18904
- /**
18905
- * Choose the model an automatic compaction summarizes on.
18906
- *
18907
- * The route lives in this plugin's preferences and is applied by the host's
18908
- * `llm/stream` listener, so a saved choice takes effect on the next compaction
18909
- * without a restart. Both selects are needed together: a model id alone belongs
18910
- * to no route.
18911
- *
18912
- * The picker loads its directory on demand rather than on mount: opening the
18913
- * settings page must not cost a request to every line (that page is opened for
18914
- * unrelated reasons all the time), and the card already knows the saved route
18915
- * from the status it was rendered with.
18916
- */
18917
- function CompactionRouteCard({ api, preferences, disabled = false, labels, onSaved }) {
18918
- const [picking, setPicking] = (0, react.useState)(false);
18919
- const [candidates, setCandidates] = (0, react.useState)(null);
18920
- const [loadError, setLoadError] = (0, react.useState)(null);
18921
- const [provider, setProvider] = (0, react.useState)("");
18922
- const [model, setModel] = (0, react.useState)("");
18923
- const [busy, setBusy] = (0, react.useState)(false);
18924
- const [notice, setNotice] = (0, react.useState)(null);
18925
- const [error, setError] = (0, react.useState)(null);
18926
- const load = (0, react.useCallback)(async () => {
18927
- try {
18928
- const value = await api.compactionCandidates();
18929
- setCandidates(value);
18930
- setLoadError(null);
18931
- } catch (cause) {
18932
- setLoadError(cause instanceof Error ? cause.message : String(cause));
18933
- }
18934
- }, [api]);
18935
- (0, react.useEffect)(() => {
18936
- if (picking && candidates === null) load();
18937
- }, [
18938
- picking,
18939
- candidates,
18940
- load
18941
- ]);
18942
- (0, react.useEffect)(() => {
18943
- const saved = preferences?.compactionRoute ?? null;
18944
- if (saved === null) return;
18945
- setProvider((current) => current === "" ? saved.provider : current);
18946
- setModel((current) => current === "" ? saved.model : current);
18947
- }, [preferences?.compactionRoute]);
18948
- const providers = candidates?.providers ?? [];
18949
- const models = providers.find((entry) => entry.id === provider)?.models ?? [];
18950
- const selected = models.find((entry) => entry.id === model);
18951
- const savedProvider = preferences?.compactionRoute?.provider ?? "";
18952
- const savedModel = preferences?.compactionRoute?.model ?? "";
18953
- const canSave = provider !== "" && model !== "" && (provider !== savedProvider || model !== savedModel) && !busy && !disabled;
18954
- const providerOptions = savedProvider === "" || providers.some((entry) => entry.id === savedProvider) ? providers : [{
18955
- id: savedProvider,
18956
- name: savedProvider
18957
- }, ...providers];
18958
- const modelOptions = model !== "" && !models.some((entry) => entry.id === model) ? [{
18959
- id: model,
18960
- name: model,
18961
- modalities: []
18962
- }, ...models] : models;
18963
- const currentLabel = savedProvider === "" ? labels.follow : `${savedProvider} / ${savedModel}`;
18964
- const save = async (target) => {
18965
- setBusy(true);
18966
- setError(null);
18967
- setNotice(null);
18968
- try {
18969
- onSaved(await api.updatePreferences({ compactionRoute: target }));
18970
- setNotice(labels.saved);
18971
- setPicking(false);
18972
- } catch (cause) {
18973
- setError(cause instanceof Error ? cause.message : String(cause));
18974
- } finally {
18975
- setBusy(false);
18976
- }
18977
- };
18978
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
18979
- className: "dsha-group",
18980
- children: [
18981
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
18982
- className: "dsha-grouphead",
18983
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: labels.title })
18984
- }),
18985
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
18986
- className: "dsha-muted",
18987
- children: labels.hint
18988
- }),
18989
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
18990
- className: "dsha-pref-row",
18991
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: currentLabel }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
18992
- className: "dsha-btn",
18993
- type: "button",
18994
- disabled: busy || disabled,
18995
- onClick: () => setPicking((current) => !current),
18996
- children: labels.change
18997
- })]
18998
- }),
18999
- picking ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
19000
- loadError !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
19001
- className: "dsh-codex-error",
19002
- role: "alert",
19003
- children: loadError
19004
- }) : null,
19005
- candidates !== null && providers.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
19006
- className: "dsh-codex-empty",
19007
- children: labels.empty
19008
- }) : null,
19009
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
19010
- className: "dsha-pref-row",
19011
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: labels.provider }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
19012
- className: "dsha-select",
19013
- "aria-label": labels.provider,
19014
- value: provider,
19015
- disabled: busy || disabled,
19016
- onChange: (event) => {
19017
- setProvider(event.currentTarget.value);
19018
- setModel("");
19019
- },
19020
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
19021
- value: "",
19022
- children: "—"
19023
- }), providerOptions.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
19024
- value: entry.id,
19025
- children: entry.name
19026
- }, entry.id))]
19027
- })]
19028
- }),
19029
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
19030
- className: "dsha-pref-row",
19031
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: labels.model }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
19032
- className: "dsha-select",
19033
- "aria-label": labels.model,
19034
- value: model,
19035
- disabled: busy || disabled || provider === "",
19036
- onChange: (event) => setModel(event.currentTarget.value),
19037
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
19038
- value: "",
19039
- children: "—"
19040
- }), modelOptions.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
19041
- value: entry.id,
19042
- children: entry.name
19043
- }, entry.id))]
19044
- })]
19045
- }),
19046
- selected !== void 0 && selected.modalities.length > 0 && !selected.modalities.includes("video") ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
19047
- className: "dsh-codex-warning",
19048
- children: labels.noVideo
19049
- }) : null,
19050
- selected !== void 0 && selected.modalities.length > 0 && !selected.modalities.includes("image") ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
19051
- className: "dsh-codex-warning",
19052
- children: labels.noImage
19053
- }) : null,
19054
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
19055
- className: "dsha-actions",
19056
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
19057
- className: "dsha-btn dsha-btn-primary",
19058
- type: "button",
19059
- disabled: !canSave,
19060
- onClick: () => void save({
19061
- provider,
19062
- model
19063
- }),
19064
- children: labels.save
19065
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
19066
- className: "dsha-btn",
19067
- type: "button",
19068
- disabled: busy || disabled || savedProvider === "",
19069
- onClick: () => void save(null),
19070
- children: labels.clear
19071
- })]
19072
- })
19073
- ] }) : null,
19074
- notice !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
19075
- className: "dsh-codex-success",
19076
- role: "status",
19077
- children: notice
19078
- }) : null,
19079
- error !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
19080
- className: "dsh-codex-error",
19081
- role: "alert",
19082
- children: [
19083
- labels.failed,
19084
- ": ",
19085
- error
19086
- ]
19087
- }) : null
19088
- ]
19089
- });
19090
- }
19091
- //#endregion
19092
- //#region src/client/CompactionRouteBundleConfig.tsx
19093
- /**
19094
- * Compaction-model settings on this plugin's own page in the DSH plugin
19095
- * manager.
19096
- *
19097
- * The setting is cross-line, so it does not belong inside any one provider's
19098
- * tab: the plugin's page is where an operator configures *this plugin*, and the
19099
- * bundle-config slot places it between the package description and the rows the
19100
- * bundle declares.
19101
- *
19102
- * The slot is keyed by the bundle's package name, so the page dispatches it for
19103
- * this package only — no subject filter is needed. It also renders its
19104
- * occupants in the `summary` view (a one-liner beside the card); bundle
19105
- * configuration has nothing to say there and renders null.
19106
- *
19107
- * @module dsh-chatgpt-subscription/compaction-route-bundle-config
19108
- */
19109
- /** The package whose page this entry configures. */
19110
- const COMPACTION_ROUTE_PACKAGE = "@eddyskywalker/dsh-chatgpt-subscription";
19111
- /**
19112
- * Render the compaction-model card on this package's bundle page only.
19113
- *
19114
- * Split in two so the hooks below never mount for the summary view: that view
19115
- * is a one-liner drawn beside the card, and fetching this plugin's preferences
19116
- * for it would be a request nobody asked for.
19117
- */
19118
- function CompactionRouteBundleConfig(props) {
19119
- if (props.view !== "page") return null;
19120
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OwnedSection, { t: props.t });
19121
- }
19122
- function OwnedSection({ t }) {
19123
- const [preferences, setPreferences] = (0, react.useState)(null);
19124
- const [api] = (0, react.useState)(() => new SubscriptionApi());
19125
- (0, react.useEffect)(() => {
19126
- let live = true;
19127
- api.status().then((status) => {
19128
- if (live) setPreferences(status.preferences);
19129
- }).catch(() => void 0);
19130
- return () => {
19131
- live = false;
19132
- };
19133
- }, [api]);
19134
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CompactionRouteCard, {
19135
- api,
19136
- preferences,
19137
- labels: {
19138
- title: t("compactionRoute"),
19139
- hint: t("compactionRouteHint"),
19140
- provider: t("compactionRouteProvider"),
19141
- model: t("compactionRouteModel"),
19142
- save: t("compactionRouteSave"),
19143
- clear: t("compactionRouteClear"),
19144
- change: t("compactionRouteChange"),
19145
- follow: t("compactionRouteFollow"),
19146
- saved: t("compactionRouteSaved"),
19147
- failed: t("compactionRouteFailed"),
19148
- noVideo: t("compactionRouteNoVideo"),
19149
- noImage: t("compactionRouteNoImage"),
19150
- empty: t("compactionRouteEmpty")
19151
- },
19152
- onSaved: setPreferences
19153
- });
19154
- }
19155
- //#endregion
19156
19124
  //#region src/client/locales.ts
19157
19125
  const NS = "dsh-chatgpt-subscription";
19158
19126
  const dictionaries$1 = {
@@ -19190,19 +19158,6 @@ window.__ModuleLoader__.load({
19190
19158
  proxyModeAuto: "系统代理(自动检测)",
19191
19159
  proxyModeCustom: "自定义代理",
19192
19160
  proxyModeDirect: "直连(禁用代理)",
19193
- compactionRoute: "压缩模型",
19194
- compactionRouteHint: "自动压缩时由哪个模型来写摘要。留空则继续用会话自己的模型,也就继续消耗那条线路的额度。",
19195
- compactionRouteProvider: "线路",
19196
- compactionRouteModel: "模型",
19197
- compactionRouteSave: "保存",
19198
- compactionRouteClear: "恢复会话模型",
19199
- compactionRouteChange: "设置",
19200
- compactionRouteFollow: "跟随会话模型(默认)",
19201
- compactionRouteSaved: "已保存,下次压缩生效。",
19202
- compactionRouteFailed: "保存失败",
19203
- compactionRouteNoVideo: "该模型不接受视频:含视频的会话会回落到会话模型完成压缩。",
19204
- compactionRouteNoImage: "该模型不接受图片:摘要里的截图会被替换为占位文本。",
19205
- compactionRouteEmpty: "没有可用的线路——先登录任意一条线路再回来设置。",
19206
19161
  customProxyUrl: "自定义代理地址",
19207
19162
  customProxyUrlHint: "支持 HTTP/HTTPS/SOCKS5 代理地址,例如 http://127.0.0.1:7890",
19208
19163
  customProxyUrlPlaceholder: "例如: http://127.0.0.1:7890",
@@ -19400,19 +19355,6 @@ window.__ModuleLoader__.load({
19400
19355
  proxyModeAuto: "System Proxy (Auto-detect)",
19401
19356
  proxyModeCustom: "Custom Proxy",
19402
19357
  proxyModeDirect: "Direct (No Proxy)",
19403
- compactionRoute: "Compaction model",
19404
- compactionRouteHint: "Which model writes the summary when the context is compacted. Leave it unset to keep the conversation's own model — and that line's quota.",
19405
- compactionRouteProvider: "Line",
19406
- compactionRouteModel: "Model",
19407
- compactionRouteSave: "Save",
19408
- compactionRouteClear: "Use the conversation model",
19409
- compactionRouteChange: "Change",
19410
- compactionRouteFollow: "Follows the conversation model (default)",
19411
- compactionRouteSaved: "Saved; it applies to the next compaction.",
19412
- compactionRouteFailed: "Save failed",
19413
- compactionRouteNoVideo: "This model takes no video: a session that carries video falls back to the conversation model.",
19414
- compactionRouteNoImage: "This model takes no images: screenshots become placeholder text in the summary.",
19415
- compactionRouteEmpty: "No route is available — sign in to one first.",
19416
19358
  customProxyUrl: "Custom Proxy URL",
19417
19359
  customProxyUrlHint: "Supports HTTP/HTTPS/SOCKS5 proxy URLs, e.g. http://127.0.0.1:7890",
19418
19360
  customProxyUrlPlaceholder: "e.g. http://127.0.0.1:7890",
@@ -20290,19 +20232,6 @@ window.__ModuleLoader__.load({
20290
20232
  proxyModeAuto: "Системный прокси (автоопределение)",
20291
20233
  proxyModeCustom: "Свой прокси",
20292
20234
  proxyModeDirect: "Напрямую (без прокси)",
20293
- compactionRoute: "Модель сжатия",
20294
- compactionRouteHint: "Какая модель пишет сводку при сжатии контекста. Если не выбрана, используется модель диалога — и её лимит.",
20295
- compactionRouteProvider: "Линия",
20296
- compactionRouteModel: "Модель",
20297
- compactionRouteSave: "Сохранить",
20298
- compactionRouteClear: "Модель диалога",
20299
- compactionRouteChange: "Изменить",
20300
- compactionRouteFollow: "Как в диалоге (по умолчанию)",
20301
- compactionRouteSaved: "Сохранено; применится при следующем сжатии.",
20302
- compactionRouteFailed: "Не удалось сохранить",
20303
- compactionRouteNoVideo: "Модель не принимает видео: диалог с видео вернётся к модели диалога.",
20304
- compactionRouteNoImage: "Модель не принимает изображения: скриншоты в сводке станут текстом-заглушкой.",
20305
- compactionRouteEmpty: "Нет доступных линий — сначала войдите в любую.",
20306
20235
  customProxyUrl: "Адрес своего прокси",
20307
20236
  customProxyUrlHint: "Поддерживаются адреса HTTP/HTTPS/SOCKS5, например http://127.0.0.1:7890",
20308
20237
  customProxyUrlPlaceholder: "например http://127.0.0.1:7890",
@@ -20566,11 +20495,7 @@ window.__ModuleLoader__.load({
20566
20495
  cacheOutput: "Выходные токены",
20567
20496
  cacheRequests: "Запросы",
20568
20497
  cacheWrite: "Запись в кеш",
20569
- cacheWrite5m: "Уровень 5 минут",
20570
- cacheWrite1h: "Уровень 1 час",
20571
- cacheWriteHint: "Запись — отдельная статья биллинга: запись на 1 ч стоит примерно вдвое дороже записи на 5 мин, а попадание продлевает её по исходному TTL. 0 на уровне означает, что сервис не сообщил запись для этого уровня (отсутствующий заголовок — молчание), а не то, что там ничего не записали.",
20572
20498
  unknownMetric: "нет данных",
20573
- cacheHint: "В пределах одной сессии: любое изменение системного промпта или списка инструментов сбрасывает весь префиксный кеш, поэтому держите набор инструментов стабильным, а новое добавляйте в конец — так доля попаданий выше.",
20574
20499
  cacheExpired: "Сессия простаивала дольше времени жизни кеша, поэтому следующий ход заново обработает около {tokens} токенов.",
20575
20500
  cacheDrift: "Состояние последнего префикса",
20576
20501
  cacheDriftFirst: "Первый запрос (кеш пуст)",
@@ -20621,7 +20546,20 @@ window.__ModuleLoader__.load({
20621
20546
  loading: "Загрузка…",
20622
20547
  loginFailed: "Вход не выполнен",
20623
20548
  requests: "запросов",
20624
- tokens: "токенов"
20549
+ tokens: "токенов",
20550
+ compactionRoute: "Модель сжатия",
20551
+ compactionRouteHint: "Какая модель пишет сводку при сжатии контекста — только для диалогов этой линии. Если не выбрана, используется модель диалога — и её лимит.",
20552
+ compactionRouteProvider: "Линия",
20553
+ compactionRouteModel: "Модель",
20554
+ compactionRouteSave: "Сохранить",
20555
+ compactionRouteClear: "Модель диалога",
20556
+ compactionRouteChange: "Изменить",
20557
+ compactionRouteFollow: "Как в диалоге (по умолчанию)",
20558
+ compactionRouteSaved: "Сохранено; применится при следующем сжатии.",
20559
+ compactionRouteFailed: "Не удалось сохранить",
20560
+ compactionRouteNoVideo: "Модель не принимает видео: диалог с видео вернётся к модели диалога.",
20561
+ compactionRouteNoImage: "Модель не принимает изображения: скриншоты в сводке станут текстом-заглушкой.",
20562
+ compactionRouteEmpty: "Нет доступных линий — сначала войдите в любую."
20625
20563
  };
20626
20564
  //#endregion
20627
20565
  //#region src/client/locales-ru/minimax-code.ts
@@ -54204,11 +54142,6 @@ window.__ModuleLoader__.load({
54204
54142
  label: () => t("hubTitle"),
54205
54143
  locale: NS
54206
54144
  }, ProviderHubSectionWrapper));
54207
- ctx.slots.inject("plugins.bundle.config", () => ctx.slots.register({
54208
- name: "plugins.bundle.config",
54209
- key: COMPACTION_ROUTE_PACKAGE,
54210
- locale: NS
54211
- }, CompactionRouteBundleConfig));
54212
54145
  ctx.slots.inject("conversation.input.right", () => ctx.slots.register({
54213
54146
  name: "conversation.input.right",
54214
54147
  id: "codex-subscription-quota",