@toddzheng024/dscode-bundle 0.7.1 → 0.7.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.
Files changed (41) hide show
  1. package/THIRD_PARTY_NOTICES.md +3 -0
  2. package/cordis.patch.yml +4 -0
  3. package/package.json +4 -2
  4. package/plugins/auto-review/index.mjs +21 -3
  5. package/plugins/auto-review/policy.mjs +25 -0
  6. package/plugins/code-review/git.mjs +24 -2
  7. package/plugins/code-review/index.mjs +13 -6
  8. package/plugins/credentials/index.mjs +7 -5
  9. package/plugins/exec/cli.mjs +63 -0
  10. package/plugins/exec/index.mjs +1 -1
  11. package/plugins/i18n/messages.mjs +6 -6
  12. package/plugins/memory/index.mjs +17 -12
  13. package/plugins/providers/catalog.mjs +134 -0
  14. package/plugins/session-cards/index.mjs +11 -8
  15. package/plugins/session-cards/manager.mjs +1 -1
  16. package/plugins/session-metrics/attribution.mjs +19 -0
  17. package/plugins/session-metrics/balance.mjs +71 -0
  18. package/plugins/session-metrics/index.mjs +39 -9
  19. package/plugins/session-metrics/pricing.mjs +39 -5
  20. package/plugins/session-metrics/view.mjs +36 -15
  21. package/plugins/tui-tools/doctor.mjs +9 -6
  22. package/plugins/tui-tools/index.mjs +2 -2
  23. package/plugins/ultra/policy.mjs +16 -0
  24. package/vendor/persistent/index.js +19 -2
  25. package/vendor/pi-ai/LICENSE +21 -0
  26. package/vendor/pi-ai/index.js +2702 -0
  27. package/vendor/pi-ai/types/adapter.d.ts +105 -0
  28. package/vendor/pi-ai/types/auth.d.ts +60 -0
  29. package/vendor/pi-ai/types/catalog.d.ts +355 -0
  30. package/vendor/pi-ai/types/config.d.ts +208 -0
  31. package/vendor/pi-ai/types/context.d.ts +42 -0
  32. package/vendor/pi-ai/types/discovery.d.ts +43 -0
  33. package/vendor/pi-ai/types/index.d.ts +69 -0
  34. package/vendor/pi-ai/types/login.d.ts +21 -0
  35. package/vendor/pi-ai/types/provider.d.ts +59 -0
  36. package/vendor/pi-ai/types/replay.d.ts +63 -0
  37. package/vendor/pi-ai/types/stream.d.ts +43 -0
  38. package/vendor/tui/dscode-providers/catalog.mjs +134 -0
  39. package/vendor/tui/index.mjs +310 -295
  40. package/vendor/tui/.dscode-viewport-probe-78546.mjs +0 -39239
  41. package/vendor/tui/dscode-email.mjs +0 -69
