dsh-subscriptions 0.2.0 → 0.2.3
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/README.md +220 -180
- package/lib/auth/claude-code-creds.js +31 -9
- package/lib/auth/device-flow.js +16 -9
- package/lib/auth/oauth-flow.js +13 -10
- package/lib/auth/rpc.d.ts +8 -4
- package/lib/auth/rpc.js +89 -25
- package/lib/auth/store.js +80 -31
- package/lib/client/CursorCard.d.ts +1 -1
- package/lib/client/CursorCard.js +50 -16
- package/lib/client/ExternalUsageCards.d.ts +1 -1
- package/lib/client/ExternalUsageCards.js +37 -16
- package/lib/client/ImageGallery.d.ts +2 -2
- package/lib/client/ImageGallery.js +92 -33
- package/lib/client/ImageGenerateToolview.js +50 -14
- package/lib/client/OllamaCard.d.ts +1 -1
- package/lib/client/OllamaCard.js +26 -8
- package/lib/client/OllamaSettingsManager.js +113 -30
- package/lib/client/ProviderAccountManager.d.ts +1 -1
- package/lib/client/ProviderAccountManager.js +164 -49
- package/lib/client/ProviderModelEditor.js +137 -34
- package/lib/client/SpeedSelect.d.ts +62 -14
- package/lib/client/SpeedSelect.js +261 -49
- package/lib/client/SubscriptionUsageBadge.d.ts +4 -10
- package/lib/client/SubscriptionUsageBadge.js +300 -112
- package/lib/client/SubscriptionsSection.d.ts +2 -0
- package/lib/client/SubscriptionsSection.js +265 -85
- package/lib/client/UsageBadgeDisplaySetting.js +35 -12
- package/lib/client/UsageMeter.d.ts +1 -1
- package/lib/client/UsageMeter.js +62 -24
- package/lib/client/VideoGenerateToolview.js +69 -17
- package/lib/client/account-preferences.d.ts +8 -0
- package/lib/client/account-preferences.js +20 -5
- package/lib/client/host-contract.d.ts +11 -2
- package/lib/client/host-contract.js +20 -3
- package/lib/client/index.d.ts +5 -5
- package/lib/client/index.js +34 -12
- package/lib/client/locales.d.ts +22 -0
- package/lib/client/locales.js +73 -51
- package/lib/client/model-filter.d.ts +37 -0
- package/lib/client/model-filter.js +45 -0
- package/lib/client/ollama-draft.js +21 -12
- package/lib/client/reset-action.js +6 -4
- package/lib/client/reset-credits-view.d.ts +1 -1
- package/lib/client/reset-credits-view.js +66 -17
- package/lib/client/subscription-card-styles.js +94 -24
- package/lib/client/usage-color-preferences.js +4 -2
- package/lib/client/usage-pace.js +30 -12
- package/lib/client/usage-window-filter.d.ts +7 -0
- package/lib/client/usage-window-filter.js +12 -0
- package/lib/client.js +480 -136
- package/lib/client.js.map +1 -1
- package/lib/http.js +1 -1
- package/lib/index.d.ts +3 -3
- package/lib/index.js +167 -76
- package/lib/model-defaults.js +4 -2
- package/lib/provider-settings.d.ts +17 -0
- package/lib/provider-settings.js +79 -16
- package/lib/providers/account-preferences.js +51 -22
- package/lib/providers/accounts.js +15 -9
- package/lib/providers/antigravity-oauth-client.js +2 -2
- package/lib/providers/antigravity.js +141 -68
- package/lib/providers/catalog-store.js +120 -41
- package/lib/providers/claude-cli-version.js +3 -3
- package/lib/providers/claude.d.ts +9 -2
- package/lib/providers/claude.js +153 -90
- package/lib/providers/codex-client-version.js +3 -3
- package/lib/providers/codex-search.js +40 -13
- package/lib/providers/codex.js +196 -112
- package/lib/providers/common.js +71 -39
- package/lib/providers/copilot.js +143 -82
- package/lib/providers/cursor-adapter.js +42 -18
- package/lib/providers/cursor-auth.d.ts +1 -1
- package/lib/providers/cursor-auth.js +69 -24
- package/lib/providers/cursor-model-settings.js +14 -5
- package/lib/providers/cursor-usage.js +27 -13
- package/lib/providers/external-usage-controller.js +14 -5
- package/lib/providers/external-usage.js +59 -23
- package/lib/providers/grok.js +152 -89
- package/lib/providers/image-pool.js +29 -11
- package/lib/providers/minimax-usage.js +31 -11
- package/lib/providers/npm-cli-version.js +10 -3
- package/lib/providers/ollama-settings.js +24 -8
- package/lib/providers/ollama-web.js +29 -11
- package/lib/providers/ollama.js +129 -66
- package/lib/providers/pool-family.js +11 -3
- package/lib/providers/pool-health.js +28 -8
- package/lib/providers/pool-usage.js +16 -9
- package/lib/providers/pool.d.ts +8 -0
- package/lib/providers/pool.js +81 -42
- package/lib/providers/rate-limit.js +8 -2
- package/lib/providers/replay.js +40 -17
- package/lib/providers/reset-redemption.js +18 -6
- package/lib/tools/image-generate.js +123 -65
- package/lib/tools/registration.js +5 -2
- package/lib/tools/video-generate.js +51 -31
- package/lib/tools/x-search.js +50 -22
- package/lib/translate/anthropic.js +85 -37
- package/lib/translate/antigravity-schema.js +71 -28
- package/lib/translate/antigravity-thinking.js +22 -9
- package/lib/translate/antigravity.js +151 -52
- package/lib/translate/chat-completions.js +67 -23
- package/lib/translate/resolved.js +55 -21
- package/lib/translate/responses.js +69 -29
- package/lib/translate/sse.js +4 -1
- package/package.json +27 -4
package/lib/client.js
CHANGED
|
@@ -56,7 +56,7 @@ const en = {
|
|
|
56
56
|
accountsCatalogUnavailable: "Model discovery is currently unavailable. Saved model selections are preserved.",
|
|
57
57
|
modelsPartialSave: "Some reasoning defaults were saved. Remaining changes are still in the draft; retry to finish.",
|
|
58
58
|
modelsEdit: "Edit model list",
|
|
59
|
-
modelsHint: "Configure model visibility, default reasoning effort, context and tools here; Save changes below submits them together with the account settings. Hidden models remain usable by existing sessions; tool changes apply to new sessions.",
|
|
59
|
+
modelsHint: "Configure model visibility, default reasoning effort, subagent speed, context and tools here; Save changes below submits them together with the account settings. Hidden models remain usable by existing sessions; tool changes apply to new sessions.",
|
|
60
60
|
modelsAutomatic: "Automatically show all models, including new models",
|
|
61
61
|
modelsSelectAll: "Select all",
|
|
62
62
|
modelsSelectNone: "Clear selection",
|
|
@@ -64,6 +64,8 @@ const en = {
|
|
|
64
64
|
modelsContextBounds: "Default {default} · Maximum {max}",
|
|
65
65
|
modelsContextHint: "Leave blank to follow the provider. The requested window is capped by the account’s advertised maximum; account pools use the smallest member window. This controls local history and compaction, not the API’s capacity.",
|
|
66
66
|
modelsContextInvalid: "Enter a positive whole number of tokens for {model}.",
|
|
67
|
+
modelsSubagentSpeed: "Subagent default speed",
|
|
68
|
+
modelsSubagentSpeedHint: "Applies to subsequent subagent requests using this model, regardless of the parent model. Main conversations keep their own Speed setting. Fast requires model support.",
|
|
67
69
|
modelsTools: "Provider tools",
|
|
68
70
|
modelsToolsHint: "Changes apply only to sessions created after saving. Existing sessions keep their tools. Shared tools remain available if any supporting provider is enabled.",
|
|
69
71
|
modelsImage: "Image generation",
|
|
@@ -72,6 +74,11 @@ const en = {
|
|
|
72
74
|
modelsWebSearch: "Web search",
|
|
73
75
|
modelsSave: "Save changes",
|
|
74
76
|
modelsUnavailable: "Currently unavailable",
|
|
77
|
+
claudeCacheTtl: "Prompt cache lifetime",
|
|
78
|
+
claudeCacheTtlFollowConfig: "Follow config",
|
|
79
|
+
claudeCacheTtl5m: "5 minutes",
|
|
80
|
+
claudeCacheTtl1h: "1 hour",
|
|
81
|
+
claudeCacheTtlHint: "Overrides the claudePromptCacheTtl config for this Claude route; Follow config uses the configured value. Saved changes apply from the next request. One hour keeps cached prefixes longer but writes them at a higher input price; compaction and session-title requests stay at five minutes.",
|
|
75
82
|
nav: "Subscriptions",
|
|
76
83
|
intro: "Log a subscription provider in or out. Login opens the provider’s authorization page in a new tab; headless setups can paste the callback URL or code instead.",
|
|
77
84
|
unavailable: "Connection unavailable; subscription status cannot be loaded.",
|
|
@@ -108,6 +115,7 @@ const en = {
|
|
|
108
115
|
usageRefresh: "Refresh",
|
|
109
116
|
usageLoading: "Loading usage…",
|
|
110
117
|
usageEmpty: "No usage windows reported.",
|
|
118
|
+
usageNoVisibleModels: "No usage windows for the models visible in the picker.",
|
|
111
119
|
usageUnsupported: "This endpoint has no usage surface.",
|
|
112
120
|
usageError: "Usage lookup failed: {message}",
|
|
113
121
|
usageSession: "5-hour window",
|
|
@@ -206,6 +214,9 @@ const en = {
|
|
|
206
214
|
modelDefaultsSummaryEmpty: "No model advertises reasoning levels",
|
|
207
215
|
modelDefaultsFilterPlaceholder: "Filter models",
|
|
208
216
|
modelDefaultsFilterEmpty: "No model matches “{query}”.",
|
|
217
|
+
modelDefaultsNoModels: "This provider reports no models.",
|
|
218
|
+
modelDefaultsNoAccount: "No connected accounts. Connect one on the provider card first.",
|
|
219
|
+
modelDefaultsDiscoveryFailed: "Model discovery failed or timed out. Use Refresh to try again.",
|
|
209
220
|
generating: "Generating image…",
|
|
210
221
|
image: "image",
|
|
211
222
|
viewImage: "View image",
|
|
@@ -291,7 +302,7 @@ const zh = {
|
|
|
291
302
|
accountsCatalogUnavailable: "暂时无法获取模型目录,已保存的模型选择会被保留。",
|
|
292
303
|
modelsPartialSave: "部分推理档已保存,其余更改仍保留在草稿中,请重试完成保存。",
|
|
293
304
|
modelsEdit: "编辑模型列表",
|
|
294
|
-
modelsHint: "
|
|
305
|
+
modelsHint: "在此配置模型显示、默认推理档、子代理速度、上下文与工具,底部的「保存更改」会连同账号设置一起提交。隐藏模型不影响已有会话;工具开关对新建会话生效。",
|
|
295
306
|
modelsAutomatic: "自动显示全部模型(包含以后新增的模型)",
|
|
296
307
|
modelsSelectAll: "全选",
|
|
297
308
|
modelsSelectNone: "清空选择",
|
|
@@ -299,6 +310,8 @@ const zh = {
|
|
|
299
310
|
modelsContextBounds: "默认 {default} · 最大 {max}",
|
|
300
311
|
modelsContextHint: "留空跟随服务商。配置值按账号返回的最大窗口限制;账号池取各成员的最小窗口。此设置控制本地历史保留和压缩时机,不会扩大 API 本身的容量。",
|
|
301
312
|
modelsContextInvalid: "请为 {model} 输入正整数 token 数。",
|
|
313
|
+
modelsSubagentSpeed: "子代理默认速度",
|
|
314
|
+
modelsSubagentSpeedHint: "对使用此模型的子代理后续请求生效,与父会话模型无关。主会话仍使用自己的速度设置;快速档仅在模型支持时生效。",
|
|
302
315
|
modelsTools: "Provider 工具",
|
|
303
316
|
modelsToolsHint: "仅对保存后新建的会话生效,已有会话保留原工具。任一支持的服务商启用共享工具时,该工具仍会显示。",
|
|
304
317
|
modelsImage: "图片生成",
|
|
@@ -307,6 +320,11 @@ const zh = {
|
|
|
307
320
|
modelsWebSearch: "网页搜索",
|
|
308
321
|
modelsSave: "保存更改",
|
|
309
322
|
modelsUnavailable: "当前不可用",
|
|
323
|
+
claudeCacheTtl: "提示词缓存时长",
|
|
324
|
+
claudeCacheTtlFollowConfig: "跟随配置",
|
|
325
|
+
claudeCacheTtl5m: "5 分钟",
|
|
326
|
+
claudeCacheTtl1h: "1 小时",
|
|
327
|
+
claudeCacheTtlHint: "覆盖此 Claude 路由的 claudePromptCacheTtl 配置;选择「跟随配置」时使用配置值。保存后自下一次请求生效。1 小时可更久保留缓存前缀,但写入按更高的输入价格计费;压缩与会话标题请求仍为 5 分钟。",
|
|
310
328
|
nav: "订阅",
|
|
311
329
|
intro: "在此登录或退出订阅服务商。点击登录会在新标签页打开服务商的授权页面;无浏览器环境可改为粘贴回调 URL 或授权码。",
|
|
312
330
|
unavailable: "连接不可用,无法加载订阅状态。",
|
|
@@ -343,6 +361,7 @@ const zh = {
|
|
|
343
361
|
usageRefresh: "刷新",
|
|
344
362
|
usageLoading: "用量加载中…",
|
|
345
363
|
usageEmpty: "服务商未返回任何用量窗口。",
|
|
364
|
+
usageNoVisibleModels: "选择器中的可见模型没有任何用量窗口。",
|
|
346
365
|
usageUnsupported: "该端点不提供用量查询。",
|
|
347
366
|
usageError: "用量查询失败:{message}",
|
|
348
367
|
usageSession: "5 小时窗口",
|
|
@@ -441,6 +460,9 @@ const zh = {
|
|
|
441
460
|
modelDefaultsSummaryEmpty: "没有模型声明推理档",
|
|
442
461
|
modelDefaultsFilterPlaceholder: "筛选模型",
|
|
443
462
|
modelDefaultsFilterEmpty: "没有匹配「{query}」的模型。",
|
|
463
|
+
modelDefaultsNoModels: "这个服务商没有报告任何模型。",
|
|
464
|
+
modelDefaultsNoAccount: "还没有连接账号。请先在卡片上连接一个账号。",
|
|
465
|
+
modelDefaultsDiscoveryFailed: "模型列表发现失败或超时。可以点「刷新」重试。",
|
|
444
466
|
generating: "正在生成图片…",
|
|
445
467
|
image: "图片",
|
|
446
468
|
viewImage: "查看图片",
|
|
@@ -517,6 +539,19 @@ function resetActionBlock(usage, now = Date.now()) {
|
|
|
517
539
|
if (!usage.resetCredits.some((c) => c.id && (c.expiresAt === void 0 || c.expiresAt > now))) return "resetUseRefresh";
|
|
518
540
|
}
|
|
519
541
|
|
|
542
|
+
//#endregion
|
|
543
|
+
//#region src/client/usage-window-filter.ts
|
|
544
|
+
/**
|
|
545
|
+
* Keep model-scoped Google quotas aligned with the saved model-picker selection.
|
|
546
|
+
* Account-wide windows and other providers are unaffected; an absent selection
|
|
547
|
+
* follows discovery, while an empty selection hides every model-scoped window.
|
|
548
|
+
*/
|
|
549
|
+
function visibleUsageWindows(provider, windows, visibleModels) {
|
|
550
|
+
if (windows === void 0) return [];
|
|
551
|
+
if (provider !== "antigravity" || visibleModels === void 0) return windows;
|
|
552
|
+
return windows.filter((window$1) => !window$1.scope || visibleModels.includes(window$1.scope));
|
|
553
|
+
}
|
|
554
|
+
|
|
520
555
|
//#endregion
|
|
521
556
|
//#region src/client/subscriptions-rpc.ts
|
|
522
557
|
/**
|
|
@@ -576,6 +611,19 @@ function mergeLatestAccounts(draft, latest) {
|
|
|
576
611
|
...latest.accounts === void 0 ? {} : { accounts: latest.accounts }
|
|
577
612
|
};
|
|
578
613
|
}
|
|
614
|
+
/**
|
|
615
|
+
* The account manager owns the Claude prompt-cache TTL picker. The model
|
|
616
|
+
* editor's draft carries the TTL it loaded, so the dialog always replaces it
|
|
617
|
+
* with the picker's choice when touched, or the freshly re-read value
|
|
618
|
+
* otherwise.
|
|
619
|
+
*/
|
|
620
|
+
function mergePromptCacheTtl(settings, promptCacheTtl) {
|
|
621
|
+
const { promptCacheTtl: _stale,...rest } = settings;
|
|
622
|
+
return promptCacheTtl === void 0 ? rest : {
|
|
623
|
+
...rest,
|
|
624
|
+
promptCacheTtl
|
|
625
|
+
};
|
|
626
|
+
}
|
|
579
627
|
/** Only touched accounts replace their latest preferences; other settings survive. */
|
|
580
628
|
function mergeAccountChanges(latest, changes) {
|
|
581
629
|
return {
|
|
@@ -587,6 +635,43 @@ function mergeAccountChanges(latest, changes) {
|
|
|
587
635
|
};
|
|
588
636
|
}
|
|
589
637
|
|
|
638
|
+
//#endregion
|
|
639
|
+
//#region src/client/model-filter.ts
|
|
640
|
+
/** Distinguish an empty provider from a query that filtered out every row. */
|
|
641
|
+
function modelListState(total, matched) {
|
|
642
|
+
if (total === 0) return "provider-empty";
|
|
643
|
+
return matched === 0 ? "filter-empty" : "rows";
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* Retain saved model ids when discovery returns no rows. A temporary catalog
|
|
647
|
+
* failure must not hide the visibility or subagent-speed settings they edit.
|
|
648
|
+
*/
|
|
649
|
+
function savedModelIds(visibleModels, subagentSpeeds) {
|
|
650
|
+
return [...new Set([...visibleModels ?? [], ...Object.keys(subagentSpeeds ?? {})])];
|
|
651
|
+
}
|
|
652
|
+
/**
|
|
653
|
+
* Use account discovery evidence to explain an empty provider. Connected
|
|
654
|
+
* accounts with failed discovery need a retry, not another login prompt.
|
|
655
|
+
*/
|
|
656
|
+
function modelEditorNotice(accounts) {
|
|
657
|
+
if (accounts === void 0) return "noModels";
|
|
658
|
+
if (accounts.length === 0) return "noAccount";
|
|
659
|
+
return accounts.every((account) => account.unavailable === true) ? "discoveryFailed" : "noModels";
|
|
660
|
+
}
|
|
661
|
+
/** Locale keys for the three empty states. */
|
|
662
|
+
const NOTICE_KEYS = {
|
|
663
|
+
noAccount: "modelDefaultsNoAccount",
|
|
664
|
+
discoveryFailed: "modelDefaultsDiscoveryFailed",
|
|
665
|
+
noModels: "modelDefaultsNoModels"
|
|
666
|
+
};
|
|
667
|
+
/** Combine discovered and saved rows, filter results, and discovery evidence. */
|
|
668
|
+
function modelEditorState(input) {
|
|
669
|
+
return {
|
|
670
|
+
listState: modelListState(input.discovered + input.saved, input.matched),
|
|
671
|
+
noticeKey: NOTICE_KEYS[modelEditorNotice(input.accounts)]
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
|
|
590
675
|
//#endregion
|
|
591
676
|
//#region src/client/ProviderModelEditor.tsx
|
|
592
677
|
/** Providers whose runtime honours a local context-window override. */
|
|
@@ -683,16 +768,23 @@ const ProviderModelEditor = (0, react.forwardRef)(function ProviderModelEditor$1
|
|
|
683
768
|
draft,
|
|
684
769
|
contexts,
|
|
685
770
|
efforts,
|
|
686
|
-
provider
|
|
771
|
+
provider,
|
|
772
|
+
t
|
|
687
773
|
]);
|
|
688
774
|
const allModels = catalog?.models ?? [];
|
|
689
775
|
const known = new Set(allModels.map((model) => model.id));
|
|
690
|
-
const missing = (draft.visibleModels
|
|
776
|
+
const missing = savedModelIds(draft.visibleModels, draft.subagentSpeeds).filter((id) => !known.has(id)).map((id) => ({
|
|
691
777
|
id,
|
|
692
778
|
name: id
|
|
693
779
|
}));
|
|
694
780
|
const models = [...allModels, ...missing].filter((model) => `${model.name} ${model.id}`.toLowerCase().includes(query.trim().toLowerCase()));
|
|
695
|
-
const
|
|
781
|
+
const editorState = modelEditorState({
|
|
782
|
+
discovered: allModels.length,
|
|
783
|
+
saved: missing.length,
|
|
784
|
+
matched: models.length,
|
|
785
|
+
accounts: catalog?.accounts
|
|
786
|
+
});
|
|
787
|
+
const selected = new Set(draft.visibleModels ?? [...allModels, ...missing].map((model) => model.id));
|
|
696
788
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
697
789
|
style: {
|
|
698
790
|
borderTop: border$2,
|
|
@@ -732,7 +824,7 @@ const ProviderModelEditor = (0, react.forwardRef)(function ProviderModelEditor$1
|
|
|
732
824
|
children: t("modelDefaultsLoading")
|
|
733
825
|
})]
|
|
734
826
|
}),
|
|
735
|
-
catalog && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("fieldset", {
|
|
827
|
+
!busy && catalog && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("fieldset", {
|
|
736
828
|
disabled: busy || disabled,
|
|
737
829
|
style: {
|
|
738
830
|
border: 0,
|
|
@@ -757,7 +849,10 @@ const ProviderModelEditor = (0, react.forwardRef)(function ProviderModelEditor$1
|
|
|
757
849
|
" ",
|
|
758
850
|
t("modelsAutomatic")
|
|
759
851
|
] }),
|
|
760
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
852
|
+
editorState.listState === "provider-empty" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
853
|
+
className: "dsh-subscription-hint",
|
|
854
|
+
children: t(editorState.noticeKey)
|
|
855
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
761
856
|
style: actions$2,
|
|
762
857
|
children: [
|
|
763
858
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
@@ -784,8 +879,7 @@ const ProviderModelEditor = (0, react.forwardRef)(function ProviderModelEditor$1
|
|
|
784
879
|
children: t("modelsSelectNone")
|
|
785
880
|
})
|
|
786
881
|
]
|
|
787
|
-
}),
|
|
788
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
882
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
789
883
|
style: {
|
|
790
884
|
maxHeight: 360,
|
|
791
885
|
overflowY: "auto",
|
|
@@ -799,87 +893,118 @@ const ProviderModelEditor = (0, react.forwardRef)(function ProviderModelEditor$1
|
|
|
799
893
|
display: "grid",
|
|
800
894
|
gap: 8
|
|
801
895
|
},
|
|
802
|
-
children: [
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
style: actions$2,
|
|
826
|
-
children: [t("modelDefaultsTitle"), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
827
|
-
"aria-label": `${model.name} ${t("modelDefaultsTitle")}`,
|
|
828
|
-
value: efforts[model.id] ?? "",
|
|
829
|
-
onChange: (event) => {
|
|
830
|
-
setEfforts({
|
|
831
|
-
...efforts,
|
|
832
|
-
[model.id]: event.target.value
|
|
833
|
-
});
|
|
834
|
-
setDirty(true);
|
|
835
|
-
},
|
|
836
|
-
children: [
|
|
837
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
838
|
-
value: "",
|
|
839
|
-
children: t("modelDefaultsFollowProvider")
|
|
840
|
-
}),
|
|
841
|
-
model.configured && !model.efforts?.some((effort) => effort.id === model.configured) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
|
|
842
|
-
value: model.configured,
|
|
843
|
-
disabled: true,
|
|
844
|
-
children: [
|
|
845
|
-
model.configured,
|
|
846
|
-
" (",
|
|
847
|
-
t("modelsUnavailable"),
|
|
848
|
-
")"
|
|
849
|
-
]
|
|
850
|
-
}),
|
|
851
|
-
model.efforts?.map((effort) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
852
|
-
value: effort.id,
|
|
853
|
-
children: effort.name
|
|
854
|
-
}, effort.id))
|
|
855
|
-
]
|
|
856
|
-
})]
|
|
857
|
-
}), supportsContext(provider) && model.maxContextWindow !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
896
|
+
children: [
|
|
897
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
898
|
+
style: { overflowWrap: "anywhere" },
|
|
899
|
+
children: [
|
|
900
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
901
|
+
type: "checkbox",
|
|
902
|
+
checked: selected.has(model.id),
|
|
903
|
+
onChange: (event) => {
|
|
904
|
+
const next = new Set(selected);
|
|
905
|
+
if (event.target.checked) next.add(model.id);
|
|
906
|
+
else next.delete(model.id);
|
|
907
|
+
edit({
|
|
908
|
+
...draft,
|
|
909
|
+
visibleModels: [...next]
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
}),
|
|
913
|
+
" ",
|
|
914
|
+
model.name,
|
|
915
|
+
!known.has(model.id) && ` (${t("modelsUnavailable")})`
|
|
916
|
+
]
|
|
917
|
+
}),
|
|
918
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
858
919
|
style: actions$2,
|
|
859
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
920
|
+
children: [(model.efforts?.length ?? 0) > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
860
921
|
style: actions$2,
|
|
861
|
-
children: [t("
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
value: contexts[model.id] ?? "",
|
|
865
|
-
"aria-label": `${model.name} ${t("modelsContext")}`,
|
|
866
|
-
placeholder: String(model.defaultContextWindow),
|
|
922
|
+
children: [t("modelDefaultsTitle"), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
923
|
+
"aria-label": `${model.name} ${t("modelDefaultsTitle")}`,
|
|
924
|
+
value: efforts[model.id] ?? "",
|
|
867
925
|
onChange: (event) => {
|
|
868
|
-
|
|
869
|
-
...
|
|
926
|
+
setEfforts({
|
|
927
|
+
...efforts,
|
|
870
928
|
[model.id]: event.target.value
|
|
871
929
|
});
|
|
872
930
|
setDirty(true);
|
|
873
|
-
}
|
|
931
|
+
},
|
|
932
|
+
children: [
|
|
933
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
934
|
+
value: "",
|
|
935
|
+
children: t("modelDefaultsFollowProvider")
|
|
936
|
+
}),
|
|
937
|
+
model.configured && !model.efforts?.some((effort) => effort.id === model.configured) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
|
|
938
|
+
value: model.configured,
|
|
939
|
+
disabled: true,
|
|
940
|
+
children: [
|
|
941
|
+
model.configured,
|
|
942
|
+
" (",
|
|
943
|
+
t("modelsUnavailable"),
|
|
944
|
+
")"
|
|
945
|
+
]
|
|
946
|
+
}),
|
|
947
|
+
model.efforts?.map((effort) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
948
|
+
value: effort.id,
|
|
949
|
+
children: effort.name
|
|
950
|
+
}, effort.id))
|
|
951
|
+
]
|
|
874
952
|
})]
|
|
875
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
953
|
+
}), supportsContext(provider) && model.maxContextWindow !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
954
|
+
style: actions$2,
|
|
955
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
956
|
+
style: actions$2,
|
|
957
|
+
children: [t("modelsContext"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
958
|
+
style: { width: 140 },
|
|
959
|
+
inputMode: "numeric",
|
|
960
|
+
value: contexts[model.id] ?? "",
|
|
961
|
+
"aria-label": `${model.name} ${t("modelsContext")}`,
|
|
962
|
+
placeholder: String(model.defaultContextWindow),
|
|
963
|
+
onChange: (event) => {
|
|
964
|
+
setContexts({
|
|
965
|
+
...contexts,
|
|
966
|
+
[model.id]: event.target.value
|
|
967
|
+
});
|
|
968
|
+
setDirty(true);
|
|
969
|
+
}
|
|
970
|
+
})]
|
|
971
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: t("modelsContextBounds", {
|
|
972
|
+
default: model.defaultContextWindow,
|
|
973
|
+
max: model.maxContextWindow
|
|
974
|
+
}) })]
|
|
975
|
+
})]
|
|
976
|
+
}),
|
|
977
|
+
provider === "codex" && (model.fastTier === true || Object.hasOwn(draft.subagentSpeeds ?? {}, model.id)) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
978
|
+
style: {
|
|
979
|
+
display: "grid",
|
|
980
|
+
gap: 6
|
|
981
|
+
},
|
|
982
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
983
|
+
style: actions$2,
|
|
984
|
+
children: [t("modelsSubagentSpeed"), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
985
|
+
"aria-label": `${model.name} ${t("modelsSubagentSpeed")}`,
|
|
986
|
+
value: Object.hasOwn(draft.subagentSpeeds ?? {}, model.id) ? draft.subagentSpeeds[model.id] : "standard",
|
|
987
|
+
onChange: (event) => edit({
|
|
988
|
+
...draft,
|
|
989
|
+
subagentSpeeds: {
|
|
990
|
+
...draft.subagentSpeeds,
|
|
991
|
+
[model.id]: event.target.value
|
|
992
|
+
}
|
|
993
|
+
}),
|
|
994
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
995
|
+
value: "standard",
|
|
996
|
+
children: t("speedStandard")
|
|
997
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
|
|
998
|
+
value: "fast",
|
|
999
|
+
disabled: !model.fastTier,
|
|
1000
|
+
children: [t("speedFast"), !model.fastTier && ` (${t("modelsUnavailable")})`]
|
|
1001
|
+
})]
|
|
1002
|
+
})]
|
|
1003
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: t("modelsSubagentSpeedHint") })]
|
|
1004
|
+
})
|
|
1005
|
+
]
|
|
881
1006
|
}, model.id)), models.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("modelDefaultsFilterEmpty", { query }) })]
|
|
882
|
-
}),
|
|
1007
|
+
})] }),
|
|
883
1008
|
supportsContext(provider) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: t("modelsContextHint") }),
|
|
884
1009
|
catalog.tools.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
885
1010
|
style: {
|
|
@@ -1149,6 +1274,8 @@ function ProviderAccountManager({ provider, name, rpc, t, onClose }) {
|
|
|
1149
1274
|
const [saveError, setSaveError] = (0, react.useState)("");
|
|
1150
1275
|
const [attempt, setAttempt] = (0, react.useState)(0);
|
|
1151
1276
|
const [modelsDirty, setModelsDirty] = (0, react.useState)(false);
|
|
1277
|
+
const [ttlDraft, setTtlDraft] = (0, react.useState)();
|
|
1278
|
+
const [ttlDirty, setTtlDirty] = (0, react.useState)(false);
|
|
1152
1279
|
const alive = (0, react.useRef)(true);
|
|
1153
1280
|
const saveLock = (0, react.useRef)(false);
|
|
1154
1281
|
const editor = (0, react.useRef)(null);
|
|
@@ -1168,7 +1295,11 @@ function ProviderAccountManager({ provider, name, rpc, t, onClose }) {
|
|
|
1168
1295
|
setLoading(true);
|
|
1169
1296
|
setError("");
|
|
1170
1297
|
callSubscriptionsAuth(rpc, provider === "cursor-subscription" ? "cursorSettings" : "providerSettings", { provider }).then((data) => {
|
|
1171
|
-
if (current)
|
|
1298
|
+
if (current) {
|
|
1299
|
+
setCatalog(data);
|
|
1300
|
+
setTtlDraft(data.settings.promptCacheTtl);
|
|
1301
|
+
setTtlDirty(false);
|
|
1302
|
+
}
|
|
1172
1303
|
}).catch((error$1) => {
|
|
1173
1304
|
if (current) setError(t("accountsLoadFailed", { message: error$1 instanceof Error ? error$1.message : String(error$1) }));
|
|
1174
1305
|
}).finally(() => {
|
|
@@ -1216,7 +1347,7 @@ function ProviderAccountManager({ provider, name, rpc, t, onClose }) {
|
|
|
1216
1347
|
if (provider === "cursor-subscription") await callSubscriptionsAuth(rpc, "cursorSetSettings", { settings: models.settings ?? latest.settings });
|
|
1217
1348
|
else await callSubscriptionsAuth(rpc, "setProviderSettings", {
|
|
1218
1349
|
provider,
|
|
1219
|
-
settings: mergeAccountChanges(models.settings === void 0 ? latest.settings : mergeLatestAccounts(models.settings, latest.settings), changes)
|
|
1350
|
+
settings: mergeAccountChanges(mergePromptCacheTtl(models.settings === void 0 ? latest.settings : mergeLatestAccounts(models.settings, latest.settings), ttlDirty ? ttlDraft : latest.settings.promptCacheTtl), changes)
|
|
1220
1351
|
});
|
|
1221
1352
|
if (alive.current) onClose();
|
|
1222
1353
|
} catch (error$1) {
|
|
@@ -1392,7 +1523,8 @@ function ProviderAccountManager({ provider, name, rpc, t, onClose }) {
|
|
|
1392
1523
|
},
|
|
1393
1524
|
children: [
|
|
1394
1525
|
t("accountsModels"),
|
|
1395
|
-
" ·
|
|
1526
|
+
" ·",
|
|
1527
|
+
" ",
|
|
1396
1528
|
preferences.poolModels === void 0 ? t("accountsModelsAll") : t("accountsModelsCount", { count: preferences.poolModels.length })
|
|
1397
1529
|
]
|
|
1398
1530
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -1484,6 +1616,52 @@ function ProviderAccountManager({ provider, name, rpc, t, onClose }) {
|
|
|
1484
1616
|
disabled: saving,
|
|
1485
1617
|
onDirtyChange: setModelsDirty
|
|
1486
1618
|
}),
|
|
1619
|
+
provider === "claude" && catalog && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1620
|
+
style: {
|
|
1621
|
+
...stack$1,
|
|
1622
|
+
gap: 6,
|
|
1623
|
+
borderTop: border$1,
|
|
1624
|
+
marginTop: 12,
|
|
1625
|
+
paddingTop: 12
|
|
1626
|
+
},
|
|
1627
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
1628
|
+
style: {
|
|
1629
|
+
...stack$1,
|
|
1630
|
+
gap: 6
|
|
1631
|
+
},
|
|
1632
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1633
|
+
className: "dsh-subscription-label",
|
|
1634
|
+
children: t("claudeCacheTtl")
|
|
1635
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
1636
|
+
"aria-label": t("claudeCacheTtl"),
|
|
1637
|
+
style: { width: 220 },
|
|
1638
|
+
value: ttlDraft ?? "",
|
|
1639
|
+
disabled: saving,
|
|
1640
|
+
onChange: (event) => {
|
|
1641
|
+
const value = event.target.value;
|
|
1642
|
+
setTtlDraft(value === "" ? void 0 : value);
|
|
1643
|
+
setTtlDirty(true);
|
|
1644
|
+
},
|
|
1645
|
+
children: [
|
|
1646
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1647
|
+
value: "",
|
|
1648
|
+
children: t("claudeCacheTtlFollowConfig")
|
|
1649
|
+
}),
|
|
1650
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1651
|
+
value: "5m",
|
|
1652
|
+
children: t("claudeCacheTtl5m")
|
|
1653
|
+
}),
|
|
1654
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1655
|
+
value: "1h",
|
|
1656
|
+
children: t("claudeCacheTtl1h")
|
|
1657
|
+
})
|
|
1658
|
+
]
|
|
1659
|
+
})]
|
|
1660
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1661
|
+
className: "dsh-subscription-hint",
|
|
1662
|
+
children: t("claudeCacheTtlHint")
|
|
1663
|
+
})]
|
|
1664
|
+
}),
|
|
1487
1665
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("footer", {
|
|
1488
1666
|
style: {
|
|
1489
1667
|
...stack$1,
|
|
@@ -1510,7 +1688,7 @@ function ProviderAccountManager({ provider, name, rpc, t, onClose }) {
|
|
|
1510
1688
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1511
1689
|
type: "button",
|
|
1512
1690
|
className: "dsh-subscription-primary",
|
|
1513
|
-
disabled: loading || saving || !Object.keys(changes).length && !modelsDirty,
|
|
1691
|
+
disabled: loading || saving || !Object.keys(changes).length && !modelsDirty && !ttlDirty,
|
|
1514
1692
|
onClick: () => {
|
|
1515
1693
|
save();
|
|
1516
1694
|
},
|
|
@@ -3588,7 +3766,12 @@ function ResetCreditsDisclosure({ mode, credits, t, now = Date.now(), nextCredit
|
|
|
3588
3766
|
}),
|
|
3589
3767
|
mode === "grants" && state === "notUsable" && credit.cooldownUntil !== void 0 && credit.cooldownUntil > now && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3590
3768
|
"aria-hidden": "true",
|
|
3591
|
-
children: [
|
|
3769
|
+
children: [
|
|
3770
|
+
t("resetCreditCooldownUntil", { date: new Date(credit.cooldownUntil).toLocaleString() }),
|
|
3771
|
+
" ",
|
|
3772
|
+
"·",
|
|
3773
|
+
" "
|
|
3774
|
+
]
|
|
3592
3775
|
}),
|
|
3593
3776
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3594
3777
|
"aria-hidden": mode === "grants" ? true : void 0,
|
|
@@ -3734,7 +3917,7 @@ const styles$6 = {
|
|
|
3734
3917
|
//#endregion
|
|
3735
3918
|
//#region src/client/SubscriptionsSection.tsx
|
|
3736
3919
|
/** Poll cadence while a provider login attempt is busy. */
|
|
3737
|
-
const POLL_INTERVAL_MS
|
|
3920
|
+
const POLL_INTERVAL_MS = 2e3;
|
|
3738
3921
|
const CLIENT_VERSION_SOURCES = {
|
|
3739
3922
|
npm: "clientVersionNpm",
|
|
3740
3923
|
local: "clientVersionLocal",
|
|
@@ -4063,7 +4246,7 @@ function SubscriptionsSection(props) {
|
|
|
4063
4246
|
if (pollersRef.current.has(provider)) return;
|
|
4064
4247
|
pollersRef.current.set(provider, setInterval(() => {
|
|
4065
4248
|
refresh();
|
|
4066
|
-
}, POLL_INTERVAL_MS
|
|
4249
|
+
}, POLL_INTERVAL_MS));
|
|
4067
4250
|
}, [refresh]);
|
|
4068
4251
|
(0, react.useEffect)(() => {
|
|
4069
4252
|
mountedRef.current = true;
|
|
@@ -4225,7 +4408,8 @@ function SubscriptionsSection(props) {
|
|
|
4225
4408
|
rpc,
|
|
4226
4409
|
t,
|
|
4227
4410
|
setProviderError,
|
|
4228
|
-
startPolling
|
|
4411
|
+
startPolling,
|
|
4412
|
+
refresh
|
|
4229
4413
|
]);
|
|
4230
4414
|
const cancel = (0, react.useCallback)(async (provider) => {
|
|
4231
4415
|
if (rpc === void 0) return;
|
|
@@ -4369,6 +4553,7 @@ function SubscriptionsSection(props) {
|
|
|
4369
4553
|
accounts.map((account) => {
|
|
4370
4554
|
const usageKey = `${id}:${account.key}`;
|
|
4371
4555
|
const usage = usages[usageKey];
|
|
4556
|
+
const shownWindows = visibleUsageWindows(id, usage?.windows, status?.visibleModels);
|
|
4372
4557
|
const usageError = usageErrors[usageKey];
|
|
4373
4558
|
const display = account.account ?? account.key;
|
|
4374
4559
|
const sortedCredits = [...usage?.resetCredits ?? []].sort((a, b) => (a.expiresAt ?? Infinity) - (b.expiresAt ?? Infinity));
|
|
@@ -4451,7 +4636,11 @@ function SubscriptionsSection(props) {
|
|
|
4451
4636
|
style: styles$5.statusLine,
|
|
4452
4637
|
children: t("usageEmpty")
|
|
4453
4638
|
}),
|
|
4454
|
-
|
|
4639
|
+
usage?.windows !== void 0 && usage.windows.length > 0 && shownWindows.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4640
|
+
style: styles$5.statusLine,
|
|
4641
|
+
children: t("usageNoVisibleModels")
|
|
4642
|
+
}),
|
|
4643
|
+
shownWindows.map((window$1, index) => {
|
|
4455
4644
|
const percent = displayUsedPercent(window$1.usedPercent);
|
|
4456
4645
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4457
4646
|
style: styles$5.usageRow,
|
|
@@ -4522,7 +4711,7 @@ function SubscriptionsSection(props) {
|
|
|
4522
4711
|
},
|
|
4523
4712
|
children: t("addAccount")
|
|
4524
4713
|
}),
|
|
4525
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4714
|
+
!busy && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4526
4715
|
type: "button",
|
|
4527
4716
|
"aria-haspopup": "dialog",
|
|
4528
4717
|
onClick: () => setManagedProvider({
|
|
@@ -4614,7 +4803,10 @@ function SubscriptionsSection(props) {
|
|
|
4614
4803
|
name: managedProvider.name,
|
|
4615
4804
|
rpc,
|
|
4616
4805
|
t,
|
|
4617
|
-
onClose: () =>
|
|
4806
|
+
onClose: () => {
|
|
4807
|
+
setManagedProvider(void 0);
|
|
4808
|
+
refresh();
|
|
4809
|
+
}
|
|
4618
4810
|
}),
|
|
4619
4811
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: ".subscriptions-reset-dialog::backdrop { background: var(--dsw-alias-bg-mask-1, #0006); backdrop-filter: var(--dsw-mask-blur, none); }" }),
|
|
4620
4812
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dialog", {
|
|
@@ -4972,7 +5164,8 @@ const HOST_CONTRACT = {
|
|
|
4972
5164
|
slots: "@deepseek-ai/dsh-client-ui-renderer"
|
|
4973
5165
|
},
|
|
4974
5166
|
markers: { composerStats: {
|
|
4975
|
-
|
|
5167
|
+
row: "data-composer-stats",
|
|
5168
|
+
dock: "data-composer-stat",
|
|
4976
5169
|
package: "@deepseek-ai/dsh-client-ui-chat"
|
|
4977
5170
|
} },
|
|
4978
5171
|
diagnostics: {
|
|
@@ -5025,6 +5218,10 @@ const HOST_CONTRACT = {
|
|
|
5025
5218
|
]
|
|
5026
5219
|
}
|
|
5027
5220
|
};
|
|
5221
|
+
/** Attribute selector for a host DOM marker (`data-composer-stats` → `[data-composer-stats]`). */
|
|
5222
|
+
function markerSelector(attribute) {
|
|
5223
|
+
return `[${attribute}]`;
|
|
5224
|
+
}
|
|
5028
5225
|
|
|
5029
5226
|
//#endregion
|
|
5030
5227
|
//#region src/client/host-icons.ts
|
|
@@ -5435,9 +5632,10 @@ function VideoGenerateToolview(props) {
|
|
|
5435
5632
|
//#region src/client/SpeedSelect.tsx
|
|
5436
5633
|
/**
|
|
5437
5634
|
* The `loadSpeed` half of the inject face: the plugin's own speed state plus
|
|
5438
|
-
* the host's current model selection (the visibility gate). A
|
|
5439
|
-
*
|
|
5440
|
-
*
|
|
5635
|
+
* the host's current model selection (the visibility gate). A session whose
|
|
5636
|
+
* model selection is unavailable (an addressed subagent) answers hidden.
|
|
5637
|
+
* Any other model-directory failure throws, so the caller keeps its last
|
|
5638
|
+
* known state instead of locking the toggle away.
|
|
5441
5639
|
*
|
|
5442
5640
|
* `sessionId` is a plain string: slot and command contexts brand it through
|
|
5443
5641
|
* different dsh-session copies, and only the service boundary needs one.
|
|
@@ -5448,19 +5646,127 @@ function VideoGenerateToolview(props) {
|
|
|
5448
5646
|
*/
|
|
5449
5647
|
function createSpeedLoader(connection, models, sessionId) {
|
|
5450
5648
|
return async () => {
|
|
5451
|
-
const state = await callSubscriptionsAuth(connection.rpc, "speed", { sessionId });
|
|
5452
5649
|
const directories = models();
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
}
|
|
5457
|
-
|
|
5650
|
+
let current = null;
|
|
5651
|
+
if (directories !== void 0) try {
|
|
5652
|
+
current = (await directories.directoryFor(sessionId).load()).current;
|
|
5653
|
+
} catch (error) {
|
|
5654
|
+
if (!modelSelectionUnavailable(error)) throw error;
|
|
5655
|
+
}
|
|
5656
|
+
const state = await callSubscriptionsAuth(connection.rpc, "speed", {
|
|
5657
|
+
sessionId,
|
|
5658
|
+
...current?.provider === "codex" ? { model: current.model } : {}
|
|
5659
|
+
});
|
|
5458
5660
|
return {
|
|
5459
5661
|
visible: current !== null && current.provider === "codex" && state.fastModels.includes(current.model),
|
|
5460
5662
|
tier: state.tier
|
|
5461
5663
|
};
|
|
5462
5664
|
};
|
|
5463
5665
|
}
|
|
5666
|
+
/** Host copy for a session that has no model seat (an addressed subagent). */
|
|
5667
|
+
const MODEL_SELECTION_UNAVAILABLE = "model selection is unavailable";
|
|
5668
|
+
function modelSelectionUnavailable(error) {
|
|
5669
|
+
return (error instanceof Error ? error.message : String(error)).includes(MODEL_SELECTION_UNAVAILABLE);
|
|
5670
|
+
}
|
|
5671
|
+
/** Stable identity of a model selection. Empty when nothing is selected. */
|
|
5672
|
+
function modelSelectionKey(current) {
|
|
5673
|
+
if (current == null) return "";
|
|
5674
|
+
return JSON.stringify([current.provider, current.model]);
|
|
5675
|
+
}
|
|
5676
|
+
/**
|
|
5677
|
+
* How often a host without a directory store is re-read, and how often a
|
|
5678
|
+
* missing directory service is retried. Neither interval calls
|
|
5679
|
+
* `subscriptions-auth.speed`.
|
|
5680
|
+
*/
|
|
5681
|
+
const MODEL_DIRECTORY_POLL_INTERVAL_MS = 3e3;
|
|
5682
|
+
/**
|
|
5683
|
+
* Mount reads of `speed`, including the first attempt. Further failures stop
|
|
5684
|
+
* instead of polling for the life of the composer.
|
|
5685
|
+
*/
|
|
5686
|
+
const SPEED_MOUNT_READ_ATTEMPTS = 4;
|
|
5687
|
+
/** Fired after `/fast` writes a tier, so an open composer re-reads `speed`. */
|
|
5688
|
+
const SPEED_CHANGED_EVENT = "dsh:subscriptions:speed-changed";
|
|
5689
|
+
/** Tell mounted Speed controls that this page changed a session tier. */
|
|
5690
|
+
function notifySpeedChanged() {
|
|
5691
|
+
if (typeof window === "undefined") return;
|
|
5692
|
+
window.dispatchEvent(new Event(SPEED_CHANGED_EVENT));
|
|
5693
|
+
}
|
|
5694
|
+
/**
|
|
5695
|
+
* Subscribe to the session's selected model. A directory store notifies the
|
|
5696
|
+
* listener only when `provider` or `model` changes. A directory with no store
|
|
5697
|
+
* is re-read on {@link MODEL_DIRECTORY_POLL_INTERVAL_MS}; a service that is
|
|
5698
|
+
* not registered yet, or whose `directoryFor` throws, is retried on that same
|
|
5699
|
+
* interval. The first successful attach after a miss notifies once, so a
|
|
5700
|
+
* mount that ran before the service existed reads `speed` again.
|
|
5701
|
+
*/
|
|
5702
|
+
function createModelSelectionWatch(models, sessionId) {
|
|
5703
|
+
return (listener) => {
|
|
5704
|
+
let stopped = false;
|
|
5705
|
+
let unsubscribeStore;
|
|
5706
|
+
let timer;
|
|
5707
|
+
let mode = "seek";
|
|
5708
|
+
let lastKey;
|
|
5709
|
+
const stopTimer = () => {
|
|
5710
|
+
if (timer === void 0) return;
|
|
5711
|
+
clearInterval(timer);
|
|
5712
|
+
timer = void 0;
|
|
5713
|
+
};
|
|
5714
|
+
const changed = (current) => {
|
|
5715
|
+
const next = modelSelectionKey(current);
|
|
5716
|
+
if (lastKey === void 0) {
|
|
5717
|
+
lastKey = next;
|
|
5718
|
+
return false;
|
|
5719
|
+
}
|
|
5720
|
+
if (next === lastKey) return false;
|
|
5721
|
+
lastKey = next;
|
|
5722
|
+
return true;
|
|
5723
|
+
};
|
|
5724
|
+
const attach = (late) => {
|
|
5725
|
+
if (stopped || mode !== "seek") return;
|
|
5726
|
+
let directory;
|
|
5727
|
+
try {
|
|
5728
|
+
directory = models()?.directoryFor(sessionId);
|
|
5729
|
+
} catch {
|
|
5730
|
+
return;
|
|
5731
|
+
}
|
|
5732
|
+
if (directory === void 0) return;
|
|
5733
|
+
const store = directory.store;
|
|
5734
|
+
if (store !== void 0) {
|
|
5735
|
+
mode = "store";
|
|
5736
|
+
stopTimer();
|
|
5737
|
+
lastKey = modelSelectionKey(store.getSnapshot().current);
|
|
5738
|
+
unsubscribeStore = store.subscribe(() => {
|
|
5739
|
+
if (!stopped && changed(store.getSnapshot().current)) listener();
|
|
5740
|
+
});
|
|
5741
|
+
if (late) listener();
|
|
5742
|
+
return;
|
|
5743
|
+
}
|
|
5744
|
+
mode = "poll";
|
|
5745
|
+
stopTimer();
|
|
5746
|
+
let force = late;
|
|
5747
|
+
const seed = () => {
|
|
5748
|
+
directory.load().then((state) => {
|
|
5749
|
+
if (stopped || mode !== "poll") return;
|
|
5750
|
+
const identityChanged = changed(state.current);
|
|
5751
|
+
const forced = force;
|
|
5752
|
+
force = false;
|
|
5753
|
+
if (identityChanged || forced) listener();
|
|
5754
|
+
}, () => {});
|
|
5755
|
+
};
|
|
5756
|
+
seed();
|
|
5757
|
+
timer = setInterval(seed, MODEL_DIRECTORY_POLL_INTERVAL_MS);
|
|
5758
|
+
};
|
|
5759
|
+
attach(false);
|
|
5760
|
+
if (mode === "seek") timer = setInterval(() => {
|
|
5761
|
+
attach(true);
|
|
5762
|
+
}, MODEL_DIRECTORY_POLL_INTERVAL_MS);
|
|
5763
|
+
return () => {
|
|
5764
|
+
stopped = true;
|
|
5765
|
+
unsubscribeStore?.();
|
|
5766
|
+
stopTimer();
|
|
5767
|
+
};
|
|
5768
|
+
};
|
|
5769
|
+
}
|
|
5464
5770
|
/** The `setSpeed` half of the inject face: boolean outcome for the component's busy state. */
|
|
5465
5771
|
function createSpeedSetter(connection, sessionId) {
|
|
5466
5772
|
return (tier) => callSubscriptionsAuth(connection.rpc, "setSpeed", {
|
|
@@ -5476,20 +5782,15 @@ const TIERS = ["standard", "fast"];
|
|
|
5476
5782
|
/**
|
|
5477
5783
|
* The composer Speed control: a trigger reading `速度 · 快速`/`速度 · 标准`
|
|
5478
5784
|
* that opens a two-row menu (standard/fast with descriptions, check mark on
|
|
5479
|
-
* the current tier).
|
|
5480
|
-
*
|
|
5481
|
-
|
|
5482
|
-
|
|
5483
|
-
|
|
5484
|
-
|
|
5485
|
-
*
|
|
5486
|
-
* that opens a two-row menu (standard/fast with descriptions, check mark on
|
|
5487
|
-
* the current tier). The host pushes nothing on a model switch, so the
|
|
5488
|
-
* control re-reads on a slow poll with a single-flight guard; a failed read
|
|
5489
|
-
* keeps the last known state, so a transient RPC failure can never lock the
|
|
5490
|
-
* toggle away (the earlier mount-only load had no recovery path).
|
|
5785
|
+
* the current tier). It reads `subscriptions-auth.speed` on mount, when the
|
|
5786
|
+
* menu opens, when {@link SpeedSelectInjected.watchModel} reports a new
|
|
5787
|
+
* selected model, and when {@link notifySpeedChanged} fires. Until the first
|
|
5788
|
+
* read succeeds, a failure is retried on
|
|
5789
|
+
* {@link MODEL_DIRECTORY_POLL_INTERVAL_MS}, at most
|
|
5790
|
+
* {@link SPEED_MOUNT_READ_ATTEMPTS} times; after that, a failed read keeps
|
|
5791
|
+
* the last known state.
|
|
5491
5792
|
*/
|
|
5492
|
-
function SpeedSelect({ loadSpeed, setSpeed, t }) {
|
|
5793
|
+
function SpeedSelect({ loadSpeed, setSpeed, watchModel, t }) {
|
|
5493
5794
|
const translate = t ?? fallbackTranslate$1;
|
|
5494
5795
|
const [state, setState] = (0, react.useState)(null);
|
|
5495
5796
|
const [open, setOpen] = (0, react.useState)(false);
|
|
@@ -5497,25 +5798,58 @@ function SpeedSelect({ loadSpeed, setSpeed, t }) {
|
|
|
5497
5798
|
const rootRef = (0, react.useRef)(null);
|
|
5498
5799
|
const loadRef = (0, react.useRef)(loadSpeed);
|
|
5499
5800
|
loadRef.current = loadSpeed;
|
|
5801
|
+
const watchRef = (0, react.useRef)(watchModel);
|
|
5802
|
+
watchRef.current = watchModel;
|
|
5500
5803
|
(0, react.useEffect)(() => {
|
|
5501
5804
|
if (loadRef.current === void 0) return;
|
|
5502
5805
|
let cancelled = false;
|
|
5503
5806
|
let inflight = false;
|
|
5807
|
+
let again = false;
|
|
5808
|
+
let loadedOnce = false;
|
|
5809
|
+
let failures = 0;
|
|
5810
|
+
let retry;
|
|
5811
|
+
const stopRetry = () => {
|
|
5812
|
+
if (retry === void 0) return;
|
|
5813
|
+
clearInterval(retry);
|
|
5814
|
+
retry = void 0;
|
|
5815
|
+
};
|
|
5504
5816
|
const reload = () => {
|
|
5505
5817
|
const load = loadRef.current;
|
|
5506
|
-
if (load === void 0
|
|
5818
|
+
if (load === void 0) return;
|
|
5819
|
+
if (inflight) {
|
|
5820
|
+
again = true;
|
|
5821
|
+
return;
|
|
5822
|
+
}
|
|
5507
5823
|
inflight = true;
|
|
5508
5824
|
load().then((loaded) => {
|
|
5509
|
-
if (
|
|
5510
|
-
|
|
5825
|
+
if (cancelled) return;
|
|
5826
|
+
loadedOnce = true;
|
|
5827
|
+
stopRetry();
|
|
5828
|
+
setState(loaded);
|
|
5829
|
+
}, () => {
|
|
5830
|
+
failures += 1;
|
|
5831
|
+
if (failures >= SPEED_MOUNT_READ_ATTEMPTS) stopRetry();
|
|
5832
|
+
}).finally(() => {
|
|
5511
5833
|
inflight = false;
|
|
5834
|
+
if (cancelled || !again) return;
|
|
5835
|
+
again = false;
|
|
5836
|
+
reload();
|
|
5512
5837
|
});
|
|
5513
5838
|
};
|
|
5514
5839
|
reload();
|
|
5515
|
-
|
|
5840
|
+
retry = setInterval(() => {
|
|
5841
|
+
if (!loadedOnce) reload();
|
|
5842
|
+
}, MODEL_DIRECTORY_POLL_INTERVAL_MS);
|
|
5843
|
+
const unsubscribe = watchRef.current?.(reload);
|
|
5844
|
+
const onSpeedChanged = () => {
|
|
5845
|
+
reload();
|
|
5846
|
+
};
|
|
5847
|
+
window.addEventListener(SPEED_CHANGED_EVENT, onSpeedChanged);
|
|
5516
5848
|
return () => {
|
|
5517
5849
|
cancelled = true;
|
|
5518
|
-
|
|
5850
|
+
unsubscribe?.();
|
|
5851
|
+
stopRetry();
|
|
5852
|
+
window.removeEventListener(SPEED_CHANGED_EVENT, onSpeedChanged);
|
|
5519
5853
|
};
|
|
5520
5854
|
}, []);
|
|
5521
5855
|
(0, react.useEffect)(() => {
|
|
@@ -5711,7 +6045,7 @@ const PROVIDER_NAMES = {
|
|
|
5711
6045
|
"cursor-subscription": "Cursor",
|
|
5712
6046
|
"opencode-go": "OpenCode Go",
|
|
5713
6047
|
"kimi-coding": "Kimi Code",
|
|
5714
|
-
|
|
6048
|
+
minimax: "MiniMax",
|
|
5715
6049
|
"minimax-cn": "MiniMax CN",
|
|
5716
6050
|
"ollama-cloud": "Ollama Cloud"
|
|
5717
6051
|
};
|
|
@@ -5946,6 +6280,8 @@ function usageWindowLabel(t, window$1) {
|
|
|
5946
6280
|
* every provider's accounts and their windows. Returns null when no data is
|
|
5947
6281
|
* available.
|
|
5948
6282
|
*/
|
|
6283
|
+
const STATS_ROW_SELECTOR = markerSelector(HOST_CONTRACT.markers.composerStats.row);
|
|
6284
|
+
const STATS_DOCK_SELECTOR = markerSelector(HOST_CONTRACT.markers.composerStats.dock);
|
|
5949
6285
|
function SubscriptionUsageBadge({ rpc, currentModel, t, rotationMs }) {
|
|
5950
6286
|
const translate = t ?? fallbackTranslate;
|
|
5951
6287
|
const [displays, setDisplays] = (0, react.useState)([]);
|
|
@@ -6105,15 +6441,18 @@ function SubscriptionUsageBadge({ rpc, currentModel, t, rotationMs }) {
|
|
|
6105
6441
|
if (seat$1 === null) return;
|
|
6106
6442
|
const scope = statsScopeOf(seat$1);
|
|
6107
6443
|
if (scope === null) return;
|
|
6108
|
-
const find = () => scope.querySelector(
|
|
6109
|
-
|
|
6110
|
-
|
|
6111
|
-
setStatsRow(
|
|
6112
|
-
|
|
6444
|
+
const find = () => scope.querySelector(STATS_ROW_SELECTOR);
|
|
6445
|
+
const settle = () => {
|
|
6446
|
+
const row = find();
|
|
6447
|
+
setStatsRow(row);
|
|
6448
|
+
if (row === null && scope.querySelector(STATS_DOCK_SELECTOR) !== null) observer.disconnect();
|
|
6449
|
+
};
|
|
6450
|
+
const observer = new MutationObserver(settle);
|
|
6113
6451
|
observer.observe(scope, {
|
|
6114
6452
|
childList: true,
|
|
6115
6453
|
subtree: true
|
|
6116
6454
|
});
|
|
6455
|
+
settle();
|
|
6117
6456
|
return () => {
|
|
6118
6457
|
observer.disconnect();
|
|
6119
6458
|
};
|
|
@@ -6237,15 +6576,19 @@ function SubscriptionUsageBadge({ rpc, currentModel, t, rotationMs }) {
|
|
|
6237
6576
|
});
|
|
6238
6577
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [seat, statsRow !== null && statsRow.isConnected ? (0, react_dom.createPortal)(pill, statsRow) : pill] });
|
|
6239
6578
|
}
|
|
6579
|
+
/** How far statsScopeOf walks looking for `composerStats.row`. */
|
|
6580
|
+
const LEGACY_STATS_SCOPE_DEPTH = 4;
|
|
6240
6581
|
/**
|
|
6241
|
-
*
|
|
6242
|
-
*
|
|
6243
|
-
*
|
|
6582
|
+
* Scope for the legacy stats row. The nearest ancestor within
|
|
6583
|
+
* LEGACY_STATS_SCOPE_DEPTH that contains `composerStats.row`, so a badge
|
|
6584
|
+
* does not adopt another composer's pills. When none does, the seat's
|
|
6585
|
+
* parent: the slot outlet, which holds sibling `composerStats.dock` anchors
|
|
6586
|
+
* on hosts whose pills are dock entries.
|
|
6244
6587
|
*/
|
|
6245
6588
|
function statsScopeOf(seat) {
|
|
6246
6589
|
let node = seat.parentElement;
|
|
6247
|
-
for (let depth = 0; node !== null && depth <
|
|
6248
|
-
if (node.querySelector(
|
|
6590
|
+
for (let depth = 0; node !== null && depth < LEGACY_STATS_SCOPE_DEPTH; depth++) {
|
|
6591
|
+
if (node.querySelector(STATS_ROW_SELECTOR) !== null) return node;
|
|
6249
6592
|
node = node.parentElement;
|
|
6250
6593
|
}
|
|
6251
6594
|
return seat.parentElement;
|
|
@@ -6260,10 +6603,10 @@ function AccountMeta({ account, translate }) {
|
|
|
6260
6603
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
6261
6604
|
style: styles.providerMeta,
|
|
6262
6605
|
title: account.account,
|
|
6263
|
-
children: [account.isDefault && /* @__PURE__ */ (0, react_jsx_runtime.
|
|
6606
|
+
children: [account.isDefault && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
6264
6607
|
style: styles.defaultStar,
|
|
6265
6608
|
"aria-label": "default",
|
|
6266
|
-
children: "★ "
|
|
6609
|
+
children: ["★", " "]
|
|
6267
6610
|
}), parts.join(" · ")]
|
|
6268
6611
|
});
|
|
6269
6612
|
}
|
|
@@ -6545,7 +6888,8 @@ function apply(ctx) {
|
|
|
6545
6888
|
locale: NS,
|
|
6546
6889
|
inject: (sessionId) => ({
|
|
6547
6890
|
loadSpeed: createSpeedLoader(connection, models, sessionId),
|
|
6548
|
-
setSpeed: createSpeedSetter(connection, sessionId)
|
|
6891
|
+
setSpeed: createSpeedSetter(connection, sessionId),
|
|
6892
|
+
watchModel: createModelSelectionWatch(models, sessionId)
|
|
6549
6893
|
})
|
|
6550
6894
|
}, SpeedSelect));
|
|
6551
6895
|
ctx.slots.inject("conversation.composer.dock", () => ctx.slots.register({
|
|
@@ -6583,7 +6927,7 @@ function apply(ctx) {
|
|
|
6583
6927
|
}));
|
|
6584
6928
|
},
|
|
6585
6929
|
onSelect: async (option, session) => {
|
|
6586
|
-
await createSpeedSetter(connection, session.sessionId)(option.id);
|
|
6930
|
+
if (await createSpeedSetter(connection, session.sessionId)(option.id)) notifySpeedChanged();
|
|
6587
6931
|
}
|
|
6588
6932
|
}
|
|
6589
6933
|
}), "dsh-subscriptions: /fast contribution");
|