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
|
@@ -6,7 +6,10 @@ import { parseProviderJson } from './common.js';
|
|
|
6
6
|
* bar.
|
|
7
7
|
*/
|
|
8
8
|
function usedFromCounts(total, count) {
|
|
9
|
-
return typeof total === 'number' &&
|
|
9
|
+
return typeof total === 'number' &&
|
|
10
|
+
Number.isFinite(total) &&
|
|
11
|
+
total > 0 &&
|
|
12
|
+
typeof count === 'number'
|
|
10
13
|
? (1 - count / total) * 100
|
|
11
14
|
: undefined;
|
|
12
15
|
}
|
|
@@ -36,13 +39,18 @@ export async function fetchMiniMaxUsage(key, region, http = fetch, signal) {
|
|
|
36
39
|
if (key.startsWith('sk-api-'))
|
|
37
40
|
throw new Error('MiniMax requires a subscription key, not a pay-as-you-go key');
|
|
38
41
|
const response = await http(`https://www.minimax.${region === 'cn' ? 'cn' : 'io'}/v1/token_plan/remains`, {
|
|
39
|
-
headers: {
|
|
42
|
+
headers: {
|
|
43
|
+
authorization: `Bearer ${key}`,
|
|
44
|
+
'content-type': 'application/json',
|
|
45
|
+
},
|
|
40
46
|
...(signal === undefined ? {} : { signal }),
|
|
41
47
|
});
|
|
42
48
|
if (!response.ok)
|
|
43
49
|
throw new Error(`MiniMax usage endpoint returned HTTP ${response.status}`);
|
|
44
|
-
const body = await parseProviderJson(response, 'MiniMax usage');
|
|
45
|
-
if ((body?.base_resp?.status_code !== undefined &&
|
|
50
|
+
const body = (await parseProviderJson(response, 'MiniMax usage'));
|
|
51
|
+
if ((body?.base_resp?.status_code !== undefined &&
|
|
52
|
+
body.base_resp.status_code !== 0) ||
|
|
53
|
+
!Array.isArray(body?.model_remains)) {
|
|
46
54
|
throw new Error('MiniMax usage response failed or has no quota records');
|
|
47
55
|
}
|
|
48
56
|
const windows = [];
|
|
@@ -60,22 +68,34 @@ export async function fetchMiniMaxUsage(key, region, http = fetch, signal) {
|
|
|
60
68
|
if (status === 3)
|
|
61
69
|
continue; // Unlimited pools cannot have a finite percentage.
|
|
62
70
|
const remaining = row[`${prefix}_remaining_percent`];
|
|
63
|
-
const boost = prefix === 'current_weekly' ? row.weekly_boost_permille ?? 1000 : 1000;
|
|
71
|
+
const boost = prefix === 'current_weekly' ? (row.weekly_boost_permille ?? 1000) : 1000;
|
|
64
72
|
if (typeof boost !== 'number' || !Number.isFinite(boost) || boost <= 0)
|
|
65
73
|
continue;
|
|
66
74
|
// An explicit percentage needs no counts: time-based plans send zero
|
|
67
75
|
// counts beside it. Official CLI compatibility: without a percentage, the
|
|
68
76
|
// legacy counts are the quota remaining.
|
|
69
|
-
const usedPercent = typeof remaining === 'number'
|
|
77
|
+
const usedPercent = typeof remaining === 'number'
|
|
78
|
+
? 100 - (remaining * boost) / 1000
|
|
70
79
|
: usedFromCounts(row[`${prefix}_total_count`], row[`${prefix}_usage_count`]);
|
|
71
|
-
if (usedPercent === undefined ||
|
|
80
|
+
if (usedPercent === undefined ||
|
|
81
|
+
!Number.isFinite(usedPercent) ||
|
|
82
|
+
usedPercent < 0 ||
|
|
83
|
+
usedPercent > 100)
|
|
72
84
|
continue;
|
|
73
85
|
const start = row[startKey];
|
|
74
86
|
const end = row[endKey];
|
|
75
|
-
const validBounds = typeof start === 'number' &&
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
87
|
+
const validBounds = typeof start === 'number' &&
|
|
88
|
+
Number.isFinite(start) &&
|
|
89
|
+
start > 0 &&
|
|
90
|
+
typeof end === 'number' &&
|
|
91
|
+
Number.isFinite(end) &&
|
|
92
|
+
end > start;
|
|
93
|
+
windows.push({
|
|
94
|
+
kind: fallback === 'session' && validBounds && end - start !== 18_000_000
|
|
95
|
+
? 'other'
|
|
96
|
+
: fallback,
|
|
97
|
+
scope: row.model_name,
|
|
98
|
+
usedPercent,
|
|
79
99
|
...(validBounds ? { startsAt: start, resetsAt: end } : {}),
|
|
80
100
|
});
|
|
81
101
|
}
|
|
@@ -34,13 +34,17 @@ export class NpmCliVersionCache {
|
|
|
34
34
|
this.timeoutMs = options.timeoutMs ?? 5000;
|
|
35
35
|
}
|
|
36
36
|
/** A manual catalog refresh also checks for a newly released CLI. */
|
|
37
|
-
invalidate() {
|
|
37
|
+
invalidate() {
|
|
38
|
+
this.expiresAt = 0;
|
|
39
|
+
}
|
|
38
40
|
resolve() {
|
|
39
41
|
if (this.pending !== undefined)
|
|
40
42
|
return this.pending;
|
|
41
43
|
if (this.presented !== undefined && this.now() < this.expiresAt)
|
|
42
44
|
return Promise.resolve(this.presented.version);
|
|
43
|
-
this.pending = this.refresh().finally(() => {
|
|
45
|
+
this.pending = this.refresh().finally(() => {
|
|
46
|
+
this.pending = undefined;
|
|
47
|
+
});
|
|
44
48
|
return this.pending;
|
|
45
49
|
}
|
|
46
50
|
/**
|
|
@@ -82,7 +86,10 @@ export class NpmCliVersionCache {
|
|
|
82
86
|
}, this.timeoutMs);
|
|
83
87
|
});
|
|
84
88
|
// Also bounded when an injected transport ignores cancellation.
|
|
85
|
-
this.presented = {
|
|
89
|
+
this.presented = {
|
|
90
|
+
version: await Promise.race([lookup(), timeout]),
|
|
91
|
+
source: 'npm',
|
|
92
|
+
};
|
|
86
93
|
this.expiresAt = this.now() + 6 * 60 * 60_000;
|
|
87
94
|
}
|
|
88
95
|
catch {
|
|
@@ -73,7 +73,9 @@ function readModel(entry, index) {
|
|
|
73
73
|
const record = entry;
|
|
74
74
|
// The id is the wire id, so surrounding whitespace is always a paste
|
|
75
75
|
// artifact; trim it before the duplicate check sees it.
|
|
76
|
-
const model = {
|
|
76
|
+
const model = {
|
|
77
|
+
id: readNonEmptyString(record.id, `models[${index}].id`).trim(),
|
|
78
|
+
};
|
|
77
79
|
if (record.name !== undefined)
|
|
78
80
|
model.name = readNonEmptyString(record.name, `models[${index}].name`);
|
|
79
81
|
if (record.contextWindow !== undefined) {
|
|
@@ -169,20 +171,34 @@ export class OllamaSettingsStore {
|
|
|
169
171
|
}
|
|
170
172
|
}
|
|
171
173
|
/** Why the stored file is ignored, when it is. */
|
|
172
|
-
corruption() {
|
|
173
|
-
|
|
174
|
+
corruption() {
|
|
175
|
+
return this.corrupt;
|
|
176
|
+
}
|
|
177
|
+
get() {
|
|
178
|
+
return structuredClone(this.current);
|
|
179
|
+
}
|
|
174
180
|
/** The stored catalog, or `undefined` when the store carries no override. */
|
|
175
|
-
models() {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
181
|
+
models() {
|
|
182
|
+
return this.current.models;
|
|
183
|
+
}
|
|
184
|
+
web() {
|
|
185
|
+
return this.current.web;
|
|
186
|
+
}
|
|
187
|
+
baseURL() {
|
|
188
|
+
return this.current.baseURL;
|
|
189
|
+
}
|
|
190
|
+
apiKeyRef() {
|
|
191
|
+
return this.current.apiKeyRef;
|
|
192
|
+
}
|
|
179
193
|
set(input) {
|
|
180
194
|
const validated = validateOllamaSettings(input);
|
|
181
195
|
const run = this.writes.then(async () => {
|
|
182
196
|
await mkdir(dirname(this.path), { recursive: true });
|
|
183
197
|
const temporary = `${this.path}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
184
198
|
try {
|
|
185
|
-
await writeFile(temporary, JSON.stringify(validated, null, 2), {
|
|
199
|
+
await writeFile(temporary, JSON.stringify(validated, null, 2), {
|
|
200
|
+
mode: 0o600,
|
|
201
|
+
});
|
|
186
202
|
await rename(temporary, this.path);
|
|
187
203
|
}
|
|
188
204
|
finally {
|
|
@@ -18,7 +18,9 @@ export const OLLAMA_WEB_MAX_RESULTS = 10;
|
|
|
18
18
|
export const OLLAMA_WEB_MAX_BYTES = 2 * 1024 * 1024;
|
|
19
19
|
/** The effective base URL; both forms are read fresh so saves apply live. */
|
|
20
20
|
function baseURLOf(options) {
|
|
21
|
-
return typeof options.baseURL === 'function'
|
|
21
|
+
return typeof options.baseURL === 'function'
|
|
22
|
+
? options.baseURL()
|
|
23
|
+
: options.baseURL;
|
|
22
24
|
}
|
|
23
25
|
function throwIfAborted(signal) {
|
|
24
26
|
signal?.throwIfAborted();
|
|
@@ -48,6 +50,7 @@ async function postJson(url, apiKey, payload, options, signal) {
|
|
|
48
50
|
controller.abort(new Error('ollama web request timed out'));
|
|
49
51
|
}, timeoutMs);
|
|
50
52
|
if (typeof timer.unref === 'function') {
|
|
53
|
+
;
|
|
51
54
|
timer.unref();
|
|
52
55
|
}
|
|
53
56
|
const onCallerAbort = () => controller.abort(signal?.reason);
|
|
@@ -57,8 +60,8 @@ async function postJson(url, apiKey, payload, options, signal) {
|
|
|
57
60
|
method: 'POST',
|
|
58
61
|
headers: {
|
|
59
62
|
'content-type': 'application/json',
|
|
60
|
-
|
|
61
|
-
|
|
63
|
+
accept: 'application/json',
|
|
64
|
+
authorization: `Bearer ${apiKey}`,
|
|
62
65
|
...attributionHeaders(),
|
|
63
66
|
},
|
|
64
67
|
body: JSON.stringify(payload),
|
|
@@ -69,7 +72,7 @@ async function postJson(url, apiKey, payload, options, signal) {
|
|
|
69
72
|
throw new WebError(`ollama web request failed (HTTP ${String(response.status)})`, 'OLLAMA_WEB_BAD_RESPONSE');
|
|
70
73
|
}
|
|
71
74
|
try {
|
|
72
|
-
return await readBoundedJson(response, 'ollama web request', OLLAMA_WEB_MAX_BYTES);
|
|
75
|
+
return (await readBoundedJson(response, 'ollama web request', OLLAMA_WEB_MAX_BYTES));
|
|
73
76
|
}
|
|
74
77
|
catch (error) {
|
|
75
78
|
if (signal?.aborted)
|
|
@@ -158,23 +161,33 @@ export class OllamaWebSearchProvider {
|
|
|
158
161
|
const base = baseURLOf(this.options).replace(/\/+$/, '');
|
|
159
162
|
const payload = await postJson(`${base}/web_search`, apiKey, {
|
|
160
163
|
query: request.query,
|
|
161
|
-
...(request.maxResults === undefined
|
|
164
|
+
...(request.maxResults === undefined
|
|
165
|
+
? {}
|
|
166
|
+
: {
|
|
167
|
+
max_results: Math.min(request.maxResults, OLLAMA_WEB_MAX_RESULTS),
|
|
168
|
+
}),
|
|
162
169
|
}, this.options, signal);
|
|
163
170
|
const results = recordOf(payload)?.['results'];
|
|
164
171
|
if (!Array.isArray(results))
|
|
165
172
|
throw new WebError('ollama web search returned no results array', 'OLLAMA_WEB_BAD_REPLY');
|
|
166
173
|
// Official shape per docs: {title, url, content}. The snippet rides
|
|
167
174
|
// `content`, not `snippet`.
|
|
168
|
-
const sources = results.flatMap(entry => {
|
|
175
|
+
const sources = results.flatMap((entry) => {
|
|
169
176
|
const record = recordOf(entry);
|
|
170
177
|
const url = record?.['url'];
|
|
171
178
|
if (typeof url !== 'string' || url.length === 0)
|
|
172
179
|
return [];
|
|
173
|
-
return [
|
|
180
|
+
return [
|
|
181
|
+
{
|
|
174
182
|
url,
|
|
175
|
-
...(typeof record?.['title'] === 'string'
|
|
176
|
-
|
|
177
|
-
|
|
183
|
+
...(typeof record?.['title'] === 'string'
|
|
184
|
+
? { title: record?.['title'] }
|
|
185
|
+
: {}),
|
|
186
|
+
...(typeof record?.['content'] === 'string'
|
|
187
|
+
? { snippet: record?.['content'] }
|
|
188
|
+
: {}),
|
|
189
|
+
},
|
|
190
|
+
];
|
|
178
191
|
});
|
|
179
192
|
return { sources, truncated: false };
|
|
180
193
|
}
|
|
@@ -208,6 +221,11 @@ export class OllamaWebFetchProvider {
|
|
|
208
221
|
if (typeof content !== 'string') {
|
|
209
222
|
throw new WebError('ollama web fetch returned a malformed reply', 'OLLAMA_WEB_BAD_REPLY');
|
|
210
223
|
}
|
|
211
|
-
return {
|
|
224
|
+
return {
|
|
225
|
+
url: request.url,
|
|
226
|
+
statusCode: 200,
|
|
227
|
+
body: { kind: 'text', content },
|
|
228
|
+
truncated: false,
|
|
229
|
+
};
|
|
212
230
|
}
|
|
213
231
|
}
|