@@ -0,0 +1,134 @@
1
+ // Model providers `/provider` switches between. DeepSeek's official API is the
2
+ // native `llm-deepseek` route; OpenRouter reaches the same DeepSeek models
3
+ // through pi-ai's catalog route, which the base composition mounts dormant until
4
+ // a `llm-pi-ai:` settings section declares it.
5
+
6
+ export const PROVIDERS = Object.freeze([
7
+ { id: 'deepseek-official', name: 'DeepSeek', aliases: ['deepseek', 'deepseek-official', 'official'], credentialRef: 'DEEPSEEK_API_KEY', defaultModel: 'deepseek-flash' },
8
+ { id: 'openrouter', name: 'OpenRouter', aliases: ['openrouter', 'open-router'], credentialRef: 'OPENROUTER_API_KEY', defaultModel: 'deepseek/deepseek-v4-flash' },
9
+ ]);
10
+
11
+ const PI_AI_NS = 'llm-pi-ai';
12
+
13
+ // OpenRouter serves DeepSeek V4 thinking as none/high/xhigh. DeepSeek itself
14
+ // answers `low` as high and `max` as xhigh, so the route offers the official
15
+ // low/high/max detents (and Ultra on top of max) with the wire spelling OpenRouter
16
+ // accepts; session cards and delegated children that ask for `low` keep working.
17
+ const OPENROUTER_EFFORTS = Object.freeze({ off: 'none', low: 'high', high: 'high', max: 'xhigh' });
18
+
19
+ /** The DeepSeek models the OpenRouter route declares, with their official-route counterparts. */
20
+ export const OPENROUTER_MODELS = Object.freeze([
21
+ { id: 'deepseek/deepseek-v4-flash', name: 'DeepSeek V4 Flash', official: ['deepseek-flash', 'deepseek-v4-flash'] },
22
+ { id: 'deepseek/deepseek-v4-pro', name: 'DeepSeek V4 Pro', official: ['deepseek-v4-pro'] },
23
+ { id: 'deepseek/deepseek-v4-flash-vision-exp', name: 'DeepSeek V4 Flash Vision Exp', official: ['deepseek-v4-flash-vision-exp'] },
24
+ ]);
25
+
26
+ /** The `llm-pi-ai` profile `/provider openrouter` writes: installed-catalog models narrowed to DeepSeek. */
27
+ export function openRouterProfile() {
28
+ return {
29
+ displayName: 'OpenRouter',
30
+ apiKeyEnv: 'OPENROUTER_API_KEY',
31
+ // Like the official route, requests default to high; it also gives /effort the DSCODE detent bar.
32
+ reasoning: 'high',
33
+ models: OPENROUTER_MODELS.map(({ id, name }) => ({ id, name, reasoningEfforts: { ...OPENROUTER_EFFORTS } })),
34
+ };
35
+ }
36
+
37
+ export function providerSpec(id) {
38
+ return PROVIDERS.find(provider => provider.id === id);
39
+ }
40
+
41
+ /**
42
+ * Resolve a `/provider` or `/login` argument.
43
+ * @param raw - text after the command name.
44
+ * @returns a provider id, `undefined` for no argument, or `null` when unrecognized. Callers must not echo the text: it may be a pasted key.
45
+ */
46
+ export function providerArgument(raw) {
47
+ const value = String(raw ?? '').trim().toLowerCase();
48
+ if (value === '') return undefined;
49
+ return PROVIDERS.find(provider => provider.aliases.includes(value))?.id ?? null;
50
+ }
51
+
52
+ /** Split a `provider/model` label at the first slash: OpenRouter model ids carry their own `vendor/` segment. */
53
+ export function splitModelLabel(label) {
54
+ const text = typeof label === 'string' ? label : '';
55
+ const cut = text.indexOf('/');
56
+ return cut > 0 ? { provider: text.slice(0, cut), model: text.slice(cut + 1) } : { provider: '', model: text };
57
+ }
58
+
59
+ /** The switchable provider a label names, defaulting to DeepSeek for unknown or bare labels. */
60
+ export function providerOfLabel(label) {
61
+ return providerSpec(splitModelLabel(label).provider)?.id ?? PROVIDERS[0].id;
62
+ }
63
+
64
+ /** Provider id leading a footer header (`provider: model @ effort`), if any. */
65
+ export function providerOfHeader(header) {
66
+ return typeof header === 'string' ? header.match(/^([^\s:/]+): /)?.[1] : undefined;
67
+ }
68
+
69
+ function counterpart(from, model, to) {
70
+ if (from === to) return model;
71
+ if (from === 'deepseek-official' && to === 'openrouter') return OPENROUTER_MODELS.find(entry => entry.official.includes(model))?.id;
72
+ if (from === 'openrouter' && to === 'deepseek-official') return OPENROUTER_MODELS.find(entry => entry.id === model)?.official[0];
73
+ return undefined;
74
+ }
75
+
76
+ /**
77
+ * The model a provider switch lands on: the current model's counterpart, else the
78
+ * provider default, else its first model. The effort carries over only when the
79
+ * target offers it; otherwise the model's own default applies.
80
+ * @param rows - model directory rows (`provider`, `model`, `reasoning`).
81
+ * @returns `{ row, effort }`, or `undefined` when the provider serves no model yet.
82
+ */
83
+ export function pickModel(rows, provider, currentLabel, effort) {
84
+ const candidates = rows.filter(row => row.provider === provider);
85
+ if (candidates.length === 0) return undefined;
86
+ const current = splitModelLabel(currentLabel);
87
+ const wanted = counterpart(current.provider, current.model, provider);
88
+ const row = candidates.find(candidate => candidate.model === wanted)
89
+ ?? candidates.find(candidate => candidate.model === providerSpec(provider)?.defaultModel)
90
+ ?? candidates[0];
91
+ const offered = row.reasoning?.efforts.map(level => level.id) ?? [];
92
+ return { row, effort: effort && offered.includes(effort) ? effort : undefined };
93
+ }
94
+
95
+ /**
96
+ * Credential status of a provider-settings row.
97
+ * @returns `saved`, `env`, `missing`, `readonly` (an empty read-only source), `error`, or `unavailable` (no row).
98
+ */
99
+ export function credentialState(row) {
100
+ if (!row) return 'unavailable';
101
+ const credential = row.credential;
102
+ if (credential?.kind === 'error') return 'error';
103
+ if (credential?.kind !== 'facts') return 'missing';
104
+ if (credential.configured) return credential.source === 'env' ? 'env' : 'saved';
105
+ return credential.writable ? 'missing' : 'readonly';
106
+ }
107
+
108
+ /**
109
+ * Declare a provider's route before it is used. Only OpenRouter needs one; a
110
+ * profile the user already has (their own models or endpoint) is left alone.
111
+ * @param settings - the host settings service.
112
+ * @returns whether the settings changed.
113
+ */
114
+ export async function ensureProviderRoute(settings, provider) {
115
+ if (provider !== 'openrouter') return false;
116
+ if (typeof settings?.describe !== 'function' || typeof settings.mutate !== 'function') throw new Error('settings are unavailable; OpenRouter cannot be configured in this profile');
117
+ const descriptor = settings.describe({ redactSecrets: true }).find(entry => entry.ns === PI_AI_NS);
118
+ if (!descriptor) throw new Error('the OpenRouter adapter (llm-pi-ai) is not mounted in this profile');
119
+ if (descriptor.value?.providers?.openrouter !== undefined) return false;
120
+ if (settings.writable !== true) throw new Error('settings are read-only; OpenRouter cannot be configured here');
121
+ await settings.mutate(PI_AI_NS, [{ op: 'set', path: ['providers', 'openrouter'], value: openRouterProfile() }], descriptor.revision);
122
+ return true;
123
+ }
124
+
125
+ /** Wait for a freshly declared route to reach the model directory. */
126
+ export async function waitForModels(loadModels, provider, { attempts = 30, delayMs = 100 } = {}) {
127
+ let directory;
128
+ for (let attempt = 0; attempt < attempts; attempt++) {
129
+ directory = await loadModels();
130
+ if (directory.rows.some(row => row.provider === provider)) return directory;
131
+ await new Promise(resolve => setTimeout(resolve, delayMs));
132
+ }
133
+ return directory;
134
+ }