dsh-subscriptions 0.2.2 → 0.3.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/README.md +28 -32
- package/lib/auth/rpc.d.ts +10 -24
- package/lib/auth/rpc.js +18 -51
- package/lib/client/ProviderAccountManager.d.ts +1 -1
- package/lib/client/ProviderAccountManager.js +86 -110
- package/lib/client/ProviderModelEditor.d.ts +1 -1
- package/lib/client/ProviderModelEditor.js +61 -43
- package/lib/client/SpeedSelect.js +13 -12
- package/lib/client/SubscriptionUsageBadge.d.ts +1 -1
- package/lib/client/SubscriptionUsageBadge.js +1 -13
- package/lib/client/SubscriptionsSection.d.ts +2 -0
- package/lib/client/SubscriptionsSection.js +10 -4
- package/lib/client/locales.d.ts +12 -32
- package/lib/client/locales.js +14 -34
- package/lib/client/model-filter.d.ts +37 -0
- package/lib/client/model-filter.js +45 -0
- package/lib/client/subscription-card-styles.d.ts +1 -1
- package/lib/client/subscription-card-styles.js +1 -1
- package/lib/client/usage-window-filter.d.ts +7 -0
- package/lib/client/usage-window-filter.js +12 -0
- package/lib/client.js +374 -584
- package/lib/client.js.map +1 -1
- package/lib/index.js +249 -3322
- package/lib/provider-settings.d.ts +5 -0
- package/lib/provider-settings.js +21 -0
- package/lib/providers/ollama-settings.d.ts +1 -1
- package/lib/providers/ollama-settings.js +1 -1
- package/lib/translate/antigravity.js +10 -2
- package/package.json +2 -7
- package/lib/client/CursorCard.d.ts +0 -9
- package/lib/client/CursorCard.js +0 -151
- package/lib/providers/cursor-adapter.d.ts +0 -50
- package/lib/providers/cursor-adapter.js +0 -304
- package/lib/providers/cursor-auth.d.ts +0 -43
- package/lib/providers/cursor-auth.js +0 -221
- package/lib/providers/cursor-model-settings.d.ts +0 -15
- package/lib/providers/cursor-model-settings.js +0 -60
- package/lib/providers/cursor-usage.d.ts +0 -9
- package/lib/providers/cursor-usage.js +0 -131
- package/vendor/cursor/LICENSE +0 -21
- package/vendor/cursor/index.d.ts +0 -18
- package/vendor/cursor/index.js +0 -3574
- package/vendor/cursor/proto.js +0 -254
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { forwardRef, useEffect, useImperativeHandle, useRef, useState, } from 'react';
|
|
3
3
|
import { callSubscriptionsAuth } from './subscriptions-rpc.js';
|
|
4
|
+
import { modelEditorState, savedModelIds } from './model-filter.js';
|
|
4
5
|
/** Providers whose runtime honours a local context-window override. */
|
|
5
6
|
const supportsContext = (provider) => provider === 'codex' || provider === 'claude';
|
|
6
7
|
const border = '0.5px solid var(--dsw-alias-border-l2, #ddd)';
|
|
@@ -47,9 +48,7 @@ export const ProviderModelEditor = forwardRef(function ProviderModelEditor({ pro
|
|
|
47
48
|
setBusy(true);
|
|
48
49
|
setError('');
|
|
49
50
|
try {
|
|
50
|
-
const data = await callSubscriptionsAuth(rpc, provider
|
|
51
|
-
? 'cursorSettings'
|
|
52
|
-
: 'providerSettings', { provider, force });
|
|
51
|
+
const data = await callSubscriptionsAuth(rpc, 'providerSettings', { provider, force });
|
|
53
52
|
if (generation.current === request)
|
|
54
53
|
reset(data);
|
|
55
54
|
}
|
|
@@ -108,18 +107,24 @@ export const ProviderModelEditor = forwardRef(function ProviderModelEditor({ pro
|
|
|
108
107
|
}), [catalog, dirty, draft, contexts, efforts, provider, t]);
|
|
109
108
|
const allModels = catalog?.models ?? [];
|
|
110
109
|
const known = new Set(allModels.map((model) => model.id));
|
|
111
|
-
const
|
|
110
|
+
const savedIds = savedModelIds(draft.visibleModels, draft.subagentSpeeds);
|
|
111
|
+
const missing = savedIds
|
|
112
112
|
.filter((id) => !known.has(id))
|
|
113
113
|
.map((id) => ({ id, name: id }));
|
|
114
114
|
const models = [...allModels, ...missing].filter((model) => `${model.name} ${model.id}`
|
|
115
115
|
.toLowerCase()
|
|
116
116
|
.includes(query.trim().toLowerCase()));
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
117
|
+
const editorState = modelEditorState({
|
|
118
|
+
discovered: allModels.length,
|
|
119
|
+
saved: missing.length,
|
|
120
|
+
matched: models.length,
|
|
121
|
+
accounts: catalog?.accounts,
|
|
122
|
+
});
|
|
123
|
+
const selected = new Set(draft.visibleModels ??
|
|
124
|
+
[...allModels, ...missing].map((model) => model.id));
|
|
125
|
+
return (_jsxs("div", { style: { borderTop: border, marginTop: 12, paddingTop: 12 }, children: [_jsx("h3", { style: { margin: 0 }, children: t('modelsEdit') }), _jsxs("div", { style: { display: 'grid', gap: 12, marginTop: 12 }, children: [_jsx("p", { className: "dsh-subscription-hint", children: t('modelsHint') }), error && _jsx("p", { role: "alert", children: error }), _jsxs("div", { style: actions, children: [_jsx("button", { type: "button", className: "dsh-subscription-quiet", disabled: busy || disabled || dirty, onClick: () => {
|
|
121
126
|
void load(true);
|
|
122
|
-
}, children: t('usageRefresh') }), busy && _jsx("span", { role: "status", children: t('modelDefaultsLoading') })] }), catalog && (_jsxs("fieldset", { disabled: busy || disabled, style: {
|
|
127
|
+
}, children: t('usageRefresh') }), busy && _jsx("span", { role: "status", children: t('modelDefaultsLoading') })] }), !busy && catalog && (_jsxs("fieldset", { disabled: busy || disabled, style: {
|
|
123
128
|
border: 0,
|
|
124
129
|
padding: 0,
|
|
125
130
|
margin: 0,
|
|
@@ -133,44 +138,57 @@ export const ProviderModelEditor = forwardRef(function ProviderModelEditor({ pro
|
|
|
133
138
|
else
|
|
134
139
|
next.visibleModels = allModels.map((model) => model.id);
|
|
135
140
|
edit(next);
|
|
136
|
-
} }), ' ', t('modelsAutomatic')] }), _jsxs("div", { style: actions, children: [_jsx("input", { style: { flex: '1 1 180px' }, value: query, onChange: (event) => setQuery(event.target.value), placeholder: t('modelDefaultsFilterPlaceholder'), "aria-label": t('modelDefaultsFilterPlaceholder') }), _jsx("button", { type: "button", onClick: () => edit({
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
display: 'grid',
|
|
143
|
-
gap: 8,
|
|
144
|
-
}, children: [models.map((model) => (_jsxs("div", { style: {
|
|
145
|
-
borderBottom: border,
|
|
146
|
-
padding: '8px 2px',
|
|
141
|
+
} }), ' ', t('modelsAutomatic')] }), editorState.listState === 'provider-empty' ? (_jsx("p", { className: "dsh-subscription-hint", children: t(editorState.noticeKey) })) : (_jsxs(_Fragment, { children: [_jsxs("div", { style: actions, children: [_jsx("input", { style: { flex: '1 1 180px' }, value: query, onChange: (event) => setQuery(event.target.value), placeholder: t('modelDefaultsFilterPlaceholder'), "aria-label": t('modelDefaultsFilterPlaceholder') }), _jsx("button", { type: "button", onClick: () => edit({
|
|
142
|
+
...draft,
|
|
143
|
+
visibleModels: allModels.map((model) => model.id),
|
|
144
|
+
}), children: t('modelsSelectAll') }), _jsx("button", { type: "button", onClick: () => edit({ ...draft, visibleModels: [] }), children: t('modelsSelectNone') })] }), _jsxs("div", { style: {
|
|
145
|
+
maxHeight: 360,
|
|
146
|
+
overflowY: 'auto',
|
|
147
147
|
display: 'grid',
|
|
148
148
|
gap: 8,
|
|
149
|
-
}, children: [
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
}, children: [
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
setContexts({
|
|
166
|
-
...contexts,
|
|
149
|
+
}, children: [models.map((model) => (_jsxs("div", { style: {
|
|
150
|
+
borderBottom: border,
|
|
151
|
+
padding: '8px 2px',
|
|
152
|
+
display: 'grid',
|
|
153
|
+
gap: 8,
|
|
154
|
+
}, children: [_jsxs("label", { style: { overflowWrap: 'anywhere' }, children: [_jsx("input", { type: "checkbox", checked: selected.has(model.id), onChange: (event) => {
|
|
155
|
+
const next = new Set(selected);
|
|
156
|
+
if (event.target.checked)
|
|
157
|
+
next.add(model.id);
|
|
158
|
+
else
|
|
159
|
+
next.delete(model.id);
|
|
160
|
+
edit({ ...draft, visibleModels: [...next] });
|
|
161
|
+
} }), ' ', model.name, !known.has(model.id) &&
|
|
162
|
+
` (${t('modelsUnavailable')})`] }), _jsxs("div", { style: actions, children: [(model.efforts?.length ?? 0) > 0 && (_jsxs("label", { style: actions, children: [t('modelDefaultsTitle'), _jsxs("select", { "aria-label": `${model.name} ${t('modelDefaultsTitle')}`, value: efforts[model.id] ?? '', onChange: (event) => {
|
|
163
|
+
setEfforts({
|
|
164
|
+
...efforts,
|
|
167
165
|
[model.id]: event.target.value,
|
|
168
166
|
});
|
|
169
167
|
setDirty(true);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
168
|
+
}, children: [_jsx("option", { value: "", children: t('modelDefaultsFollowProvider') }), model.configured &&
|
|
169
|
+
!model.efforts?.some((effort) => effort.id === model.configured) && (_jsxs("option", { value: model.configured, disabled: true, children: [model.configured, " (", t('modelsUnavailable'), ")"] })), model.efforts?.map((effort) => (_jsx("option", { value: effort.id, children: effort.name }, effort.id)))] })] })), supportsContext(provider) &&
|
|
170
|
+
model.maxContextWindow !== undefined && (_jsxs("div", { style: actions, children: [_jsxs("label", { style: actions, children: [t('modelsContext'), _jsx("input", { style: { width: 140 }, inputMode: "numeric", value: contexts[model.id] ?? '', "aria-label": `${model.name} ${t('modelsContext')}`, placeholder: String(model.defaultContextWindow), onChange: (event) => {
|
|
171
|
+
setContexts({
|
|
172
|
+
...contexts,
|
|
173
|
+
[model.id]: event.target.value,
|
|
174
|
+
});
|
|
175
|
+
setDirty(true);
|
|
176
|
+
} })] }), _jsx("small", { children: t('modelsContextBounds', {
|
|
177
|
+
default: model.defaultContextWindow,
|
|
178
|
+
max: model.maxContextWindow,
|
|
179
|
+
}) })] }))] }), provider === 'codex' &&
|
|
180
|
+
(model.fastTier === true ||
|
|
181
|
+
Object.hasOwn(draft.subagentSpeeds ?? {}, model.id)) && (_jsxs("div", { style: { display: 'grid', gap: 6 }, children: [_jsxs("label", { style: actions, children: [t('modelsSubagentSpeed'), _jsxs("select", { "aria-label": `${model.name} ${t('modelsSubagentSpeed')}`, value: Object.hasOwn(draft.subagentSpeeds ?? {}, model.id)
|
|
182
|
+
? draft.subagentSpeeds[model.id]
|
|
183
|
+
: 'standard', onChange: (event) => edit({
|
|
184
|
+
...draft,
|
|
185
|
+
subagentSpeeds: {
|
|
186
|
+
...draft.subagentSpeeds,
|
|
187
|
+
[model.id]: event.target
|
|
188
|
+
.value,
|
|
189
|
+
},
|
|
190
|
+
}), children: [_jsx("option", { value: "standard", children: t('speedStandard') }), _jsxs("option", { value: "fast", disabled: !model.fastTier, children: [t('speedFast'), !model.fastTier &&
|
|
191
|
+
` (${t('modelsUnavailable')})`] })] })] }), _jsx("small", { children: t('modelsSubagentSpeedHint') })] }))] }, model.id))), models.length === 0 && (_jsx("span", { children: t('modelDefaultsFilterEmpty', { query }) }))] })] })), supportsContext(provider) && (_jsx("small", { children: t('modelsContextHint') })), catalog.tools.length > 0 && (_jsxs("div", { style: { display: 'grid', gap: 8 }, children: [_jsx("strong", { children: t('modelsTools') }), _jsx("small", { children: t('modelsToolsHint') }), catalog.tools.map((tool) => (_jsxs("label", { children: [_jsx("input", { type: "checkbox", checked: draft.tools?.[tool] !== false, onChange: (event) => edit({
|
|
174
192
|
...draft,
|
|
175
193
|
tools: {
|
|
176
194
|
...draft.tools,
|
|
@@ -32,20 +32,21 @@ import { en } from './locales.js';
|
|
|
32
32
|
*/
|
|
33
33
|
export function createSpeedLoader(connection, models, sessionId) {
|
|
34
34
|
return async () => {
|
|
35
|
-
const state = await callSubscriptionsAuth(connection.rpc, 'speed', { sessionId });
|
|
36
35
|
const directories = models();
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
throw error;
|
|
47
|
-
return { visible: false, tier: state.tier };
|
|
36
|
+
let current = null;
|
|
37
|
+
if (directories !== undefined) {
|
|
38
|
+
try {
|
|
39
|
+
current = (await directories.directoryFor(sessionId).load()).current;
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
if (!modelSelectionUnavailable(error))
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
48
45
|
}
|
|
46
|
+
const state = await callSubscriptionsAuth(connection.rpc, 'speed', {
|
|
47
|
+
sessionId,
|
|
48
|
+
...(current?.provider === 'codex' ? { model: current.model } : {}),
|
|
49
|
+
});
|
|
49
50
|
const visible = current !== null &&
|
|
50
51
|
current.provider === 'codex' &&
|
|
51
52
|
state.fastModels.includes(current.model);
|
|
@@ -48,7 +48,7 @@ export interface ProviderUsageDisplay {
|
|
|
48
48
|
/** The account the collapsed pill reads: the default one, else the first listed. */
|
|
49
49
|
export declare function pillAccountOf(d: ProviderUsageDisplay): AccountUsageDisplay;
|
|
50
50
|
/** Brand display names (short form for the compact badge). */
|
|
51
|
-
export type BadgeProvider = SubscriptionProvider | '
|
|
51
|
+
export type BadgeProvider = SubscriptionProvider | 'opencode-go' | 'kimi-coding' | 'minimax' | 'minimax-cn' | 'ollama-cloud';
|
|
52
52
|
export interface UsageRosterEntry {
|
|
53
53
|
provider: BadgeProvider;
|
|
54
54
|
account: AccountStatus;
|
|
@@ -60,7 +60,6 @@ const PROVIDER_NAMES = {
|
|
|
60
60
|
grok: 'Grok',
|
|
61
61
|
copilot: 'Copilot',
|
|
62
62
|
antigravity: 'Antigravity',
|
|
63
|
-
'cursor-subscription': 'Cursor',
|
|
64
63
|
'opencode-go': 'OpenCode Go',
|
|
65
64
|
'kimi-coding': 'Kimi Code',
|
|
66
65
|
minimax: 'MiniMax',
|
|
@@ -110,9 +109,8 @@ export function groupUsageDisplays(roster, windowsOf, plans) {
|
|
|
110
109
|
}
|
|
111
110
|
/** Read each account source independently so a failing endpoint cannot hide the others. */
|
|
112
111
|
export async function loadBadgeRoster(rpc) {
|
|
113
|
-
const [subscriptions,
|
|
112
|
+
const [subscriptions, external] = await Promise.allSettled([
|
|
114
113
|
callSubscriptionsAuth(rpc, 'status', {}),
|
|
115
|
-
callSubscriptionsAuth(rpc, 'cursorStatus', {}),
|
|
116
114
|
callSubscriptionsAuth(rpc, 'externalStatus', {}),
|
|
117
115
|
]);
|
|
118
116
|
const roster = [];
|
|
@@ -130,14 +128,6 @@ export async function loadBadgeRoster(rpc) {
|
|
|
130
128
|
roster.push({ provider, account });
|
|
131
129
|
}
|
|
132
130
|
}
|
|
133
|
-
if (cursor.status === 'fulfilled') {
|
|
134
|
-
refreshed.add('cursor-subscription');
|
|
135
|
-
if (cursor.value.authenticated)
|
|
136
|
-
roster.push({
|
|
137
|
-
provider: 'cursor-subscription',
|
|
138
|
-
account: { key: 'cursor', isDefault: true },
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
131
|
if (external.status === 'fulfilled') {
|
|
142
132
|
for (const [source, provider] of [
|
|
143
133
|
['opencode-go', 'opencode-go'],
|
|
@@ -157,8 +147,6 @@ export async function loadBadgeRoster(rpc) {
|
|
|
157
147
|
return { roster, refreshed };
|
|
158
148
|
}
|
|
159
149
|
export async function usageOf(rpc, { provider, account }) {
|
|
160
|
-
if (provider === 'cursor-subscription')
|
|
161
|
-
return callSubscriptionsAuth(rpc, 'cursorUsage', {});
|
|
162
150
|
if (provider === 'opencode-go' ||
|
|
163
151
|
provider === 'kimi-coding' ||
|
|
164
152
|
provider === 'minimax' ||
|
|
@@ -13,6 +13,8 @@ export interface AccountStatus {
|
|
|
13
13
|
}
|
|
14
14
|
/** One provider's login state as answered by the `status` endpoint. */
|
|
15
15
|
export interface ProviderStatus {
|
|
16
|
+
/** Picker selection; absent follows discovery, [] hides all model quotas. */
|
|
17
|
+
visibleModels?: string[];
|
|
16
18
|
busy: boolean;
|
|
17
19
|
accounts: AccountStatus[];
|
|
18
20
|
detail?: string;
|
|
@@ -15,10 +15,10 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
15
15
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
16
16
|
import { en } from './locales.js';
|
|
17
17
|
import { resetActionBlock } from './reset-action.js';
|
|
18
|
+
import { visibleUsageWindows } from './usage-window-filter.js';
|
|
18
19
|
import { ProviderAccountManager } from './ProviderAccountManager.js';
|
|
19
20
|
import { providerSettingsCss } from './provider-settings-styles.js';
|
|
20
21
|
import { ExternalUsageCards } from './ExternalUsageCards.js';
|
|
21
|
-
import { CursorCard } from './CursorCard.js';
|
|
22
22
|
import { OllamaCard } from './OllamaCard.js';
|
|
23
23
|
import { UsageMeter } from './UsageMeter.js';
|
|
24
24
|
import { ResetCreditsDisclosure, ResetCreditsErrorLine, showsResetCredits, } from './reset-credits-view.js';
|
|
@@ -641,6 +641,7 @@ export function SubscriptionsSection(props) {
|
|
|
641
641
|
return (_jsxs("div", { style: styles.card, children: [_jsxs("div", { style: styles.cardHeader, children: [_jsx("span", { style: { ...styles.dot, background: dotColor(status) } }), _jsx("span", { style: styles.name, children: name }), status?.clientVersion !== undefined && (_jsx("span", { style: styles.clientVersion, title: t('clientVersionHint'), children: clientVersionText(t, status.clientVersion) }))] }), _jsx("p", { style: styles.statusLine, children: statusText(t, status) }), status?.detail !== undefined && status.detail !== '' && (_jsx("p", { style: styles.statusLine, children: status.detail })), errors[id] !== undefined && (_jsx("p", { style: styles.errorLine, children: errors[id] })), accounts.map((account) => {
|
|
642
642
|
const usageKey = `${id}:${account.key}`;
|
|
643
643
|
const usage = usages[usageKey];
|
|
644
|
+
const shownWindows = visibleUsageWindows(id, usage?.windows, status?.visibleModels);
|
|
644
645
|
const usageError = usageErrors[usageKey];
|
|
645
646
|
const display = account.account ?? account.key;
|
|
646
647
|
const sortedCredits = [...(usage?.resetCredits ?? [])].sort((a, b) => (a.expiresAt ?? Infinity) - (b.expiresAt ?? Infinity));
|
|
@@ -664,7 +665,9 @@ export function SubscriptionsSection(props) {
|
|
|
664
665
|
}, children: t('logout') })] }), showUsage && (_jsxs("div", { style: styles.usage, children: [_jsxs("div", { style: styles.usageHeader, children: [_jsx("span", { style: styles.usageTitle, children: t('usageTitle') }), usage?.plan !== undefined && (_jsx("span", { style: styles.usagePlan, children: t('usagePlan', { plan: usage.plan }) })), _jsx("button", { type: "button", style: styles.usageRefresh, disabled: usageLoading[usageKey] === true, onClick: () => {
|
|
665
666
|
void loadUsage(id, account.key, true);
|
|
666
667
|
}, children: t('usageRefresh') })] }), usage === undefined && usageError === undefined && (_jsx("p", { style: styles.statusLine, children: t('usageLoading') })), usageError !== undefined && (_jsx("p", { style: styles.errorLine, children: t('usageError', { message: usageError }) })), usage?.windows !== undefined &&
|
|
667
|
-
usage.windows.length === 0 && (_jsx("p", { style: styles.statusLine, children: t('usageEmpty') })),
|
|
668
|
+
usage.windows.length === 0 && (_jsx("p", { style: styles.statusLine, children: t('usageEmpty') })), usage?.windows !== undefined &&
|
|
669
|
+
usage.windows.length > 0 &&
|
|
670
|
+
shownWindows.length === 0 && (_jsx("p", { style: styles.statusLine, children: t('usageNoVisibleModels') })), shownWindows.map((window, index) => {
|
|
668
671
|
const percent = displayUsedPercent(window.usedPercent);
|
|
669
672
|
return (_jsxs("div", { style: styles.usageRow, children: [_jsxs("div", { style: styles.usageMeta, children: [_jsx("span", { children: usageWindowLabel(t, window) }), _jsxs("span", { children: [percent === undefined
|
|
670
673
|
? t('usageMeterInvalid')
|
|
@@ -702,7 +705,7 @@ export function SubscriptionsSection(props) {
|
|
|
702
705
|
void login(id, 'keychain');
|
|
703
706
|
}, children: t('addAccountKeychain') })] })), !busy && accounts.length > 0 && id !== 'claude' && (_jsx("button", { type: "button", onClick: () => {
|
|
704
707
|
void login(id);
|
|
705
|
-
}, children: t('addAccount') })), _jsx("button", { type: "button", "aria-haspopup": "dialog", onClick: () => setManagedProvider({ id, name }), children: t('accountsManage') }), busy && (_jsx("button", { type: "button", onClick: () => {
|
|
708
|
+
}, children: t('addAccount') })), !busy && (_jsx("button", { type: "button", "aria-haspopup": "dialog", onClick: () => setManagedProvider({ id, name }), children: t('accountsManage') })), busy && (_jsx("button", { type: "button", onClick: () => {
|
|
706
709
|
void cancel(id);
|
|
707
710
|
}, children: t('cancel') }))] }), !busy && accounts.length > 0 && (_jsx("p", { style: styles.statusLine, children: t('addAccountHint') })), busy && deviceCode !== undefined && (_jsxs("div", { style: styles.deviceCode, children: [_jsx("span", { style: styles.statusLine, children: t('deviceCodePrompt') }), _jsx("span", { style: styles.deviceCodeText, children: deviceCode.userCode }), _jsxs("div", { style: styles.actions, children: [_jsx("button", { type: "button", onClick: () => {
|
|
708
711
|
copyDeviceCode(id, deviceCode.userCode);
|
|
@@ -716,7 +719,10 @@ export function SubscriptionsSection(props) {
|
|
|
716
719
|
})) }), _jsx("button", { type: "button", onClick: () => {
|
|
717
720
|
void submitManual(id);
|
|
718
721
|
}, children: t('submit') })] })] }))] }, id));
|
|
719
|
-
}), _jsx(
|
|
722
|
+
}), _jsx(OllamaCard, { rpc: rpc, t: t }), _jsx(ExternalUsageCards, { rpc: rpc, t: t }), managedProvider && (_jsx(ProviderAccountManager, { provider: managedProvider.id, name: managedProvider.name, rpc: rpc, t: t, onClose: () => {
|
|
723
|
+
setManagedProvider(undefined);
|
|
724
|
+
void refresh();
|
|
725
|
+
} })), _jsx("style", { children: '.subscriptions-reset-dialog::backdrop { background: var(--dsw-alias-bg-mask-1, #0006); backdrop-filter: var(--dsw-mask-blur, none); }' }), _jsx("dialog", { ref: resetDialogRef, className: "subscriptions-reset-dialog", "aria-labelledby": "subscriptions-reset-title", onCancel: () => setResetConfirmation(undefined), onClose: () => setResetConfirmation(undefined), style: {
|
|
720
726
|
...styles.modal,
|
|
721
727
|
display: resetConfirmation ? 'flex' : 'none',
|
|
722
728
|
margin: 'auto',
|
package/lib/client/locales.d.ts
CHANGED
|
@@ -29,6 +29,8 @@ export declare const en: {
|
|
|
29
29
|
modelsContextBounds: string;
|
|
30
30
|
modelsContextHint: string;
|
|
31
31
|
modelsContextInvalid: string;
|
|
32
|
+
modelsSubagentSpeed: string;
|
|
33
|
+
modelsSubagentSpeedHint: string;
|
|
32
34
|
modelsTools: string;
|
|
33
35
|
modelsToolsHint: string;
|
|
34
36
|
modelsImage: string;
|
|
@@ -78,6 +80,7 @@ export declare const en: {
|
|
|
78
80
|
usageRefresh: string;
|
|
79
81
|
usageLoading: string;
|
|
80
82
|
usageEmpty: string;
|
|
83
|
+
usageNoVisibleModels: string;
|
|
81
84
|
usageUnsupported: string;
|
|
82
85
|
usageError: string;
|
|
83
86
|
usageSession: string;
|
|
@@ -145,22 +148,6 @@ export declare const en: {
|
|
|
145
148
|
externalUsageApiKey: string;
|
|
146
149
|
externalUsageConfigure: string;
|
|
147
150
|
externalUsageUnavailable: string;
|
|
148
|
-
cursorTitle: string;
|
|
149
|
-
cursorOpenLogin: string;
|
|
150
|
-
cursorWaiting: string;
|
|
151
|
-
cursorConnected: string;
|
|
152
|
-
cursorManageTitle: string;
|
|
153
|
-
cursorManageSignIn: string;
|
|
154
|
-
cursorAccountsHint: string;
|
|
155
|
-
cursorManageHint: string;
|
|
156
|
-
cursorFilterModels: string;
|
|
157
|
-
cursorModelsLoading: string;
|
|
158
|
-
cursorModelsError: string;
|
|
159
|
-
cursorModelsCount: string;
|
|
160
|
-
cursorIncluded: string;
|
|
161
|
-
cursorModels: string;
|
|
162
|
-
cursorOtherModels: string;
|
|
163
|
-
cursorIncludedRequests: string;
|
|
164
151
|
modelDefaultsTitle: string;
|
|
165
152
|
modelDefaultsHint: string;
|
|
166
153
|
modelDefaultsFollowProvider: string;
|
|
@@ -176,6 +163,9 @@ export declare const en: {
|
|
|
176
163
|
modelDefaultsSummaryEmpty: string;
|
|
177
164
|
modelDefaultsFilterPlaceholder: string;
|
|
178
165
|
modelDefaultsFilterEmpty: string;
|
|
166
|
+
modelDefaultsNoModels: string;
|
|
167
|
+
modelDefaultsNoAccount: string;
|
|
168
|
+
modelDefaultsDiscoveryFailed: string;
|
|
179
169
|
generating: string;
|
|
180
170
|
image: string;
|
|
181
171
|
viewImage: string;
|
|
@@ -269,6 +259,8 @@ export declare const zh: {
|
|
|
269
259
|
modelsContextBounds: string;
|
|
270
260
|
modelsContextHint: string;
|
|
271
261
|
modelsContextInvalid: string;
|
|
262
|
+
modelsSubagentSpeed: string;
|
|
263
|
+
modelsSubagentSpeedHint: string;
|
|
272
264
|
modelsTools: string;
|
|
273
265
|
modelsToolsHint: string;
|
|
274
266
|
modelsImage: string;
|
|
@@ -318,6 +310,7 @@ export declare const zh: {
|
|
|
318
310
|
usageRefresh: string;
|
|
319
311
|
usageLoading: string;
|
|
320
312
|
usageEmpty: string;
|
|
313
|
+
usageNoVisibleModels: string;
|
|
321
314
|
usageUnsupported: string;
|
|
322
315
|
usageError: string;
|
|
323
316
|
usageSession: string;
|
|
@@ -385,22 +378,6 @@ export declare const zh: {
|
|
|
385
378
|
externalUsageApiKey: string;
|
|
386
379
|
externalUsageConfigure: string;
|
|
387
380
|
externalUsageUnavailable: string;
|
|
388
|
-
cursorTitle: string;
|
|
389
|
-
cursorOpenLogin: string;
|
|
390
|
-
cursorWaiting: string;
|
|
391
|
-
cursorConnected: string;
|
|
392
|
-
cursorManageTitle: string;
|
|
393
|
-
cursorManageSignIn: string;
|
|
394
|
-
cursorAccountsHint: string;
|
|
395
|
-
cursorManageHint: string;
|
|
396
|
-
cursorFilterModels: string;
|
|
397
|
-
cursorModelsLoading: string;
|
|
398
|
-
cursorModelsError: string;
|
|
399
|
-
cursorModelsCount: string;
|
|
400
|
-
cursorIncluded: string;
|
|
401
|
-
cursorModels: string;
|
|
402
|
-
cursorOtherModels: string;
|
|
403
|
-
cursorIncludedRequests: string;
|
|
404
381
|
modelDefaultsTitle: string;
|
|
405
382
|
modelDefaultsHint: string;
|
|
406
383
|
modelDefaultsFollowProvider: string;
|
|
@@ -416,6 +393,9 @@ export declare const zh: {
|
|
|
416
393
|
modelDefaultsSummaryEmpty: string;
|
|
417
394
|
modelDefaultsFilterPlaceholder: string;
|
|
418
395
|
modelDefaultsFilterEmpty: string;
|
|
396
|
+
modelDefaultsNoModels: string;
|
|
397
|
+
modelDefaultsNoAccount: string;
|
|
398
|
+
modelDefaultsDiscoveryFailed: string;
|
|
419
399
|
generating: string;
|
|
420
400
|
image: string;
|
|
421
401
|
viewImage: string;
|
package/lib/client/locales.js
CHANGED
|
@@ -21,7 +21,7 @@ export const en = {
|
|
|
21
21
|
accountsCatalogUnavailable: 'Model discovery is currently unavailable. Saved model selections are preserved.',
|
|
22
22
|
modelsPartialSave: 'Some reasoning defaults were saved. Remaining changes are still in the draft; retry to finish.',
|
|
23
23
|
modelsEdit: 'Edit model list',
|
|
24
|
-
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.',
|
|
24
|
+
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.',
|
|
25
25
|
modelsAutomatic: 'Automatically show all models, including new models',
|
|
26
26
|
modelsSelectAll: 'Select all',
|
|
27
27
|
modelsSelectNone: 'Clear selection',
|
|
@@ -29,6 +29,8 @@ export const en = {
|
|
|
29
29
|
modelsContextBounds: 'Default {default} · Maximum {max}',
|
|
30
30
|
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.',
|
|
31
31
|
modelsContextInvalid: 'Enter a positive whole number of tokens for {model}.',
|
|
32
|
+
modelsSubagentSpeed: 'Subagent default speed',
|
|
33
|
+
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.',
|
|
32
34
|
modelsTools: 'Provider tools',
|
|
33
35
|
modelsToolsHint: 'Changes apply only to sessions created after saving. Existing sessions keep their tools. Shared tools remain available if any supporting provider is enabled.',
|
|
34
36
|
modelsImage: 'Image generation',
|
|
@@ -78,6 +80,7 @@ export const en = {
|
|
|
78
80
|
usageRefresh: 'Refresh',
|
|
79
81
|
usageLoading: 'Loading usage…',
|
|
80
82
|
usageEmpty: 'No usage windows reported.',
|
|
83
|
+
usageNoVisibleModels: 'No usage windows for the models visible in the picker.',
|
|
81
84
|
usageUnsupported: 'This endpoint has no usage surface.',
|
|
82
85
|
usageError: 'Usage lookup failed: {message}',
|
|
83
86
|
usageSession: '5-hour window',
|
|
@@ -145,22 +148,6 @@ export const en = {
|
|
|
145
148
|
externalUsageApiKey: 'API key',
|
|
146
149
|
externalUsageConfigure: 'No API key found at {ref}. Add one in Settings → Models.',
|
|
147
150
|
externalUsageUnavailable: 'Usage status unavailable: {message}',
|
|
148
|
-
cursorTitle: 'Cursor',
|
|
149
|
-
cursorOpenLogin: 'Open Cursor sign-in',
|
|
150
|
-
cursorWaiting: 'Waiting for Cursor sign-in…',
|
|
151
|
-
cursorConnected: 'Connected',
|
|
152
|
-
cursorManageTitle: 'Manage Cursor',
|
|
153
|
-
cursorManageSignIn: 'Sign in to see the models available to this Cursor account.',
|
|
154
|
-
cursorAccountsHint: 'Cursor uses one connected account. Choose which of its models appear in the model picker.',
|
|
155
|
-
cursorManageHint: 'Choose model visibility here. Refresh fetches the current models from Cursor; Save changes updates the model picker.',
|
|
156
|
-
cursorFilterModels: 'Filter models',
|
|
157
|
-
cursorModelsLoading: 'Loading Cursor models…',
|
|
158
|
-
cursorModelsError: 'Could not load Cursor models: {message}',
|
|
159
|
-
cursorModelsCount: '{count} models available',
|
|
160
|
-
cursorIncluded: 'Included usage',
|
|
161
|
-
cursorModels: 'Cursor Models',
|
|
162
|
-
cursorOtherModels: 'Other Models',
|
|
163
|
-
cursorIncludedRequests: 'Included requests',
|
|
164
151
|
modelDefaultsTitle: 'Default reasoning effort',
|
|
165
152
|
modelDefaultsHint: 'The model picker preselects this level when you switch to the model; models without levels always follow the provider.',
|
|
166
153
|
modelDefaultsFollowProvider: 'Follow provider',
|
|
@@ -176,6 +163,9 @@ export const en = {
|
|
|
176
163
|
modelDefaultsSummaryEmpty: 'No model advertises reasoning levels',
|
|
177
164
|
modelDefaultsFilterPlaceholder: 'Filter models',
|
|
178
165
|
modelDefaultsFilterEmpty: 'No model matches “{query}”.',
|
|
166
|
+
modelDefaultsNoModels: 'This provider reports no models.',
|
|
167
|
+
modelDefaultsNoAccount: 'No connected accounts. Connect one on the provider card first.',
|
|
168
|
+
modelDefaultsDiscoveryFailed: 'Model discovery failed or timed out. Use Refresh to try again.',
|
|
179
169
|
generating: 'Generating image…',
|
|
180
170
|
image: 'image',
|
|
181
171
|
viewImage: 'View image',
|
|
@@ -269,7 +259,7 @@ export const zh = {
|
|
|
269
259
|
accountsCatalogUnavailable: '暂时无法获取模型目录,已保存的模型选择会被保留。',
|
|
270
260
|
modelsPartialSave: '部分推理档已保存,其余更改仍保留在草稿中,请重试完成保存。',
|
|
271
261
|
modelsEdit: '编辑模型列表',
|
|
272
|
-
modelsHint: '
|
|
262
|
+
modelsHint: '在此配置模型显示、默认推理档、子代理速度、上下文与工具,底部的「保存更改」会连同账号设置一起提交。隐藏模型不影响已有会话;工具开关对新建会话生效。',
|
|
273
263
|
modelsAutomatic: '自动显示全部模型(包含以后新增的模型)',
|
|
274
264
|
modelsSelectAll: '全选',
|
|
275
265
|
modelsSelectNone: '清空选择',
|
|
@@ -277,6 +267,8 @@ export const zh = {
|
|
|
277
267
|
modelsContextBounds: '默认 {default} · 最大 {max}',
|
|
278
268
|
modelsContextHint: '留空跟随服务商。配置值按账号返回的最大窗口限制;账号池取各成员的最小窗口。此设置控制本地历史保留和压缩时机,不会扩大 API 本身的容量。',
|
|
279
269
|
modelsContextInvalid: '请为 {model} 输入正整数 token 数。',
|
|
270
|
+
modelsSubagentSpeed: '子代理默认速度',
|
|
271
|
+
modelsSubagentSpeedHint: '对使用此模型的子代理后续请求生效,与父会话模型无关。主会话仍使用自己的速度设置;快速档仅在模型支持时生效。',
|
|
280
272
|
modelsTools: 'Provider 工具',
|
|
281
273
|
modelsToolsHint: '仅对保存后新建的会话生效,已有会话保留原工具。任一支持的服务商启用共享工具时,该工具仍会显示。',
|
|
282
274
|
modelsImage: '图片生成',
|
|
@@ -326,6 +318,7 @@ export const zh = {
|
|
|
326
318
|
usageRefresh: '刷新',
|
|
327
319
|
usageLoading: '用量加载中…',
|
|
328
320
|
usageEmpty: '服务商未返回任何用量窗口。',
|
|
321
|
+
usageNoVisibleModels: '选择器中的可见模型没有任何用量窗口。',
|
|
329
322
|
usageUnsupported: '该端点不提供用量查询。',
|
|
330
323
|
usageError: '用量查询失败:{message}',
|
|
331
324
|
usageSession: '5 小时窗口',
|
|
@@ -393,22 +386,6 @@ export const zh = {
|
|
|
393
386
|
externalUsageApiKey: 'API 密钥',
|
|
394
387
|
externalUsageConfigure: '未在 {ref} 找到 API 密钥。请在「设置 → 模型」中添加。',
|
|
395
388
|
externalUsageUnavailable: '无法获取用量状态:{message}',
|
|
396
|
-
cursorTitle: 'Cursor',
|
|
397
|
-
cursorOpenLogin: '打开 Cursor 登录页面',
|
|
398
|
-
cursorWaiting: '等待 Cursor 登录…',
|
|
399
|
-
cursorConnected: '已连接',
|
|
400
|
-
cursorManageTitle: '管理 Cursor',
|
|
401
|
-
cursorManageSignIn: '登录后可查看此 Cursor 账号可用的模型。',
|
|
402
|
-
cursorAccountsHint: 'Cursor 使用一个已连接账号。可在此选择模型选择器中显示的模型。',
|
|
403
|
-
cursorManageHint: '在此选择模型是否显示。刷新可从 Cursor 获取当前模型;保存更改后更新模型选择器。',
|
|
404
|
-
cursorFilterModels: '筛选模型',
|
|
405
|
-
cursorModelsLoading: '正在加载 Cursor 模型…',
|
|
406
|
-
cursorModelsError: '无法加载 Cursor 模型:{message}',
|
|
407
|
-
cursorModelsCount: '可用模型:{count} 个',
|
|
408
|
-
cursorIncluded: '套餐内用量',
|
|
409
|
-
cursorModels: 'Cursor 模型',
|
|
410
|
-
cursorOtherModels: '其他模型',
|
|
411
|
-
cursorIncludedRequests: '套餐内请求',
|
|
412
389
|
modelDefaultsTitle: '默认推理档',
|
|
413
390
|
modelDefaultsHint: '切换到该模型时,模型选择器会预选此档位;没有推理档的模型始终跟随服务商默认。',
|
|
414
391
|
modelDefaultsFollowProvider: '跟随服务商',
|
|
@@ -424,6 +401,9 @@ export const zh = {
|
|
|
424
401
|
modelDefaultsSummaryEmpty: '没有模型声明推理档',
|
|
425
402
|
modelDefaultsFilterPlaceholder: '筛选模型',
|
|
426
403
|
modelDefaultsFilterEmpty: '没有匹配「{query}」的模型。',
|
|
404
|
+
modelDefaultsNoModels: '这个服务商没有报告任何模型。',
|
|
405
|
+
modelDefaultsNoAccount: '还没有连接账号。请先在卡片上连接一个账号。',
|
|
406
|
+
modelDefaultsDiscoveryFailed: '模型列表发现失败或超时。可以点「刷新」重试。',
|
|
427
407
|
generating: '正在生成图片…',
|
|
428
408
|
image: '图片',
|
|
429
409
|
viewImage: '查看图片',
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { SubagentSpeedTier } from '../provider-settings.js';
|
|
2
|
+
import type { AccountCatalogRow } from './account-preferences.js';
|
|
3
|
+
/** What the model editor shows for the current filter state. */
|
|
4
|
+
export type ModelListState = 'provider-empty' | 'filter-empty' | 'rows';
|
|
5
|
+
/** Distinguish an empty provider from a query that filtered out every row. */
|
|
6
|
+
export declare function modelListState(total: number, matched: number): ModelListState;
|
|
7
|
+
/**
|
|
8
|
+
* Retain saved model ids when discovery returns no rows. A temporary catalog
|
|
9
|
+
* failure must not hide the visibility or subagent-speed settings they edit.
|
|
10
|
+
*/
|
|
11
|
+
export declare function savedModelIds(visibleModels: readonly string[] | undefined, subagentSpeeds: Readonly<Record<string, SubagentSpeedTier>> | undefined): string[];
|
|
12
|
+
/** Which notice replaces inert model controls. */
|
|
13
|
+
export type ModelEditorNotice = 'noAccount' | 'discoveryFailed' | 'noModels';
|
|
14
|
+
/**
|
|
15
|
+
* Use account discovery evidence to explain an empty provider. Connected
|
|
16
|
+
* accounts with failed discovery need a retry, not another login prompt.
|
|
17
|
+
*/
|
|
18
|
+
export declare function modelEditorNotice(accounts: readonly AccountCatalogRow[] | undefined): ModelEditorNotice;
|
|
19
|
+
/** Everything the component needs to choose rows or one explanatory notice. */
|
|
20
|
+
export interface ModelEditorState {
|
|
21
|
+
listState: ModelListState;
|
|
22
|
+
/** Rendered only when `listState` is `provider-empty`. */
|
|
23
|
+
noticeKey: (typeof NOTICE_KEYS)[ModelEditorNotice];
|
|
24
|
+
}
|
|
25
|
+
/** Locale keys for the three empty states. */
|
|
26
|
+
export declare const NOTICE_KEYS: {
|
|
27
|
+
readonly noAccount: "modelDefaultsNoAccount";
|
|
28
|
+
readonly discoveryFailed: "modelDefaultsDiscoveryFailed";
|
|
29
|
+
readonly noModels: "modelDefaultsNoModels";
|
|
30
|
+
};
|
|
31
|
+
/** Combine discovered and saved rows, filter results, and discovery evidence. */
|
|
32
|
+
export declare function modelEditorState(input: {
|
|
33
|
+
discovered: number;
|
|
34
|
+
saved: number;
|
|
35
|
+
matched: number;
|
|
36
|
+
accounts: readonly AccountCatalogRow[] | undefined;
|
|
37
|
+
}): ModelEditorState;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/** Distinguish an empty provider from a query that filtered out every row. */
|
|
2
|
+
export function modelListState(total, matched) {
|
|
3
|
+
if (total === 0)
|
|
4
|
+
return 'provider-empty';
|
|
5
|
+
return matched === 0 ? 'filter-empty' : 'rows';
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Retain saved model ids when discovery returns no rows. A temporary catalog
|
|
9
|
+
* failure must not hide the visibility or subagent-speed settings they edit.
|
|
10
|
+
*/
|
|
11
|
+
export function savedModelIds(visibleModels, subagentSpeeds) {
|
|
12
|
+
return [
|
|
13
|
+
...new Set([
|
|
14
|
+
...(visibleModels ?? []),
|
|
15
|
+
...Object.keys(subagentSpeeds ?? {}),
|
|
16
|
+
]),
|
|
17
|
+
];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Use account discovery evidence to explain an empty provider. Connected
|
|
21
|
+
* accounts with failed discovery need a retry, not another login prompt.
|
|
22
|
+
*/
|
|
23
|
+
export function modelEditorNotice(accounts) {
|
|
24
|
+
// Missing account evidence uses the neutral empty-catalog copy.
|
|
25
|
+
if (accounts === undefined)
|
|
26
|
+
return 'noModels';
|
|
27
|
+
if (accounts.length === 0)
|
|
28
|
+
return 'noAccount';
|
|
29
|
+
return accounts.every((account) => account.unavailable === true)
|
|
30
|
+
? 'discoveryFailed'
|
|
31
|
+
: 'noModels';
|
|
32
|
+
}
|
|
33
|
+
/** Locale keys for the three empty states. */
|
|
34
|
+
export const NOTICE_KEYS = {
|
|
35
|
+
noAccount: 'modelDefaultsNoAccount',
|
|
36
|
+
discoveryFailed: 'modelDefaultsDiscoveryFailed',
|
|
37
|
+
noModels: 'modelDefaultsNoModels',
|
|
38
|
+
};
|
|
39
|
+
/** Combine discovered and saved rows, filter results, and discovery evidence. */
|
|
40
|
+
export function modelEditorState(input) {
|
|
41
|
+
return {
|
|
42
|
+
listState: modelListState(input.discovered + input.saved, input.matched),
|
|
43
|
+
noticeKey: NOTICE_KEYS[modelEditorNotice(input.accounts)],
|
|
44
|
+
};
|
|
45
|
+
}
|