@serviceme/devtools-shared 0.4.5

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/dist/index.js ADDED
@@ -0,0 +1,1283 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ BUILTIN_PROVIDER_PRESETS: () => BUILTIN_PROVIDER_PRESETS,
24
+ ByomSettingsResponse: () => ByomSettingsResponse,
25
+ CERTIFICATE_BUNDLE_FORMATS: () => CERTIFICATE_BUNDLE_FORMATS,
26
+ CachedServerUrlResponse: () => CachedServerUrlResponse,
27
+ GIT_REMOTE_HOST_ALIASES: () => GIT_REMOTE_HOST_ALIASES,
28
+ GetByomSettings: () => GetByomSettings,
29
+ GetCachedServerUrl: () => GetCachedServerUrl,
30
+ GetServerProxyState: () => GetServerProxyState,
31
+ GetUtilityModels: () => GetUtilityModels,
32
+ LogLevel: () => LogLevel,
33
+ MODEL_METADATA: () => MODEL_METADATA,
34
+ PROVIDER_BASE_URL_PRESETS: () => PROVIDER_BASE_URL_PRESETS,
35
+ PROVIDER_CACHE_CONTROL_METADATA: () => PROVIDER_CACHE_CONTROL_METADATA,
36
+ ServerProxyStateResponse: () => ServerProxyStateResponse,
37
+ SetCacheControlEnabled: () => SetCacheControlEnabled,
38
+ SetServerProxyAllowOverride: () => SetServerProxyAllowOverride,
39
+ SetServerProxyEnabled: () => SetServerProxyEnabled,
40
+ UpdateUtilityModels: () => UpdateUtilityModels,
41
+ UtilityModelsResponse: () => UtilityModelsResponse,
42
+ WebviewMessageType: () => WebviewMessageType,
43
+ __internal: () => __internal,
44
+ asAbortSignal: () => asAbortSignal,
45
+ buildGitHubLocalEmail: () => buildGitHubLocalEmail,
46
+ checkGitHubOrgMembership: () => checkGitHubOrgMembership,
47
+ createConsoleLogger: () => createConsoleLogger,
48
+ currencyForBaseUrl: () => currencyForBaseUrl,
49
+ fetchGitHubUser: () => fetchGitHubUser,
50
+ getBuiltinProviderPreset: () => getBuiltinProviderPreset,
51
+ getGitHubOrgMembership: () => getGitHubOrgMembership,
52
+ getProviderBaseUrlPresets: () => getProviderBaseUrlPresets,
53
+ isGitHubLocalEmail: () => isGitHubLocalEmail,
54
+ isProviderCacheControlAware: () => isProviderCacheControlAware,
55
+ isValidCanonicalSlug: () => isValidCanonicalSlug,
56
+ lookupModelMetadata: () => lookupModelMetadata,
57
+ normalizeCanonicalSlug: () => normalizeCanonicalSlug,
58
+ normalizeErrorForLog: () => normalizeErrorForLog,
59
+ normalizeGitUrl: () => normalizeGitUrl,
60
+ parsePayload: () => parsePayload,
61
+ resolvePrimaryEmail: () => resolvePrimaryEmail,
62
+ safeJson: () => safeJson
63
+ });
64
+ module.exports = __toCommonJS(index_exports);
65
+
66
+ // src/ai/providers.base-url.ts
67
+ var PROVIDER_BASE_URL_PRESETS = {
68
+ "openai-compatible": [],
69
+ "anthropic-compatible": [],
70
+ minimax: [
71
+ { label: "\u56FD\u5185", baseUrl: "https://api.minimaxi.com/anthropic" },
72
+ { label: "\u5168\u7403", baseUrl: "https://api.minimax.io/anthropic" }
73
+ ],
74
+ deepseek: [{ label: "\u5B98\u65B9", baseUrl: "https://api.deepseek.com/v1" }],
75
+ kimi: [
76
+ { label: "\u56FD\u5185", baseUrl: "https://api.moonshot.cn/v1" },
77
+ { label: "\u5168\u7403", baseUrl: "https://api.moonshot.ai/v1" }
78
+ ],
79
+ zhipu: [
80
+ { label: "\u56FD\u5185", baseUrl: "https://open.bigmodel.cn/api/agent" },
81
+ { label: "\u5168\u7403", baseUrl: "https://api.zhipuai.com/v1" }
82
+ ],
83
+ stepfun: [{ label: "\u5B98\u65B9", baseUrl: "https://api.stepfun.com/v1" }],
84
+ siliconflow: [
85
+ { label: "\u56FD\u5185", baseUrl: "https://api.siliconflow.cn/v1" },
86
+ { label: "\u5168\u7403", baseUrl: "https://api.siliconflow.com/v1" }
87
+ ],
88
+ openrouter: [{ label: "\u5B98\u65B9", baseUrl: "https://openrouter.ai/api/v1" }],
89
+ novita: [{ label: "\u5B98\u65B9", baseUrl: "https://api.novita.ai/openai/v1" }],
90
+ agnes: [
91
+ { label: "\u56FD\u5185", baseUrl: "https://api.agnes-ai.cn/v1" },
92
+ { label: "\u5168\u7403", baseUrl: "https://apihub.agnes-ai.com/v1" }
93
+ ],
94
+ "vscode-builtin": []
95
+ };
96
+ function getProviderBaseUrlPresets(type) {
97
+ return PROVIDER_BASE_URL_PRESETS[type] ?? [];
98
+ }
99
+
100
+ // src/ai/providers.cache-control.ts
101
+ var PROVIDER_CACHE_CONTROL_METADATA = {
102
+ "openai-compatible": {},
103
+ "anthropic-compatible": {},
104
+ minimax: {
105
+ supportsCacheControl: true
106
+ // Anthropic-compatible ephemeral cache (P0.1 4-breakpoint)
107
+ },
108
+ deepseek: {
109
+ supportsCacheControl: true
110
+ // OpenAI-compat prompt_cache_key
111
+ },
112
+ kimi: {},
113
+ zhipu: {},
114
+ stepfun: {},
115
+ siliconflow: {},
116
+ openrouter: {},
117
+ novita: {},
118
+ agnes: {
119
+ supportsCacheControl: true
120
+ },
121
+ "vscode-builtin": {}
122
+ };
123
+ function isProviderCacheControlAware(type) {
124
+ return Boolean(PROVIDER_CACHE_CONTROL_METADATA[type]?.supportsCacheControl);
125
+ }
126
+
127
+ // src/ai/providers.metadata.ts
128
+ var MODEL_METADATA = {
129
+ "MiniMax-M3": {
130
+ detail: "Native multimodal frontier coding model (1M context, 512K effective)",
131
+ imageInput: true,
132
+ toolCalling: true,
133
+ pricingUSD: { input: 0.3, output: 1.2, cacheRead: 0.06 },
134
+ pricingCNY: { input: 2.1, output: 8.4, cacheRead: 0.42 },
135
+ priceCategory: "medium",
136
+ thinkingSchema: "thinkingEnabled",
137
+ // Official spec is 1M context, but the >512K input tier is still
138
+ // limited-availability (needs sales access + 2x billing per the
139
+ // pricing-page footnote) and most accounts get a 400 past 512K —
140
+ // see `docs/references/minimax-vscode-main/src/models/registry.ts`.
141
+ // 512K is the safe effective cap for both input and output.
142
+ maxInputTokens: 512e3,
143
+ maxOutputTokens: 512e3
144
+ },
145
+ "MiniMax-M2.7": {
146
+ detail: "Self-iterating coding model (~60 TPS)",
147
+ imageInput: false,
148
+ toolCalling: true,
149
+ pricingUSD: { input: 0.3, output: 1.2, cacheRead: 0.06 },
150
+ pricingCNY: { input: 2.1, output: 8.4, cacheRead: 0.42 },
151
+ priceCategory: "low",
152
+ // Official spec: 204,800 total context, split 131,072 input /
153
+ // 73,728 output (no separate docs split exists beyond this).
154
+ maxInputTokens: 131072,
155
+ maxOutputTokens: 73728
156
+ },
157
+ "MiniMax-M2.7-highspeed": {
158
+ detail: "M2.7 high-speed: same quality, faster (~100 TPS)",
159
+ imageInput: false,
160
+ toolCalling: true,
161
+ pricingUSD: { input: 0.6, output: 2.4, cacheRead: 0.06 },
162
+ pricingCNY: { input: 2.1, output: 8.4, cacheRead: 0.42 },
163
+ priceCategory: "low",
164
+ // Inherits M2.7's context window.
165
+ maxInputTokens: 131072,
166
+ maxOutputTokens: 73728
167
+ },
168
+ "deepseek-v4-flash": {
169
+ detail: "Fast, general-purpose model",
170
+ imageInput: true,
171
+ toolCalling: true,
172
+ pricingUSD: { input: 0.14, output: 0.28, cacheRead: 28e-4 },
173
+ pricingCNY: { input: 1, output: 2, cacheRead: 0.02 },
174
+ priceCategory: "low",
175
+ // Official docs (api-docs.deepseek.com/quick_start/pricing, fetched
176
+ // 2026-07-27): "THINKING MODE: Supports both non-thinking and
177
+ // thinking (default) modes" — a binary on/off switch, not a
178
+ // leveled effort scale. Tool Calls confirmed supported for both
179
+ // flash and pro. Was mis-set to reasoningEffort — corrected.
180
+ thinkingSchema: "thinkingEnabled",
181
+ // V4 context window per `docs/references/deepseek-v4-for-copilot-main/src/consts.ts`.
182
+ maxInputTokens: 655360,
183
+ maxOutputTokens: 393216
184
+ },
185
+ "deepseek-v4-pro": {
186
+ detail: "Most capable reasoning model",
187
+ imageInput: true,
188
+ toolCalling: true,
189
+ pricingUSD: { input: 0.435, output: 0.87, cacheRead: 3625e-6 },
190
+ pricingCNY: { input: 2.1, output: 4.2, cacheRead: 0.025 },
191
+ priceCategory: "low",
192
+ thinkingSchema: "thinkingEnabled",
193
+ maxInputTokens: 655360,
194
+ maxOutputTokens: 393216
195
+ },
196
+ "agnes-2.0-flash": {
197
+ detail: "Fast agentic model \u2014 tool calling, coding, image understanding (512K context)",
198
+ imageInput: true,
199
+ toolCalling: true,
200
+ // Standard (post-promo) pricing per Sapiens AI's docs
201
+ // (https://wiki.agnes-ai.com — $0.03 / $0.15 per 1M tokens).
202
+ // The docs also list a temporary $0 / $0 promotional rate;
203
+ // we curate the standard rate here since a promo is
204
+ // time-limited and would silently go stale in this table.
205
+ // No separate CNY tier is published, so pricingCNY mirrors
206
+ // the USD numbers rather than inventing a discount.
207
+ pricingUSD: { input: 0.03, output: 0.15, cacheRead: null },
208
+ pricingCNY: { input: 0.03, output: 0.15, cacheRead: null },
209
+ priceCategory: "low",
210
+ // Binary Thinking on/off, same `thinkingEnabled` schema as
211
+ // MiniMax M3 — but translated differently on the wire: Agnes
212
+ // ships only the OpenAI-compatible shape, so
213
+ // `OpenAIAdapter.chat()` maps `thinkingEnabled` to
214
+ // `chat_template_kwargs.enable_thinking` (per Sapiens AI's
215
+ // docs) rather than AnthropicAdapter's `thinking.type` field.
216
+ thinkingSchema: "thinkingEnabled",
217
+ maxInputTokens: 512e3,
218
+ maxOutputTokens: 65536
219
+ },
220
+ "agnes-2.5-pro-alpha": {
221
+ detail: "Agnes 2.5 Pro Alpha \u2014 \u4ED8\u8D39\u63A8\u7406\u6A21\u578B\uFF08\u9AD8\u7EA7\u7F16\u7801\u3001\u79D1\u5B66\u63A8\u7406\u3001\u957F\u4E0A\u4E0B\u6587\u3001\u591A\u6A21\u6001\uFF09\u30021M \u4E0A\u4E0B\u6587 / 65K \u6700\u5927\u8F93\u51FA\u3002",
222
+ imageInput: true,
223
+ toolCalling: true,
224
+ // 官方价格:$0.45 input / $0.0038 cache read / $0.90 output per 1M tokens
225
+ pricingUSD: { input: 0.45, output: 0.9, cacheRead: 38e-4 },
226
+ pricingCNY: { input: 0.45, output: 0.9, cacheRead: 38e-4 },
227
+ priceCategory: "medium",
228
+ thinkingSchema: "thinkingEnabled",
229
+ // 上下文窗口 1M = maxInput 1M - maxOutput 65K = 934,976
230
+ maxInputTokens: 934976,
231
+ maxOutputTokens: 65536
232
+ },
233
+ // ── Kimi (月之暗面 / Moonshot AI) ────────────────────────────────
234
+ // Source: https://platform.kimi.com/docs/pricing/chat-k3,
235
+ // chat-k27-code, chat-k26 (fetched 2026-07-27). Prices are per 1M
236
+ // tokens; context window is the combined input+output budget the
237
+ // docs publish for the model (not two independent maxima), so the
238
+ // input/output split below sums to that published number.
239
+ // Capability audit (fetched 2026-07-27, platform.kimi.com/docs/guide/
240
+ // use-kimi-api-to-complete-tool-calls, use-reasoning-effort,
241
+ // use-thinking-models): all four models support `tools` (confirmed
242
+ // via the tool-calls guide's worked example using kimi-k3, and the
243
+ // "配置多步工具调用" section explicitly naming kimi-k2.7-code /
244
+ // kimi-k2.6). Thinking config differs per model:
245
+ // - k3: always reasoning, leveled via top-level `reasoning_effort`
246
+ // (low/high/max) -> thinkingSchema: reasoningEffort.
247
+ // - k2.7-code(-highspeed): thinking is ALWAYS on, `thinking.type`
248
+ // only accepts "enabled" (passing "disabled" errors) -- no user
249
+ // control, so no dropdown is shown (thinkingSchema omitted).
250
+ // - k2.6: thinking is a binary toggle via `thinking.type`
251
+ // ("enabled" default / "disabled") -> thinkingSchema: thinkingEnabled
252
+ // (not reasoningEffort -- k2.6 has no effort levels).
253
+ "kimi-k3": {
254
+ detail: "2.8\u4E07\u4EBF\u53C2\u6570\u65D7\u8230\u5F00\u6E90\u6A21\u578B\uFF0C1M \u4E0A\u4E0B\u6587",
255
+ imageInput: true,
256
+ toolCalling: true,
257
+ // ¥2 cache hit / ¥20 input (cache miss) / ¥100 output per 1M tokens
258
+ pricingUSD: { input: 2.8, output: 14, cacheRead: 0.28 },
259
+ pricingCNY: { input: 20, output: 100, cacheRead: 2 },
260
+ priceCategory: "high",
261
+ thinkingSchema: "reasoningEffort",
262
+ // Official context window is 1,048,576 tokens total; output
263
+ // capped at 128K (consistent with other flagship models in
264
+ // this table), remainder reserved for input.
265
+ maxInputTokens: 920576,
266
+ maxOutputTokens: 128e3
267
+ },
268
+ "kimi-k2.7-code": {
269
+ detail: "Coding \u4E13\u7528\uFF0C\u652F\u6301\u56FE\u7247+\u89C6\u9891\u8F93\u5165\uFF0C\u5DE5\u5177\u8C03\u7528\uFF0C\u59CB\u7EC8\u5F00\u542F\u601D\u8003",
270
+ imageInput: true,
271
+ toolCalling: true,
272
+ // ¥6.5 input / ¥27 output / ¥1.3 cache hit per 1M tokens
273
+ pricingUSD: { input: 0.91, output: 3.78, cacheRead: 0.182 },
274
+ pricingCNY: { input: 6.5, output: 27, cacheRead: 1.3 },
275
+ priceCategory: "medium",
276
+ // Official context window: 262,144 tokens total.
277
+ maxInputTokens: 229376,
278
+ maxOutputTokens: 32768
279
+ },
280
+ "kimi-k2.7-code-highspeed": {
281
+ detail: "K2.7 Code \u9AD8\u901F\u7248\uFF08\u53CC\u500D\u4EF7\u683C\uFF09\uFF0C\u4E0E K2.7 Code \u540C\u4E00\u6A21\u578B",
282
+ imageInput: true,
283
+ toolCalling: true,
284
+ // ¥13 input / ¥54 output / ¥2.6 cache hit per 1M tokens
285
+ pricingUSD: { input: 1.82, output: 7.57, cacheRead: 0.364 },
286
+ pricingCNY: { input: 13, output: 54, cacheRead: 2.6 },
287
+ priceCategory: "high",
288
+ maxInputTokens: 229376,
289
+ maxOutputTokens: 32768
290
+ },
291
+ "kimi-k2.6": {
292
+ detail: "K2.6 \u2014 \u652F\u6301\u56FE\u7247+\u89C6\u9891\u3001\u5DE5\u5177\u8C03\u7528\uFF0C\u601D\u8003\u53EF\u5F00\u5173",
293
+ imageInput: true,
294
+ toolCalling: true,
295
+ // ¥6.5 input / ¥27 output / ¥1.1 cache hit per 1M tokens
296
+ pricingUSD: { input: 0.91, output: 3.78, cacheRead: 0.154 },
297
+ pricingCNY: { input: 6.5, output: 27, cacheRead: 1.1 },
298
+ priceCategory: "medium",
299
+ thinkingSchema: "thinkingEnabled",
300
+ maxInputTokens: 229376,
301
+ maxOutputTokens: 32768
302
+ },
303
+ // ── Zhipu / 智谱 AI (GLM) ────────────────────────────────────────
304
+ // Source: https://docs.bigmodel.cn/cn/guide/start/model-overview
305
+ // (context windows) + https://open.bigmodel.cn/pricing (fetched
306
+ // 2026-07-27). Several GLM-5.x models publish tiered pricing by
307
+ // input length ([0,32K) vs [32K+)); we curate the higher/[32K+)
308
+ // tier here since our schema has no tiering — the ProvidersTab
309
+ // user can still override per-provider.
310
+ // toolCalling for GLM-5.x: docs.bigmodel.cn's model overview describes
311
+ // GLM-5.2 as "从代码生成走向工程交付" (engineering delivery, not just
312
+ // code gen), GLM-5.1 as "可自主工作长达 8 小时" (autonomous 8h work),
313
+ // and GLM-5-Turbo as "复杂长任务执行连续性好" (sustained complex-task
314
+ // execution) — all imply agentic tool use consistent with sibling
315
+ // GLM-5's explicit "Agentic 长程规划与执行" description. Corrected
316
+ // from false (inconsistent with the rest of the GLM-5 family).
317
+ "glm-5.2": {
318
+ detail: "GLM-5.2 \u2014 1M \u4E0A\u4E0B\u6587\uFF0C\u6700\u5927\u8F93\u51FA 128K",
319
+ imageInput: false,
320
+ toolCalling: true,
321
+ // ¥8 input / ¥28 output / ¥2 cache hit per 1M tokens (输入长度 32K+ 档)
322
+ pricingUSD: { input: 1.12, output: 3.92, cacheRead: 0.28 },
323
+ pricingCNY: { input: 8, output: 28, cacheRead: 2 },
324
+ priceCategory: "high",
325
+ maxInputTokens: 1e6,
326
+ maxOutputTokens: 128e3
327
+ },
328
+ "glm-4.7": {
329
+ detail: "200K \u4E0A\u4E0B\u6587\uFF0C\u5DE5\u5177\u8C03\u7528",
330
+ imageInput: false,
331
+ toolCalling: true,
332
+ // cc-switch 标价:$0.6 input / $2.2 output
333
+ pricingUSD: { input: 0.6, output: 2.2, cacheRead: 0.11 },
334
+ pricingCNY: { input: 4.3, output: 15.7, cacheRead: 0.79 },
335
+ priceCategory: "medium",
336
+ maxInputTokens: 2e5,
337
+ maxOutputTokens: 128e3
338
+ },
339
+ "glm-5.1": {
340
+ detail: "GLM-5.1 \u2014 200K \u4E0A\u4E0B\u6587\uFF0C\u6700\u5927\u8F93\u51FA 128K",
341
+ imageInput: false,
342
+ toolCalling: true,
343
+ // ¥8 input / ¥28 output / ¥2 cache hit per 1M tokens (输入长度 32K+ 档)
344
+ pricingUSD: { input: 1.12, output: 3.92, cacheRead: 0.28 },
345
+ pricingCNY: { input: 8, output: 28, cacheRead: 2 },
346
+ priceCategory: "high",
347
+ maxInputTokens: 2e5,
348
+ maxOutputTokens: 128e3
349
+ },
350
+ "glm-5": {
351
+ detail: "GLM-5 \u2014 200K \u4E0A\u4E0B\u6587\uFF0CAgentic \u5DE5\u5177\u8C03\u7528\uFF0C\u6700\u5927\u8F93\u51FA 128K",
352
+ imageInput: false,
353
+ toolCalling: true,
354
+ // ¥6 input / ¥22 output / ¥1.5 cache hit per 1M tokens (输入长度 32K+ 档)
355
+ pricingUSD: { input: 0.84, output: 3.08, cacheRead: 0.21 },
356
+ pricingCNY: { input: 6, output: 22, cacheRead: 1.5 },
357
+ priceCategory: "high",
358
+ maxInputTokens: 2e5,
359
+ maxOutputTokens: 128e3
360
+ },
361
+ "glm-5-turbo": {
362
+ detail: "GLM-5 Turbo \u2014 200K \u4E0A\u4E0B\u6587\uFF0C\u6700\u5927\u8F93\u51FA 128K",
363
+ imageInput: false,
364
+ toolCalling: true,
365
+ // ¥7 input / ¥26 output / ¥1.8 cache hit per 1M tokens (输入长度 32K+ 档)
366
+ pricingUSD: { input: 0.98, output: 3.64, cacheRead: 0.252 },
367
+ pricingCNY: { input: 7, output: 26, cacheRead: 1.8 },
368
+ priceCategory: "medium",
369
+ maxInputTokens: 2e5,
370
+ maxOutputTokens: 128e3
371
+ },
372
+ "glm-4.7-flashx": {
373
+ detail: "GLM-4.7 FlashX \u2014 \u5FEB\u901F\u7248",
374
+ imageInput: false,
375
+ toolCalling: false,
376
+ // ¥0.5 input / ¥3 output / ¥0.1 cache hit per 1M tokens
377
+ pricingUSD: { input: 0.07, output: 0.42, cacheRead: 0.014 },
378
+ pricingCNY: { input: 0.5, output: 3, cacheRead: 0.1 },
379
+ priceCategory: "low",
380
+ maxInputTokens: 2e5,
381
+ maxOutputTokens: 128e3
382
+ },
383
+ "glm-4.6": {
384
+ detail: "GLM-4.6 \u2014 \u5DE5\u5177\u8C03\u7528",
385
+ imageInput: false,
386
+ toolCalling: true,
387
+ pricingUSD: { input: 0.6, output: 2.2, cacheRead: 0.11 },
388
+ pricingCNY: { input: 4.3, output: 15.7, cacheRead: 0.79 },
389
+ priceCategory: "medium",
390
+ maxInputTokens: 2e5,
391
+ maxOutputTokens: 128e3
392
+ },
393
+ "glm-4.5-air": {
394
+ detail: "GLM-4.5 Air \u2014 \u5DE5\u5177\u8C03\u7528",
395
+ imageInput: false,
396
+ toolCalling: true,
397
+ pricingUSD: { input: 0, output: 0, cacheRead: null },
398
+ pricingCNY: { input: 0, output: 0, cacheRead: null },
399
+ priceCategory: "low",
400
+ maxInputTokens: 128e3,
401
+ maxOutputTokens: 96e3
402
+ },
403
+ "glm-4.5-airx": {
404
+ detail: "GLM-4.5 AirX \u2014 \u5FEB\u901F\u7248",
405
+ imageInput: false,
406
+ toolCalling: false,
407
+ pricingUSD: { input: 0, output: 0, cacheRead: null },
408
+ pricingCNY: { input: 0, output: 0, cacheRead: null },
409
+ priceCategory: "low",
410
+ maxInputTokens: 128e3,
411
+ maxOutputTokens: 96e3
412
+ },
413
+ "glm-4-long": {
414
+ detail: "GLM-4 Long \u2014 1M \u4E0A\u4E0B\u6587\uFF0C\u957F\u6587\u6863\u5904\u7406",
415
+ imageInput: false,
416
+ toolCalling: false,
417
+ // ¥1 / M tokens (single rate, input == output per 官方定价页)
418
+ pricingUSD: { input: 0.14, output: 0.14, cacheRead: null },
419
+ pricingCNY: { input: 1, output: 1, cacheRead: null },
420
+ priceCategory: "low",
421
+ maxInputTokens: 1e6,
422
+ maxOutputTokens: 4e3
423
+ },
424
+ "glm-4-flashx": {
425
+ detail: "GLM-4 FlashX \u2014 \u5FEB\u901F\u7248",
426
+ imageInput: false,
427
+ toolCalling: false,
428
+ // ¥0.1 / M tokens (single rate, input == output per 官方定价页)
429
+ pricingUSD: { input: 0.014, output: 0.014, cacheRead: null },
430
+ pricingCNY: { input: 0.1, output: 0.1, cacheRead: null },
431
+ priceCategory: "low",
432
+ maxInputTokens: 128e3,
433
+ maxOutputTokens: 16e3
434
+ },
435
+ "glm-4-plus": {
436
+ detail: "GLM-4 Plus \u2014 Function Call \u652F\u6301",
437
+ imageInput: false,
438
+ toolCalling: true,
439
+ // ¥5 input per 1M tokens
440
+ pricingUSD: { input: 0.7, output: 0.7, cacheRead: null },
441
+ pricingCNY: { input: 5, output: 5, cacheRead: null },
442
+ priceCategory: "low",
443
+ maxInputTokens: 128e3,
444
+ maxOutputTokens: 4e3
445
+ },
446
+ "glm-4.5v": {
447
+ detail: "GLM-4.5V \u89C6\u89C9\u63A8\u7406\u6A21\u578B \u2014 \u56FE\u50CF/\u89C6\u9891/\u6587\u6863/GUI",
448
+ imageInput: true,
449
+ toolCalling: true,
450
+ pricingUSD: { input: 0, output: 0, cacheRead: null },
451
+ pricingCNY: { input: 0, output: 0, cacheRead: null },
452
+ priceCategory: "medium",
453
+ maxInputTokens: 128e3,
454
+ maxOutputTokens: 8192
455
+ },
456
+ "glm-5v-turbo": {
457
+ detail: "GLM-5V Turbo \u2014 \u591A\u6A21\u6001 Coding \u6A21\u578B",
458
+ imageInput: true,
459
+ toolCalling: true,
460
+ pricingUSD: { input: 0, output: 0, cacheRead: null },
461
+ pricingCNY: { input: 0, output: 0, cacheRead: null },
462
+ priceCategory: "medium",
463
+ // Official model overview: 200K context / 128K max output
464
+ // (previously mis-set to 128K/8_192 — corrected 2026-07-27).
465
+ maxInputTokens: 2e5,
466
+ maxOutputTokens: 128e3
467
+ },
468
+ "glm-3-turbo": {
469
+ detail: "GLM-3 Turbo \u2014 \u5165\u95E8\u7EA7\uFF08\xA51/1M tokens\uFF09",
470
+ imageInput: false,
471
+ toolCalling: false,
472
+ pricingUSD: { input: 0.14, output: 0.14, cacheRead: null },
473
+ pricingCNY: { input: 1, output: 1, cacheRead: null },
474
+ priceCategory: "low",
475
+ maxInputTokens: 128e3,
476
+ maxOutputTokens: 8192
477
+ },
478
+ // ── StepFun / 阶跃星辰 ───────────────────────────────────────────
479
+ // Source: https://platform.stepfun.com/docs/zh/guides/models/
480
+ // step-3.7-flash, step-3.5-flash + guides/models (overview page,
481
+ // fetched 2026-07-27). Both flash models publish "256K tokens" as
482
+ // a single combined context number; the exact input/output split
483
+ // below matches Novita's real-world deployment of step-3.7-flash
484
+ // (262,144 total / 256,000 max output) since StepFun's own docs
485
+ // don't split it further.
486
+ "step-3.7-flash": {
487
+ detail: "Step 3.7 Flash \u2014 \u591A\u6A21\u6001\u63A8\u7406\u6A21\u578B\uFF08198B/11B MoE\uFF09",
488
+ imageInput: true,
489
+ // Official model page lists "🛠️ 工具调用: 可靠的工具调用能力,支持多步
490
+ // 任务分解与计划执行" as a core capability — was mis-set to false.
491
+ toolCalling: true,
492
+ // ¥1.35 input / ¥8.1 output / ¥0.27 cache hit per 1M tokens
493
+ pricingUSD: { input: 0.189, output: 1.134, cacheRead: 0.038 },
494
+ pricingCNY: { input: 1.35, output: 8.1, cacheRead: 0.27 },
495
+ priceCategory: "medium",
496
+ thinkingSchema: "reasoningEffort",
497
+ // Was 0 (a bug — VSCode's chat picker hides entries whose
498
+ // maxInputTokens is 0). Corrected to the official 256K context
499
+ // window (262,144 tokens) minus the published 256,000 max output.
500
+ maxInputTokens: 262144,
501
+ maxOutputTokens: 256e3
502
+ },
503
+ "step-3.5-flash": {
504
+ detail: "Step 3.5 Flash \u2014 \u63A8\u7406\u6A21\u578B",
505
+ imageInput: false,
506
+ // Official model page lists "🛠️ 工具调用: 可靠的 tools / tool_choice
507
+ // 调用能力" as a core capability — was mis-set to false.
508
+ toolCalling: true,
509
+ // ¥0.7 input / ¥2.1 output / ¥0.14 cache hit per 1M tokens
510
+ pricingUSD: { input: 0.098, output: 0.294, cacheRead: 0.02 },
511
+ pricingCNY: { input: 0.7, output: 2.1, cacheRead: 0.14 },
512
+ priceCategory: "low",
513
+ thinkingSchema: "reasoningEffort",
514
+ maxInputTokens: 256e3,
515
+ maxOutputTokens: 256e3
516
+ },
517
+ "step-1o-turbo-vision": {
518
+ detail: "Step 1o Turbo Vision \u2014 \u89C6\u89C9\u6A21\u578B",
519
+ imageInput: true,
520
+ toolCalling: false,
521
+ // ¥2.5 input / ¥8 output per 1M tokens
522
+ pricingUSD: { input: 0.35, output: 1.12, cacheRead: null },
523
+ pricingCNY: { input: 2.5, output: 8, cacheRead: null },
524
+ priceCategory: "low",
525
+ // Official model overview: 32K context window.
526
+ maxInputTokens: 32768,
527
+ maxOutputTokens: 32768
528
+ },
529
+ // ── 聚合平台占位 ────────────────────────────────────────────────────
530
+ // 这些平台是 model aggregator — 模型列表由平台动态维护。
531
+ // 当 `provider.baseUrl` 与对应平台匹配时(即 OpenRouter / Novita),
532
+ // 用户可通过 `Fetch from API` 从 `/v1/models` 拉取真实列表;
533
+ // 下面这些 metadata 条目只是为了支持 `BUILTIN_PROVIDER_PRESETS` 中的预设。
534
+ "openrouter/auto": {
535
+ detail: "OpenRouter Auto \u2014 \u81EA\u52A8\u8DEF\u7531\u5230\u5E73\u53F0\u6700\u4F73\u6A21\u578B",
536
+ imageInput: true,
537
+ toolCalling: true,
538
+ // OpenRouter 透传上游价格,自身不标价。占位 0 由用户在 provider form 调整。
539
+ pricingUSD: { input: 0, output: 0, cacheRead: null },
540
+ pricingCNY: { input: 0, output: 0, cacheRead: null },
541
+ priceCategory: "medium",
542
+ maxInputTokens: 0,
543
+ maxOutputTokens: 0
544
+ },
545
+ "novita-ai/novita-3.5-flash": {
546
+ detail: "Novita 3.5 Flash \u2014 Novita \u5E73\u53F0\u70ED\u95E8\u6A21\u578B\uFF08\u5360\u4F4D\uFF0C\u6309\u5B9E\u9645\u9009\u62E9\u8C03\u6574\uFF09",
547
+ imageInput: true,
548
+ toolCalling: true,
549
+ pricingUSD: { input: 0.1, output: 0.4, cacheRead: null },
550
+ pricingCNY: { input: 0.1, output: 0.4, cacheRead: null },
551
+ priceCategory: "low",
552
+ maxInputTokens: 32768,
553
+ maxOutputTokens: 32768
554
+ }
555
+ };
556
+ function lookupModelMetadata(modelId) {
557
+ return MODEL_METADATA[modelId];
558
+ }
559
+ function currencyForBaseUrl(baseUrl) {
560
+ try {
561
+ const hostname = new URL(baseUrl).hostname.toLowerCase();
562
+ if (hostname === "api.minimaxi.com" || hostname === "api.minimaxi.cn" || hostname === "api.deepseek.com" || hostname === "api.moonshot.cn" || hostname === "open.bigmodel.cn") {
563
+ return "CNY";
564
+ }
565
+ if (hostname === "api.minimax.io" || hostname === "api.minimaxi.io" || hostname === "api.siliconflow.cn" || hostname === "api.siliconflow.com" || hostname === "api.stepfun.com" || hostname === "openrouter.ai" || hostname === "api.novita.ai") {
566
+ return "USD";
567
+ }
568
+ } catch {
569
+ }
570
+ return "USD";
571
+ }
572
+
573
+ // src/ai/providers.presets.ts
574
+ function buildPresetModel(id, displayName) {
575
+ const meta = MODEL_METADATA[id];
576
+ if (!meta) {
577
+ throw new Error(
578
+ `buildPresetModel: no curated MODEL_METADATA entry for '${id}' \u2014 add one before referencing it from BUILTIN_PROVIDER_PRESETS.`
579
+ );
580
+ }
581
+ return {
582
+ id,
583
+ displayName,
584
+ maxInputTokens: meta.maxInputTokens,
585
+ maxOutputTokens: meta.maxOutputTokens,
586
+ detail: meta.detail,
587
+ capabilities: {
588
+ supportsImageToText: meta.imageInput,
589
+ supportsToolCalling: meta.toolCalling
590
+ },
591
+ pricing: meta.pricingUSD,
592
+ priceCategory: meta.priceCategory,
593
+ thinkingSchema: meta.thinkingSchema ?? "none"
594
+ };
595
+ }
596
+ var BUILTIN_PROVIDER_PRESETS = {
597
+ minimax: {
598
+ displayName: "MiniMax",
599
+ baseUrl: "https://api.minimaxi.com/anthropic",
600
+ // Token limits (see the `MODEL_METADATA` entries above) are
601
+ // aligned with the upstream `minimax-vscode-main` reference
602
+ // (`docs/references/minimax-vscode-main/src/models/registry.ts`),
603
+ // which scrapes the official model cards:
604
+ // - M3: official spec is 1M context, but the >512K input tier
605
+ // is still limited-availability (needs sales access + 2x
606
+ // billing per the pricing-page footnote) and most accounts
607
+ // get a 400 past 512K. We report the safe 512K effective cap
608
+ // for both input and output so VSCode's "上下文窗口" indicator
609
+ // matches what a normal account can actually push.
610
+ // - M2.7 / M2.7-highspeed: official spec is 204,800 total
611
+ // context, split 131,072 input / 73,728 output (no separate
612
+ // docs split exists beyond this; do not invent a different
613
+ // one).
614
+ // `maxInputTokens` / `maxOutputTokens` are forwarded to
615
+ // `vscode.lm.registerLanguageModelChatProvider` — VSCode's chat
616
+ // model picker hides / disables entries whose `maxInputTokens`
617
+ // is 0, so every curated entry must carry an explicit number
618
+ // (otherwise the user's added model never shows up in the
619
+ // Copilot Chat picker).
620
+ models: [
621
+ buildPresetModel("MiniMax-M3", "MiniMax-M3"),
622
+ buildPresetModel("MiniMax-M2.7", "MiniMax-M2.7"),
623
+ buildPresetModel("MiniMax-M2.7-highspeed", "MiniMax-M2.7-highspeed")
624
+ ]
625
+ },
626
+ deepseek: {
627
+ displayName: "DeepSeek",
628
+ // DeepSeek v4 ships only the OpenAI-compatible endpoint (no
629
+ // Anthropic-compatible surface). The /v1 suffix is intentional
630
+ // — it matches the public docs (https://api.deepseek.com/v1)
631
+ // and is the path the OpenAI client uses to build
632
+ // `/v1/chat/completions`. The reference implementation
633
+ // (`docs/references/deepseek-v4-for-copilot-main`) uses
634
+ // `https://api.deepseek.com` and appends `/chat/completions`
635
+ // (no /v1), which is technically a different host layout — we
636
+ // prefer the explicit /v1 here so the user can verify the path
637
+ // in their browser without ambiguity.
638
+ baseUrl: "https://api.deepseek.com/v1",
639
+ // V4 context window: 655,360 input / 393,216 output per
640
+ // `docs/references/deepseek-v4-for-copilot-main/src/consts.ts`.
641
+ // V3 (deepseek-chat / deepseek-reasoner, 64K) is no longer
642
+ // the recommended default — users still on V3 can override
643
+ // these defaults in the ProvidersTab form.
644
+ models: [
645
+ buildPresetModel("deepseek-v4-flash", "DeepSeek V4 Flash"),
646
+ buildPresetModel("deepseek-v4-pro", "DeepSeek V4 Pro")
647
+ ]
648
+ },
649
+ agnes: {
650
+ displayName: "Agnes",
651
+ // Sapiens AI's Agnes — OpenAI-compatible endpoint per
652
+ // https://wiki.agnes-ai.com (`POST /v1/chat/completions`).
653
+ baseUrl: "https://apihub.agnes-ai.com/v1",
654
+ models: [
655
+ buildPresetModel("agnes-2.5-pro-alpha", "Agnes 2.5 Pro Alpha"),
656
+ buildPresetModel("agnes-2.0-flash", "Agnes 2.0 Flash")
657
+ ]
658
+ },
659
+ kimi: {
660
+ displayName: "Kimi",
661
+ baseUrl: "https://api.moonshot.cn/v1",
662
+ models: [
663
+ buildPresetModel("kimi-k3", "Kimi K3"),
664
+ buildPresetModel("kimi-k2.7-code", "Kimi K2.7 Code"),
665
+ buildPresetModel("kimi-k2.7-code-highspeed", "Kimi K2.7 Code HighSpeed"),
666
+ buildPresetModel("kimi-k2.6", "Kimi K2.6")
667
+ ]
668
+ },
669
+ zhipu: {
670
+ displayName: "Zhipu",
671
+ baseUrl: "https://open.bigmodel.cn/api/paas/v4",
672
+ // Every id below has a curated `MODEL_METADATA` entry (see above) —
673
+ // keep this list in sync with that table. It previously only
674
+ // listed the GLM-4.x models, so newer flagship releases (GLM-5.2 /
675
+ // 5.1 / 5 / 5-Turbo / 5V-Turbo) never appeared in the ProvidersTab
676
+ // "add provider" preset even though their metadata existed.
677
+ models: [
678
+ buildPresetModel("glm-5.2", "GLM-5.2"),
679
+ buildPresetModel("glm-5.1", "GLM-5.1"),
680
+ buildPresetModel("glm-5", "GLM-5"),
681
+ buildPresetModel("glm-5-turbo", "GLM-5 Turbo"),
682
+ buildPresetModel("glm-5v-turbo", "GLM-5V Turbo"),
683
+ buildPresetModel("glm-4.7", "GLM-4.7"),
684
+ buildPresetModel("glm-4.7-flashx", "GLM-4.7 FlashX"),
685
+ buildPresetModel("glm-4.6", "GLM-4.6"),
686
+ buildPresetModel("glm-4.5v", "GLM-4.5V"),
687
+ buildPresetModel("glm-4.5-air", "GLM-4.5 Air"),
688
+ buildPresetModel("glm-4.5-airx", "GLM-4.5 AirX"),
689
+ buildPresetModel("glm-4-plus", "GLM-4 Plus"),
690
+ buildPresetModel("glm-4-long", "GLM-4 Long"),
691
+ buildPresetModel("glm-4-flashx", "GLM-4 FlashX"),
692
+ buildPresetModel("glm-3-turbo", "GLM-3 Turbo")
693
+ ]
694
+ },
695
+ stepfun: {
696
+ displayName: "StepFun",
697
+ baseUrl: "https://api.stepfun.com/v1",
698
+ models: [
699
+ buildPresetModel("step-3.7-flash", "Step 3.7 Flash"),
700
+ buildPresetModel("step-3.5-flash", "Step 3.5 Flash"),
701
+ buildPresetModel("step-1o-turbo-vision", "Step 1o Turbo Vision")
702
+ ]
703
+ },
704
+ siliconflow: {
705
+ displayName: "SiliconFlow",
706
+ baseUrl: "https://api.siliconflow.cn/v1",
707
+ // 聚合平台:模型列表动态,用户添加后通过 /v1/models 拉取。
708
+ // 占位 0 容量模型避免 picker 显示空。
709
+ models: []
710
+ },
711
+ openrouter: {
712
+ displayName: "OpenRouter",
713
+ baseUrl: "https://openrouter.ai/api/v1",
714
+ models: [buildPresetModel("openrouter/auto", "OpenRouter Auto")]
715
+ },
716
+ novita: {
717
+ displayName: "Novita",
718
+ baseUrl: "https://api.novita.ai/openai/v1",
719
+ // 聚合平台:Novita 本身不产出自有模型,只是把 Kimi K3 / GLM 5.2 /
720
+ // DeepSeek V4 / MiniMax M3 / Step 3.7 Flash 等第三方开源或授权模型
721
+ // 挂到统一 OpenAI 兼容网关下(见 https://novita.ai/llm-api)。此前
722
+ // 占位的 "novita-ai/novita-3.5-flash" 在其模型列表中查无此模型 id,
723
+ // 已移除。用户添加 provider 后通过 /v1/models 拉取真实列表。
724
+ models: []
725
+ }
726
+ };
727
+ function getBuiltinProviderPreset(type) {
728
+ switch (type) {
729
+ case "minimax":
730
+ case "deepseek":
731
+ case "agnes":
732
+ case "kimi":
733
+ case "zhipu":
734
+ case "stepfun":
735
+ case "siliconflow":
736
+ case "openrouter":
737
+ case "novita":
738
+ return BUILTIN_PROVIDER_PRESETS[type];
739
+ default:
740
+ return null;
741
+ }
742
+ }
743
+
744
+ // src/certificate-bundle.ts
745
+ var CERTIFICATE_BUNDLE_FORMATS = [
746
+ {
747
+ format: "pem",
748
+ label: "Nginx / Generic PEM",
749
+ description: "cert.pem + key.pem + fullchain.pem (Nginx, HAProxy, most Unix servers).",
750
+ icon: "file-lock-2",
751
+ requiresPassword: false,
752
+ artifactExtension: ".pem"
753
+ },
754
+ {
755
+ format: "pfx",
756
+ label: "IIS / Tomcat PFX",
757
+ description: "PKCS#12 bundle (.pfx) for Windows IIS, Tomcat, ColdFusion. Requires a password.",
758
+ icon: "shield",
759
+ requiresPassword: true,
760
+ artifactExtension: ".pfx"
761
+ },
762
+ {
763
+ format: "crt",
764
+ label: "Apache CRT",
765
+ description: "Separate .crt + .key for Apache httpd and other Unix servers that prefer DER/PEM split.",
766
+ icon: "file-badge",
767
+ requiresPassword: false,
768
+ artifactExtension: ".crt"
769
+ },
770
+ {
771
+ format: "jks",
772
+ label: "Java JKS",
773
+ description: "Java KeyStore (.jks) for Tomcat and Java applications. Requires a JDK with `keytool` on PATH.",
774
+ icon: "coffee",
775
+ requiresPassword: true,
776
+ artifactExtension: ".jks"
777
+ }
778
+ ];
779
+
780
+ // src/git-utils.ts
781
+ var GIT_REMOTE_HOST_ALIASES = {
782
+ "github-msc": "github.com"
783
+ };
784
+ function normalizeGitRemoteHost(host) {
785
+ const normalizedHost = host.trim().toLowerCase();
786
+ return GIT_REMOTE_HOST_ALIASES[normalizedHost] ?? normalizedHost;
787
+ }
788
+ function normalizeCanonicalSlug(slug) {
789
+ const trimmedSlug = slug.trim().toLowerCase();
790
+ const firstSlashIndex = trimmedSlug.indexOf("/");
791
+ if (firstSlashIndex <= 0) {
792
+ return trimmedSlug;
793
+ }
794
+ const host = trimmedSlug.slice(0, firstSlashIndex);
795
+ const path = trimmedSlug.slice(firstSlashIndex + 1);
796
+ return `${normalizeGitRemoteHost(host)}/${path}`;
797
+ }
798
+ function normalizeGitUrl(input) {
799
+ const trimmed = input.trim();
800
+ const scpMatch = !trimmed.includes("://") && trimmed.match(/^([^@]+)@([^:]+):(.+)$/);
801
+ if (scpMatch) {
802
+ const host = scpMatch[2];
803
+ const path = scpMatch[3];
804
+ return toCanonicalSlug(`ssh://${host}/${path}`);
805
+ }
806
+ try {
807
+ const url = new URL(trimmed);
808
+ const host = normalizeGitRemoteHost(url.hostname);
809
+ let pathname = url.pathname;
810
+ if (pathname.startsWith("/")) pathname = pathname.slice(1);
811
+ if (pathname.endsWith(".git")) pathname = pathname.slice(0, -4);
812
+ const parts = pathname.split("/").filter(Boolean);
813
+ if (parts.length < 2) {
814
+ throw new Error("Invalid Git URL: missing owner/repo path");
815
+ }
816
+ const normalizedPath = parts.map((part) => part.toLowerCase()).join("/");
817
+ return `${host}/${normalizedPath}`;
818
+ } catch {
819
+ if (isValidCanonicalSlug(trimmed)) return normalizeCanonicalSlug(trimmed);
820
+ throw new Error(`Unparseable Git URL: ${input}`);
821
+ }
822
+ }
823
+ function isValidCanonicalSlug(slug) {
824
+ const segment = "(?:[a-zA-Z0-9_.-]|%[0-9A-Fa-f]{2})+";
825
+ const canonicalSlugPattern = new RegExp(`^${segment}(?:\\/${segment}){2,}$`);
826
+ return canonicalSlugPattern.test(slug.trim());
827
+ }
828
+ function toCanonicalSlug(coerced) {
829
+ const url = new URL(coerced);
830
+ const host = normalizeGitRemoteHost(url.hostname);
831
+ let pathname = url.pathname;
832
+ if (pathname.startsWith("/")) pathname = pathname.slice(1);
833
+ if (pathname.endsWith(".git")) pathname = pathname.slice(0, -4);
834
+ const parts = pathname.split("/").filter(Boolean);
835
+ if (parts.length < 2) throw new Error("Invalid Git URL: missing owner/repo path");
836
+ return `${host}/${parts.map((part) => part.toLowerCase()).join("/")}`;
837
+ }
838
+
839
+ // src/github-api.ts
840
+ async function fetchGitHubUser(token) {
841
+ const resp = await fetch("https://api.github.com/user", {
842
+ headers: {
843
+ Authorization: `token ${token}`,
844
+ Accept: "application/vnd.github.v3+json"
845
+ }
846
+ });
847
+ if (resp.ok) {
848
+ const user = await resp.json();
849
+ user.email = await resolveGitHubUserEmail(token, user);
850
+ return user;
851
+ }
852
+ if (resp.status === 401) {
853
+ throw new Error("GitHub API Error: 401 Unauthorized");
854
+ }
855
+ const status = resp.status;
856
+ const statusText = resp.statusText;
857
+ let body = "";
858
+ try {
859
+ body = await resp.text();
860
+ } catch (_e) {
861
+ body = "[Failed to read response body]";
862
+ }
863
+ throw new Error(`GitHub API Error: ${status} ${statusText} - ${body}`);
864
+ }
865
+ async function resolveGitHubUserEmail(token, user) {
866
+ const directEmail = sanitizeEmail(user.email);
867
+ if (directEmail) {
868
+ return directEmail;
869
+ }
870
+ const emails = await fetchGitHubUserEmails(token);
871
+ const preferredEmail = pickPreferredGitHubEmail(emails);
872
+ return preferredEmail ?? directEmail;
873
+ }
874
+ async function fetchGitHubUserEmails(token) {
875
+ const resp = await fetch("https://api.github.com/user/emails", {
876
+ headers: {
877
+ Authorization: `token ${token}`,
878
+ Accept: "application/vnd.github.v3+json"
879
+ }
880
+ });
881
+ if (resp.ok) {
882
+ return await resp.json();
883
+ }
884
+ if (resp.status === 404 || resp.status === 403) {
885
+ return [];
886
+ }
887
+ if (resp.status === 401) {
888
+ throw new Error("GitHub API Error: 401 Unauthorized");
889
+ }
890
+ let body = "";
891
+ try {
892
+ body = await resp.text();
893
+ } catch (_e) {
894
+ body = "[Failed to read response body]";
895
+ }
896
+ throw new Error(`GitHub API Error: ${resp.status} ${resp.statusText} - ${body}`);
897
+ }
898
+ function pickPreferredGitHubEmail(emails) {
899
+ const sanitizedEmails = emails.map((emailRecord) => ({
900
+ ...emailRecord,
901
+ email: sanitizeEmail(emailRecord.email)
902
+ })).filter(
903
+ (emailRecord) => Boolean(emailRecord.email)
904
+ );
905
+ const preferred = sanitizedEmails.find((emailRecord) => emailRecord.primary && emailRecord.verified) ?? sanitizedEmails.find((emailRecord) => emailRecord.verified) ?? sanitizedEmails.find((emailRecord) => emailRecord.primary) ?? sanitizedEmails[0];
906
+ return preferred?.email ?? null;
907
+ }
908
+ function sanitizeEmail(email) {
909
+ if (!email) {
910
+ return null;
911
+ }
912
+ const normalizedEmail = email.trim();
913
+ if (!normalizedEmail) {
914
+ return null;
915
+ }
916
+ return normalizedEmail;
917
+ }
918
+ var __internal = {
919
+ pickPreferredGitHubEmail,
920
+ sanitizeEmail
921
+ };
922
+ function createOrgMembershipResult(organization, status, httpStatus, membership) {
923
+ return {
924
+ status,
925
+ httpStatus,
926
+ organization,
927
+ role: typeof membership?.role === "string" ? membership.role : null,
928
+ directMembership: typeof membership?.direct_membership === "boolean" ? membership.direct_membership : null
929
+ };
930
+ }
931
+ async function getGitHubOrgMembership(token, org) {
932
+ const normalizedOrg = org.trim().toLowerCase();
933
+ const resp = await fetch("https://api.github.com/user/memberships/orgs", {
934
+ headers: {
935
+ Authorization: `token ${token}`,
936
+ Accept: "application/vnd.github+json",
937
+ "X-GitHub-Api-Version": "2022-11-28"
938
+ }
939
+ });
940
+ if (resp.status === 401) {
941
+ return createOrgMembershipResult(org, "unauthorized", resp.status);
942
+ }
943
+ if (resp.status === 403) {
944
+ return createOrgMembershipResult(org, "forbidden", resp.status);
945
+ }
946
+ if (!resp.ok) {
947
+ let body = "";
948
+ try {
949
+ body = await resp.text();
950
+ } catch (_e) {
951
+ body = "[Failed to read response body]";
952
+ }
953
+ throw new Error(`GitHub API Error: ${resp.status} ${resp.statusText} - ${body}`);
954
+ }
955
+ const memberships = await resp.json();
956
+ const membership = memberships.find(
957
+ (entry) => entry.organization?.login?.toLowerCase() === normalizedOrg
958
+ );
959
+ if (!membership) {
960
+ return createOrgMembershipResult(org, "not_member", resp.status);
961
+ }
962
+ if (membership.state === "pending") {
963
+ return createOrgMembershipResult(org, "pending", resp.status, membership);
964
+ }
965
+ if (membership.state === "active") {
966
+ return createOrgMembershipResult(org, "active", resp.status, membership);
967
+ }
968
+ return createOrgMembershipResult(org, "not_member", resp.status, membership);
969
+ }
970
+ async function checkGitHubOrgMembership(token, org) {
971
+ const membership = await getGitHubOrgMembership(token, org);
972
+ if (membership.status === "forbidden" || membership.status === "unauthorized") {
973
+ throw new Error(`GitHub org membership indeterminate: HTTP ${membership.httpStatus}`);
974
+ }
975
+ return membership.status === "active" || membership.status === "pending";
976
+ }
977
+
978
+ // src/github-user-email.ts
979
+ function isGitHubLocalEmail(email) {
980
+ return typeof email === "string" && email.trim().toLowerCase().endsWith("@github.local");
981
+ }
982
+ function buildGitHubLocalEmail(login) {
983
+ return `${login}@github.local`;
984
+ }
985
+ function resolvePrimaryEmail(login, email) {
986
+ const normalizedEmail = email?.trim();
987
+ return normalizedEmail || buildGitHubLocalEmail(login);
988
+ }
989
+
990
+ // src/logger/index.ts
991
+ var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
992
+ LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
993
+ LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
994
+ LogLevel2[LogLevel2["WARN"] = 2] = "WARN";
995
+ LogLevel2[LogLevel2["ERROR"] = 3] = "ERROR";
996
+ return LogLevel2;
997
+ })(LogLevel || {});
998
+ function normalizeErrorForLog(error) {
999
+ if (error === null || error === void 0) {
1000
+ return { kind: "nullish", value: null };
1001
+ }
1002
+ const t = typeof error;
1003
+ if (t === "string") {
1004
+ return { kind: "string", message: error };
1005
+ }
1006
+ if (t === "number" || t === "boolean" || t === "bigint") {
1007
+ return { kind: t, value: String(error) };
1008
+ }
1009
+ if (t === "object") {
1010
+ const obj = error;
1011
+ if (error instanceof Error) {
1012
+ const result2 = {
1013
+ kind: "error",
1014
+ name: error.name,
1015
+ message: error.message,
1016
+ stack: error.stack
1017
+ };
1018
+ if (typeof obj.code === "string") {
1019
+ result2.code = obj.code;
1020
+ }
1021
+ if (typeof obj.retryable === "boolean") {
1022
+ result2.retryable = obj.retryable;
1023
+ }
1024
+ if ("details" in obj) {
1025
+ result2.details = obj.details;
1026
+ }
1027
+ if (typeof obj.cause !== "undefined") {
1028
+ result2.cause = normalizeErrorForLog(obj.cause);
1029
+ }
1030
+ return result2;
1031
+ }
1032
+ const result = { kind: "object" };
1033
+ let captured = 0;
1034
+ for (const [k, v] of Object.entries(obj)) {
1035
+ result[k] = v;
1036
+ captured += 1;
1037
+ }
1038
+ if (captured === 0) {
1039
+ result.message = "(empty object)";
1040
+ }
1041
+ return result;
1042
+ }
1043
+ return { kind: t, value: String(error) };
1044
+ }
1045
+ function isProduction() {
1046
+ const proc = globalThis.process;
1047
+ return proc?.env?.NODE_ENV === "production";
1048
+ }
1049
+ function createConsoleLogger(name) {
1050
+ let level = isProduction() ? 1 /* INFO */ : 0 /* DEBUG */;
1051
+ const emit = (lvl, method, message, args) => {
1052
+ if (lvl < level) {
1053
+ return;
1054
+ }
1055
+ const prefixed = `[${name}] ${message}`;
1056
+ switch (method) {
1057
+ case "log":
1058
+ console.log(prefixed, ...args);
1059
+ break;
1060
+ case "info":
1061
+ console.info(prefixed, ...args);
1062
+ break;
1063
+ case "warn":
1064
+ console.warn(prefixed, ...args);
1065
+ break;
1066
+ case "error":
1067
+ console.error(prefixed, ...args);
1068
+ break;
1069
+ }
1070
+ };
1071
+ return {
1072
+ debug(message, ...args) {
1073
+ emit(0 /* DEBUG */, "log", message, args);
1074
+ },
1075
+ info(message, ...args) {
1076
+ emit(1 /* INFO */, "info", message, args);
1077
+ },
1078
+ warn(message, ...args) {
1079
+ emit(2 /* WARN */, "warn", message, args);
1080
+ },
1081
+ error(message, error, ...args) {
1082
+ const payload = error === void 0 ? args : [normalizeErrorForLog(error), ...args];
1083
+ emit(3 /* ERROR */, "error", message, payload);
1084
+ },
1085
+ setLevel(next) {
1086
+ level = next;
1087
+ }
1088
+ };
1089
+ }
1090
+
1091
+ // src/messages.ts
1092
+ var WebviewMessageType = /* @__PURE__ */ ((WebviewMessageType2) => {
1093
+ WebviewMessageType2["WebviewReady"] = "webviewReady";
1094
+ WebviewMessageType2["Ready"] = "ready";
1095
+ WebviewMessageType2["Log"] = "log";
1096
+ WebviewMessageType2["ExecuteCommand"] = "executeCommand";
1097
+ WebviewMessageType2["OpenUrl"] = "openUrl";
1098
+ WebviewMessageType2["UsePrompt"] = "usePrompt";
1099
+ WebviewMessageType2["UpdateAzureProfiles"] = "updateAzureProfiles";
1100
+ WebviewMessageType2["UpdateAzureProfile"] = "updateAzureProfile";
1101
+ WebviewMessageType2["GetApiConfig"] = "getApiConfig";
1102
+ WebviewMessageType2["UpdateApiConfig"] = "updateApiConfig";
1103
+ WebviewMessageType2["SaveApiConfig"] = "saveApiConfig";
1104
+ WebviewMessageType2["ApiConfigSaveFailed"] = "apiConfigSaveFailed";
1105
+ WebviewMessageType2["ExportApiConfig"] = "exportApiConfig";
1106
+ WebviewMessageType2["ImportApiConfig"] = "importApiConfig";
1107
+ WebviewMessageType2["ApiConfigImported"] = "apiConfigImported";
1108
+ WebviewMessageType2["AddExternalTool"] = "addExternalTool";
1109
+ WebviewMessageType2["UpdateExternalTool"] = "updateExternalTool";
1110
+ WebviewMessageType2["UpdateExternalTools"] = "updateExternalTools";
1111
+ WebviewMessageType2["DeleteExternalTool"] = "deleteExternalTool";
1112
+ WebviewMessageType2["ReorderExternalTools"] = "reorderExternalTools";
1113
+ WebviewMessageType2["UpdateNgrokStatus"] = "updateNgrokStatus";
1114
+ WebviewMessageType2["UpdateServerStatus"] = "updateServerStatus";
1115
+ WebviewMessageType2["GetAuthState"] = "getAuthState";
1116
+ WebviewMessageType2["UpdateAuthState"] = "updateAuthState";
1117
+ WebviewMessageType2["Login"] = "login";
1118
+ WebviewMessageType2["Logout"] = "logout";
1119
+ WebviewMessageType2["GetAccounts"] = "getAccounts";
1120
+ WebviewMessageType2["UpdateAccounts"] = "updateAccounts";
1121
+ WebviewMessageType2["SwitchAccount"] = "switchAccount";
1122
+ WebviewMessageType2["GetUserProfile"] = "getUserProfile";
1123
+ WebviewMessageType2["UpdateUserProfile"] = "updateUserProfile";
1124
+ WebviewMessageType2["UserProfileUpdated"] = "userProfileUpdated";
1125
+ WebviewMessageType2["AddExtraEmail"] = "addExtraEmail";
1126
+ WebviewMessageType2["DeleteExtraEmail"] = "deleteExtraEmail";
1127
+ WebviewMessageType2["GetCalendarHolidays"] = "getCalendarHolidays";
1128
+ WebviewMessageType2["UpdateCalendarHolidays"] = "updateCalendarHolidays";
1129
+ WebviewMessageType2["GetCalendarLeaves"] = "getCalendarLeaves";
1130
+ WebviewMessageType2["UpdateCalendarLeaves"] = "updateCalendarLeaves";
1131
+ WebviewMessageType2["CreateCalendarLeave"] = "createCalendarLeave";
1132
+ WebviewMessageType2["DeleteCalendarLeave"] = "deleteCalendarLeave";
1133
+ WebviewMessageType2["GetCalendarNotes"] = "getCalendarNotes";
1134
+ WebviewMessageType2["UpdateCalendarNotes"] = "updateCalendarNotes";
1135
+ WebviewMessageType2["SaveCalendarNote"] = "saveCalendarNote";
1136
+ WebviewMessageType2["GetCalendarMonthSchedule"] = "getCalendarMonthSchedule";
1137
+ WebviewMessageType2["GetScheduledTasks"] = "getScheduledTasks";
1138
+ WebviewMessageType2["UpdateScheduledTasks"] = "updateScheduledTasks";
1139
+ WebviewMessageType2["CreateScheduledTask"] = "createScheduledTask";
1140
+ WebviewMessageType2["EditScheduledTask"] = "editScheduledTask";
1141
+ WebviewMessageType2["DeleteScheduledTask"] = "deleteScheduledTask";
1142
+ WebviewMessageType2["ToggleScheduledTask"] = "toggleScheduledTask";
1143
+ WebviewMessageType2["TriggerScheduledTask"] = "triggerScheduledTask";
1144
+ WebviewMessageType2["CancelTaskExecution"] = "cancelTaskExecution";
1145
+ WebviewMessageType2["GetTaskExecutionLogs"] = "getTaskExecutionLogs";
1146
+ WebviewMessageType2["ClearTaskExecutionLogs"] = "clearTaskExecutionLogs";
1147
+ WebviewMessageType2["UpdateTaskExecutionLogs"] = "updateTaskExecutionLogs";
1148
+ WebviewMessageType2["UpdateTaskExecutionState"] = "updateTaskExecutionState";
1149
+ WebviewMessageType2["GetCurrentWorkspace"] = "getCurrentWorkspace";
1150
+ WebviewMessageType2["UpdateCurrentWorkspace"] = "updateCurrentWorkspace";
1151
+ WebviewMessageType2["GetProviders"] = "getProviders";
1152
+ WebviewMessageType2["ProvidersResponse"] = "providersResponse";
1153
+ WebviewMessageType2["AddProvider"] = "addProvider";
1154
+ WebviewMessageType2["UpdateProvider"] = "updateProvider";
1155
+ WebviewMessageType2["RemoveProvider"] = "removeProvider";
1156
+ WebviewMessageType2["SetDefaultProvider"] = "setDefaultProvider";
1157
+ WebviewMessageType2["SetProviderEnabled"] = "setProviderEnabled";
1158
+ WebviewMessageType2["SetProviderOrder"] = "setProviderOrder";
1159
+ WebviewMessageType2["TestProvider"] = "testProvider";
1160
+ WebviewMessageType2["ProviderTestResultMessage"] = "providerTestResult";
1161
+ WebviewMessageType2["DefaultProviderChanged"] = "defaultProviderChanged";
1162
+ WebviewMessageType2["FetchProviderModels"] = "fetchProviderModels";
1163
+ WebviewMessageType2["FetchProviderModelsResult"] = "fetchProviderModelsResult";
1164
+ WebviewMessageType2["GetProviderUsage"] = "getProviderUsage";
1165
+ WebviewMessageType2["ProviderUsageResponse"] = "providerUsageResponse";
1166
+ WebviewMessageType2["SetCacheControlEnabled"] = "setCacheControlEnabled";
1167
+ WebviewMessageType2["GetByomSettings"] = "getByomSettings";
1168
+ WebviewMessageType2["ByomSettingsResponse"] = "byomSettingsResponse";
1169
+ WebviewMessageType2["ListSkillRepoEntries"] = "listSkillRepoEntries";
1170
+ WebviewMessageType2["GetSkillRepoEntry"] = "getSkillRepoEntry";
1171
+ WebviewMessageType2["InstallSkillRepoEntry"] = "installSkillRepoEntry";
1172
+ WebviewMessageType2["ConvertSkillRepoEntryToSymlink"] = "convertSkillRepoEntryToSymlink";
1173
+ WebviewMessageType2["UninstallSkillRepoEntry"] = "uninstallSkillRepoEntry";
1174
+ WebviewMessageType2["ListLinkedSkills"] = "listLinkedSkills";
1175
+ WebviewMessageType2["UpdateSkillRepoCatalog"] = "updateSkillRepoCatalog";
1176
+ WebviewMessageType2["UpdateSkillRepoInstall"] = "updateSkillRepoInstall";
1177
+ WebviewMessageType2["UpdateLinkedSkills"] = "updateLinkedSkills";
1178
+ WebviewMessageType2["CreateSkillRepoDraft"] = "createSkillRepoDraft";
1179
+ WebviewMessageType2["CommitSkillRepoDraft"] = "commitSkillRepoDraft";
1180
+ WebviewMessageType2["ListSkillRepoDrafts"] = "listSkillRepoDrafts";
1181
+ WebviewMessageType2["DeleteSkillRepoDraft"] = "deleteSkillRepoDraft";
1182
+ WebviewMessageType2["UpdateSkillRepoDraft"] = "updateSkillRepoDraft";
1183
+ WebviewMessageType2["ListRepositories"] = "listRepositories";
1184
+ WebviewMessageType2["AddRepository"] = "addRepository";
1185
+ WebviewMessageType2["UpdateRepository"] = "updateRepository";
1186
+ WebviewMessageType2["RemoveRepository"] = "removeRepository";
1187
+ WebviewMessageType2["EnableRepository"] = "enableRepository";
1188
+ WebviewMessageType2["DisableRepository"] = "disableRepository";
1189
+ WebviewMessageType2["SyncRepository"] = "syncRepository";
1190
+ WebviewMessageType2["SyncAllRepositories"] = "syncAllRepositories";
1191
+ WebviewMessageType2["UpdateRepositoryList"] = "updateRepositoryList";
1192
+ WebviewMessageType2["UpdateRepositoryAdd"] = "updateRepositoryAdd";
1193
+ WebviewMessageType2["UpdateRepositoryUpdate"] = "updateRepositoryUpdate";
1194
+ WebviewMessageType2["UpdateRepositoryRemove"] = "updateRepositoryRemove";
1195
+ WebviewMessageType2["UpdateRepositoryToggle"] = "updateRepositoryToggle";
1196
+ WebviewMessageType2["UpdateRepositorySyncResult"] = "updateRepositorySyncResult";
1197
+ WebviewMessageType2["GetUtilityModels"] = "getUtilityModels";
1198
+ WebviewMessageType2["UtilityModelsResponse"] = "utilityModelsResponse";
1199
+ WebviewMessageType2["UpdateUtilityModels"] = "updateUtilityModels";
1200
+ WebviewMessageType2["GetServerProxyState"] = "getServerProxyState";
1201
+ WebviewMessageType2["ServerProxyStateResponse"] = "serverProxyStateResponse";
1202
+ WebviewMessageType2["SetServerProxyEnabled"] = "setServerProxyEnabled";
1203
+ WebviewMessageType2["SetServerProxyAllowOverride"] = "setServerProxyAllowOverride";
1204
+ WebviewMessageType2["GetCachedServerUrl"] = "getCachedServerUrl";
1205
+ WebviewMessageType2["CachedServerUrlResponse"] = "cachedServerUrlResponse";
1206
+ return WebviewMessageType2;
1207
+ })(WebviewMessageType || {});
1208
+ var GetUtilityModels = "getUtilityModels";
1209
+ var UtilityModelsResponse = "utilityModelsResponse";
1210
+ var UpdateUtilityModels = "updateUtilityModels";
1211
+ var SetCacheControlEnabled = "setCacheControlEnabled";
1212
+ var GetByomSettings = "getByomSettings";
1213
+ var ByomSettingsResponse = "byomSettingsResponse";
1214
+ var GetServerProxyState = "getServerProxyState";
1215
+ var ServerProxyStateResponse = "serverProxyStateResponse";
1216
+ var SetServerProxyEnabled = "setServerProxyEnabled";
1217
+ var SetServerProxyAllowOverride = "setServerProxyAllowOverride";
1218
+ var GetCachedServerUrl = "getCachedServerUrl";
1219
+ var CachedServerUrlResponse = "cachedServerUrlResponse";
1220
+
1221
+ // src/utils/safe-cast.ts
1222
+ function parsePayload(raw) {
1223
+ return raw;
1224
+ }
1225
+ function asAbortSignal(input) {
1226
+ if (input !== null && typeof input === "object") {
1227
+ const candidate = input.signal;
1228
+ if (candidate instanceof AbortSignal) {
1229
+ return candidate;
1230
+ }
1231
+ }
1232
+ return void 0;
1233
+ }
1234
+ function safeJson(text, fallback) {
1235
+ try {
1236
+ return JSON.parse(text);
1237
+ } catch {
1238
+ return fallback;
1239
+ }
1240
+ }
1241
+ // Annotate the CommonJS export names for ESM import in node:
1242
+ 0 && (module.exports = {
1243
+ BUILTIN_PROVIDER_PRESETS,
1244
+ ByomSettingsResponse,
1245
+ CERTIFICATE_BUNDLE_FORMATS,
1246
+ CachedServerUrlResponse,
1247
+ GIT_REMOTE_HOST_ALIASES,
1248
+ GetByomSettings,
1249
+ GetCachedServerUrl,
1250
+ GetServerProxyState,
1251
+ GetUtilityModels,
1252
+ LogLevel,
1253
+ MODEL_METADATA,
1254
+ PROVIDER_BASE_URL_PRESETS,
1255
+ PROVIDER_CACHE_CONTROL_METADATA,
1256
+ ServerProxyStateResponse,
1257
+ SetCacheControlEnabled,
1258
+ SetServerProxyAllowOverride,
1259
+ SetServerProxyEnabled,
1260
+ UpdateUtilityModels,
1261
+ UtilityModelsResponse,
1262
+ WebviewMessageType,
1263
+ __internal,
1264
+ asAbortSignal,
1265
+ buildGitHubLocalEmail,
1266
+ checkGitHubOrgMembership,
1267
+ createConsoleLogger,
1268
+ currencyForBaseUrl,
1269
+ fetchGitHubUser,
1270
+ getBuiltinProviderPreset,
1271
+ getGitHubOrgMembership,
1272
+ getProviderBaseUrlPresets,
1273
+ isGitHubLocalEmail,
1274
+ isProviderCacheControlAware,
1275
+ isValidCanonicalSlug,
1276
+ lookupModelMetadata,
1277
+ normalizeCanonicalSlug,
1278
+ normalizeErrorForLog,
1279
+ normalizeGitUrl,
1280
+ parsePayload,
1281
+ resolvePrimaryEmail,
1282
+ safeJson
1283
+ });