@toddzheng024/dscode-bundle 0.7.5 → 0.7.7
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/THIRD_PARTY_NOTICES.md +3 -3
- package/cordis.patch.yml +26 -5
- package/package.json +4 -4
- package/plugins/auto-review/index.mjs +6 -1
- package/plugins/code-review/index.mjs +9 -4
- package/plugins/compaction/tetris.mjs +65 -0
- package/plugins/compaction/threshold.mjs +46 -0
- package/plugins/credentials/index.mjs +2 -2
- package/plugins/dscode/index.mjs +4 -10
- package/plugins/exec/cli.mjs +3 -2
- package/plugins/exec/index.mjs +6 -1
- package/plugins/i18n/messages.mjs +18 -0
- package/plugins/memory/index.mjs +7 -3
- package/plugins/openrouter/adapter.mjs +157 -0
- package/plugins/openrouter/index.mjs +112 -0
- package/plugins/openrouter/models.mjs +151 -0
- package/plugins/openrouter/search.mjs +109 -0
- package/plugins/openrouter/wire.mjs +413 -0
- package/plugins/providers/catalog.mjs +28 -26
- package/plugins/providers/effort.mjs +35 -0
- package/plugins/providers/openrouter-account.mjs +171 -0
- package/plugins/session-cards/index.mjs +5 -1
- package/plugins/session-metrics/balance.mjs +29 -19
- package/plugins/session-metrics/index.mjs +19 -6
- package/plugins/session-metrics/pricing.mjs +45 -14
- package/plugins/session-metrics/view.mjs +1 -1
- package/plugins/tui-tools/doctor.mjs +3 -1
- package/plugins/tui-tools/index.mjs +1 -1
- package/plugins/ultra/policy.mjs +0 -16
- package/presets/dscode/agent.cordis.yml +1 -1
- package/vendor/compaction-basic/index.js +983 -0
- package/vendor/compaction-basic/types/config.d.ts +37 -0
- package/vendor/compaction-basic/types/index.d.ts +84 -0
- package/vendor/compaction-basic/types/region.d.ts +65 -0
- package/vendor/compaction-basic/types/summarizer.d.ts +64 -0
- package/vendor/compaction-basic/types/types.d.ts +73 -0
- package/vendor/deepseek/index.js +1 -1
- package/vendor/subagent/index.js +3 -3
- package/vendor/tui/dscode-providers/catalog.mjs +28 -26
- package/vendor/tui/dscode-providers/effort.mjs +35 -0
- package/vendor/tui/dscode-providers/openrouter-account.mjs +171 -0
- package/vendor/tui/index.mjs +395 -133
- package/vendor/pi-ai/index.js +0 -2702
- package/vendor/pi-ai/types/adapter.d.ts +0 -105
- package/vendor/pi-ai/types/auth.d.ts +0 -60
- package/vendor/pi-ai/types/catalog.d.ts +0 -355
- package/vendor/pi-ai/types/config.d.ts +0 -208
- package/vendor/pi-ai/types/context.d.ts +0 -42
- package/vendor/pi-ai/types/discovery.d.ts +0 -43
- package/vendor/pi-ai/types/index.d.ts +0 -69
- package/vendor/pi-ai/types/login.d.ts +0 -21
- package/vendor/pi-ai/types/provider.d.ts +0 -59
- package/vendor/pi-ai/types/replay.d.ts +0 -63
- package/vendor/pi-ai/types/stream.d.ts +0 -43
- /package/vendor/{pi-ai → compaction-basic}/LICENSE +0 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
// OpenRouter account facts for /openrouter and the footer balance. The inference key
|
|
2
|
+
// reads the account credits and its own limit and usage; an optional management key,
|
|
3
|
+
// which cannot call models, adds every key's usage and the last 30 days of spend.
|
|
4
|
+
// This directory also ships beside the TUI, so the module imports nothing.
|
|
5
|
+
export const OPENROUTER_API = 'https://openrouter.ai/api/v1';
|
|
6
|
+
export const MANAGEMENT_REF = 'OPENROUTER_MANAGEMENT_KEY';
|
|
7
|
+
|
|
8
|
+
const finite = value => {
|
|
9
|
+
const number = Number(value);
|
|
10
|
+
return value !== null && value !== undefined && value !== '' && Number.isFinite(number) ? number : undefined;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export class OpenRouterAccountError extends Error {
|
|
14
|
+
constructor(message, status) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.name = 'OpenRouterAccountError';
|
|
17
|
+
this.status = status;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function get(path, key, { fetch: fetchImpl = globalThis.fetch, signal } = {}) {
|
|
22
|
+
let response;
|
|
23
|
+
try {
|
|
24
|
+
response = await fetchImpl(`${OPENROUTER_API}${path}`, { headers: { authorization: `Bearer ${key}`, accept: 'application/json' }, signal });
|
|
25
|
+
} catch (error) {
|
|
26
|
+
throw new OpenRouterAccountError(`OpenRouter is unreachable: ${error instanceof Error ? error.message : String(error)}`);
|
|
27
|
+
}
|
|
28
|
+
let body;
|
|
29
|
+
try { body = await response.json(); } catch { body = undefined; }
|
|
30
|
+
if (!response.ok) throw new OpenRouterAccountError(typeof body?.error?.message === 'string' ? body.error.message : `HTTP ${response.status}`, response.status);
|
|
31
|
+
return body;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Account credits from a `/credits` body, or undefined when it carries none. */
|
|
35
|
+
export function creditsOf(body) {
|
|
36
|
+
const total = finite(body?.data?.total_credits), used = finite(body?.data?.total_usage);
|
|
37
|
+
if (total === undefined || used === undefined || total < 0 || used < 0) return undefined;
|
|
38
|
+
return { total, used, remaining: Math.max(0, total - used) };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Remaining USD account credits from a `/credits` body: purchased minus used. */
|
|
42
|
+
export function parseOpenRouterCredits(body) {
|
|
43
|
+
return creditsOf(body)?.remaining ?? null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Remaining USD credit limit from a `/key` body; null when the key has no limit. */
|
|
47
|
+
export function parseOpenRouterKeyRemaining(body) {
|
|
48
|
+
const remaining = body?.data?.limit_remaining;
|
|
49
|
+
if (remaining == null) return null;
|
|
50
|
+
const value = Number(remaining);
|
|
51
|
+
return Number.isFinite(value) ? Math.max(0, value) : null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function keyOf(raw) {
|
|
55
|
+
return {
|
|
56
|
+
label: typeof raw?.label === 'string' ? raw.label : undefined,
|
|
57
|
+
name: typeof raw?.name === 'string' && raw.name.length > 0 ? raw.name : undefined,
|
|
58
|
+
disabled: raw?.disabled === true,
|
|
59
|
+
limit: finite(raw?.limit),
|
|
60
|
+
limitRemaining: finite(raw?.limit_remaining),
|
|
61
|
+
usage: finite(raw?.usage),
|
|
62
|
+
usageDaily: finite(raw?.usage_daily),
|
|
63
|
+
usageWeekly: finite(raw?.usage_weekly),
|
|
64
|
+
usageMonthly: finite(raw?.usage_monthly),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Verify a management key before it is stored. Account activity is the reading OpenRouter
|
|
70
|
+
* refuses an inference key; `/credits`, though documented as management-only, serves both.
|
|
71
|
+
* @throws OpenRouterAccountError with a message fit for the key prompt.
|
|
72
|
+
*/
|
|
73
|
+
export async function verifyManagementKey(key, options) {
|
|
74
|
+
try {
|
|
75
|
+
await get('/activity', key, options);
|
|
76
|
+
} catch (error) {
|
|
77
|
+
if (error instanceof OpenRouterAccountError && (error.status === 401 || error.status === 403)) {
|
|
78
|
+
throw new OpenRouterAccountError('This is not a management key: OpenRouter refused it for account data. Create one under Settings → Management keys.', error.status);
|
|
79
|
+
}
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** The `/activity` rows (last 30 completed UTC days) as totals and the top models with the providers that served them. */
|
|
85
|
+
export function summarizeActivity(rows, top = 5) {
|
|
86
|
+
const models = new Map(), days = new Set();
|
|
87
|
+
let usage = 0, requests = 0;
|
|
88
|
+
for (const row of Array.isArray(rows) ? rows : []) {
|
|
89
|
+
if (typeof row?.model !== 'string') continue;
|
|
90
|
+
const cost = finite(row.usage) ?? 0, count = finite(row.requests) ?? 0;
|
|
91
|
+
usage += cost;
|
|
92
|
+
requests += count;
|
|
93
|
+
if (typeof row.date === 'string') days.add(row.date);
|
|
94
|
+
const entry = models.get(row.model) ?? { model: row.model, usage: 0, requests: 0, providers: new Map() };
|
|
95
|
+
entry.usage += cost;
|
|
96
|
+
entry.requests += count;
|
|
97
|
+
const name = typeof row.provider_name === 'string' && row.provider_name.length > 0 ? row.provider_name : 'unknown';
|
|
98
|
+
const served = entry.providers.get(name) ?? { name, usage: 0, requests: 0 };
|
|
99
|
+
served.usage += cost;
|
|
100
|
+
served.requests += count;
|
|
101
|
+
entry.providers.set(name, served);
|
|
102
|
+
models.set(row.model, entry);
|
|
103
|
+
}
|
|
104
|
+
const ranked = [...models.values()].sort((left, right) => right.usage - left.usage || right.requests - left.requests).slice(0, top)
|
|
105
|
+
.map(entry => ({ ...entry, providers: [...entry.providers.values()].sort((left, right) => right.usage - left.usage) }));
|
|
106
|
+
return { usage, requests, days: days.size, modelCount: models.size, models: ranked };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Everything /openrouter shows. Each section settles on its own: `{ value }`, `{ error }`,
|
|
111
|
+
* or undefined when the key it needs is missing.
|
|
112
|
+
*/
|
|
113
|
+
export async function loadOpenRouterAccount({ apiKey, managementKey, fetch, signal } = {}) {
|
|
114
|
+
const options = { fetch, signal };
|
|
115
|
+
const section = (key, run) => key ? run().then(value => ({ value }), error => ({ error: error instanceof Error ? error.message : String(error) })) : Promise.resolve(undefined);
|
|
116
|
+
const [key, credits, keys, activity] = await Promise.all([
|
|
117
|
+
section(apiKey, async () => keyOf((await get('/key', apiKey, options))?.data)),
|
|
118
|
+
section(managementKey ?? apiKey, async () => {
|
|
119
|
+
const credits = creditsOf(await get('/credits', managementKey ?? apiKey, options));
|
|
120
|
+
if (!credits) throw new Error('OpenRouter returned no account credits');
|
|
121
|
+
return credits;
|
|
122
|
+
}),
|
|
123
|
+
section(managementKey, async () => {
|
|
124
|
+
const body = await get('/keys', managementKey, options);
|
|
125
|
+
return (Array.isArray(body?.data) ? body.data : []).map(keyOf);
|
|
126
|
+
}),
|
|
127
|
+
section(managementKey, async () => summarizeActivity((await get('/activity', managementKey, options))?.data)),
|
|
128
|
+
]);
|
|
129
|
+
return { hasApiKey: Boolean(apiKey), hasManagementKey: Boolean(managementKey), key, credits, keys, activity };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const money = value => Number.isFinite(value) ? `$${value.toFixed(2)}` : '$--';
|
|
133
|
+
const limitText = key => key.limit === undefined ? 'no limit' : `limit ${money(key.limit)}, ${money(key.limitRemaining)} left`;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* The panel's lines for a loaded account.
|
|
137
|
+
* @returns `{ text, tone }` rows; tone is `title`, `value`, `dim` or `error`.
|
|
138
|
+
*/
|
|
139
|
+
export function openRouterAccountLines(account, { maxKeys = 5 } = {}) {
|
|
140
|
+
const lines = [];
|
|
141
|
+
const push = (text, tone = 'value') => lines.push({ text, tone });
|
|
142
|
+
if (!account.hasApiKey) push('No OpenRouter API key: run /login openrouter.', 'error');
|
|
143
|
+
if (account.credits?.value) {
|
|
144
|
+
const { remaining, total, used } = account.credits.value;
|
|
145
|
+
push(`Account balance ${money(remaining)} · credits ${money(total)} · used ${money(used)}`, 'title');
|
|
146
|
+
} else if (account.credits?.error) push(`Account balance unavailable: ${account.credits.error}`, 'error');
|
|
147
|
+
else push('Account balance $--', 'dim');
|
|
148
|
+
if (account.key?.value) {
|
|
149
|
+
const key = account.key.value;
|
|
150
|
+
push(`This key ${key.label ?? 'unnamed'} · ${limitText(key)}`, 'title');
|
|
151
|
+
push(` today ${money(key.usageDaily)} · week ${money(key.usageWeekly)} · month ${money(key.usageMonthly)}`, 'dim');
|
|
152
|
+
} else if (account.key?.error) push(`This key unavailable: ${account.key.error}`, 'error');
|
|
153
|
+
if (!account.hasManagementKey) push('API keys and 30-day spend press m to add a management key', 'dim');
|
|
154
|
+
if (account.keys?.value) {
|
|
155
|
+
const keys = [...account.keys.value].sort((left, right) => (right.usageMonthly ?? 0) - (left.usageMonthly ?? 0));
|
|
156
|
+
push(`API keys (${keys.length})`, 'title');
|
|
157
|
+
for (const key of keys.slice(0, maxKeys)) {
|
|
158
|
+
const current = account.key?.value?.label !== undefined && key.label === account.key.value.label;
|
|
159
|
+
push(` ${key.name ?? key.label ?? 'unnamed'}${current ? ' (this key)' : ''}${key.disabled ? ' · disabled' : ''} · today ${money(key.usageDaily)} · month ${money(key.usageMonthly)} · ${limitText(key)}`, key.disabled ? 'dim' : 'value');
|
|
160
|
+
}
|
|
161
|
+
if (keys.length > maxKeys) push(` +${keys.length - maxKeys} more`, 'dim');
|
|
162
|
+
} else if (account.keys?.error) push(`API keys unavailable: ${account.keys.error}`, 'error');
|
|
163
|
+
if (account.activity?.value) {
|
|
164
|
+
const activity = account.activity.value;
|
|
165
|
+
push(`Last 30 days ${money(activity.usage)} · ${activity.requests} requests · ${activity.modelCount} models`, 'title');
|
|
166
|
+
for (const model of activity.models) {
|
|
167
|
+
push(` ${model.model} · ${money(model.usage)} · ${model.requests} req · ${model.providers.map(provider => `${provider.name} ${money(provider.usage)}`).join(', ')}`);
|
|
168
|
+
}
|
|
169
|
+
} else if (account.activity?.error) push(`Activity unavailable: ${account.activity.error}`, 'error');
|
|
170
|
+
return lines;
|
|
171
|
+
}
|