@serviceme/devtools-shared 2.0.0 → 2.0.2
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.d.mts +1025 -998
- package/dist/index.d.ts +1025 -998
- package/dist/index.js +2308 -2347
- package/dist/index.mjs +2252 -2266
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -1,2451 +1,2412 @@
|
|
|
1
|
-
"
|
|
2
|
-
|
|
3
|
-
|
|
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
|
-
DeviceAuthHeaders: () => DeviceAuthHeaders,
|
|
28
|
-
GIT_REMOTE_HOST_ALIASES: () => GIT_REMOTE_HOST_ALIASES,
|
|
29
|
-
GetByomSettings: () => GetByomSettings,
|
|
30
|
-
GetCachedServerUrl: () => GetCachedServerUrl,
|
|
31
|
-
GetServerProxyState: () => GetServerProxyState,
|
|
32
|
-
GetUtilityModels: () => GetUtilityModels,
|
|
33
|
-
LISTABLE_PRESET_MODELS: () => LISTABLE_PRESET_MODELS,
|
|
34
|
-
LogLevel: () => LogLevel,
|
|
35
|
-
MEDALSOFT_NUGET_PRIVATE_SOURCE: () => MEDALSOFT_NUGET_PRIVATE_SOURCE,
|
|
36
|
-
MEDALSOFT_PRIVATE_GATEWAY_URL: () => MEDALSOFT_PRIVATE_GATEWAY_URL,
|
|
37
|
-
MODEL_METADATA: () => MODEL_METADATA,
|
|
38
|
-
NAMESPACE_ALIASES: () => NAMESPACE_ALIASES,
|
|
39
|
-
NAMESPACE_ALIAS_FAMILY: () => NAMESPACE_ALIAS_FAMILY,
|
|
40
|
-
PRESET_MODEL_FAMILIES: () => PRESET_MODEL_FAMILIES,
|
|
41
|
-
PROVIDER_BASE_URL_PRESETS: () => PROVIDER_BASE_URL_PRESETS,
|
|
42
|
-
PROVIDER_CACHE_CONTROL_METADATA: () => PROVIDER_CACHE_CONTROL_METADATA,
|
|
43
|
-
ServerProxyStateResponse: () => ServerProxyStateResponse,
|
|
44
|
-
SetCacheControlEnabled: () => SetCacheControlEnabled,
|
|
45
|
-
SetServerProxyAllowOverride: () => SetServerProxyAllowOverride,
|
|
46
|
-
SetServerProxyEnabled: () => SetServerProxyEnabled,
|
|
47
|
-
UpdateUtilityModels: () => UpdateUtilityModels,
|
|
48
|
-
UtilityModelsResponse: () => UtilityModelsResponse,
|
|
49
|
-
WebviewMessageType: () => WebviewMessageType,
|
|
50
|
-
__internal: () => __internal,
|
|
51
|
-
asAbortSignal: () => asAbortSignal,
|
|
52
|
-
buildGitHubLocalEmail: () => buildGitHubLocalEmail,
|
|
53
|
-
buildPresetModel: () => buildPresetModel,
|
|
54
|
-
checkGitHubOrgMembership: () => checkGitHubOrgMembership,
|
|
55
|
-
createConsoleLogger: () => createConsoleLogger,
|
|
56
|
-
createDeviceRequestSignature: () => createDeviceRequestSignature,
|
|
57
|
-
currencyForBaseUrl: () => currencyForBaseUrl,
|
|
58
|
-
effectiveAdapterType: () => effectiveAdapterType,
|
|
59
|
-
fetchGitHubUser: () => fetchGitHubUser,
|
|
60
|
-
getBuiltinProviderPreset: () => getBuiltinProviderPreset,
|
|
61
|
-
getGitHubOrgMembership: () => getGitHubOrgMembership,
|
|
62
|
-
getPresetModelDisplayName: () => getPresetModelDisplayName,
|
|
63
|
-
getProviderBaseUrlPresets: () => getProviderBaseUrlPresets,
|
|
64
|
-
isGitHubLocalEmail: () => isGitHubLocalEmail,
|
|
65
|
-
isProviderCacheControlAware: () => isProviderCacheControlAware,
|
|
66
|
-
isValidCanonicalSlug: () => isValidCanonicalSlug,
|
|
67
|
-
isWholePackageInstall: () => isWholePackageInstall,
|
|
68
|
-
listPresetModelGroups: () => listPresetModelGroups,
|
|
69
|
-
lookupModelMetadata: () => lookupModelMetadata,
|
|
70
|
-
normalizeCanonicalSlug: () => normalizeCanonicalSlug,
|
|
71
|
-
normalizeErrorForLog: () => normalizeErrorForLog,
|
|
72
|
-
normalizeGitUrl: () => normalizeGitUrl,
|
|
73
|
-
parsePayload: () => parsePayload,
|
|
74
|
-
protocolForBaseUrl: () => protocolForBaseUrl,
|
|
75
|
-
resolvePrimaryEmail: () => resolvePrimaryEmail,
|
|
76
|
-
safeJson: () => safeJson,
|
|
77
|
-
unionProviderModelWithPreset: () => unionProviderModelWithPreset
|
|
78
|
-
});
|
|
79
|
-
module.exports = __toCommonJS(index_exports);
|
|
80
|
-
|
|
81
|
-
// src/ai/protocol.ts
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let crypto = require("crypto");
|
|
3
|
+
//#region src/ai/protocol.ts
|
|
82
4
|
function protocolForBaseUrl(baseUrl) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
5
|
+
try {
|
|
6
|
+
const url = new URL(baseUrl);
|
|
7
|
+
return /(^|\/)anthropic(\/|$)/i.test(url.pathname) ? "anthropic" : "openai";
|
|
8
|
+
} catch {
|
|
9
|
+
return "openai";
|
|
10
|
+
}
|
|
89
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* Resolve the actual `ProviderType` whose adapter should handle a
|
|
14
|
+
* request with the given `configuredType` + `baseUrl`.
|
|
15
|
+
*
|
|
16
|
+
* Most of the time this is just `configuredType`. The exception is
|
|
17
|
+
* the named `zhipu` provider with an Anthropic-protocol baseUrl
|
|
18
|
+
* (`/api/anthropic`); we route it through the `anthropic-compatible`
|
|
19
|
+
* adapter so the URL construction appends `/v1/messages` instead of
|
|
20
|
+
* the OpenAI `/v1/chat/completions`. Without this, the OpenAIAdapter
|
|
21
|
+
* would build `.../api/anthropic/chat/completions` and GLM would
|
|
22
|
+
* 404 (the Anthropic-compatible endpoint only serves
|
|
23
|
+
* `.../api/anthropic/v1/messages`).
|
|
24
|
+
*
|
|
25
|
+
* `minimax` is intentionally NOT in the override list because the
|
|
26
|
+
* curated preset is `https://api.minimaxi.com/anthropic` (already
|
|
27
|
+
* Anthropic-protocol), and the `minimax` case in the adapter
|
|
28
|
+
* factory already wires AnthropicAdapter unconditionally.
|
|
29
|
+
*
|
|
30
|
+
* Everything else (deepseek / kimi / stepfun / agnes / openrouter /
|
|
31
|
+
* novita / openai-compatible / anthropic-compatible) returns
|
|
32
|
+
* `configuredType` unchanged — these vendors don't publish an
|
|
33
|
+
* alternate-protocol endpoint on the same host.
|
|
34
|
+
*/
|
|
90
35
|
function effectiveAdapterType(configuredType, baseUrl) {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
return configuredType;
|
|
36
|
+
if (configuredType === "zhipu" && protocolForBaseUrl(baseUrl) === "anthropic") return "anthropic-compatible";
|
|
37
|
+
return configuredType;
|
|
95
38
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
39
|
+
//#endregion
|
|
40
|
+
//#region src/constants.ts
|
|
41
|
+
/**
|
|
42
|
+
* Medalsoft-internal infrastructure addresses.
|
|
43
|
+
*
|
|
44
|
+
* These are **private-network only** endpoints — reachable from the
|
|
45
|
+
* company LAN/VPN, NOT from the public internet. External users must
|
|
46
|
+
* use the public equivalents (`nexus.servicemecloud.com`, …). Keeping
|
|
47
|
+
* them as named constants here (instead of inlined literals) gives a
|
|
48
|
+
* single place to update when the internal fleet moves, and makes the
|
|
49
|
+
* "this is an internal address" intent explicit at every use site.
|
|
50
|
+
*
|
|
51
|
+
* NOTE: `packages/serviceme-core` keeps its own copies of the ones it
|
|
52
|
+
* needs — ADL-003 forbids core → shared. Keep those in lock-step.
|
|
53
|
+
*/
|
|
54
|
+
/** Medalsoft 内网 LLM 网关(OpenAI-compatible `/v1`),仅办公网可达。 */
|
|
55
|
+
const MEDALSOFT_PRIVATE_GATEWAY_URL = "http://10.10.10.249:3000/v1";
|
|
56
|
+
/** Medalsoft 私有 NuGet 源,仅办公网可达。 */
|
|
57
|
+
const MEDALSOFT_NUGET_PRIVATE_SOURCE = "http://192.168.20.209:10010/nuget";
|
|
58
|
+
//#endregion
|
|
59
|
+
//#region src/ai/providers.base-url.ts
|
|
60
|
+
/**
|
|
61
|
+
* Provider-type → known baseUrl candidates (e.g. mainland-China vs.
|
|
62
|
+
* global endpoints for the same vendor, like Agnes/MiniMax/DeepSeek).
|
|
63
|
+
* Purely a UI convenience for the Add/Edit form's baseUrl dropdown —
|
|
64
|
+
* no runtime auto-switching reads this (that mechanism was removed;
|
|
65
|
+
* see git history for the retired `autoSwitch` feature).
|
|
66
|
+
*/
|
|
67
|
+
const PROVIDER_BASE_URL_PRESETS = {
|
|
68
|
+
"openai-compatible": [],
|
|
69
|
+
"anthropic-compatible": [],
|
|
70
|
+
minimax: [{
|
|
71
|
+
label: "国内",
|
|
72
|
+
baseUrl: "https://api.minimaxi.com/anthropic"
|
|
73
|
+
}, {
|
|
74
|
+
label: "全球",
|
|
75
|
+
baseUrl: "https://api.minimax.io/anthropic"
|
|
76
|
+
}],
|
|
77
|
+
deepseek: [{
|
|
78
|
+
label: "官方",
|
|
79
|
+
baseUrl: "https://api.deepseek.com/v1"
|
|
80
|
+
}],
|
|
81
|
+
kimi: [{
|
|
82
|
+
label: "国内",
|
|
83
|
+
baseUrl: "https://api.moonshot.cn/v1"
|
|
84
|
+
}, {
|
|
85
|
+
label: "全球",
|
|
86
|
+
baseUrl: "https://api.moonshot.ai/v1"
|
|
87
|
+
}],
|
|
88
|
+
zhipu: [
|
|
89
|
+
{
|
|
90
|
+
label: "国内 · 标准 API",
|
|
91
|
+
baseUrl: "https://open.bigmodel.cn/api/paas/v4"
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
label: "国内 · Coding Plan",
|
|
95
|
+
baseUrl: "https://open.bigmodel.cn/api/coding/paas/v4"
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
label: "国内 · Coding Plan · Anthropic 协议",
|
|
99
|
+
baseUrl: "https://open.bigmodel.cn/api/anthropic"
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
label: "国际 · 标准 API",
|
|
103
|
+
baseUrl: "https://api.z.ai/api/paas/v4"
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
label: "国际 · Coding Plan",
|
|
107
|
+
baseUrl: "https://api.z.ai/api/coding/paas/v4"
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
label: "国际 · Coding Plan · Anthropic 协议",
|
|
111
|
+
baseUrl: "https://api.z.ai/api/anthropic"
|
|
112
|
+
}
|
|
113
|
+
],
|
|
114
|
+
stepfun: [{
|
|
115
|
+
label: "官方",
|
|
116
|
+
baseUrl: "https://api.stepfun.com/v1"
|
|
117
|
+
}],
|
|
118
|
+
siliconflow: [{
|
|
119
|
+
label: "国内",
|
|
120
|
+
baseUrl: "https://api.siliconflow.cn/v1"
|
|
121
|
+
}, {
|
|
122
|
+
label: "全球",
|
|
123
|
+
baseUrl: "https://api.siliconflow.com/v1"
|
|
124
|
+
}],
|
|
125
|
+
openrouter: [{
|
|
126
|
+
label: "官方",
|
|
127
|
+
baseUrl: "https://openrouter.ai/api/v1"
|
|
128
|
+
}],
|
|
129
|
+
novita: [{
|
|
130
|
+
label: "官方",
|
|
131
|
+
baseUrl: "https://api.novita.ai/openai/v1"
|
|
132
|
+
}],
|
|
133
|
+
agnes: [{
|
|
134
|
+
label: "国内",
|
|
135
|
+
baseUrl: "https://api.agnes-ai.cn/v1"
|
|
136
|
+
}, {
|
|
137
|
+
label: "全球",
|
|
138
|
+
baseUrl: "https://apihub.agnes-ai.com/v1"
|
|
139
|
+
}],
|
|
140
|
+
medalsoft: [{
|
|
141
|
+
label: "内网",
|
|
142
|
+
baseUrl: MEDALSOFT_PRIVATE_GATEWAY_URL
|
|
143
|
+
}, {
|
|
144
|
+
label: "外网",
|
|
145
|
+
baseUrl: "https://nexus.servicemecloud.com/v1"
|
|
146
|
+
}],
|
|
147
|
+
sensenova: [{
|
|
148
|
+
label: "官方",
|
|
149
|
+
baseUrl: "https://token.sensenova.cn/v1"
|
|
150
|
+
}],
|
|
151
|
+
"vscode-builtin": []
|
|
195
152
|
};
|
|
153
|
+
/**
|
|
154
|
+
* Resolve the dropdown candidate list for a provider type. Returns
|
|
155
|
+
* an empty array (NEVER throws) for `-compatible` types or provider
|
|
156
|
+
* types the table doesn't cover.
|
|
157
|
+
*/
|
|
196
158
|
function getProviderBaseUrlPresets(type) {
|
|
197
|
-
|
|
159
|
+
return PROVIDER_BASE_URL_PRESETS[type] ?? [];
|
|
198
160
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
// ignores this flag.
|
|
222
|
-
zhipu: {
|
|
223
|
-
supportsCacheControl: true
|
|
224
|
-
},
|
|
225
|
-
stepfun: {},
|
|
226
|
-
siliconflow: {},
|
|
227
|
-
openrouter: {},
|
|
228
|
-
novita: {},
|
|
229
|
-
agnes: {
|
|
230
|
-
supportsCacheControl: true
|
|
231
|
-
},
|
|
232
|
-
// Medalsoft internal gateway — OpenAI-compatible pass-through; the
|
|
233
|
-
// upstream's prompt-cache behaviour is unknown from outside, so we
|
|
234
|
-
// don't declare support (no `prompt_cache_key` header is written).
|
|
235
|
-
// Flip to `supportsCacheControl: true` once the gateway is verified
|
|
236
|
-
// to honour stable prompt caching.
|
|
237
|
-
medalsoft: {},
|
|
238
|
-
// SenseNova (SenseTime) token-plan gateway — the /v1/models payload
|
|
239
|
-
// publishes an `input_cache_read` pricing field (all-zero today), so
|
|
240
|
-
// the upstream is cache-aware in principle, but there is no
|
|
241
|
-
// documented prompt-cache key contract. Off until verified.
|
|
242
|
-
sensenova: {},
|
|
243
|
-
"vscode-builtin": {}
|
|
161
|
+
//#endregion
|
|
162
|
+
//#region src/ai/providers.cache-control.ts
|
|
163
|
+
/**
|
|
164
|
+
* Provider-type → cache_control capability lookup. Read by the
|
|
165
|
+
* OpenAI adapter's `prompt_cache_key` decision (T-04) via
|
|
166
|
+
* {@link isProviderCacheControlAware}.
|
|
167
|
+
*/
|
|
168
|
+
const PROVIDER_CACHE_CONTROL_METADATA = {
|
|
169
|
+
"openai-compatible": {},
|
|
170
|
+
"anthropic-compatible": {},
|
|
171
|
+
minimax: { supportsCacheControl: true },
|
|
172
|
+
deepseek: { supportsCacheControl: true },
|
|
173
|
+
kimi: {},
|
|
174
|
+
zhipu: { supportsCacheControl: true },
|
|
175
|
+
stepfun: {},
|
|
176
|
+
siliconflow: {},
|
|
177
|
+
openrouter: {},
|
|
178
|
+
novita: {},
|
|
179
|
+
agnes: { supportsCacheControl: true },
|
|
180
|
+
medalsoft: {},
|
|
181
|
+
sensenova: {},
|
|
182
|
+
"vscode-builtin": {}
|
|
244
183
|
};
|
|
184
|
+
/**
|
|
185
|
+
* Does this provider type declare "stable prompt cache" support?
|
|
186
|
+
* The OpenAI path reads this flag to decide whether to write
|
|
187
|
+
* `prompt_cache_key`; the Anthropic path uses its own 4-breakpoint
|
|
188
|
+
* logic and ignores this flag.
|
|
189
|
+
*/
|
|
245
190
|
function isProviderCacheControlAware(type) {
|
|
246
|
-
|
|
191
|
+
return Boolean(PROVIDER_CACHE_CONTROL_METADATA[type]?.supportsCacheControl);
|
|
247
192
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
pricingCNY: { input: 0.1, output: 0.4, cacheRead: null },
|
|
1079
|
-
priceCategory: "low",
|
|
1080
|
-
maxInputTokens: 32768,
|
|
1081
|
-
maxOutputTokens: 32768
|
|
1082
|
-
},
|
|
1083
|
-
// ── MiniMax M2.5 (2026-02-13) ──────────────────────────────────────
|
|
1084
|
-
// 229B MoE, 80.2% SWE-Bench Verified, the predecessor to M2.7.
|
|
1085
|
-
// Source: https://siliconflow.cn/models?series=qwen (MiniMax card),
|
|
1086
|
-
// platform.minimaxi.com/docs/release-notes/models (M2.5 release note).
|
|
1087
|
-
"MiniMax-M2.5": {
|
|
1088
|
-
detail: "MiniMax M2.5 \u2014 229B MoE, SOTA \u7F16\u7A0B / Agent / \u529E\u516C\u751F\u4EA7\u529B\uFF08192K \u4E0A\u4E0B\u6587\uFF09",
|
|
1089
|
-
imageInput: false,
|
|
1090
|
-
toolCalling: true,
|
|
1091
|
-
// Per https://minimax-ai.chat/pricing (M2.5 legacy line):
|
|
1092
|
-
// ¥2.1 input / ¥8.4 output / ¥0.21 cache hit per 1M tokens;
|
|
1093
|
-
// USD = $0.30 / $1.20 / $0.03 (cloudprice.net 2026-08-13).
|
|
1094
|
-
// Cache hit IS the published rate — the previous comment
|
|
1095
|
-
// "按官方 10% cache 命中率回填" was wrong (it implied we
|
|
1096
|
-
// were estimating, when actually the cache rate is
|
|
1097
|
-
// documented at ¥0.21 / $0.03 per 1M tokens).
|
|
1098
|
-
pricingUSD: { input: 0.3, output: 1.2, cacheRead: 0.03 },
|
|
1099
|
-
pricingCNY: { input: 2.1, output: 8.4, cacheRead: 0.21 },
|
|
1100
|
-
priceCategory: "medium",
|
|
1101
|
-
// 官方 context 192K;output 按 16K 保守估值(M2.7 标 128K,M2.5
|
|
1102
|
-
// 官方未单独发布 max output 数字,按其同代老模型惯例取 16K)。
|
|
1103
|
-
maxInputTokens: 192e3,
|
|
1104
|
-
maxOutputTokens: 16384
|
|
1105
|
-
},
|
|
1106
|
-
// ── Qwen3.6-35B-A3B(2026-04-17 通义千问)────────────────────────────
|
|
1107
|
-
// 35B MoE,激活仅 3B,256K 上下文;2026 年 Qwen3.6 系列首发
|
|
1108
|
-
// small-size open-weight。"激活成本 1/10" 是其与前代 Qwen3.5-27B
|
|
1109
|
-
// 相比的核心卖点。Source:
|
|
1110
|
-
// https://siliconflow.cn/news/z12t3edpv6ypbuja3o65lgh2
|
|
1111
|
-
"Qwen3.6-35B-A3B": {
|
|
1112
|
-
detail: "Qwen3.6-35B-A3B \u2014 35B MoE (3B \u6FC0\u6D3B)\uFF0C\u601D\u8003/\u975E\u601D\u8003\u53CC\u6A21\uFF0C256K \u4E0A\u4E0B\u6587",
|
|
1113
|
-
imageInput: true,
|
|
1114
|
-
toolCalling: true,
|
|
1115
|
-
// SiliconFlow 官方价格页(https://siliconflow.cn/pricing,
|
|
1116
|
-
// re-fetched 2026-08-20):¥1.8 input / ¥10.8 output per 1M
|
|
1117
|
-
// tokens,无缓存价格列。此前 ¥1.6/¥12.8 来自发布新闻稿,
|
|
1118
|
-
// 已过时(输入上调、输出下调)。USD = CNY ÷7 镜像取两位
|
|
1119
|
-
// 小数(0.26 / 1.54)。
|
|
1120
|
-
pricingUSD: { input: 0.26, output: 1.54, cacheRead: null },
|
|
1121
|
-
pricingCNY: { input: 1.8, output: 10.8, cacheRead: null },
|
|
1122
|
-
priceCategory: "low",
|
|
1123
|
-
// Qwen3.6-35B-A3B 官方 256K context;output 上限按同代 27B
|
|
1124
|
-
// 同样 32K 取值(Qwen3.6 系列 max output 未单独公布)。
|
|
1125
|
-
maxInputTokens: 256e3,
|
|
1126
|
-
maxOutputTokens: 32768
|
|
1127
|
-
}
|
|
193
|
+
//#endregion
|
|
194
|
+
//#region src/ai/providers.metadata.ts
|
|
195
|
+
const PRIMARY_METADATA = {
|
|
196
|
+
"MiniMax-M3": {
|
|
197
|
+
detail: "Native multimodal frontier coding model (1M context, 512K effective)",
|
|
198
|
+
imageInput: true,
|
|
199
|
+
toolCalling: true,
|
|
200
|
+
pricingUSD: {
|
|
201
|
+
input: .3,
|
|
202
|
+
output: 1.2,
|
|
203
|
+
cacheRead: .06
|
|
204
|
+
},
|
|
205
|
+
pricingCNY: {
|
|
206
|
+
input: 2.1,
|
|
207
|
+
output: 8.4,
|
|
208
|
+
cacheRead: .42
|
|
209
|
+
},
|
|
210
|
+
priceCategory: "medium",
|
|
211
|
+
thinkingSchema: "thinkingEnabled",
|
|
212
|
+
maxInputTokens: 512e3,
|
|
213
|
+
maxOutputTokens: 512e3
|
|
214
|
+
},
|
|
215
|
+
"MiniMax-M2.7": {
|
|
216
|
+
detail: "Self-iterating coding model (~60 TPS)",
|
|
217
|
+
imageInput: false,
|
|
218
|
+
toolCalling: true,
|
|
219
|
+
pricingUSD: {
|
|
220
|
+
input: .3,
|
|
221
|
+
output: 1.2,
|
|
222
|
+
cacheRead: .06
|
|
223
|
+
},
|
|
224
|
+
pricingCNY: {
|
|
225
|
+
input: 2.1,
|
|
226
|
+
output: 8.4,
|
|
227
|
+
cacheRead: .42
|
|
228
|
+
},
|
|
229
|
+
priceCategory: "low",
|
|
230
|
+
maxInputTokens: 131072,
|
|
231
|
+
maxOutputTokens: 73728
|
|
232
|
+
},
|
|
233
|
+
"MiniMax-M2.7-highspeed": {
|
|
234
|
+
detail: "M2.7 high-speed: same quality, faster (~100 TPS)",
|
|
235
|
+
imageInput: false,
|
|
236
|
+
toolCalling: true,
|
|
237
|
+
pricingUSD: {
|
|
238
|
+
input: .6,
|
|
239
|
+
output: 2.4,
|
|
240
|
+
cacheRead: .06
|
|
241
|
+
},
|
|
242
|
+
pricingCNY: {
|
|
243
|
+
input: 4.2,
|
|
244
|
+
output: 16.8,
|
|
245
|
+
cacheRead: .42
|
|
246
|
+
},
|
|
247
|
+
priceCategory: "low",
|
|
248
|
+
maxInputTokens: 131072,
|
|
249
|
+
maxOutputTokens: 73728
|
|
250
|
+
},
|
|
251
|
+
"sensenova-6.8-flash-lite": {
|
|
252
|
+
detail: "SenseNova 6.8 Flash-Lite — 轻量多模态智能体模型,支持图像理解 / 工具调用(262K 上下文,token-plan 限频)",
|
|
253
|
+
imageInput: true,
|
|
254
|
+
toolCalling: true,
|
|
255
|
+
pricingUSD: {
|
|
256
|
+
input: 0,
|
|
257
|
+
output: 0,
|
|
258
|
+
cacheRead: 0
|
|
259
|
+
},
|
|
260
|
+
pricingCNY: {
|
|
261
|
+
input: 0,
|
|
262
|
+
output: 0,
|
|
263
|
+
cacheRead: 0
|
|
264
|
+
},
|
|
265
|
+
priceCategory: "low",
|
|
266
|
+
thinkingSchema: "reasoningEffort",
|
|
267
|
+
supportsReasoningEffort: true,
|
|
268
|
+
maxInputTokens: 262144,
|
|
269
|
+
maxOutputTokens: 65536
|
|
270
|
+
},
|
|
271
|
+
"deepseek-v4-flash": {
|
|
272
|
+
detail: "Fast, general-purpose model",
|
|
273
|
+
imageInput: true,
|
|
274
|
+
toolCalling: true,
|
|
275
|
+
pricingUSD: {
|
|
276
|
+
input: .44,
|
|
277
|
+
output: 1.32,
|
|
278
|
+
cacheRead: .014
|
|
279
|
+
},
|
|
280
|
+
pricingCNY: {
|
|
281
|
+
input: 3,
|
|
282
|
+
output: 9,
|
|
283
|
+
cacheRead: .1
|
|
284
|
+
},
|
|
285
|
+
priceCategory: "low",
|
|
286
|
+
thinkingSchema: "thinkingEnabled",
|
|
287
|
+
maxInputTokens: 655360,
|
|
288
|
+
maxOutputTokens: 393216
|
|
289
|
+
},
|
|
290
|
+
"deepseek-v4-pro": {
|
|
291
|
+
detail: "Most capable reasoning model",
|
|
292
|
+
imageInput: true,
|
|
293
|
+
toolCalling: true,
|
|
294
|
+
pricingUSD: {
|
|
295
|
+
input: 1.32,
|
|
296
|
+
output: 3.96,
|
|
297
|
+
cacheRead: .044
|
|
298
|
+
},
|
|
299
|
+
pricingCNY: {
|
|
300
|
+
input: 9,
|
|
301
|
+
output: 27,
|
|
302
|
+
cacheRead: .3
|
|
303
|
+
},
|
|
304
|
+
priceCategory: "low",
|
|
305
|
+
thinkingSchema: "thinkingEnabled",
|
|
306
|
+
maxInputTokens: 655360,
|
|
307
|
+
maxOutputTokens: 393216
|
|
308
|
+
},
|
|
309
|
+
"agnes-2.0-flash": {
|
|
310
|
+
detail: "Fast agentic model — tool calling, coding, image understanding (512K context)",
|
|
311
|
+
imageInput: true,
|
|
312
|
+
toolCalling: true,
|
|
313
|
+
pricingUSD: {
|
|
314
|
+
input: .03,
|
|
315
|
+
output: .15,
|
|
316
|
+
cacheRead: null
|
|
317
|
+
},
|
|
318
|
+
pricingCNY: {
|
|
319
|
+
input: .03,
|
|
320
|
+
output: .15,
|
|
321
|
+
cacheRead: null
|
|
322
|
+
},
|
|
323
|
+
priceCategory: "low",
|
|
324
|
+
thinkingSchema: "thinkingEnabled",
|
|
325
|
+
maxInputTokens: 512e3,
|
|
326
|
+
maxOutputTokens: 65536
|
|
327
|
+
},
|
|
328
|
+
"agnes-2.5-pro-alpha": {
|
|
329
|
+
detail: "Agnes 2.5 Pro Alpha — 付费推理模型(高级编码、科学推理、长上下文、多模态)。1M 上下文 / 65K 最大输出。",
|
|
330
|
+
imageInput: true,
|
|
331
|
+
toolCalling: true,
|
|
332
|
+
pricingUSD: {
|
|
333
|
+
input: .45,
|
|
334
|
+
output: .9,
|
|
335
|
+
cacheRead: .0038
|
|
336
|
+
},
|
|
337
|
+
pricingCNY: {
|
|
338
|
+
input: .45,
|
|
339
|
+
output: .9,
|
|
340
|
+
cacheRead: .0038
|
|
341
|
+
},
|
|
342
|
+
priceCategory: "medium",
|
|
343
|
+
thinkingSchema: "thinkingEnabled",
|
|
344
|
+
maxInputTokens: 934976,
|
|
345
|
+
maxOutputTokens: 65536
|
|
346
|
+
},
|
|
347
|
+
"agnes-2.5-pro": {
|
|
348
|
+
detail: "Paid reasoning model — commercial stable of 2.5 Pro Alpha (advanced coding, scientific reasoning, long context, multimodal). 1M context / 65K max output",
|
|
349
|
+
imageInput: true,
|
|
350
|
+
toolCalling: true,
|
|
351
|
+
pricingUSD: {
|
|
352
|
+
input: .45,
|
|
353
|
+
output: .9,
|
|
354
|
+
cacheRead: .0038
|
|
355
|
+
},
|
|
356
|
+
pricingCNY: {
|
|
357
|
+
input: .45,
|
|
358
|
+
output: .9,
|
|
359
|
+
cacheRead: .0038
|
|
360
|
+
},
|
|
361
|
+
priceCategory: "medium",
|
|
362
|
+
thinkingSchema: "thinkingEnabled",
|
|
363
|
+
maxInputTokens: 934976,
|
|
364
|
+
maxOutputTokens: 65536
|
|
365
|
+
},
|
|
366
|
+
"agnes-2.5-flash": {
|
|
367
|
+
detail: "GA upgrade of Agnes 2.0 Flash — stronger coding, agent workflows, tool calling, image understanding (512K context)",
|
|
368
|
+
imageInput: true,
|
|
369
|
+
toolCalling: true,
|
|
370
|
+
pricingUSD: {
|
|
371
|
+
input: .03,
|
|
372
|
+
output: .15,
|
|
373
|
+
cacheRead: null
|
|
374
|
+
},
|
|
375
|
+
pricingCNY: {
|
|
376
|
+
input: .03,
|
|
377
|
+
output: .15,
|
|
378
|
+
cacheRead: null
|
|
379
|
+
},
|
|
380
|
+
priceCategory: "low",
|
|
381
|
+
thinkingSchema: "thinkingEnabled",
|
|
382
|
+
maxInputTokens: 512e3,
|
|
383
|
+
maxOutputTokens: 65536
|
|
384
|
+
},
|
|
385
|
+
"kimi-k3": {
|
|
386
|
+
detail: "2.8万亿参数旗舰开源模型,1M 上下文",
|
|
387
|
+
imageInput: true,
|
|
388
|
+
toolCalling: true,
|
|
389
|
+
pricingUSD: {
|
|
390
|
+
input: 2.8,
|
|
391
|
+
output: 14,
|
|
392
|
+
cacheRead: .28
|
|
393
|
+
},
|
|
394
|
+
pricingCNY: {
|
|
395
|
+
input: 20,
|
|
396
|
+
output: 100,
|
|
397
|
+
cacheRead: 2
|
|
398
|
+
},
|
|
399
|
+
priceCategory: "high",
|
|
400
|
+
thinkingSchema: "reasoningEffort",
|
|
401
|
+
maxInputTokens: 920576,
|
|
402
|
+
maxOutputTokens: 128e3
|
|
403
|
+
},
|
|
404
|
+
"kimi-k2.7-code": {
|
|
405
|
+
detail: "Coding 专用,支持图片+视频输入,工具调用,始终开启思考",
|
|
406
|
+
imageInput: true,
|
|
407
|
+
toolCalling: true,
|
|
408
|
+
pricingUSD: {
|
|
409
|
+
input: .91,
|
|
410
|
+
output: 3.78,
|
|
411
|
+
cacheRead: .182
|
|
412
|
+
},
|
|
413
|
+
pricingCNY: {
|
|
414
|
+
input: 6.5,
|
|
415
|
+
output: 27,
|
|
416
|
+
cacheRead: 1.3
|
|
417
|
+
},
|
|
418
|
+
priceCategory: "medium",
|
|
419
|
+
maxInputTokens: 229376,
|
|
420
|
+
maxOutputTokens: 32768
|
|
421
|
+
},
|
|
422
|
+
"kimi-k2.7-code-highspeed": {
|
|
423
|
+
detail: "K2.7 Code 高速版(双倍价格),与 K2.7 Code 同一模型",
|
|
424
|
+
imageInput: true,
|
|
425
|
+
toolCalling: true,
|
|
426
|
+
pricingUSD: {
|
|
427
|
+
input: 1.82,
|
|
428
|
+
output: 7.57,
|
|
429
|
+
cacheRead: .364
|
|
430
|
+
},
|
|
431
|
+
pricingCNY: {
|
|
432
|
+
input: 13,
|
|
433
|
+
output: 54,
|
|
434
|
+
cacheRead: 2.6
|
|
435
|
+
},
|
|
436
|
+
priceCategory: "high",
|
|
437
|
+
maxInputTokens: 229376,
|
|
438
|
+
maxOutputTokens: 32768
|
|
439
|
+
},
|
|
440
|
+
"kimi-k2.6": {
|
|
441
|
+
detail: "K2.6 — 支持图片+视频、工具调用,思考可开关",
|
|
442
|
+
imageInput: true,
|
|
443
|
+
toolCalling: true,
|
|
444
|
+
pricingUSD: {
|
|
445
|
+
input: .91,
|
|
446
|
+
output: 3.78,
|
|
447
|
+
cacheRead: .154
|
|
448
|
+
},
|
|
449
|
+
pricingCNY: {
|
|
450
|
+
input: 6.5,
|
|
451
|
+
output: 27,
|
|
452
|
+
cacheRead: 1.1
|
|
453
|
+
},
|
|
454
|
+
priceCategory: "medium",
|
|
455
|
+
thinkingSchema: "thinkingEnabled",
|
|
456
|
+
maxInputTokens: 229376,
|
|
457
|
+
maxOutputTokens: 32768
|
|
458
|
+
},
|
|
459
|
+
"glm-5.2": {
|
|
460
|
+
detail: "GLM-5.2 — 1M 上下文,最大输出 128K(单档 pricing)",
|
|
461
|
+
imageInput: false,
|
|
462
|
+
toolCalling: true,
|
|
463
|
+
thinkingSchema: "reasoningEffort",
|
|
464
|
+
supportsReasoningEffort: true,
|
|
465
|
+
pricingUSD: {
|
|
466
|
+
input: 1.12,
|
|
467
|
+
output: 3.92,
|
|
468
|
+
cacheRead: .28
|
|
469
|
+
},
|
|
470
|
+
pricingCNY: {
|
|
471
|
+
input: 8,
|
|
472
|
+
output: 28,
|
|
473
|
+
cacheRead: 2
|
|
474
|
+
},
|
|
475
|
+
priceCategory: "high",
|
|
476
|
+
maxInputTokens: 1e6,
|
|
477
|
+
maxOutputTokens: 128e3
|
|
478
|
+
},
|
|
479
|
+
"glm-5.3": {
|
|
480
|
+
detail: "GLM-5.3 — 1M 上下文,后训练增强代码 / 网络安全",
|
|
481
|
+
imageInput: false,
|
|
482
|
+
toolCalling: true,
|
|
483
|
+
thinkingSchema: "reasoningEffort",
|
|
484
|
+
supportsReasoningEffort: true,
|
|
485
|
+
pricingUSD: {
|
|
486
|
+
input: 1.4,
|
|
487
|
+
output: 4.4,
|
|
488
|
+
cacheRead: .26
|
|
489
|
+
},
|
|
490
|
+
pricingCNY: {
|
|
491
|
+
input: 8,
|
|
492
|
+
output: 28,
|
|
493
|
+
cacheRead: 2
|
|
494
|
+
},
|
|
495
|
+
priceCategory: "high",
|
|
496
|
+
maxInputTokens: 1e6,
|
|
497
|
+
maxOutputTokens: 128e3
|
|
498
|
+
},
|
|
499
|
+
"glm-5.3-flash": {
|
|
500
|
+
detail: "GLM-5.3 Flash — 320B/18B 激活,稀疏+线性注意力混合架构,原生多模态视觉 Coding,1M 上下文",
|
|
501
|
+
imageInput: true,
|
|
502
|
+
toolCalling: true,
|
|
503
|
+
thinkingSchema: "reasoningEffort",
|
|
504
|
+
supportsReasoningEffort: true,
|
|
505
|
+
pricingUSD: {
|
|
506
|
+
input: .057,
|
|
507
|
+
output: .2,
|
|
508
|
+
cacheRead: .0164
|
|
509
|
+
},
|
|
510
|
+
pricingCNY: {
|
|
511
|
+
input: .4,
|
|
512
|
+
output: 1.4,
|
|
513
|
+
cacheRead: .115
|
|
514
|
+
},
|
|
515
|
+
priceCategory: "low",
|
|
516
|
+
maxInputTokens: 1e6,
|
|
517
|
+
maxOutputTokens: 128e3
|
|
518
|
+
},
|
|
519
|
+
"glm-5.1-highspeed": {
|
|
520
|
+
detail: "GLM-5.1 HighSpeed — 400 TPS 高吞吐生产变体([0, 32K) tier 镜像 GLM-5.1)",
|
|
521
|
+
imageInput: false,
|
|
522
|
+
toolCalling: true,
|
|
523
|
+
thinkingSchema: "reasoningEffort",
|
|
524
|
+
pricingUSD: {
|
|
525
|
+
input: .84,
|
|
526
|
+
output: 3.36,
|
|
527
|
+
cacheRead: .182
|
|
528
|
+
},
|
|
529
|
+
pricingCNY: {
|
|
530
|
+
input: 6,
|
|
531
|
+
output: 24,
|
|
532
|
+
cacheRead: 1.3
|
|
533
|
+
},
|
|
534
|
+
priceCategory: "medium",
|
|
535
|
+
maxInputTokens: 2e5,
|
|
536
|
+
maxOutputTokens: 128e3
|
|
537
|
+
},
|
|
538
|
+
"glm-4.7-flash": {
|
|
539
|
+
detail: "GLM-4.7 Flash — 完全免费(200K 上下文)",
|
|
540
|
+
imageInput: false,
|
|
541
|
+
toolCalling: true,
|
|
542
|
+
thinkingSchema: "reasoningEffort",
|
|
543
|
+
pricingUSD: {
|
|
544
|
+
input: 0,
|
|
545
|
+
output: 0,
|
|
546
|
+
cacheRead: 0
|
|
547
|
+
},
|
|
548
|
+
pricingCNY: {
|
|
549
|
+
input: 0,
|
|
550
|
+
output: 0,
|
|
551
|
+
cacheRead: 0
|
|
552
|
+
},
|
|
553
|
+
priceCategory: "low",
|
|
554
|
+
maxInputTokens: 2e5,
|
|
555
|
+
maxOutputTokens: 128e3
|
|
556
|
+
},
|
|
557
|
+
"glm-4.7": {
|
|
558
|
+
detail: "GLM-4.7 — 200K 上下文,工具调用(3-tier pricing)",
|
|
559
|
+
imageInput: false,
|
|
560
|
+
toolCalling: true,
|
|
561
|
+
thinkingSchema: "reasoningEffort",
|
|
562
|
+
pricingUSD: {
|
|
563
|
+
input: .28,
|
|
564
|
+
output: 1.12,
|
|
565
|
+
cacheRead: .056
|
|
566
|
+
},
|
|
567
|
+
pricingCNY: {
|
|
568
|
+
input: 2,
|
|
569
|
+
output: 8,
|
|
570
|
+
cacheRead: .4
|
|
571
|
+
},
|
|
572
|
+
priceCategory: "medium",
|
|
573
|
+
maxInputTokens: 2e5,
|
|
574
|
+
maxOutputTokens: 128e3
|
|
575
|
+
},
|
|
576
|
+
"glm-5.1": {
|
|
577
|
+
detail: "GLM-5.1 — 200K 上下文,最大输出 128K(2-tier pricing)",
|
|
578
|
+
imageInput: false,
|
|
579
|
+
toolCalling: true,
|
|
580
|
+
thinkingSchema: "reasoningEffort",
|
|
581
|
+
pricingUSD: {
|
|
582
|
+
input: .84,
|
|
583
|
+
output: 3.36,
|
|
584
|
+
cacheRead: .182
|
|
585
|
+
},
|
|
586
|
+
pricingCNY: {
|
|
587
|
+
input: 6,
|
|
588
|
+
output: 24,
|
|
589
|
+
cacheRead: 1.3
|
|
590
|
+
},
|
|
591
|
+
priceCategory: "high",
|
|
592
|
+
maxInputTokens: 2e5,
|
|
593
|
+
maxOutputTokens: 128e3
|
|
594
|
+
},
|
|
595
|
+
"glm-5": {
|
|
596
|
+
detail: "GLM-5 — 200K 上下文,Agentic 工具调用,最大输出 128K(2-tier pricing)",
|
|
597
|
+
imageInput: false,
|
|
598
|
+
toolCalling: true,
|
|
599
|
+
thinkingSchema: "reasoningEffort",
|
|
600
|
+
pricingUSD: {
|
|
601
|
+
input: .56,
|
|
602
|
+
output: 2.52,
|
|
603
|
+
cacheRead: .14
|
|
604
|
+
},
|
|
605
|
+
pricingCNY: {
|
|
606
|
+
input: 4,
|
|
607
|
+
output: 18,
|
|
608
|
+
cacheRead: 1
|
|
609
|
+
},
|
|
610
|
+
priceCategory: "high",
|
|
611
|
+
maxInputTokens: 2e5,
|
|
612
|
+
maxOutputTokens: 128e3
|
|
613
|
+
},
|
|
614
|
+
"glm-5-turbo": {
|
|
615
|
+
detail: "GLM-5 Turbo — 200K 上下文,最大输出 128K(2-tier pricing)",
|
|
616
|
+
imageInput: false,
|
|
617
|
+
toolCalling: true,
|
|
618
|
+
thinkingSchema: "reasoningEffort",
|
|
619
|
+
pricingUSD: {
|
|
620
|
+
input: .7,
|
|
621
|
+
output: 3.08,
|
|
622
|
+
cacheRead: .168
|
|
623
|
+
},
|
|
624
|
+
pricingCNY: {
|
|
625
|
+
input: 5,
|
|
626
|
+
output: 22,
|
|
627
|
+
cacheRead: 1.2
|
|
628
|
+
},
|
|
629
|
+
priceCategory: "medium",
|
|
630
|
+
maxInputTokens: 2e5,
|
|
631
|
+
maxOutputTokens: 128e3
|
|
632
|
+
},
|
|
633
|
+
"glm-4.7-flashx": {
|
|
634
|
+
detail: "GLM-4.7 FlashX — 快速版",
|
|
635
|
+
imageInput: false,
|
|
636
|
+
toolCalling: false,
|
|
637
|
+
thinkingSchema: "reasoningEffort",
|
|
638
|
+
pricingUSD: {
|
|
639
|
+
input: .07,
|
|
640
|
+
output: .42,
|
|
641
|
+
cacheRead: .014
|
|
642
|
+
},
|
|
643
|
+
pricingCNY: {
|
|
644
|
+
input: .5,
|
|
645
|
+
output: 3,
|
|
646
|
+
cacheRead: .1
|
|
647
|
+
},
|
|
648
|
+
priceCategory: "low",
|
|
649
|
+
maxInputTokens: 2e5,
|
|
650
|
+
maxOutputTokens: 128e3
|
|
651
|
+
},
|
|
652
|
+
"glm-4.6": {
|
|
653
|
+
detail: "GLM-4.6 — 工具调用",
|
|
654
|
+
imageInput: false,
|
|
655
|
+
toolCalling: true,
|
|
656
|
+
thinkingSchema: "reasoningEffort",
|
|
657
|
+
pricingUSD: {
|
|
658
|
+
input: .6,
|
|
659
|
+
output: 2.2,
|
|
660
|
+
cacheRead: .11
|
|
661
|
+
},
|
|
662
|
+
pricingCNY: {
|
|
663
|
+
input: 4.3,
|
|
664
|
+
output: 15.7,
|
|
665
|
+
cacheRead: .79
|
|
666
|
+
},
|
|
667
|
+
priceCategory: "medium",
|
|
668
|
+
maxInputTokens: 2e5,
|
|
669
|
+
maxOutputTokens: 128e3
|
|
670
|
+
},
|
|
671
|
+
"glm-4.5": {
|
|
672
|
+
detail: "GLM-4.5 — 工具调用(裸名;Zhipu /v1/models 暴露的 legacy alias)",
|
|
673
|
+
imageInput: false,
|
|
674
|
+
toolCalling: true,
|
|
675
|
+
thinkingSchema: "reasoningEffort",
|
|
676
|
+
pricingUSD: {
|
|
677
|
+
input: 0,
|
|
678
|
+
output: 0,
|
|
679
|
+
cacheRead: null
|
|
680
|
+
},
|
|
681
|
+
pricingCNY: {
|
|
682
|
+
input: 0,
|
|
683
|
+
output: 0,
|
|
684
|
+
cacheRead: null
|
|
685
|
+
},
|
|
686
|
+
priceCategory: "low",
|
|
687
|
+
maxInputTokens: 128e3,
|
|
688
|
+
maxOutputTokens: 96e3
|
|
689
|
+
},
|
|
690
|
+
"glm-4.5-air": {
|
|
691
|
+
detail: "GLM-4.5 Air — 工具调用(3-tier pricing)",
|
|
692
|
+
imageInput: false,
|
|
693
|
+
toolCalling: true,
|
|
694
|
+
thinkingSchema: "reasoningEffort",
|
|
695
|
+
pricingUSD: {
|
|
696
|
+
input: .112,
|
|
697
|
+
output: .28,
|
|
698
|
+
cacheRead: .0224
|
|
699
|
+
},
|
|
700
|
+
pricingCNY: {
|
|
701
|
+
input: .8,
|
|
702
|
+
output: 2,
|
|
703
|
+
cacheRead: .16
|
|
704
|
+
},
|
|
705
|
+
priceCategory: "low",
|
|
706
|
+
maxInputTokens: 128e3,
|
|
707
|
+
maxOutputTokens: 96e3
|
|
708
|
+
},
|
|
709
|
+
"glm-4.5-airx": {
|
|
710
|
+
detail: "GLM-4.5 AirX — 快速版(¥10/M 单档)",
|
|
711
|
+
imageInput: false,
|
|
712
|
+
toolCalling: false,
|
|
713
|
+
thinkingSchema: "reasoningEffort",
|
|
714
|
+
pricingUSD: {
|
|
715
|
+
input: 1.4,
|
|
716
|
+
output: 1.4,
|
|
717
|
+
cacheRead: null
|
|
718
|
+
},
|
|
719
|
+
pricingCNY: {
|
|
720
|
+
input: 10,
|
|
721
|
+
output: 10,
|
|
722
|
+
cacheRead: null
|
|
723
|
+
},
|
|
724
|
+
priceCategory: "low",
|
|
725
|
+
maxInputTokens: 8192,
|
|
726
|
+
maxOutputTokens: 96e3
|
|
727
|
+
},
|
|
728
|
+
"glm-4-long": {
|
|
729
|
+
detail: "GLM-4 Long — 1M 上下文,长文档处理",
|
|
730
|
+
imageInput: false,
|
|
731
|
+
toolCalling: false,
|
|
732
|
+
pricingUSD: {
|
|
733
|
+
input: .14,
|
|
734
|
+
output: .14,
|
|
735
|
+
cacheRead: null
|
|
736
|
+
},
|
|
737
|
+
pricingCNY: {
|
|
738
|
+
input: 1,
|
|
739
|
+
output: 1,
|
|
740
|
+
cacheRead: null
|
|
741
|
+
},
|
|
742
|
+
priceCategory: "low",
|
|
743
|
+
maxInputTokens: 1e6,
|
|
744
|
+
maxOutputTokens: 4e3
|
|
745
|
+
},
|
|
746
|
+
"glm-4-flashx": {
|
|
747
|
+
detail: "GLM-4 FlashX — 快速版",
|
|
748
|
+
imageInput: false,
|
|
749
|
+
toolCalling: false,
|
|
750
|
+
pricingUSD: {
|
|
751
|
+
input: .014,
|
|
752
|
+
output: .014,
|
|
753
|
+
cacheRead: null
|
|
754
|
+
},
|
|
755
|
+
pricingCNY: {
|
|
756
|
+
input: .1,
|
|
757
|
+
output: .1,
|
|
758
|
+
cacheRead: null
|
|
759
|
+
},
|
|
760
|
+
priceCategory: "low",
|
|
761
|
+
maxInputTokens: 128e3,
|
|
762
|
+
maxOutputTokens: 16e3
|
|
763
|
+
},
|
|
764
|
+
"glm-4-plus": {
|
|
765
|
+
detail: "GLM-4 Plus — Function Call 支持",
|
|
766
|
+
imageInput: false,
|
|
767
|
+
toolCalling: true,
|
|
768
|
+
pricingUSD: {
|
|
769
|
+
input: .7,
|
|
770
|
+
output: .7,
|
|
771
|
+
cacheRead: null
|
|
772
|
+
},
|
|
773
|
+
pricingCNY: {
|
|
774
|
+
input: 5,
|
|
775
|
+
output: 5,
|
|
776
|
+
cacheRead: null
|
|
777
|
+
},
|
|
778
|
+
priceCategory: "low",
|
|
779
|
+
maxInputTokens: 128e3,
|
|
780
|
+
maxOutputTokens: 4e3
|
|
781
|
+
},
|
|
782
|
+
"glm-4.5v": {
|
|
783
|
+
detail: "GLM-4.5V 视觉推理模型 — 图像/视频/文档/GUI(2-tier pricing)",
|
|
784
|
+
imageInput: true,
|
|
785
|
+
toolCalling: true,
|
|
786
|
+
thinkingSchema: "reasoningEffort",
|
|
787
|
+
pricingUSD: {
|
|
788
|
+
input: .28,
|
|
789
|
+
output: .84,
|
|
790
|
+
cacheRead: .056
|
|
791
|
+
},
|
|
792
|
+
pricingCNY: {
|
|
793
|
+
input: 2,
|
|
794
|
+
output: 6,
|
|
795
|
+
cacheRead: .4
|
|
796
|
+
},
|
|
797
|
+
priceCategory: "medium",
|
|
798
|
+
maxInputTokens: 64e3,
|
|
799
|
+
maxOutputTokens: 8192
|
|
800
|
+
},
|
|
801
|
+
"glm-4.6v": {
|
|
802
|
+
detail: "GLM-4.6V 视觉推理模型 — 图像/视频/文档/GUI(2-tier pricing)",
|
|
803
|
+
imageInput: true,
|
|
804
|
+
toolCalling: true,
|
|
805
|
+
thinkingSchema: "reasoningEffort",
|
|
806
|
+
pricingUSD: {
|
|
807
|
+
input: .14,
|
|
808
|
+
output: .42,
|
|
809
|
+
cacheRead: .028
|
|
810
|
+
},
|
|
811
|
+
pricingCNY: {
|
|
812
|
+
input: 1,
|
|
813
|
+
output: 3,
|
|
814
|
+
cacheRead: .2
|
|
815
|
+
},
|
|
816
|
+
priceCategory: "medium",
|
|
817
|
+
maxInputTokens: 128e3,
|
|
818
|
+
maxOutputTokens: 8192
|
|
819
|
+
},
|
|
820
|
+
"glm-4.6v-flash": {
|
|
821
|
+
detail: "GLM-4.6V Flash — 完全免费视觉理解模型",
|
|
822
|
+
imageInput: true,
|
|
823
|
+
toolCalling: true,
|
|
824
|
+
thinkingSchema: "reasoningEffort",
|
|
825
|
+
pricingUSD: {
|
|
826
|
+
input: 0,
|
|
827
|
+
output: 0,
|
|
828
|
+
cacheRead: 0
|
|
829
|
+
},
|
|
830
|
+
pricingCNY: {
|
|
831
|
+
input: 0,
|
|
832
|
+
output: 0,
|
|
833
|
+
cacheRead: 0
|
|
834
|
+
},
|
|
835
|
+
priceCategory: "low",
|
|
836
|
+
maxInputTokens: 98304,
|
|
837
|
+
maxOutputTokens: 32768
|
|
838
|
+
},
|
|
839
|
+
"glm-4.6v-flashx": {
|
|
840
|
+
detail: "GLM-4.6V FlashX — 快速版视觉理解(2-tier pricing)",
|
|
841
|
+
imageInput: true,
|
|
842
|
+
toolCalling: false,
|
|
843
|
+
thinkingSchema: "reasoningEffort",
|
|
844
|
+
pricingUSD: {
|
|
845
|
+
input: .021,
|
|
846
|
+
output: .21,
|
|
847
|
+
cacheRead: .0042
|
|
848
|
+
},
|
|
849
|
+
pricingCNY: {
|
|
850
|
+
input: .15,
|
|
851
|
+
output: 1.5,
|
|
852
|
+
cacheRead: .03
|
|
853
|
+
},
|
|
854
|
+
priceCategory: "low",
|
|
855
|
+
maxInputTokens: 128e3,
|
|
856
|
+
maxOutputTokens: 8192
|
|
857
|
+
},
|
|
858
|
+
"glm-5v-turbo": {
|
|
859
|
+
detail: "GLM-5V Turbo — 多模态 Coding 模型(2-tier pricing)",
|
|
860
|
+
imageInput: true,
|
|
861
|
+
toolCalling: true,
|
|
862
|
+
thinkingSchema: "reasoningEffort",
|
|
863
|
+
pricingUSD: {
|
|
864
|
+
input: .7,
|
|
865
|
+
output: 3.08,
|
|
866
|
+
cacheRead: .168
|
|
867
|
+
},
|
|
868
|
+
pricingCNY: {
|
|
869
|
+
input: 5,
|
|
870
|
+
output: 22,
|
|
871
|
+
cacheRead: 1.2
|
|
872
|
+
},
|
|
873
|
+
priceCategory: "medium",
|
|
874
|
+
maxInputTokens: 2e5,
|
|
875
|
+
maxOutputTokens: 128e3
|
|
876
|
+
},
|
|
877
|
+
"glm-3-turbo": {
|
|
878
|
+
detail: "GLM-3 Turbo — 入门级(¥1/1M tokens)",
|
|
879
|
+
imageInput: false,
|
|
880
|
+
toolCalling: false,
|
|
881
|
+
pricingUSD: {
|
|
882
|
+
input: .14,
|
|
883
|
+
output: .14,
|
|
884
|
+
cacheRead: null
|
|
885
|
+
},
|
|
886
|
+
pricingCNY: {
|
|
887
|
+
input: 1,
|
|
888
|
+
output: 1,
|
|
889
|
+
cacheRead: null
|
|
890
|
+
},
|
|
891
|
+
priceCategory: "low",
|
|
892
|
+
maxInputTokens: 128e3,
|
|
893
|
+
maxOutputTokens: 8192
|
|
894
|
+
},
|
|
895
|
+
"step-3.7-flash": {
|
|
896
|
+
detail: "Step 3.7 Flash — 多模态推理模型(198B/11B MoE)",
|
|
897
|
+
imageInput: true,
|
|
898
|
+
toolCalling: true,
|
|
899
|
+
pricingUSD: {
|
|
900
|
+
input: .2,
|
|
901
|
+
output: 1.15,
|
|
902
|
+
cacheRead: .04
|
|
903
|
+
},
|
|
904
|
+
pricingCNY: {
|
|
905
|
+
input: 1.35,
|
|
906
|
+
output: 8.1,
|
|
907
|
+
cacheRead: .27
|
|
908
|
+
},
|
|
909
|
+
priceCategory: "medium",
|
|
910
|
+
thinkingSchema: "reasoningEffort",
|
|
911
|
+
maxInputTokens: 262144,
|
|
912
|
+
maxOutputTokens: 256e3
|
|
913
|
+
},
|
|
914
|
+
"step-3.5-flash": {
|
|
915
|
+
detail: "Step 3.5 Flash — 推理模型",
|
|
916
|
+
imageInput: false,
|
|
917
|
+
toolCalling: true,
|
|
918
|
+
pricingUSD: {
|
|
919
|
+
input: .1,
|
|
920
|
+
output: .3,
|
|
921
|
+
cacheRead: .02
|
|
922
|
+
},
|
|
923
|
+
pricingCNY: {
|
|
924
|
+
input: .7,
|
|
925
|
+
output: 2.1,
|
|
926
|
+
cacheRead: .14
|
|
927
|
+
},
|
|
928
|
+
priceCategory: "low",
|
|
929
|
+
thinkingSchema: "reasoningEffort",
|
|
930
|
+
maxInputTokens: 256e3,
|
|
931
|
+
maxOutputTokens: 256e3
|
|
932
|
+
},
|
|
933
|
+
"step-1o-turbo-vision": {
|
|
934
|
+
detail: "Step 1o Turbo Vision — 视觉模型",
|
|
935
|
+
imageInput: true,
|
|
936
|
+
toolCalling: false,
|
|
937
|
+
pricingUSD: {
|
|
938
|
+
input: .357,
|
|
939
|
+
output: 1.143,
|
|
940
|
+
cacheRead: .071
|
|
941
|
+
},
|
|
942
|
+
pricingCNY: {
|
|
943
|
+
input: 2.5,
|
|
944
|
+
output: 8,
|
|
945
|
+
cacheRead: .5
|
|
946
|
+
},
|
|
947
|
+
priceCategory: "low",
|
|
948
|
+
maxInputTokens: 32768,
|
|
949
|
+
maxOutputTokens: 32768
|
|
950
|
+
},
|
|
951
|
+
"openrouter/auto": {
|
|
952
|
+
detail: "OpenRouter Auto — 自动路由到平台最佳模型",
|
|
953
|
+
imageInput: true,
|
|
954
|
+
toolCalling: true,
|
|
955
|
+
pricingUSD: {
|
|
956
|
+
input: 0,
|
|
957
|
+
output: 0,
|
|
958
|
+
cacheRead: null
|
|
959
|
+
},
|
|
960
|
+
pricingCNY: {
|
|
961
|
+
input: 0,
|
|
962
|
+
output: 0,
|
|
963
|
+
cacheRead: null
|
|
964
|
+
},
|
|
965
|
+
priceCategory: "medium",
|
|
966
|
+
maxInputTokens: 0,
|
|
967
|
+
maxOutputTokens: 0
|
|
968
|
+
},
|
|
969
|
+
"novita-ai/novita-3.5-flash": {
|
|
970
|
+
detail: "Novita 3.5 Flash — Novita 平台热门模型(占位,按实际选择调整)",
|
|
971
|
+
imageInput: true,
|
|
972
|
+
toolCalling: true,
|
|
973
|
+
pricingUSD: {
|
|
974
|
+
input: .1,
|
|
975
|
+
output: .4,
|
|
976
|
+
cacheRead: null
|
|
977
|
+
},
|
|
978
|
+
pricingCNY: {
|
|
979
|
+
input: .1,
|
|
980
|
+
output: .4,
|
|
981
|
+
cacheRead: null
|
|
982
|
+
},
|
|
983
|
+
priceCategory: "low",
|
|
984
|
+
maxInputTokens: 32768,
|
|
985
|
+
maxOutputTokens: 32768
|
|
986
|
+
},
|
|
987
|
+
"MiniMax-M2.5": {
|
|
988
|
+
detail: "MiniMax M2.5 — 229B MoE, SOTA 编程 / Agent / 办公生产力(192K 上下文)",
|
|
989
|
+
imageInput: false,
|
|
990
|
+
toolCalling: true,
|
|
991
|
+
pricingUSD: {
|
|
992
|
+
input: .3,
|
|
993
|
+
output: 1.2,
|
|
994
|
+
cacheRead: .03
|
|
995
|
+
},
|
|
996
|
+
pricingCNY: {
|
|
997
|
+
input: 2.1,
|
|
998
|
+
output: 8.4,
|
|
999
|
+
cacheRead: .21
|
|
1000
|
+
},
|
|
1001
|
+
priceCategory: "medium",
|
|
1002
|
+
maxInputTokens: 192e3,
|
|
1003
|
+
maxOutputTokens: 16384
|
|
1004
|
+
},
|
|
1005
|
+
"Qwen3.6-35B-A3B": {
|
|
1006
|
+
detail: "Qwen3.6-35B-A3B — 35B MoE (3B 激活),思考/非思考双模,256K 上下文",
|
|
1007
|
+
imageInput: true,
|
|
1008
|
+
toolCalling: true,
|
|
1009
|
+
pricingUSD: {
|
|
1010
|
+
input: .26,
|
|
1011
|
+
output: 1.54,
|
|
1012
|
+
cacheRead: null
|
|
1013
|
+
},
|
|
1014
|
+
pricingCNY: {
|
|
1015
|
+
input: 1.8,
|
|
1016
|
+
output: 10.8,
|
|
1017
|
+
cacheRead: null
|
|
1018
|
+
},
|
|
1019
|
+
priceCategory: "low",
|
|
1020
|
+
maxInputTokens: 256e3,
|
|
1021
|
+
maxOutputTokens: 32768
|
|
1022
|
+
}
|
|
1128
1023
|
};
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1024
|
+
/**
|
|
1025
|
+
* Map of namespaced model ids (used by aggregator platforms like
|
|
1026
|
+
* SiliconFlow and Novita) to their primary entry. **Exported** so
|
|
1027
|
+
* downstream code (e.g. the "Pick from preset" dropdown in
|
|
1028
|
+
* ProvidersTab) can identify which entries in `MODEL_METADATA` are
|
|
1029
|
+
* aliases vs primary curated ids — listing aliases would
|
|
1030
|
+
* duplicate primaries in the UI.
|
|
1031
|
+
*
|
|
1032
|
+
* **Read-only by design.** Don't mutate; if you need to add a new
|
|
1033
|
+
* aggregator alias, do it here so the merged `MODEL_METADATA`
|
|
1034
|
+
* auto-tracks the primary.
|
|
1035
|
+
*/
|
|
1036
|
+
const NAMESPACE_ALIASES = {
|
|
1037
|
+
"deepseek-ai/DeepSeek-V4-Pro": "deepseek-v4-pro",
|
|
1038
|
+
"deepseek-ai/DeepSeek-V4-Flash": "deepseek-v4-flash",
|
|
1039
|
+
"zai-org/GLM-5.2": "glm-5.2",
|
|
1040
|
+
"Qwen/Qwen3.6-35B-A3B": "Qwen3.6-35B-A3B",
|
|
1041
|
+
"moonshotai/Kimi-K2.7-Code": "kimi-k2.7-code",
|
|
1042
|
+
"deepseek/deepseek-v4-pro": "deepseek-v4-pro",
|
|
1043
|
+
"deepseek/deepseek-v4-flash": "deepseek-v4-flash",
|
|
1044
|
+
"zai/glm-5.2": "glm-5.2",
|
|
1045
|
+
"zai/glm-5.1": "glm-5.1",
|
|
1046
|
+
"moonshotai/kimi-k3": "kimi-k3",
|
|
1047
|
+
"glm-4-flashx-250414": "glm-4-flashx"
|
|
1151
1048
|
};
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
"zai/glm-5.1": "Novita (aggregator)",
|
|
1164
|
-
"moonshotai/kimi-k3": "Novita (aggregator)"
|
|
1049
|
+
const NAMESPACE_ALIAS_FAMILY = {
|
|
1050
|
+
"deepseek-ai/DeepSeek-V4-Pro": "SiliconFlow (aggregator)",
|
|
1051
|
+
"deepseek-ai/DeepSeek-V4-Flash": "SiliconFlow (aggregator)",
|
|
1052
|
+
"zai-org/GLM-5.2": "SiliconFlow (aggregator)",
|
|
1053
|
+
"Qwen/Qwen3.6-35B-A3B": "SiliconFlow (aggregator)",
|
|
1054
|
+
"moonshotai/Kimi-K2.7-Code": "SiliconFlow (aggregator)",
|
|
1055
|
+
"deepseek/deepseek-v4-pro": "Novita (aggregator)",
|
|
1056
|
+
"deepseek/deepseek-v4-flash": "Novita (aggregator)",
|
|
1057
|
+
"zai/glm-5.2": "Novita (aggregator)",
|
|
1058
|
+
"zai/glm-5.1": "Novita (aggregator)",
|
|
1059
|
+
"moonshotai/kimi-k3": "Novita (aggregator)"
|
|
1165
1060
|
};
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
merged[alias] = targetEntry;
|
|
1174
|
-
}
|
|
1175
|
-
}
|
|
1176
|
-
return Object.freeze(merged);
|
|
1061
|
+
const MODEL_METADATA = (() => {
|
|
1062
|
+
const merged = { ...PRIMARY_METADATA };
|
|
1063
|
+
for (const [alias, target] of Object.entries(NAMESPACE_ALIASES)) {
|
|
1064
|
+
const targetEntry = PRIMARY_METADATA[target];
|
|
1065
|
+
if (targetEntry !== void 0) merged[alias] = targetEntry;
|
|
1066
|
+
}
|
|
1067
|
+
return Object.freeze(merged);
|
|
1177
1068
|
})();
|
|
1069
|
+
/**
|
|
1070
|
+
* Look up curated metadata for a model by its un-qualified id
|
|
1071
|
+
* (the part after the last `::` in a qualified id, or the raw id
|
|
1072
|
+
* for a non-namespaced provider). Returns `undefined` for
|
|
1073
|
+
* user-added / `-compatible` models that have no curated entry;
|
|
1074
|
+
* callers should then fall back to whatever the user typed into
|
|
1075
|
+
* the ProvidersTab.
|
|
1076
|
+
*/
|
|
1178
1077
|
function lookupModelMetadata(modelId) {
|
|
1179
|
-
|
|
1078
|
+
return MODEL_METADATA[modelId];
|
|
1180
1079
|
}
|
|
1080
|
+
/**
|
|
1081
|
+
* Resolve the currency for a baseUrl. Strict hostname match —
|
|
1082
|
+
* `api.minimaxi.com` and `api.minimaxi.cn` map to CNY (the China
|
|
1083
|
+
* platform), `api.minimax.io` maps to USD (the global platform),
|
|
1084
|
+
* everything else falls back to USD (the global default). The
|
|
1085
|
+
* match is exact-host so a typo in the hostname never silently
|
|
1086
|
+
* flips currency.
|
|
1087
|
+
*
|
|
1088
|
+
* `api.deepseek.com` maps to CNY (2026-08-20 product decision —
|
|
1089
|
+
* user request). History: the 8/18 draft put it in CNY by mistake
|
|
1090
|
+
* (per the $ block on the English pricing page), 8/19 moved it to
|
|
1091
|
+
* the USD catch-all, and 8/20 moved it BACK to CNY deliberately —
|
|
1092
|
+
* our user base bills in ¥ on this endpoint (the official zh-cn
|
|
1093
|
+
* page publishes the ¥ table for it), and the picker should show
|
|
1094
|
+
* the price those users actually pay. The DeepSeek entries pin the
|
|
1095
|
+
* PEAK tier (conservative worst-case; off-peak is exactly half).
|
|
1096
|
+
*/
|
|
1181
1097
|
function currencyForBaseUrl(baseUrl) {
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
// don't surface it in the baseUrl dropdown, but a user
|
|
1189
|
-
// may paste it from a saved settings.json, so the
|
|
1190
|
-
// currency has to match (CNY, same as the new host).
|
|
1191
|
-
hostname === "dev.bigmodel.cn" || // DeepSeek — 2026-08-20 product decision (user request):
|
|
1192
|
-
// surface the domestic ¥ prices. The `.com` endpoint
|
|
1193
|
-
// serves both regions, but our user base bills in CNY on
|
|
1194
|
-
// it (the official zh-cn pricing page publishes the ¥
|
|
1195
|
-
// table for this endpoint), so the picker now shows the
|
|
1196
|
-
// CNY block. This REVERSES the 2026-08-19 mapping that
|
|
1197
|
-
// left it in the USD catch-all ("$ block corresponds to
|
|
1198
|
-
// the .com endpoint") — with a CNY-billed user base, the
|
|
1199
|
-
// ¥ block is the relevant one. The pricingCNY block in
|
|
1200
|
-
// MODEL_METADATA pins the PEAK tier for these entries
|
|
1201
|
-
// (conservative worst-case; see the deepseek-v4-flash
|
|
1202
|
-
// comment).
|
|
1203
|
-
hostname === "api.deepseek.com" || // SenseNova (SenseTime) token-plan gateway — CN-domiciled,
|
|
1204
|
-
// quota-billed (all-zero published pricing, so the currency is
|
|
1205
|
-
// cosmetic today, but pinning it keeps a future per-token ¥ table
|
|
1206
|
-
// from silently rendering as USD).
|
|
1207
|
-
hostname === "token.sensenova.cn") {
|
|
1208
|
-
return "CNY";
|
|
1209
|
-
}
|
|
1210
|
-
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" || // Z.ai / Zhipu international. Billed in USD per the
|
|
1211
|
-
// official `bigmodel.cn/pricing` page (the CNY-billed
|
|
1212
|
-
// list is the China-domiciled `open.bigmodel.cn` only;
|
|
1213
|
-
// the international `api.z.ai` is USD regardless of
|
|
1214
|
-
// which apiMode / protocol path the user picked). The
|
|
1215
|
-
// GLM-for-copilot reference uses the same split
|
|
1216
|
-
// (`docs/references/GLM-for-copilot-main/src/endpoint.ts:160-173`).
|
|
1217
|
-
// Without this explicit entry, `api.z.ai` would still
|
|
1218
|
-
// resolve to USD via the catch-all below — adding it
|
|
1219
|
-
// here makes the intent grep-able and pins the host
|
|
1220
|
-
// list against accidental removal.
|
|
1221
|
-
hostname === "api.z.ai") {
|
|
1222
|
-
return "USD";
|
|
1223
|
-
}
|
|
1224
|
-
} catch {
|
|
1225
|
-
}
|
|
1226
|
-
return "USD";
|
|
1098
|
+
try {
|
|
1099
|
+
const hostname = new URL(baseUrl).hostname.toLowerCase();
|
|
1100
|
+
if (hostname === "api.minimaxi.com" || hostname === "api.minimaxi.cn" || hostname === "api.moonshot.cn" || hostname === "open.bigmodel.cn" || hostname === "dev.bigmodel.cn" || hostname === "api.deepseek.com" || hostname === "token.sensenova.cn") return "CNY";
|
|
1101
|
+
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" || hostname === "api.z.ai") return "USD";
|
|
1102
|
+
} catch {}
|
|
1103
|
+
return "USD";
|
|
1227
1104
|
}
|
|
1228
|
-
|
|
1229
|
-
|
|
1105
|
+
//#endregion
|
|
1106
|
+
//#region src/ai/providers.presets.ts
|
|
1107
|
+
/**
|
|
1108
|
+
* Default configuration snippets shipped with named vendor types.
|
|
1109
|
+
*
|
|
1110
|
+
* When the user picks `minimax` / `deepseek` / `agnes` in the
|
|
1111
|
+
* ProvidersTab, the form pre-fills `displayName` + `baseUrl` + a
|
|
1112
|
+
* starter `models` list from this table. The user still has to
|
|
1113
|
+
* paste their own `apiKey` (always empty by default — secrets
|
|
1114
|
+
* never ship with the extension).
|
|
1115
|
+
*
|
|
1116
|
+
* Keep `baseUrl` here in sync with the upstream vendor docs.
|
|
1117
|
+
*
|
|
1118
|
+
* Adapter routing (see `apps/extension/src/services/providers/adapters/index.ts`):
|
|
1119
|
+
* - minimax: Anthropic-compatible at `https://api.minimaxi.com/anthropic`
|
|
1120
|
+
* → routed to AnthropicAdapter
|
|
1121
|
+
* - deepseek: OpenAI-compatible at `https://api.deepseek.com/v1`
|
|
1122
|
+
* → routed to OpenAIAdapter (the /anthropic surface v3 exposed
|
|
1123
|
+
* is no longer documented for v4)
|
|
1124
|
+
* - agnes: OpenAI-compatible at `https://apihub.agnes-ai.com/v1`
|
|
1125
|
+
* → routed to OpenAIAdapter
|
|
1126
|
+
* - medalsoft: OpenAI-compatible internal gateway at
|
|
1127
|
+
* `https://nexus.servicemecloud.com/v1` → routed to OpenAIAdapter
|
|
1128
|
+
* (empty starter model list — populate via "Fetch from API")
|
|
1129
|
+
*
|
|
1130
|
+
* Earlier iterations of the minimax default 401'd on the team; do
|
|
1131
|
+
* NOT swap minimax back to one of these without checking with the
|
|
1132
|
+
* user first:
|
|
1133
|
+
* - `https://agent.minimaxi.com/mavis/api/v1/llm/v1` (opencode.json
|
|
1134
|
+
* proxy URL — AnthropicAdapter would double-prefix /v1 to it)
|
|
1135
|
+
* - `https://agent.minimaxi.com/v1` (OpenAI-compat
|
|
1136
|
+
* variant — user tried this in commit 8bd867c then asked to
|
|
1137
|
+
* revert in 71faf6e-era because it 401'd as well)
|
|
1138
|
+
*/
|
|
1139
|
+
/**
|
|
1140
|
+
* Build a `ProviderModel` preset entry by joining the static
|
|
1141
|
+
* context-window numbers (token caps are user-visible and depend
|
|
1142
|
+
* on the wire-protocol spec, not the curated metadata) with the
|
|
1143
|
+
* curated metadata in `MODEL_METADATA` (detail / capabilities /
|
|
1144
|
+
* pricing / thinking). The user can override any field in the
|
|
1145
|
+
* ProvidersTab form; the curated values are just the starter
|
|
1146
|
+
* defaults so the picker shows the cost column + thinking
|
|
1147
|
+
* dropdown out of the box for the named vendors.
|
|
1148
|
+
*
|
|
1149
|
+
* Pricing block is selected to match `baseUrl`'s currency (via
|
|
1150
|
+
* `currencyForBaseUrl`): CNY for `open.bigmodel.cn` /
|
|
1151
|
+
* `api.moonshot.cn` / etc., USD for `api.z.ai` / `openrouter.ai` /
|
|
1152
|
+
* etc. Falls back to USD for unrecognised hosts. The caller is
|
|
1153
|
+
* expected to pass a real `baseUrl` — when adding a brand-new
|
|
1154
|
+
* provider the host is already known (came from the
|
|
1155
|
+
* `PROVIDER_BASE_URL_PRESETS` dropdown or user-typed); the empty
|
|
1156
|
+
* `""` default picks USD as a safe fallback.
|
|
1157
|
+
*/
|
|
1230
1158
|
function buildPresetModel(id, displayName, baseUrl) {
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
pricing: meta[`pricing${currency}`],
|
|
1249
|
-
priceCategory: meta.priceCategory,
|
|
1250
|
-
thinkingSchema: meta.thinkingSchema ?? "none"
|
|
1251
|
-
};
|
|
1159
|
+
const meta = MODEL_METADATA[id];
|
|
1160
|
+
if (!meta) throw new Error(`buildPresetModel: no curated MODEL_METADATA entry for '${id}' — add one before referencing it from BUILTIN_PROVIDER_PRESETS.`);
|
|
1161
|
+
const currency = baseUrl ? currencyForBaseUrl(baseUrl) : "USD";
|
|
1162
|
+
return {
|
|
1163
|
+
id,
|
|
1164
|
+
displayName,
|
|
1165
|
+
maxInputTokens: meta.maxInputTokens,
|
|
1166
|
+
maxOutputTokens: meta.maxOutputTokens,
|
|
1167
|
+
detail: meta.detail,
|
|
1168
|
+
capabilities: {
|
|
1169
|
+
supportsImageToText: meta.imageInput,
|
|
1170
|
+
supportsToolCalling: meta.toolCalling
|
|
1171
|
+
},
|
|
1172
|
+
pricing: meta[`pricing${currency}`],
|
|
1173
|
+
priceCategory: meta.priceCategory,
|
|
1174
|
+
thinkingSchema: meta.thinkingSchema ?? "none"
|
|
1175
|
+
};
|
|
1252
1176
|
}
|
|
1177
|
+
/**
|
|
1178
|
+
* Union a model row returned by the "Fetch from API" flow with its
|
|
1179
|
+
* curated preset metadata, when the id matches an entry in
|
|
1180
|
+
* `MODEL_METADATA`. The API typically returns just `id` (sometimes
|
|
1181
|
+
* `displayName`); the user expects the picker's `detail` / `pricing`
|
|
1182
|
+
* / `capabilities` / `maxInputTokens` columns to be filled in for
|
|
1183
|
+
* any model we curate, not blank.
|
|
1184
|
+
*
|
|
1185
|
+
* Precedence (TDD-pinned 2026-08-19, see
|
|
1186
|
+
* `test/unionProviderModelWithPreset.test.mjs`):
|
|
1187
|
+
*
|
|
1188
|
+
* • **Preset wins** for the curated fields: `detail`,
|
|
1189
|
+
* `capabilities`, `pricing`, `priceCategory`, `thinkingSchema`,
|
|
1190
|
+
* `maxInputTokens`, `maxOutputTokens`. These are the values we
|
|
1191
|
+
* maintain by hand and trust more than what the API publishes,
|
|
1192
|
+
* which is often missing or stale (e.g. the OpenAI `/v1/models`
|
|
1193
|
+
* endpoint does not return pricing or token caps; DeepSeek's
|
|
1194
|
+
* `GET /models` likewise returns id + owned_by only).
|
|
1195
|
+
* • **Fetched wins** for `id` (the API is the source of truth for
|
|
1196
|
+
* what the endpoint actually exposes — a stale preset could list
|
|
1197
|
+
* a model the user no longer has access to).
|
|
1198
|
+
* • **`displayName`** — the 2026-08-19 follow-up: fetched wins
|
|
1199
|
+
* when set, the preset's curated `displayName` (from
|
|
1200
|
+
* `BUILTIN_PROVIDER_PRESETS`, see
|
|
1201
|
+
* `getPresetModelDisplayName`) fills in when fetched omits it
|
|
1202
|
+
* or sends an empty string. Without this fallback, the picker
|
|
1203
|
+
* would render the bare id (`deepseek-v4-flash`) as the model
|
|
1204
|
+
* name right after the fetch result lands — the user picked
|
|
1205
|
+
* "Fetch from API" because they wanted the live catalog, but
|
|
1206
|
+
* the friendly label they would have seen if they'd picked
|
|
1207
|
+
* the vendor from the named-vendor `<select>` should also
|
|
1208
|
+
* surface here. Empty string is treated the same as missing
|
|
1209
|
+
* (a blank label in the picker is strictly worse than the
|
|
1210
|
+
* curated friendly name).
|
|
1211
|
+
* • **No preset match** → returns the fetched model unchanged
|
|
1212
|
+
* (custom / aggregator-only models stay bare; the user fills
|
|
1213
|
+
* in detail / pricing by hand).
|
|
1214
|
+
*
|
|
1215
|
+
* `baseUrl` is consulted to pick the right currency for `pricing`
|
|
1216
|
+
* (CNY for `open.bigmodel.cn` / `api.moonshot.cn` / etc., USD
|
|
1217
|
+
* otherwise — see `currencyForBaseUrl`). Pass the form's current
|
|
1218
|
+
* `editing.baseUrl`; that is the same baseUrl the row was just
|
|
1219
|
+
* fetched from, so the resolved currency matches what the user
|
|
1220
|
+
* will see in the picker. Empty / unset baseUrl → USD fallback.
|
|
1221
|
+
*
|
|
1222
|
+
* Namespaced ids (e.g. `deepseek-ai/DeepSeek-V4-Pro` on
|
|
1223
|
+
* SiliconFlow) resolve transparently — `MODEL_METADATA` aliases
|
|
1224
|
+
* the primary entry's metadata reference under every namespace key,
|
|
1225
|
+
* so `lookupModelMetadata("deepseek-ai/DeepSeek-V4-Pro")` returns
|
|
1226
|
+
* the same object as `lookupModelMetadata("deepseek-v4-pro")`.
|
|
1227
|
+
*/
|
|
1253
1228
|
function unionProviderModelWithPreset(fetched, baseUrl) {
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
pricing: meta[`pricing${currency}`],
|
|
1274
|
-
priceCategory: meta.priceCategory,
|
|
1275
|
-
// thinkingSchema: prefer preset (curated), fall back to
|
|
1276
|
-
// fetched (in case the API publishes a schema the
|
|
1277
|
-
// preset doesn't know about), then "none" to match
|
|
1278
|
-
// `buildPresetModel`'s explicit-none convention so the
|
|
1279
|
-
// picker renders identically for preset vs fetched rows.
|
|
1280
|
-
thinkingSchema: meta.thinkingSchema ?? fetched.thinkingSchema ?? "none",
|
|
1281
|
-
// Token caps: preset is curated; if the preset doesn't
|
|
1282
|
-
// publish a cap, keep whatever fetched supplied (the
|
|
1283
|
-
// user may have entered it by hand earlier).
|
|
1284
|
-
maxInputTokens: meta.maxInputTokens ?? fetched.maxInputTokens,
|
|
1285
|
-
maxOutputTokens: meta.maxOutputTokens ?? fetched.maxOutputTokens
|
|
1286
|
-
};
|
|
1229
|
+
const meta = MODEL_METADATA[fetched.id];
|
|
1230
|
+
if (!meta) return fetched;
|
|
1231
|
+
const currency = baseUrl ? currencyForBaseUrl(baseUrl) : "USD";
|
|
1232
|
+
const presetDisplayName = getPresetModelDisplayName(fetched.id);
|
|
1233
|
+
const displayName = typeof fetched.displayName === "string" && fetched.displayName.length > 0 ? fetched.displayName : presetDisplayName;
|
|
1234
|
+
return {
|
|
1235
|
+
...fetched,
|
|
1236
|
+
displayName,
|
|
1237
|
+
detail: meta.detail,
|
|
1238
|
+
capabilities: {
|
|
1239
|
+
supportsImageToText: meta.imageInput,
|
|
1240
|
+
supportsToolCalling: meta.toolCalling
|
|
1241
|
+
},
|
|
1242
|
+
pricing: meta[`pricing${currency}`],
|
|
1243
|
+
priceCategory: meta.priceCategory,
|
|
1244
|
+
thinkingSchema: meta.thinkingSchema ?? fetched.thinkingSchema ?? "none",
|
|
1245
|
+
maxInputTokens: meta.maxInputTokens ?? fetched.maxInputTokens,
|
|
1246
|
+
maxOutputTokens: meta.maxOutputTokens ?? fetched.maxOutputTokens
|
|
1247
|
+
};
|
|
1287
1248
|
}
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1249
|
+
/**
|
|
1250
|
+
* The list of vendor family groups shown in the preset dropdown.
|
|
1251
|
+
* Order is intentional (most common presets first):
|
|
1252
|
+
* 1. GLM (Zhipu / 智谱) — the user explicitly asked us to
|
|
1253
|
+
* support 4-channel endpoints; GLM has the richest preset
|
|
1254
|
+
* list (15+ models) so it gets the top slot.
|
|
1255
|
+
* 2. DeepSeek / Kimi / StepFun / MiniMax — the other named
|
|
1256
|
+
* vendors with curated presets.
|
|
1257
|
+
* 3. Agnes / Qwen — the smaller curated lists.
|
|
1258
|
+
* 4. Aggregators (SiliconFlow / Novita / OpenRouter) — listed
|
|
1259
|
+
* last because users on aggregators usually type the namespaced
|
|
1260
|
+
* id by hand rather than reach for a curated dropdown.
|
|
1261
|
+
*/
|
|
1262
|
+
const PRESET_MODEL_FAMILIES = [
|
|
1263
|
+
"GLM",
|
|
1264
|
+
"DeepSeek",
|
|
1265
|
+
"Kimi",
|
|
1266
|
+
"StepFun",
|
|
1267
|
+
"MiniMax",
|
|
1268
|
+
"Agnes",
|
|
1269
|
+
"SenseNova",
|
|
1270
|
+
"Qwen",
|
|
1271
|
+
"SiliconFlow (aggregator)",
|
|
1272
|
+
"Novita (aggregator)",
|
|
1273
|
+
"OpenRouter (aggregator)"
|
|
1300
1274
|
];
|
|
1275
|
+
/**
|
|
1276
|
+
* Map a `MODEL_METADATA` id to its vendor family for dropdown
|
|
1277
|
+
* grouping. Pure data — keeps the dropdown order in one place.
|
|
1278
|
+
*
|
|
1279
|
+
* Namespaced alias ids (the SiliconFlow `deepseek-ai/…` /
|
|
1280
|
+
* `zai-org/…` / `Qwen/Qwen*` / `moonshotai/…` cluster, the Novita
|
|
1281
|
+
* `zai/…` / `deepseek/…` cluster, and the historical
|
|
1282
|
+
* `moonshotai/kimi-k3`) consult `NAMESPACE_ALIAS_FAMILY` first so
|
|
1283
|
+
* they land in the matching aggregator group instead of being
|
|
1284
|
+
* filtered out as aliases. The primary entries fall through to the
|
|
1285
|
+
* prefix-based rules below.
|
|
1286
|
+
*/
|
|
1301
1287
|
function vendorFamilyForId(id) {
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1288
|
+
const aliasFamily = NAMESPACE_ALIAS_FAMILY[id];
|
|
1289
|
+
if (aliasFamily) return aliasFamily;
|
|
1290
|
+
if (id.startsWith("glm-")) return "GLM";
|
|
1291
|
+
if (id.startsWith("deepseek-")) return "DeepSeek";
|
|
1292
|
+
if (id.startsWith("kimi-")) return "Kimi";
|
|
1293
|
+
if (id.startsWith("step-")) return "StepFun";
|
|
1294
|
+
if (id.startsWith("MiniMax-")) return "MiniMax";
|
|
1295
|
+
if (id.startsWith("agnes-")) return "Agnes";
|
|
1296
|
+
if (id.startsWith("sensenova-")) return "SenseNova";
|
|
1297
|
+
if (id.startsWith("Qwen")) return "Qwen";
|
|
1298
|
+
if (id.startsWith("openrouter/")) return "OpenRouter (aggregator)";
|
|
1299
|
+
return "Other";
|
|
1314
1300
|
}
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1301
|
+
/**
|
|
1302
|
+
* The "Pick from preset" dropdown options — every curated primary
|
|
1303
|
+
* entry in `MODEL_METADATA` PLUS every alias tagged with an
|
|
1304
|
+
* aggregator family in `NAMESPACE_ALIAS_FAMILY`. Grouped by
|
|
1305
|
+
* vendor family. Sorted alphabetically within each family so the
|
|
1306
|
+
* dropdown order is stable across runs.
|
|
1307
|
+
*
|
|
1308
|
+
* Two alias flavours exist in `NAMESPACE_ALIASES`:
|
|
1309
|
+
* - **Aggregator aliases** (SiliconFlow `deepseek-ai/…`,
|
|
1310
|
+
* Novita `zai/…` / `deepseek/…`, etc.) — KEPT in the list so
|
|
1311
|
+
* the dropdown surfaces the namespaced ids aggregator users
|
|
1312
|
+
* actually need to type. They are routed to the matching
|
|
1313
|
+
* aggregator <optgroup> via `NAMESPACE_ALIAS_FAMILY`.
|
|
1314
|
+
* - **Historical / naming aliases** (e.g. the Zhipu
|
|
1315
|
+
* `glm-4-flashx-250414` rebrand of `glm-4-flashx`) — DROPPED
|
|
1316
|
+
* because they're duplicates of an existing primary entry
|
|
1317
|
+
* that already appears in the dropdown. Users with the
|
|
1318
|
+
* historical id already in their settings.json keep
|
|
1319
|
+
* working at the chat-registration layer
|
|
1320
|
+
* (see `MODEL_METADATA`'s alias merge) — the dropdown just
|
|
1321
|
+
* doesn't surface a redundant second option.
|
|
1322
|
+
*
|
|
1323
|
+
* The filter rule is the inverse of the aggregator tag presence:
|
|
1324
|
+
* any alias with a `NAMESPACE_ALIAS_FAMILY` entry is kept, every
|
|
1325
|
+
* other alias is filtered. The pinning test
|
|
1326
|
+
* `test/listPresetModelGroups.test.mjs` asserts this 1:1 mapping
|
|
1327
|
+
* between the two structures.
|
|
1328
|
+
*
|
|
1329
|
+
* Used by `ProvidersTab.tsx` to render the `<select>` next to the
|
|
1330
|
+
* "+ Add model" button. Selecting an option calls
|
|
1331
|
+
* `buildPresetModel(id, displayName, baseUrl)` and appends the
|
|
1332
|
+
* resulting `ProviderModel` to the editing list. The `displayName`
|
|
1333
|
+
* mirrors the model id verbatim (the canonical form is what users
|
|
1334
|
+
* see in /v1/models, what VSCode's chat picker surfaces, and what
|
|
1335
|
+
* the existing `BUILTIN_PROVIDER_PRESETS` pre-fills); the user can
|
|
1336
|
+
* still rename the field after picking — this is just a starter
|
|
1337
|
+
* label.
|
|
1338
|
+
*/
|
|
1339
|
+
const LISTABLE_PRESET_MODELS = (() => {
|
|
1340
|
+
const aggregatorAliasKeys = new Set(Object.keys(NAMESPACE_ALIAS_FAMILY));
|
|
1341
|
+
return Object.keys(MODEL_METADATA).filter((id) => {
|
|
1342
|
+
if (NAMESPACE_ALIASES[id] === void 0) return true;
|
|
1343
|
+
return aggregatorAliasKeys.has(id);
|
|
1344
|
+
}).sort().map((id) => ({
|
|
1345
|
+
id,
|
|
1346
|
+
displayName: id,
|
|
1347
|
+
vendorFamily: vendorFamilyForId(id)
|
|
1348
|
+
}));
|
|
1326
1349
|
})();
|
|
1327
1350
|
function listPresetModelGroups() {
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1351
|
+
const byFamily = /* @__PURE__ */ new Map();
|
|
1352
|
+
for (const entry of LISTABLE_PRESET_MODELS) {
|
|
1353
|
+
const bucket = byFamily.get(entry.vendorFamily) ?? [];
|
|
1354
|
+
bucket.push(entry);
|
|
1355
|
+
byFamily.set(entry.vendorFamily, bucket);
|
|
1356
|
+
}
|
|
1357
|
+
return PRESET_MODEL_FAMILIES.filter((f) => byFamily.has(f)).map((family) => ({
|
|
1358
|
+
family,
|
|
1359
|
+
entries: byFamily.get(family) ?? []
|
|
1360
|
+
}));
|
|
1338
1361
|
}
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
// GLM-5.1 served via TileRT; still
|
|
1458
|
-
// listed in some third-party mirrors
|
|
1459
|
-
// (Alibaba Cloud DashScope) but not
|
|
1460
|
-
// on Zhipu's own /v1/models.
|
|
1461
|
-
// glm-5v-turbo — multimodal coding base; only
|
|
1462
|
-
// reachable via the dedicated
|
|
1463
|
-
// multimodal endpoint, not
|
|
1464
|
-
// /v1/chat/completions.
|
|
1465
|
-
// glm-4.7-flash — free-tier 4.7 lite; advertised on
|
|
1466
|
-
// the docs pricing page but absent
|
|
1467
|
-
// from /v1/models.
|
|
1468
|
-
// glm-4.7-flashx — quick-response 4.7; same situation
|
|
1469
|
-
// as glm-4.7-flash.
|
|
1470
|
-
// glm-4.5v — multimodal 4.5; only on the
|
|
1471
|
-
// dedicated VLM endpoint.
|
|
1472
|
-
// glm-4.5-airx — quick-response 4.5 Air; not in
|
|
1473
|
-
// /v1/models anymore.
|
|
1474
|
-
// glm-4-long — 1M-context 4-Long; the `/long`
|
|
1475
|
-
// path was retired in 2026 H1.
|
|
1476
|
-
// glm-4-flashx — quick-response 4 FlashX; the
|
|
1477
|
-
// `-250414` dated alias (see
|
|
1478
|
-
// `NAMESPACE_ALIASES` in
|
|
1479
|
-
// `providers.metadata.ts`) is the
|
|
1480
|
-
// only spelling still exposed.
|
|
1481
|
-
//
|
|
1482
|
-
// Note: `glm-4.5` (no suffix) IS in the preset now. It is
|
|
1483
|
-
// NOT listed on the public "模型概览" page but it IS
|
|
1484
|
-
// returned by /v1/models — almost certainly a legacy alias
|
|
1485
|
-
// that routes to one of the suffixed 4.5 variants. The
|
|
1486
|
-
// curated metadata entry marks it as such; users on a
|
|
1487
|
-
// private coding-plan endpoint that distinguishes `glm-4.5`
|
|
1488
|
-
// from `glm-4.5-air` should override the model id in the
|
|
1489
|
-
// ProvidersTab.
|
|
1490
|
-
//
|
|
1491
|
-
// Earlier (also 2026-08-18) trim — `glm-4-plus` and
|
|
1492
|
-
// `glm-3-turbo` were removed from the preset on the same
|
|
1493
|
-
// date. Both are no longer listed in Zhipu's public
|
|
1494
|
-
// "模型一览": `GLM-4-0520` is in the "即将弃用模型" list
|
|
1495
|
-
// and `GLM-3-Turbo` has been retired without a formal
|
|
1496
|
-
// redirect. Their API endpoints may still respond for
|
|
1497
|
-
// legacy accounts (the `glm-4-plus` 429 "余额不足" log we
|
|
1498
|
-
// saw on 2026-08-18 is one such case), but they shouldn't
|
|
1499
|
-
// be the default starter pick for a freshly added Zhipu
|
|
1500
|
-
// provider. Users with a paid legacy plan that still works
|
|
1501
|
-
// can add the id back by hand in the ProvidersTab; the
|
|
1502
|
-
// `MODEL_METADATA` entries are kept so the id is still
|
|
1503
|
-
// resolvable for the curated detail / pricing columns.
|
|
1504
|
-
models: [
|
|
1505
|
-
buildPresetModel("glm-5.3", "GLM-5.3", "https://open.bigmodel.cn/api/paas/v4"),
|
|
1506
|
-
// GLM-5.3-Flash (2026-08-26) — GLM-5 系列首个原生多模态
|
|
1507
|
-
// 模型,性价比高(¥0.4/¥1.4 per 1M tokens),接入 GLM Coding Plan
|
|
1508
|
-
// 后额度较 GLM-5.3 翻 3 倍。放在 5.3 后面,符合
|
|
1509
|
-
// 旗舰/快速版成对的展示惯例。
|
|
1510
|
-
buildPresetModel("glm-5.3-flash", "GLM-5.3 Flash", "https://open.bigmodel.cn/api/paas/v4"),
|
|
1511
|
-
buildPresetModel("glm-5.2", "GLM-5.2", "https://open.bigmodel.cn/api/paas/v4"),
|
|
1512
|
-
buildPresetModel("glm-5.1", "GLM-5.1", "https://open.bigmodel.cn/api/paas/v4"),
|
|
1513
|
-
buildPresetModel("glm-5", "GLM-5", "https://open.bigmodel.cn/api/paas/v4"),
|
|
1514
|
-
buildPresetModel("glm-5-turbo", "GLM-5 Turbo", "https://open.bigmodel.cn/api/paas/v4"),
|
|
1515
|
-
buildPresetModel("glm-4.7", "GLM-4.7", "https://open.bigmodel.cn/api/paas/v4"),
|
|
1516
|
-
buildPresetModel("glm-4.6", "GLM-4.6", "https://open.bigmodel.cn/api/paas/v4"),
|
|
1517
|
-
buildPresetModel("glm-4.5-air", "GLM-4.5 Air", "https://open.bigmodel.cn/api/paas/v4"),
|
|
1518
|
-
buildPresetModel("glm-4.5", "GLM-4.5", "https://open.bigmodel.cn/api/paas/v4")
|
|
1519
|
-
]
|
|
1520
|
-
},
|
|
1521
|
-
stepfun: {
|
|
1522
|
-
displayName: "StepFun",
|
|
1523
|
-
baseUrl: "https://api.stepfun.com/v1",
|
|
1524
|
-
models: [
|
|
1525
|
-
buildPresetModel("step-3.7-flash", "Step 3.7 Flash", "https://api.stepfun.com/v1"),
|
|
1526
|
-
buildPresetModel("step-3.5-flash", "Step 3.5 Flash", "https://api.stepfun.com/v1"),
|
|
1527
|
-
buildPresetModel(
|
|
1528
|
-
"step-1o-turbo-vision",
|
|
1529
|
-
"Step 1o Turbo Vision",
|
|
1530
|
-
"https://api.stepfun.com/v1"
|
|
1531
|
-
)
|
|
1532
|
-
]
|
|
1533
|
-
},
|
|
1534
|
-
siliconflow: {
|
|
1535
|
-
displayName: "SiliconFlow",
|
|
1536
|
-
baseUrl: "https://api.siliconflow.cn/v1",
|
|
1537
|
-
// 聚合平台 — 模型列表由平台动态维护(>100 个)。这里列的
|
|
1538
|
-
// 6 个是 2026 年 7-8 月各家最新的旗舰/代表型号,给 ProvidersTab
|
|
1539
|
-
// 一个 "一眼能看到" 的起点;用户添加 provider 后可继续通过
|
|
1540
|
-
// `/v1/models` 拉取完整列表。
|
|
1541
|
-
// 这里的 id 是 SiliconFlow API 用的 namespaced 字符串,必须
|
|
1542
|
-
// 与 MODEL_METADATA 的 alias 严格一致。
|
|
1543
|
-
models: [
|
|
1544
|
-
// DeepSeek V4 系列 (2026-04)
|
|
1545
|
-
buildPresetModel(
|
|
1546
|
-
"deepseek-ai/DeepSeek-V4-Pro",
|
|
1547
|
-
"DeepSeek V4 Pro (via SiliconFlow)",
|
|
1548
|
-
"https://api.siliconflow.cn/v1"
|
|
1549
|
-
),
|
|
1550
|
-
buildPresetModel(
|
|
1551
|
-
"deepseek-ai/DeepSeek-V4-Flash",
|
|
1552
|
-
"DeepSeek V4 Flash (via SiliconFlow)",
|
|
1553
|
-
"https://api.siliconflow.cn/v1"
|
|
1554
|
-
),
|
|
1555
|
-
// GLM-5.2 (2026-06-17) — open-weight 编程旗舰,1M context
|
|
1556
|
-
buildPresetModel(
|
|
1557
|
-
"zai-org/GLM-5.2",
|
|
1558
|
-
"GLM-5.2 (via SiliconFlow)",
|
|
1559
|
-
"https://api.siliconflow.cn/v1"
|
|
1560
|
-
),
|
|
1561
|
-
// Qwen3.6-35B-A3B (2026-04) — 35B MoE, 3B 激活,"小而强"
|
|
1562
|
-
buildPresetModel(
|
|
1563
|
-
"Qwen/Qwen3.6-35B-A3B",
|
|
1564
|
-
"Qwen3.6-35B-A3B (via SiliconFlow)",
|
|
1565
|
-
"https://api.siliconflow.cn/v1"
|
|
1566
|
-
),
|
|
1567
|
-
// Kimi K2.7-Code (2026-06-12) — Moonshot coding 旗舰
|
|
1568
|
-
buildPresetModel(
|
|
1569
|
-
"moonshotai/Kimi-K2.7-Code",
|
|
1570
|
-
"Kimi K2.7 Code (via SiliconFlow)",
|
|
1571
|
-
"https://api.siliconflow.cn/v1"
|
|
1572
|
-
)
|
|
1573
|
-
]
|
|
1574
|
-
},
|
|
1575
|
-
openrouter: {
|
|
1576
|
-
displayName: "OpenRouter",
|
|
1577
|
-
baseUrl: "https://openrouter.ai/api/v1",
|
|
1578
|
-
models: [
|
|
1579
|
-
buildPresetModel("openrouter/auto", "OpenRouter Auto", "https://openrouter.ai/api/v1")
|
|
1580
|
-
]
|
|
1581
|
-
},
|
|
1582
|
-
novita: {
|
|
1583
|
-
displayName: "Novita",
|
|
1584
|
-
baseUrl: "https://api.novita.ai/openai/v1",
|
|
1585
|
-
// 聚合平台:Novita 本身不产出自有模型,只是把 Kimi K3 / GLM 5.x /
|
|
1586
|
-
// DeepSeek V4 等第三方开源或授权模型挂到统一 OpenAI 兼容网关下
|
|
1587
|
-
// (见 https://novita.ai/llm-api)。这里列 5 个 2026 旗舰作为
|
|
1588
|
-
// preset 起点;用户添加 provider 后可继续通过 `/v1/models` 拉取
|
|
1589
|
-
// 完整列表。id 严格匹配 Novita API 的 namespaced 字符串。
|
|
1590
|
-
models: [
|
|
1591
|
-
// DeepSeek V4 系列 (2026-04)
|
|
1592
|
-
buildPresetModel(
|
|
1593
|
-
"deepseek/deepseek-v4-pro",
|
|
1594
|
-
"DeepSeek V4 Pro (via Novita)",
|
|
1595
|
-
"https://api.novita.ai/openai/v1"
|
|
1596
|
-
),
|
|
1597
|
-
buildPresetModel(
|
|
1598
|
-
"deepseek/deepseek-v4-flash",
|
|
1599
|
-
"DeepSeek V4 Flash (via Novita)",
|
|
1600
|
-
"https://api.novita.ai/openai/v1"
|
|
1601
|
-
),
|
|
1602
|
-
// GLM-5 系列 (2026-04/06)
|
|
1603
|
-
buildPresetModel("zai/glm-5.2", "GLM-5.2 (via Novita)", "https://api.novita.ai/openai/v1"),
|
|
1604
|
-
buildPresetModel("zai/glm-5.1", "GLM-5.1 (via Novita)", "https://api.novita.ai/openai/v1"),
|
|
1605
|
-
// Kimi K3 (2026-07-16 API, 2026-07-27 开源) — 1M context, 2.8T MoE
|
|
1606
|
-
buildPresetModel(
|
|
1607
|
-
"moonshotai/kimi-k3",
|
|
1608
|
-
"Kimi K3 (via Novita)",
|
|
1609
|
-
"https://api.novita.ai/openai/v1"
|
|
1610
|
-
)
|
|
1611
|
-
]
|
|
1612
|
-
},
|
|
1613
|
-
medalsoft: {
|
|
1614
|
-
displayName: "Medalsoft",
|
|
1615
|
-
// Medalsoft internal LLM gateway (公司内部代理) — OpenAI-compatible
|
|
1616
|
-
// `/v1/chat/completions`. The gateway forwards to upstream vendors
|
|
1617
|
-
// (GLM / DeepSeek / Kimi / ...), so the model catalogue is dynamic
|
|
1618
|
-
// and NOT curated here: the preset ships an EMPTY starter list and
|
|
1619
|
-
// the user populates it via "Fetch from API" (`GET /v1/models`) in
|
|
1620
|
-
// the ProvidersTab. `buildPresetModel`'s fail-loudly contract is
|
|
1621
|
-
// why we don't guess ids — a curated `MODEL_METADATA` entry only
|
|
1622
|
-
// exists for vendor-native ids, not the gateway's routing table.
|
|
1623
|
-
//
|
|
1624
|
-
// NOTE: models fetched from the gateway carry the upstream model
|
|
1625
|
-
// ids, so the curated metadata (pricing / thinking dropdown /
|
|
1626
|
-
// token caps) still resolves via `MODEL_METADATA` after the fetch.
|
|
1627
|
-
baseUrl: "https://nexus.servicemecloud.com/v1",
|
|
1628
|
-
models: []
|
|
1629
|
-
},
|
|
1630
|
-
sensenova: {
|
|
1631
|
-
displayName: "SenseNova",
|
|
1632
|
-
// SenseTime's SenseNova OpenAI-compatible gateway (see
|
|
1633
|
-
// https://platform.sensenova.cn/docs — `POST /v1/chat/completions`,
|
|
1634
|
-
// `Authorization: Bearer`, `GET /v1/models`). Token-plan quota per
|
|
1635
|
-
// model (e.g. 1500 req / 5h for flash-lite) is enforced upstream.
|
|
1636
|
-
//
|
|
1637
|
-
// The starter list mirrors the live `GET /v1/models` response
|
|
1638
|
-
// (fetched 2026-08-25 with a real key). The image-generation
|
|
1639
|
-
// models (sensenova-u1.5-lite / sensenova-u1-fast) live on
|
|
1640
|
-
// /v1/images/* endpoints and are explicitly documented as NOT
|
|
1641
|
-
// usable as chat Model IDs, so they are not curated here.
|
|
1642
|
-
// `sensenova-6.7-flash-lite` is likewise excluded — the docs
|
|
1643
|
-
// state it is a compat alias whose calls auto-redirect to
|
|
1644
|
-
// `sensenova-6.8-flash-lite` (through 2026-08-31).
|
|
1645
|
-
//
|
|
1646
|
-
// The relayed deepseek-v4-flash / glm-5.2 rows reuse their
|
|
1647
|
-
// MODEL_METADATA entries (keyed on the un-qualified id) and then
|
|
1648
|
-
// OVERRIDE the gateway-published specifics below: SenseNova
|
|
1649
|
-
// serves them with a 1M context window and TEXT-ONLY input
|
|
1650
|
-
// (per the live /v1/models payload), which differs from the
|
|
1651
|
-
// vendor-official curated values (655K input / vision on).
|
|
1652
|
-
// The global MODEL_METADATA stays vendor-official; only this
|
|
1653
|
-
// preset carries the gateway-specific caps so other providers
|
|
1654
|
-
// (DeepSeek official / Zhipu) are unaffected.
|
|
1655
|
-
baseUrl: "https://token.sensenova.cn/v1",
|
|
1656
|
-
models: [
|
|
1657
|
-
buildPresetModel(
|
|
1658
|
-
"sensenova-6.8-flash-lite",
|
|
1659
|
-
"SenseNova 6.8 Flash-Lite",
|
|
1660
|
-
"https://token.sensenova.cn/v1"
|
|
1661
|
-
),
|
|
1662
|
-
{
|
|
1663
|
-
// Live /v1/models: context_length 1048576,
|
|
1664
|
-
// max_output_length 65536, input_modalities ["text"] only.
|
|
1665
|
-
...buildPresetModel(
|
|
1666
|
-
"deepseek-v4-flash",
|
|
1667
|
-
"DeepSeek V4 Flash (via SenseNova)",
|
|
1668
|
-
"https://token.sensenova.cn/v1"
|
|
1669
|
-
),
|
|
1670
|
-
maxInputTokens: 1048576,
|
|
1671
|
-
maxOutputTokens: 65536,
|
|
1672
|
-
capabilities: { supportsImageToText: false, supportsToolCalling: true }
|
|
1673
|
-
},
|
|
1674
|
-
{
|
|
1675
|
-
// Live /v1/models: context_length 1048576,
|
|
1676
|
-
// max_output_length 131072, input_modalities ["text"] only.
|
|
1677
|
-
...buildPresetModel("glm-5.2", "GLM-5.2 (via SenseNova)", "https://token.sensenova.cn/v1"),
|
|
1678
|
-
maxInputTokens: 1048576,
|
|
1679
|
-
maxOutputTokens: 131072,
|
|
1680
|
-
capabilities: { supportsImageToText: false, supportsToolCalling: true }
|
|
1681
|
-
}
|
|
1682
|
-
]
|
|
1683
|
-
}
|
|
1362
|
+
const BUILTIN_PROVIDER_PRESETS = {
|
|
1363
|
+
minimax: {
|
|
1364
|
+
displayName: "MiniMax",
|
|
1365
|
+
baseUrl: "https://api.minimaxi.com/anthropic",
|
|
1366
|
+
models: [
|
|
1367
|
+
buildPresetModel("MiniMax-M3", "MiniMax-M3", "https://api.minimaxi.com/anthropic"),
|
|
1368
|
+
buildPresetModel("MiniMax-M2.7", "MiniMax-M2.7", "https://api.minimaxi.com/anthropic"),
|
|
1369
|
+
buildPresetModel("MiniMax-M2.7-highspeed", "MiniMax-M2.7-highspeed", "https://api.minimaxi.com/anthropic"),
|
|
1370
|
+
buildPresetModel("MiniMax-M2.5", "MiniMax-M2.5", "https://api.minimaxi.com/anthropic")
|
|
1371
|
+
]
|
|
1372
|
+
},
|
|
1373
|
+
deepseek: {
|
|
1374
|
+
displayName: "DeepSeek",
|
|
1375
|
+
baseUrl: "https://api.deepseek.com/v1",
|
|
1376
|
+
models: [buildPresetModel("deepseek-v4-flash", "DeepSeek V4 Flash", "https://api.deepseek.com/v1"), buildPresetModel("deepseek-v4-pro", "DeepSeek V4 Pro", "https://api.deepseek.com/v1")]
|
|
1377
|
+
},
|
|
1378
|
+
agnes: {
|
|
1379
|
+
displayName: "Agnes",
|
|
1380
|
+
baseUrl: "https://apihub.agnes-ai.com/v1",
|
|
1381
|
+
models: [
|
|
1382
|
+
buildPresetModel("agnes-2.5-pro", "Agnes 2.5 Pro", "https://apihub.agnes-ai.com/v1"),
|
|
1383
|
+
buildPresetModel("agnes-2.5-pro-alpha", "Agnes 2.5 Pro Alpha", "https://apihub.agnes-ai.com/v1"),
|
|
1384
|
+
buildPresetModel("agnes-2.5-flash", "Agnes 2.5 Flash", "https://apihub.agnes-ai.com/v1"),
|
|
1385
|
+
buildPresetModel("agnes-2.0-flash", "Agnes 2.0 Flash", "https://apihub.agnes-ai.com/v1")
|
|
1386
|
+
]
|
|
1387
|
+
},
|
|
1388
|
+
kimi: {
|
|
1389
|
+
displayName: "Kimi",
|
|
1390
|
+
baseUrl: "https://api.moonshot.cn/v1",
|
|
1391
|
+
models: [
|
|
1392
|
+
buildPresetModel("kimi-k3", "Kimi K3", "https://api.moonshot.cn/v1"),
|
|
1393
|
+
buildPresetModel("kimi-k2.7-code", "Kimi K2.7 Code", "https://api.moonshot.cn/v1"),
|
|
1394
|
+
buildPresetModel("kimi-k2.7-code-highspeed", "Kimi K2.7 Code HighSpeed", "https://api.moonshot.cn/v1"),
|
|
1395
|
+
buildPresetModel("kimi-k2.6", "Kimi K2.6", "https://api.moonshot.cn/v1")
|
|
1396
|
+
]
|
|
1397
|
+
},
|
|
1398
|
+
zhipu: {
|
|
1399
|
+
displayName: "Zhipu",
|
|
1400
|
+
baseUrl: "https://open.bigmodel.cn/api/paas/v4",
|
|
1401
|
+
models: [
|
|
1402
|
+
buildPresetModel("glm-5.3", "GLM-5.3", "https://open.bigmodel.cn/api/paas/v4"),
|
|
1403
|
+
buildPresetModel("glm-5.3-flash", "GLM-5.3 Flash", "https://open.bigmodel.cn/api/paas/v4"),
|
|
1404
|
+
buildPresetModel("glm-5.2", "GLM-5.2", "https://open.bigmodel.cn/api/paas/v4"),
|
|
1405
|
+
buildPresetModel("glm-5.1", "GLM-5.1", "https://open.bigmodel.cn/api/paas/v4"),
|
|
1406
|
+
buildPresetModel("glm-5", "GLM-5", "https://open.bigmodel.cn/api/paas/v4"),
|
|
1407
|
+
buildPresetModel("glm-5-turbo", "GLM-5 Turbo", "https://open.bigmodel.cn/api/paas/v4"),
|
|
1408
|
+
buildPresetModel("glm-4.7", "GLM-4.7", "https://open.bigmodel.cn/api/paas/v4"),
|
|
1409
|
+
buildPresetModel("glm-4.6", "GLM-4.6", "https://open.bigmodel.cn/api/paas/v4"),
|
|
1410
|
+
buildPresetModel("glm-4.5-air", "GLM-4.5 Air", "https://open.bigmodel.cn/api/paas/v4"),
|
|
1411
|
+
buildPresetModel("glm-4.5", "GLM-4.5", "https://open.bigmodel.cn/api/paas/v4")
|
|
1412
|
+
]
|
|
1413
|
+
},
|
|
1414
|
+
stepfun: {
|
|
1415
|
+
displayName: "StepFun",
|
|
1416
|
+
baseUrl: "https://api.stepfun.com/v1",
|
|
1417
|
+
models: [
|
|
1418
|
+
buildPresetModel("step-3.7-flash", "Step 3.7 Flash", "https://api.stepfun.com/v1"),
|
|
1419
|
+
buildPresetModel("step-3.5-flash", "Step 3.5 Flash", "https://api.stepfun.com/v1"),
|
|
1420
|
+
buildPresetModel("step-1o-turbo-vision", "Step 1o Turbo Vision", "https://api.stepfun.com/v1")
|
|
1421
|
+
]
|
|
1422
|
+
},
|
|
1423
|
+
siliconflow: {
|
|
1424
|
+
displayName: "SiliconFlow",
|
|
1425
|
+
baseUrl: "https://api.siliconflow.cn/v1",
|
|
1426
|
+
models: [
|
|
1427
|
+
buildPresetModel("deepseek-ai/DeepSeek-V4-Pro", "DeepSeek V4 Pro (via SiliconFlow)", "https://api.siliconflow.cn/v1"),
|
|
1428
|
+
buildPresetModel("deepseek-ai/DeepSeek-V4-Flash", "DeepSeek V4 Flash (via SiliconFlow)", "https://api.siliconflow.cn/v1"),
|
|
1429
|
+
buildPresetModel("zai-org/GLM-5.2", "GLM-5.2 (via SiliconFlow)", "https://api.siliconflow.cn/v1"),
|
|
1430
|
+
buildPresetModel("Qwen/Qwen3.6-35B-A3B", "Qwen3.6-35B-A3B (via SiliconFlow)", "https://api.siliconflow.cn/v1"),
|
|
1431
|
+
buildPresetModel("moonshotai/Kimi-K2.7-Code", "Kimi K2.7 Code (via SiliconFlow)", "https://api.siliconflow.cn/v1")
|
|
1432
|
+
]
|
|
1433
|
+
},
|
|
1434
|
+
openrouter: {
|
|
1435
|
+
displayName: "OpenRouter",
|
|
1436
|
+
baseUrl: "https://openrouter.ai/api/v1",
|
|
1437
|
+
models: [buildPresetModel("openrouter/auto", "OpenRouter Auto", "https://openrouter.ai/api/v1")]
|
|
1438
|
+
},
|
|
1439
|
+
novita: {
|
|
1440
|
+
displayName: "Novita",
|
|
1441
|
+
baseUrl: "https://api.novita.ai/openai/v1",
|
|
1442
|
+
models: [
|
|
1443
|
+
buildPresetModel("deepseek/deepseek-v4-pro", "DeepSeek V4 Pro (via Novita)", "https://api.novita.ai/openai/v1"),
|
|
1444
|
+
buildPresetModel("deepseek/deepseek-v4-flash", "DeepSeek V4 Flash (via Novita)", "https://api.novita.ai/openai/v1"),
|
|
1445
|
+
buildPresetModel("zai/glm-5.2", "GLM-5.2 (via Novita)", "https://api.novita.ai/openai/v1"),
|
|
1446
|
+
buildPresetModel("zai/glm-5.1", "GLM-5.1 (via Novita)", "https://api.novita.ai/openai/v1"),
|
|
1447
|
+
buildPresetModel("moonshotai/kimi-k3", "Kimi K3 (via Novita)", "https://api.novita.ai/openai/v1")
|
|
1448
|
+
]
|
|
1449
|
+
},
|
|
1450
|
+
medalsoft: {
|
|
1451
|
+
displayName: "Medalsoft",
|
|
1452
|
+
baseUrl: "https://nexus.servicemecloud.com/v1",
|
|
1453
|
+
models: []
|
|
1454
|
+
},
|
|
1455
|
+
sensenova: {
|
|
1456
|
+
displayName: "SenseNova",
|
|
1457
|
+
baseUrl: "https://token.sensenova.cn/v1",
|
|
1458
|
+
models: [
|
|
1459
|
+
buildPresetModel("sensenova-6.8-flash-lite", "SenseNova 6.8 Flash-Lite", "https://token.sensenova.cn/v1"),
|
|
1460
|
+
{
|
|
1461
|
+
...buildPresetModel("deepseek-v4-flash", "DeepSeek V4 Flash (via SenseNova)", "https://token.sensenova.cn/v1"),
|
|
1462
|
+
maxInputTokens: 1048576,
|
|
1463
|
+
maxOutputTokens: 65536,
|
|
1464
|
+
capabilities: {
|
|
1465
|
+
supportsImageToText: false,
|
|
1466
|
+
supportsToolCalling: true
|
|
1467
|
+
}
|
|
1468
|
+
},
|
|
1469
|
+
{
|
|
1470
|
+
...buildPresetModel("glm-5.2", "GLM-5.2 (via SenseNova)", "https://token.sensenova.cn/v1"),
|
|
1471
|
+
maxInputTokens: 1048576,
|
|
1472
|
+
maxOutputTokens: 131072,
|
|
1473
|
+
capabilities: {
|
|
1474
|
+
supportsImageToText: false,
|
|
1475
|
+
supportsToolCalling: true
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
]
|
|
1479
|
+
}
|
|
1684
1480
|
};
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1481
|
+
/**
|
|
1482
|
+
* Curated `id → displayName` map derived from `BUILTIN_PROVIDER_PRESETS`
|
|
1483
|
+
* at module-load time. Single source of truth for the friendly
|
|
1484
|
+
* model label shown in the picker — both the "Reset to preset
|
|
1485
|
+
* models" / vendor `<select>` flows (which call `buildPresetModel`
|
|
1486
|
+
* and get the displayName as a parameter) and the "Fetch from API"
|
|
1487
|
+
* union (which calls `getPresetModelDisplayName` to fill in the
|
|
1488
|
+
* `displayName` field when the API doesn't return one).
|
|
1489
|
+
*
|
|
1490
|
+
* Built at module load (not lazily) so the lookup is O(1) on the
|
|
1491
|
+
* hot path — `unionProviderModelWithPreset` runs once per fetched
|
|
1492
|
+
* model row, and the union runs every time the user clicks "Fetch
|
|
1493
|
+
* from API". A lazy Map would also work, but the upfront cost is
|
|
1494
|
+
* ~30 entries (the largest preset is Zhipu with ~20 GLM rows) and
|
|
1495
|
+
* pays for itself after the first fetch.
|
|
1496
|
+
*/
|
|
1497
|
+
const PRESET_MODEL_DISPLAY_NAMES = (() => {
|
|
1498
|
+
const map = {};
|
|
1499
|
+
for (const preset of Object.values(BUILTIN_PROVIDER_PRESETS)) for (const model of preset.models) if (model.displayName !== void 0) map[model.id] = model.displayName;
|
|
1500
|
+
return Object.freeze(map);
|
|
1695
1501
|
})();
|
|
1502
|
+
/**
|
|
1503
|
+
* Look up the curated friendly displayName for a model id. Two
|
|
1504
|
+
* sources, in priority order:
|
|
1505
|
+
*
|
|
1506
|
+
* 1. **Explicit map** (`PRESET_MODEL_DISPLAY_NAMES`, derived from
|
|
1507
|
+
* `BUILTIN_PROVIDER_PRESETS` at module load). Curated by hand;
|
|
1508
|
+
* wins when present so a curated prettier name
|
|
1509
|
+
* (e.g. "DeepSeek V4 Flash" for `deepseek-v4-flash`) is
|
|
1510
|
+
* always preferred over whatever the detail's prefix would
|
|
1511
|
+
* produce.
|
|
1512
|
+
* 2. **`MODEL_METADATA.detail` fallback** (2026-08-19 follow-up).
|
|
1513
|
+
* For ids that are in `MODEL_METADATA` (have curated pricing
|
|
1514
|
+
* / capabilities) but NOT in any vendor preset — e.g.
|
|
1515
|
+
* `glm-4.7-flash`, `glm-4.5v`, `glm-5v-turbo`, `glm-4.6v` —
|
|
1516
|
+
* derive the display name from the `detail` field by
|
|
1517
|
+
* splitting on the first ` — ` and keeping the left half.
|
|
1518
|
+
* This restores friendly labels for models the v1 lookup
|
|
1519
|
+
* missed (the user-reported case: `glm-4.7-flash` came
|
|
1520
|
+
* back from "Fetch from API" without a display name
|
|
1521
|
+
* because it was excluded from the Zhipu preset on
|
|
1522
|
+
* 2026-08-18, but its detail field already said
|
|
1523
|
+
* "GLM-4.7 Flash — 完全免费(200K 上下文)").
|
|
1524
|
+
*
|
|
1525
|
+
* Returns `undefined` for:
|
|
1526
|
+
* • ids that aren't in `BUILTIN_PROVIDER_PRESETS` AND aren't in
|
|
1527
|
+
* `MODEL_METADATA` (genuinely custom / aggregator-only
|
|
1528
|
+
* models the user added by hand — the consumer falls back
|
|
1529
|
+
* to the literal fetched id)
|
|
1530
|
+
* • ids in `MODEL_METADATA` whose `detail` is empty /
|
|
1531
|
+
* whitespace, or whose detail has no ` — ` boundary and the
|
|
1532
|
+
* whole string is the qualifier (defensive — every entry
|
|
1533
|
+
* today has a usable detail).
|
|
1534
|
+
*
|
|
1535
|
+
* Used by `unionProviderModelWithPreset` to fill in the
|
|
1536
|
+
* `displayName` field when the API payload omits it — the
|
|
1537
|
+
* picker's model name column then renders "DeepSeek V4 Flash"
|
|
1538
|
+
* or "GLM-4.7 Flash" instead of the bare id right after the
|
|
1539
|
+
* fetch result lands.
|
|
1540
|
+
*/
|
|
1696
1541
|
function getPresetModelDisplayName(id) {
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
return void 0;
|
|
1708
|
-
}
|
|
1709
|
-
const dashIndex = detail.indexOf(" \u2014 ");
|
|
1710
|
-
if (dashIndex === -1) {
|
|
1711
|
-
return detail;
|
|
1712
|
-
}
|
|
1713
|
-
const head = detail.slice(0, dashIndex).trim();
|
|
1714
|
-
return head === "" ? void 0 : head;
|
|
1542
|
+
const explicit = PRESET_MODEL_DISPLAY_NAMES[id];
|
|
1543
|
+
if (explicit !== void 0) return explicit;
|
|
1544
|
+
const meta = MODEL_METADATA[id];
|
|
1545
|
+
if (meta === void 0) return;
|
|
1546
|
+
const detail = meta.detail;
|
|
1547
|
+
if (typeof detail !== "string" || detail.trim() === "") return;
|
|
1548
|
+
const dashIndex = detail.indexOf(" — ");
|
|
1549
|
+
if (dashIndex === -1) return detail;
|
|
1550
|
+
const head = detail.slice(0, dashIndex).trim();
|
|
1551
|
+
return head === "" ? void 0 : head;
|
|
1715
1552
|
}
|
|
1553
|
+
/**
|
|
1554
|
+
* Look up the default config (displayName / baseUrl / models) for a
|
|
1555
|
+
* named vendor type. Returns `null` for the `-compatible` family —
|
|
1556
|
+
* those have no canned defaults; the user enters them by hand.
|
|
1557
|
+
*/
|
|
1716
1558
|
function getBuiltinProviderPreset(type) {
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
return null;
|
|
1732
|
-
}
|
|
1559
|
+
switch (type) {
|
|
1560
|
+
case "minimax":
|
|
1561
|
+
case "deepseek":
|
|
1562
|
+
case "agnes":
|
|
1563
|
+
case "kimi":
|
|
1564
|
+
case "zhipu":
|
|
1565
|
+
case "stepfun":
|
|
1566
|
+
case "siliconflow":
|
|
1567
|
+
case "openrouter":
|
|
1568
|
+
case "novita":
|
|
1569
|
+
case "medalsoft":
|
|
1570
|
+
case "sensenova": return BUILTIN_PROVIDER_PRESETS[type];
|
|
1571
|
+
default: return null;
|
|
1572
|
+
}
|
|
1733
1573
|
}
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1574
|
+
//#endregion
|
|
1575
|
+
//#region src/certificate-bundle.ts
|
|
1576
|
+
const CERTIFICATE_BUNDLE_FORMATS = [
|
|
1577
|
+
{
|
|
1578
|
+
format: "pem",
|
|
1579
|
+
label: "Nginx / Generic PEM",
|
|
1580
|
+
description: "cert.pem + key.pem + fullchain.pem (Nginx, HAProxy, most Unix servers).",
|
|
1581
|
+
icon: "file-lock-2",
|
|
1582
|
+
requiresPassword: false,
|
|
1583
|
+
artifactExtension: ".pem"
|
|
1584
|
+
},
|
|
1585
|
+
{
|
|
1586
|
+
format: "pfx",
|
|
1587
|
+
label: "IIS / Tomcat PFX",
|
|
1588
|
+
description: "PKCS#12 bundle (.pfx) for Windows IIS, Tomcat, ColdFusion. Requires a password.",
|
|
1589
|
+
icon: "shield",
|
|
1590
|
+
requiresPassword: true,
|
|
1591
|
+
artifactExtension: ".pfx"
|
|
1592
|
+
},
|
|
1593
|
+
{
|
|
1594
|
+
format: "crt",
|
|
1595
|
+
label: "Apache CRT",
|
|
1596
|
+
description: "Separate .crt + .key for Apache httpd and other Unix servers that prefer DER/PEM split.",
|
|
1597
|
+
icon: "file-badge",
|
|
1598
|
+
requiresPassword: false,
|
|
1599
|
+
artifactExtension: ".crt"
|
|
1600
|
+
},
|
|
1601
|
+
{
|
|
1602
|
+
format: "jks",
|
|
1603
|
+
label: "Java JKS",
|
|
1604
|
+
description: "Java KeyStore (.jks) for Tomcat and Java applications. Requires a JDK with `keytool` on PATH.",
|
|
1605
|
+
icon: "coffee",
|
|
1606
|
+
requiresPassword: true,
|
|
1607
|
+
artifactExtension: ".jks"
|
|
1608
|
+
}
|
|
1769
1609
|
];
|
|
1770
|
-
|
|
1771
|
-
|
|
1610
|
+
//#endregion
|
|
1611
|
+
//#region src/copilot-customizations.ts
|
|
1612
|
+
/**
|
|
1613
|
+
* Whole-package registrar install marker: the explicit `wholePackage`
|
|
1614
|
+
* flag, or (legacy shape) the synthetic `::package:` artifact. Such
|
|
1615
|
+
* packages are PERSONAL-scope only — registrar-owned, so per-artifact
|
|
1616
|
+
* scope actions (move to workspace) do not apply.
|
|
1617
|
+
*/
|
|
1772
1618
|
function isWholePackageInstall(definition) {
|
|
1773
|
-
|
|
1619
|
+
return definition.wholePackage === true || definition.artifacts?.some((artifact) => artifact.id.includes("::package:")) === true;
|
|
1774
1620
|
}
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1621
|
+
/**
|
|
1622
|
+
* Registrar record id (`repo:plugin`) from a view/package payload id
|
|
1623
|
+
* (`repo::plugin`). Both spellings float around the wire; the registrar
|
|
1624
|
+
* and the settings keys always use the single-colon form.
|
|
1625
|
+
*/
|
|
1626
|
+
function registrationIdFromPackageId(packageId) {
|
|
1627
|
+
return packageId.replace("::", ":");
|
|
1628
|
+
}
|
|
1629
|
+
//#endregion
|
|
1630
|
+
//#region ../../node_modules/.pnpm/js-sha256@1.0.0/node_modules/js-sha256/src/shared.mjs
|
|
1780
1631
|
var INPUT_ERROR = "input is invalid type";
|
|
1781
1632
|
var FINALIZE_ERROR = "finalize already called";
|
|
1782
1633
|
var ARRAY_BUFFER = typeof ArrayBuffer !== "undefined";
|
|
1783
1634
|
var formatMessage = function(message) {
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
if (ARRAY_BUFFER && message) {
|
|
1792
|
-
if (message.constructor === ArrayBuffer) {
|
|
1793
|
-
return [new Uint8Array(message), false];
|
|
1794
|
-
} else if (ArrayBuffer.isView(message)) {
|
|
1795
|
-
return [message, false];
|
|
1796
|
-
}
|
|
1797
|
-
}
|
|
1798
|
-
throw new Error(INPUT_ERROR);
|
|
1635
|
+
if (typeof message === "string") return [message, true];
|
|
1636
|
+
if (Array.isArray(message)) return [message, false];
|
|
1637
|
+
if (ARRAY_BUFFER && message) {
|
|
1638
|
+
if (message.constructor === ArrayBuffer) return [new Uint8Array(message), false];
|
|
1639
|
+
else if (ArrayBuffer.isView(message)) return [message, false];
|
|
1640
|
+
}
|
|
1641
|
+
throw new Error(INPUT_ERROR);
|
|
1799
1642
|
};
|
|
1800
|
-
|
|
1801
|
-
|
|
1643
|
+
//#endregion
|
|
1644
|
+
//#region ../../node_modules/.pnpm/js-sha256@1.0.0/node_modules/js-sha256/src/node.mjs
|
|
1802
1645
|
function toNodeInput(message) {
|
|
1803
|
-
|
|
1804
|
-
|
|
1646
|
+
const [msg, isString] = formatMessage(message);
|
|
1647
|
+
return isString ? Buffer.from(msg, "utf8") : Buffer.from(msg);
|
|
1805
1648
|
}
|
|
1806
1649
|
var NodeHasher = class {
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
return Uint8Array.from(this.array()).buffer;
|
|
1840
|
-
}
|
|
1650
|
+
constructor(hash) {
|
|
1651
|
+
this.hash = hash;
|
|
1652
|
+
this.result = void 0;
|
|
1653
|
+
}
|
|
1654
|
+
update(message) {
|
|
1655
|
+
if (this.result) throw new Error(FINALIZE_ERROR);
|
|
1656
|
+
this.hash.update(toNodeInput(message));
|
|
1657
|
+
return this;
|
|
1658
|
+
}
|
|
1659
|
+
finalize() {
|
|
1660
|
+
if (!this.result) {
|
|
1661
|
+
this.result = this.hash.digest();
|
|
1662
|
+
this.hash = void 0;
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
hex() {
|
|
1666
|
+
this.finalize();
|
|
1667
|
+
return this.result.toString("hex");
|
|
1668
|
+
}
|
|
1669
|
+
toString() {
|
|
1670
|
+
return this.hex();
|
|
1671
|
+
}
|
|
1672
|
+
array() {
|
|
1673
|
+
this.finalize();
|
|
1674
|
+
return Array.from(this.result);
|
|
1675
|
+
}
|
|
1676
|
+
digest() {
|
|
1677
|
+
return this.array();
|
|
1678
|
+
}
|
|
1679
|
+
arrayBuffer() {
|
|
1680
|
+
return Uint8Array.from(this.array()).buffer;
|
|
1681
|
+
}
|
|
1841
1682
|
};
|
|
1842
1683
|
function addOutputMethods(method, createHasher) {
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1684
|
+
method.hex = method;
|
|
1685
|
+
method.array = function(...args) {
|
|
1686
|
+
return createHasher(...args.slice(0, -1)).update(args[args.length - 1]).array();
|
|
1687
|
+
};
|
|
1688
|
+
method.digest = method.array;
|
|
1689
|
+
method.arrayBuffer = function(...args) {
|
|
1690
|
+
return createHasher(...args.slice(0, -1)).update(args[args.length - 1]).arrayBuffer();
|
|
1691
|
+
};
|
|
1692
|
+
return method;
|
|
1852
1693
|
}
|
|
1853
1694
|
function createNodeMethod(algorithm) {
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1695
|
+
const createHasher = () => new NodeHasher((0, crypto.createHash)(algorithm));
|
|
1696
|
+
const method = function(message) {
|
|
1697
|
+
return createHasher().update(message).hex();
|
|
1698
|
+
};
|
|
1699
|
+
addOutputMethods(method, createHasher);
|
|
1700
|
+
method.create = createHasher;
|
|
1701
|
+
method.update = function(message) {
|
|
1702
|
+
return method.create().update(message);
|
|
1703
|
+
};
|
|
1704
|
+
return method;
|
|
1864
1705
|
}
|
|
1865
1706
|
function createNodeHmacMethod(algorithm) {
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1707
|
+
const createHasher = (key) => new NodeHasher((0, crypto.createHmac)(algorithm, toNodeInput(key)));
|
|
1708
|
+
const method = function(key, message) {
|
|
1709
|
+
return createHasher(key).update(message).hex();
|
|
1710
|
+
};
|
|
1711
|
+
addOutputMethods(method, createHasher);
|
|
1712
|
+
method.create = createHasher;
|
|
1713
|
+
method.update = function(key, message) {
|
|
1714
|
+
return method.create(key).update(message);
|
|
1715
|
+
};
|
|
1716
|
+
return method;
|
|
1876
1717
|
}
|
|
1877
|
-
|
|
1878
|
-
|
|
1718
|
+
const sha256 = createNodeMethod("sha256");
|
|
1719
|
+
const sha224 = createNodeMethod("sha224");
|
|
1879
1720
|
sha256.sha256 = sha256;
|
|
1880
1721
|
sha256.sha224 = sha224;
|
|
1881
1722
|
sha256.hmac = createNodeHmacMethod("sha256");
|
|
1882
1723
|
sha224.hmac = createNodeHmacMethod("sha224");
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1724
|
+
//#endregion
|
|
1725
|
+
//#region src/device-auth.ts
|
|
1726
|
+
/**
|
|
1727
|
+
* Device-auth wire contract shared by the extension (signer) and the
|
|
1728
|
+
* server (verifier).
|
|
1729
|
+
*
|
|
1730
|
+
* Single source of truth for the `x-ms-device-*` header names and the
|
|
1731
|
+
* HMAC-SHA-256 request-signature algorithm. Previously the same
|
|
1732
|
+
* constants + function were copy-pasted in three places
|
|
1733
|
+
* (extension `services/device/deviceAuth.ts`, core `device/deviceAuth.ts`,
|
|
1734
|
+
* server `lib/auth/device-signature-guard.ts`) and kept in sync by
|
|
1735
|
+
* comments alone. Server and extension now import from here.
|
|
1736
|
+
*
|
|
1737
|
+
* NOTE: `packages/serviceme-core/src/device/deviceAuth.ts` keeps its own
|
|
1738
|
+
* copy — ADL-003 forbids core → shared (and shared → core) so the core
|
|
1739
|
+
* copy is a documented boundary exception. Keep it in lock-step with
|
|
1740
|
+
* this file. See `docs/architecture/phase-5-device-header-spec.md` §5
|
|
1741
|
+
* for the wire format.
|
|
1742
|
+
*/
|
|
1743
|
+
/** Canonical header names — MUST match the server's verifier. */
|
|
1744
|
+
const DeviceAuthHeaders = {
|
|
1745
|
+
deviceId: "x-ms-device-id",
|
|
1746
|
+
deviceSecret: "x-ms-device-secret",
|
|
1747
|
+
signature: "x-ms-device-signature",
|
|
1748
|
+
timestamp: "x-ms-device-timestamp",
|
|
1749
|
+
secretVersion: "x-ms-device-secret-version"
|
|
1891
1750
|
};
|
|
1751
|
+
/**
|
|
1752
|
+
* Basis is `METHOD\nPATH\nTIMESTAMP\nBODY\nSECRET` (LF-joined, NOT JSON).
|
|
1753
|
+
* Output is lowercase hex SHA-256.
|
|
1754
|
+
*
|
|
1755
|
+
* Uses `js-sha256` (pure JS, synchronous, browser + Node) instead of
|
|
1756
|
+
* `node:crypto` so this module stays importable from the webview (the
|
|
1757
|
+
* shared barrel is consumed by browser bundles — `node:crypto` breaks
|
|
1758
|
+
* the vite/rollup build).
|
|
1759
|
+
*/
|
|
1892
1760
|
function createDeviceRequestSignature(params) {
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1761
|
+
const basis = [
|
|
1762
|
+
params.method.toUpperCase(),
|
|
1763
|
+
params.path,
|
|
1764
|
+
String(params.timestamp),
|
|
1765
|
+
params.body,
|
|
1766
|
+
params.secret
|
|
1767
|
+
].join("\n");
|
|
1768
|
+
return sha256(basis);
|
|
1901
1769
|
}
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1770
|
+
//#endregion
|
|
1771
|
+
//#region src/git-utils.ts
|
|
1772
|
+
/**
|
|
1773
|
+
* Git URL Utilities
|
|
1774
|
+
*
|
|
1775
|
+
* Pure functions for parsing and validating Git remote URLs into canonical slugs.
|
|
1776
|
+
* No platform-specific logic — suitable for both Node.js and browser environments.
|
|
1777
|
+
*/
|
|
1778
|
+
/**
|
|
1779
|
+
* Built-in hostname aliases for Git remotes.
|
|
1780
|
+
*
|
|
1781
|
+
* Some users/teams configure `~/.ssh/config` `Host` aliases (e.g. to pick a
|
|
1782
|
+
* specific SSH identity for a work GitHub account) so their remotes read
|
|
1783
|
+
* `git@github-msc:owner/repo.git` instead of `git@github.com:owner/repo.git`.
|
|
1784
|
+
* Resolving these here — rather than only on the client — keeps the server's
|
|
1785
|
+
* independently-recomputed canonical slug (see `ensureRemotesMatchCanonicalSlug`
|
|
1786
|
+
* in `apps/server/src/app/api/v1/projects/_validators.ts`) consistent with
|
|
1787
|
+
* whatever the client already resolved and sent as `canonical_slug`.
|
|
1788
|
+
*/
|
|
1789
|
+
const GIT_REMOTE_HOST_ALIASES = { "github-msc": "github.com" };
|
|
1907
1790
|
function normalizeGitRemoteHost(host) {
|
|
1908
|
-
|
|
1909
|
-
|
|
1791
|
+
const normalizedHost = host.trim().toLowerCase();
|
|
1792
|
+
return GIT_REMOTE_HOST_ALIASES[normalizedHost] ?? normalizedHost;
|
|
1910
1793
|
}
|
|
1794
|
+
/**
|
|
1795
|
+
* Apply {@link GIT_REMOTE_HOST_ALIASES} to an already-canonical `host/owner/repo`
|
|
1796
|
+
* slug string (as opposed to a raw Git URL — see `normalizeGitUrl` for that).
|
|
1797
|
+
*/
|
|
1911
1798
|
function normalizeCanonicalSlug(slug) {
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
const path = trimmedSlug.slice(firstSlashIndex + 1);
|
|
1919
|
-
return `${normalizeGitRemoteHost(host)}/${path}`;
|
|
1799
|
+
const trimmedSlug = slug.trim().toLowerCase();
|
|
1800
|
+
const firstSlashIndex = trimmedSlug.indexOf("/");
|
|
1801
|
+
if (firstSlashIndex <= 0) return trimmedSlug;
|
|
1802
|
+
const host = trimmedSlug.slice(0, firstSlashIndex);
|
|
1803
|
+
const path = trimmedSlug.slice(firstSlashIndex + 1);
|
|
1804
|
+
return `${normalizeGitRemoteHost(host)}/${path}`;
|
|
1920
1805
|
}
|
|
1806
|
+
/**
|
|
1807
|
+
* Normalize a Git remote URL to a canonical slug: host/owner/repo
|
|
1808
|
+
* - Supports HTTPS, SSH, and SCP-like syntax
|
|
1809
|
+
* - Strips credentials, ports, and .git suffix
|
|
1810
|
+
* - Lowercases host and every path segment
|
|
1811
|
+
* - Resolves known host aliases (see {@link GIT_REMOTE_HOST_ALIASES})
|
|
1812
|
+
*
|
|
1813
|
+
* @throws Error if the input cannot be parsed into a valid slug
|
|
1814
|
+
*/
|
|
1921
1815
|
function normalizeGitUrl(input) {
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
if (isValidCanonicalSlug(trimmed)) return normalizeCanonicalSlug(trimmed);
|
|
1943
|
-
throw new Error(`Unparseable Git URL: ${input}`);
|
|
1944
|
-
}
|
|
1816
|
+
const trimmed = input.trim();
|
|
1817
|
+
const scpMatch = !trimmed.includes("://") && trimmed.match(/^([^@]+)@([^:]+):(.+)$/);
|
|
1818
|
+
if (scpMatch) {
|
|
1819
|
+
const host = scpMatch[2];
|
|
1820
|
+
const path = scpMatch[3];
|
|
1821
|
+
return toCanonicalSlug(`ssh://${host}/${path}`);
|
|
1822
|
+
}
|
|
1823
|
+
try {
|
|
1824
|
+
const url = new URL(trimmed);
|
|
1825
|
+
const host = normalizeGitRemoteHost(url.hostname);
|
|
1826
|
+
let pathname = url.pathname;
|
|
1827
|
+
if (pathname.startsWith("/")) pathname = pathname.slice(1);
|
|
1828
|
+
if (pathname.endsWith(".git")) pathname = pathname.slice(0, -4);
|
|
1829
|
+
const parts = pathname.split("/").filter(Boolean);
|
|
1830
|
+
if (parts.length < 2) throw new Error("Invalid Git URL: missing owner/repo path");
|
|
1831
|
+
return `${host}/${parts.map((part) => part.toLowerCase()).join("/")}`;
|
|
1832
|
+
} catch {
|
|
1833
|
+
if (isValidCanonicalSlug(trimmed)) return normalizeCanonicalSlug(trimmed);
|
|
1834
|
+
throw new Error(`Unparseable Git URL: ${input}`);
|
|
1835
|
+
}
|
|
1945
1836
|
}
|
|
1837
|
+
/**
|
|
1838
|
+
* Validate whether a string matches the canonical slug format: host/owner/repo
|
|
1839
|
+
* Requires at least three segments (host + two path parts).
|
|
1840
|
+
* Allows percent-encoded characters (e.g. %20) for hosts like Azure DevOps
|
|
1841
|
+
* that permit spaces in project/repo names.
|
|
1842
|
+
*/
|
|
1946
1843
|
function isValidCanonicalSlug(slug) {
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
return canonicalSlugPattern.test(slug.trim());
|
|
1844
|
+
const segment = "(?:[a-zA-Z0-9_.-]|%[0-9A-Fa-f]{2})+";
|
|
1845
|
+
return new RegExp(`^${segment}(?:\\/${segment}){2,}$`).test(slug.trim());
|
|
1950
1846
|
}
|
|
1951
1847
|
function toCanonicalSlug(coerced) {
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1848
|
+
const url = new URL(coerced);
|
|
1849
|
+
const host = normalizeGitRemoteHost(url.hostname);
|
|
1850
|
+
let pathname = url.pathname;
|
|
1851
|
+
if (pathname.startsWith("/")) pathname = pathname.slice(1);
|
|
1852
|
+
if (pathname.endsWith(".git")) pathname = pathname.slice(0, -4);
|
|
1853
|
+
const parts = pathname.split("/").filter(Boolean);
|
|
1854
|
+
if (parts.length < 2) throw new Error("Invalid Git URL: missing owner/repo path");
|
|
1855
|
+
return `${host}/${parts.map((part) => part.toLowerCase()).join("/")}`;
|
|
1960
1856
|
}
|
|
1961
|
-
|
|
1962
|
-
|
|
1857
|
+
//#endregion
|
|
1858
|
+
//#region src/github-api.ts
|
|
1859
|
+
/**
|
|
1860
|
+
* Fetch user info from GitHub API
|
|
1861
|
+
* @param token GitHub Personal Access Token or OAuth Access Token
|
|
1862
|
+
* @returns GitHubUser object
|
|
1863
|
+
* @throws Error if request fails or token is invalid
|
|
1864
|
+
*/
|
|
1963
1865
|
async function fetchGitHubUser(token) {
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
} catch (_e) {
|
|
1984
|
-
body = "[Failed to read response body]";
|
|
1985
|
-
}
|
|
1986
|
-
throw new Error(`GitHub API Error: ${status} ${statusText} - ${body}`);
|
|
1866
|
+
const resp = await fetch("https://api.github.com/user", { headers: {
|
|
1867
|
+
Authorization: `token ${token}`,
|
|
1868
|
+
Accept: "application/vnd.github.v3+json"
|
|
1869
|
+
} });
|
|
1870
|
+
if (resp.ok) {
|
|
1871
|
+
const user = await resp.json();
|
|
1872
|
+
user.email = await resolveGitHubUserEmail(token, user);
|
|
1873
|
+
return user;
|
|
1874
|
+
}
|
|
1875
|
+
if (resp.status === 401) throw new Error("GitHub API Error: 401 Unauthorized");
|
|
1876
|
+
const status = resp.status;
|
|
1877
|
+
const statusText = resp.statusText;
|
|
1878
|
+
let body = "";
|
|
1879
|
+
try {
|
|
1880
|
+
body = await resp.text();
|
|
1881
|
+
} catch (_e) {
|
|
1882
|
+
body = "[Failed to read response body]";
|
|
1883
|
+
}
|
|
1884
|
+
throw new Error(`GitHub API Error: ${status} ${statusText} - ${body}`);
|
|
1987
1885
|
}
|
|
1988
1886
|
async function resolveGitHubUserEmail(token, user) {
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
}
|
|
1993
|
-
const emails = await fetchGitHubUserEmails(token);
|
|
1994
|
-
const preferredEmail = pickPreferredGitHubEmail(emails);
|
|
1995
|
-
return preferredEmail ?? directEmail;
|
|
1887
|
+
const directEmail = sanitizeEmail(user.email);
|
|
1888
|
+
if (directEmail) return directEmail;
|
|
1889
|
+
return pickPreferredGitHubEmail(await fetchGitHubUserEmails(token)) ?? directEmail;
|
|
1996
1890
|
}
|
|
1997
1891
|
async function fetchGitHubUserEmails(token) {
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
}
|
|
2013
|
-
let body = "";
|
|
2014
|
-
try {
|
|
2015
|
-
body = await resp.text();
|
|
2016
|
-
} catch (_e) {
|
|
2017
|
-
body = "[Failed to read response body]";
|
|
2018
|
-
}
|
|
2019
|
-
throw new Error(`GitHub API Error: ${resp.status} ${resp.statusText} - ${body}`);
|
|
1892
|
+
const resp = await fetch("https://api.github.com/user/emails", { headers: {
|
|
1893
|
+
Authorization: `token ${token}`,
|
|
1894
|
+
Accept: "application/vnd.github.v3+json"
|
|
1895
|
+
} });
|
|
1896
|
+
if (resp.ok) return await resp.json();
|
|
1897
|
+
if (resp.status === 404 || resp.status === 403) return [];
|
|
1898
|
+
if (resp.status === 401) throw new Error("GitHub API Error: 401 Unauthorized");
|
|
1899
|
+
let body = "";
|
|
1900
|
+
try {
|
|
1901
|
+
body = await resp.text();
|
|
1902
|
+
} catch (_e) {
|
|
1903
|
+
body = "[Failed to read response body]";
|
|
1904
|
+
}
|
|
1905
|
+
throw new Error(`GitHub API Error: ${resp.status} ${resp.statusText} - ${body}`);
|
|
2020
1906
|
}
|
|
2021
1907
|
function pickPreferredGitHubEmail(emails) {
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
);
|
|
2028
|
-
const preferred = sanitizedEmails.find((emailRecord) => emailRecord.primary && emailRecord.verified) ?? sanitizedEmails.find((emailRecord) => emailRecord.verified) ?? sanitizedEmails.find((emailRecord) => emailRecord.primary) ?? sanitizedEmails[0];
|
|
2029
|
-
return preferred?.email ?? null;
|
|
1908
|
+
const sanitizedEmails = emails.map((emailRecord) => ({
|
|
1909
|
+
...emailRecord,
|
|
1910
|
+
email: sanitizeEmail(emailRecord.email)
|
|
1911
|
+
})).filter((emailRecord) => Boolean(emailRecord.email));
|
|
1912
|
+
return (sanitizedEmails.find((emailRecord) => emailRecord.primary && emailRecord.verified) ?? sanitizedEmails.find((emailRecord) => emailRecord.verified) ?? sanitizedEmails.find((emailRecord) => emailRecord.primary) ?? sanitizedEmails[0])?.email ?? null;
|
|
2030
1913
|
}
|
|
2031
1914
|
function sanitizeEmail(email) {
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
if (!normalizedEmail) {
|
|
2037
|
-
return null;
|
|
2038
|
-
}
|
|
2039
|
-
return normalizedEmail;
|
|
1915
|
+
if (!email) return null;
|
|
1916
|
+
const normalizedEmail = email.trim();
|
|
1917
|
+
if (!normalizedEmail) return null;
|
|
1918
|
+
return normalizedEmail;
|
|
2040
1919
|
}
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
1920
|
+
const __internal = {
|
|
1921
|
+
pickPreferredGitHubEmail,
|
|
1922
|
+
sanitizeEmail
|
|
2044
1923
|
};
|
|
2045
1924
|
function createOrgMembershipResult(organization, status, httpStatus, membership) {
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
1925
|
+
return {
|
|
1926
|
+
status,
|
|
1927
|
+
httpStatus,
|
|
1928
|
+
organization,
|
|
1929
|
+
role: typeof membership?.role === "string" ? membership.role : null,
|
|
1930
|
+
directMembership: typeof membership?.direct_membership === "boolean" ? membership.direct_membership : null
|
|
1931
|
+
};
|
|
2053
1932
|
}
|
|
1933
|
+
/**
|
|
1934
|
+
* Fetch the authenticated user's membership details for a GitHub organization.
|
|
1935
|
+
* Uses the memberships list endpoint so callers can distinguish active, pending,
|
|
1936
|
+
* and indeterminate states instead of collapsing everything into a boolean.
|
|
1937
|
+
*/
|
|
2054
1938
|
async function getGitHubOrgMembership(token, org) {
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
}
|
|
2078
|
-
const memberships = await resp.json();
|
|
2079
|
-
const membership = memberships.find(
|
|
2080
|
-
(entry) => entry.organization?.login?.toLowerCase() === normalizedOrg
|
|
2081
|
-
);
|
|
2082
|
-
if (!membership) {
|
|
2083
|
-
return createOrgMembershipResult(org, "not_member", resp.status);
|
|
2084
|
-
}
|
|
2085
|
-
if (membership.state === "pending") {
|
|
2086
|
-
return createOrgMembershipResult(org, "pending", resp.status, membership);
|
|
2087
|
-
}
|
|
2088
|
-
if (membership.state === "active") {
|
|
2089
|
-
return createOrgMembershipResult(org, "active", resp.status, membership);
|
|
2090
|
-
}
|
|
2091
|
-
return createOrgMembershipResult(org, "not_member", resp.status, membership);
|
|
1939
|
+
const normalizedOrg = org.trim().toLowerCase();
|
|
1940
|
+
const resp = await fetch("https://api.github.com/user/memberships/orgs", { headers: {
|
|
1941
|
+
Authorization: `token ${token}`,
|
|
1942
|
+
Accept: "application/vnd.github+json",
|
|
1943
|
+
"X-GitHub-Api-Version": "2022-11-28"
|
|
1944
|
+
} });
|
|
1945
|
+
if (resp.status === 401) return createOrgMembershipResult(org, "unauthorized", resp.status);
|
|
1946
|
+
if (resp.status === 403) return createOrgMembershipResult(org, "forbidden", resp.status);
|
|
1947
|
+
if (!resp.ok) {
|
|
1948
|
+
let body = "";
|
|
1949
|
+
try {
|
|
1950
|
+
body = await resp.text();
|
|
1951
|
+
} catch (_e) {
|
|
1952
|
+
body = "[Failed to read response body]";
|
|
1953
|
+
}
|
|
1954
|
+
throw new Error(`GitHub API Error: ${resp.status} ${resp.statusText} - ${body}`);
|
|
1955
|
+
}
|
|
1956
|
+
const membership = (await resp.json()).find((entry) => entry.organization?.login?.toLowerCase() === normalizedOrg);
|
|
1957
|
+
if (!membership) return createOrgMembershipResult(org, "not_member", resp.status);
|
|
1958
|
+
if (membership.state === "pending") return createOrgMembershipResult(org, "pending", resp.status, membership);
|
|
1959
|
+
if (membership.state === "active") return createOrgMembershipResult(org, "active", resp.status, membership);
|
|
1960
|
+
return createOrgMembershipResult(org, "not_member", resp.status, membership);
|
|
2092
1961
|
}
|
|
1962
|
+
/**
|
|
1963
|
+
* Check if the token owner is authorized for a given GitHub organization.
|
|
1964
|
+
* Uses the authenticated user's own token — works for both public and private membership.
|
|
1965
|
+
* Returns true if the user is active or has a pending invitation, false otherwise.
|
|
1966
|
+
* @param token GitHub Personal Access Token or OAuth Access Token
|
|
1967
|
+
* @param org GitHub organization name
|
|
1968
|
+
*/
|
|
2093
1969
|
async function checkGitHubOrgMembership(token, org) {
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
}
|
|
2098
|
-
return membership.status === "active" || membership.status === "pending";
|
|
1970
|
+
const membership = await getGitHubOrgMembership(token, org);
|
|
1971
|
+
if (membership.status === "forbidden" || membership.status === "unauthorized") throw new Error(`GitHub org membership indeterminate: HTTP ${membership.httpStatus}`);
|
|
1972
|
+
return membership.status === "active" || membership.status === "pending";
|
|
2099
1973
|
}
|
|
2100
|
-
|
|
2101
|
-
|
|
1974
|
+
//#endregion
|
|
1975
|
+
//#region src/github-user-email.ts
|
|
2102
1976
|
function isGitHubLocalEmail(email) {
|
|
2103
|
-
|
|
1977
|
+
return typeof email === "string" && email.trim().toLowerCase().endsWith("@github.local");
|
|
2104
1978
|
}
|
|
2105
1979
|
function buildGitHubLocalEmail(login) {
|
|
2106
|
-
|
|
1980
|
+
return `${login}@github.local`;
|
|
2107
1981
|
}
|
|
2108
1982
|
function resolvePrimaryEmail(login, email) {
|
|
2109
|
-
|
|
2110
|
-
return normalizedEmail || buildGitHubLocalEmail(login);
|
|
1983
|
+
return email?.trim() || buildGitHubLocalEmail(login);
|
|
2111
1984
|
}
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
1985
|
+
//#endregion
|
|
1986
|
+
//#region src/logger/index.ts
|
|
1987
|
+
/**
|
|
1988
|
+
* Unified, environment-agnostic logger contract for the SERVICEME monorepo.
|
|
1989
|
+
*
|
|
1990
|
+
* This module intentionally has NO dependency on `vscode`, the extension
|
|
1991
|
+
* runtime, or `@serviceme/devtools-core` so it can be consumed by every
|
|
1992
|
+
* package (shared → protocol → core → cli → webview → extension → server)
|
|
1993
|
+
* without creating cycles or pulling in heavyweight environment-specific
|
|
1994
|
+
* APIs.
|
|
1995
|
+
*
|
|
1996
|
+
* Consumers route their telemetry through an {@link ILogger}:
|
|
1997
|
+
* - the extension uses the OutputChannel + file-backed `Logger` (see
|
|
1998
|
+
* `apps/extension/src/core/logger/Logger.ts`);
|
|
1999
|
+
* - server / cli / webview use the console / postMessage-backed
|
|
2000
|
+
* implementations provided by their own package, or the
|
|
2001
|
+
* {@link createConsoleLogger} fallback defined here.
|
|
2002
|
+
*/
|
|
2003
|
+
/** Severity levels, ordered low → high. */
|
|
2004
|
+
let LogLevel = /* @__PURE__ */ function(LogLevel) {
|
|
2005
|
+
LogLevel[LogLevel["DEBUG"] = 0] = "DEBUG";
|
|
2006
|
+
LogLevel[LogLevel["INFO"] = 1] = "INFO";
|
|
2007
|
+
LogLevel[LogLevel["WARN"] = 2] = "WARN";
|
|
2008
|
+
LogLevel[LogLevel["ERROR"] = 3] = "ERROR";
|
|
2009
|
+
return LogLevel;
|
|
2010
|
+
}({});
|
|
2011
|
+
/**
|
|
2012
|
+
* Normalize any thrown value into a serializable record for logging.
|
|
2013
|
+
*
|
|
2014
|
+
* Handles `Error` (canonical fields + protocol-level extras), strings,
|
|
2015
|
+
* primitives, and plain objects, so log sinks can `JSON.stringify` the
|
|
2016
|
+
* result without throwing on circular refs or dropping context. This is a
|
|
2017
|
+
* pure function with no environment dependencies and was promoted from
|
|
2018
|
+
* `apps/extension/src/core/logger/Logger.ts` so every package shares one
|
|
2019
|
+
* normalization path.
|
|
2020
|
+
*/
|
|
2121
2021
|
function normalizeErrorForLog(error) {
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
}
|
|
2164
|
-
return result;
|
|
2165
|
-
}
|
|
2166
|
-
return { kind: t, value: String(error) };
|
|
2022
|
+
if (error === null || error === void 0) return {
|
|
2023
|
+
kind: "nullish",
|
|
2024
|
+
value: null
|
|
2025
|
+
};
|
|
2026
|
+
const t = typeof error;
|
|
2027
|
+
if (t === "string") return {
|
|
2028
|
+
kind: "string",
|
|
2029
|
+
message: error
|
|
2030
|
+
};
|
|
2031
|
+
if (t === "number" || t === "boolean" || t === "bigint") return {
|
|
2032
|
+
kind: t,
|
|
2033
|
+
value: String(error)
|
|
2034
|
+
};
|
|
2035
|
+
if (t === "object") {
|
|
2036
|
+
const obj = error;
|
|
2037
|
+
if (error instanceof Error) {
|
|
2038
|
+
const result = {
|
|
2039
|
+
kind: "error",
|
|
2040
|
+
name: error.name,
|
|
2041
|
+
message: error.message,
|
|
2042
|
+
stack: error.stack
|
|
2043
|
+
};
|
|
2044
|
+
if (typeof obj.code === "string") result.code = obj.code;
|
|
2045
|
+
if (typeof obj.retryable === "boolean") result.retryable = obj.retryable;
|
|
2046
|
+
if ("details" in obj) result.details = obj.details;
|
|
2047
|
+
if (typeof obj.cause !== "undefined") result.cause = normalizeErrorForLog(obj.cause);
|
|
2048
|
+
return result;
|
|
2049
|
+
}
|
|
2050
|
+
const result = { kind: "object" };
|
|
2051
|
+
let captured = 0;
|
|
2052
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
2053
|
+
result[k] = v;
|
|
2054
|
+
captured += 1;
|
|
2055
|
+
}
|
|
2056
|
+
if (captured === 0) result.message = "(empty object)";
|
|
2057
|
+
return result;
|
|
2058
|
+
}
|
|
2059
|
+
return {
|
|
2060
|
+
kind: t,
|
|
2061
|
+
value: String(error)
|
|
2062
|
+
};
|
|
2167
2063
|
}
|
|
2064
|
+
/**
|
|
2065
|
+
* Best-effort check for `NODE_ENV === "production"`.
|
|
2066
|
+
*
|
|
2067
|
+
* Written against `globalThis` (rather than the `process` global) so this
|
|
2068
|
+
* module stays environment-agnostic and type-checks without `@types/node`.
|
|
2069
|
+
* In a browser / webview `globalThis.process` is undefined, so we treat that
|
|
2070
|
+
* as "not production" (verbose logging on) — which matches the prior
|
|
2071
|
+
* always-on `console.*` behavior of the webview.
|
|
2072
|
+
*/
|
|
2168
2073
|
function isProduction() {
|
|
2169
|
-
|
|
2170
|
-
return proc?.env?.NODE_ENV === "production";
|
|
2074
|
+
return globalThis.process?.env?.NODE_ENV === "production";
|
|
2171
2075
|
}
|
|
2076
|
+
/**
|
|
2077
|
+
* Create a console-backed {@link ILogger}.
|
|
2078
|
+
*
|
|
2079
|
+
* - `debug` is gated to `NODE_ENV !== "production"` (verbose traces only in
|
|
2080
|
+
* dev / test), mirroring the server's legacy `logDebug` behavior.
|
|
2081
|
+
* - `info` / `warn` / `error` always emit to the matching `console` method.
|
|
2082
|
+
* - `error`'s leading `error` argument is run through
|
|
2083
|
+
* {@link normalizeErrorForLog} so serialized errors stay structured and
|
|
2084
|
+
* circular-ref safe.
|
|
2085
|
+
*
|
|
2086
|
+
* This is the canonical fallback for packages without a richer sink (cli,
|
|
2087
|
+
* webview bootstrap, extension logger self-diagnostics).
|
|
2088
|
+
*/
|
|
2172
2089
|
function createConsoleLogger(name) {
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
setLevel(next) {
|
|
2209
|
-
level = next;
|
|
2210
|
-
}
|
|
2211
|
-
};
|
|
2090
|
+
let level = isProduction() ? 1 : 0;
|
|
2091
|
+
const emit = (lvl, method, message, args) => {
|
|
2092
|
+
if (lvl < level) return;
|
|
2093
|
+
const prefixed = `[${name}] ${message}`;
|
|
2094
|
+
switch (method) {
|
|
2095
|
+
case "log":
|
|
2096
|
+
console.log(prefixed, ...args);
|
|
2097
|
+
break;
|
|
2098
|
+
case "info":
|
|
2099
|
+
console.info(prefixed, ...args);
|
|
2100
|
+
break;
|
|
2101
|
+
case "warn":
|
|
2102
|
+
console.warn(prefixed, ...args);
|
|
2103
|
+
break;
|
|
2104
|
+
case "error": console.error(prefixed, ...args);
|
|
2105
|
+
}
|
|
2106
|
+
};
|
|
2107
|
+
return {
|
|
2108
|
+
debug(message, ...args) {
|
|
2109
|
+
emit(0, "log", message, args);
|
|
2110
|
+
},
|
|
2111
|
+
info(message, ...args) {
|
|
2112
|
+
emit(1, "info", message, args);
|
|
2113
|
+
},
|
|
2114
|
+
warn(message, ...args) {
|
|
2115
|
+
emit(2, "warn", message, args);
|
|
2116
|
+
},
|
|
2117
|
+
error(message, error, ...args) {
|
|
2118
|
+
const payload = error === void 0 ? args : [normalizeErrorForLog(error), ...args];
|
|
2119
|
+
emit(3, "error", message, payload);
|
|
2120
|
+
},
|
|
2121
|
+
setLevel(next) {
|
|
2122
|
+
level = next;
|
|
2123
|
+
}
|
|
2124
|
+
};
|
|
2212
2125
|
}
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
}
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2126
|
+
//#endregion
|
|
2127
|
+
//#region src/messages.ts
|
|
2128
|
+
let WebviewMessageType = /* @__PURE__ */ function(WebviewMessageType) {
|
|
2129
|
+
WebviewMessageType["WebviewReady"] = "webviewReady";
|
|
2130
|
+
WebviewMessageType["Ready"] = "ready";
|
|
2131
|
+
WebviewMessageType["Log"] = "log";
|
|
2132
|
+
WebviewMessageType["ExecuteCommand"] = "executeCommand";
|
|
2133
|
+
WebviewMessageType["OpenUrl"] = "openUrl";
|
|
2134
|
+
WebviewMessageType["UsePrompt"] = "usePrompt";
|
|
2135
|
+
WebviewMessageType["UpdateAzureProfiles"] = "updateAzureProfiles";
|
|
2136
|
+
WebviewMessageType["UpdateAzureProfile"] = "updateAzureProfile";
|
|
2137
|
+
WebviewMessageType["GetApiConfig"] = "getApiConfig";
|
|
2138
|
+
WebviewMessageType["UpdateApiConfig"] = "updateApiConfig";
|
|
2139
|
+
WebviewMessageType["SaveApiConfig"] = "saveApiConfig";
|
|
2140
|
+
WebviewMessageType["ApiConfigSaveFailed"] = "apiConfigSaveFailed";
|
|
2141
|
+
WebviewMessageType["ExportApiConfig"] = "exportApiConfig";
|
|
2142
|
+
WebviewMessageType["ImportApiConfig"] = "importApiConfig";
|
|
2143
|
+
WebviewMessageType["ApiConfigImported"] = "apiConfigImported";
|
|
2144
|
+
WebviewMessageType["AddExternalTool"] = "addExternalTool";
|
|
2145
|
+
WebviewMessageType["UpdateExternalTool"] = "updateExternalTool";
|
|
2146
|
+
WebviewMessageType["UpdateExternalTools"] = "updateExternalTools";
|
|
2147
|
+
WebviewMessageType["DeleteExternalTool"] = "deleteExternalTool";
|
|
2148
|
+
WebviewMessageType["ReorderExternalTools"] = "reorderExternalTools";
|
|
2149
|
+
WebviewMessageType["UpdateNgrokStatus"] = "updateNgrokStatus";
|
|
2150
|
+
WebviewMessageType["UpdateServerStatus"] = "updateServerStatus";
|
|
2151
|
+
WebviewMessageType["UpdateOcxStatus"] = "updateOcxStatus";
|
|
2152
|
+
WebviewMessageType["UpdateRtkStatus"] = "updateRtkStatus";
|
|
2153
|
+
WebviewMessageType["UpdateCodegraphStatus"] = "updateCodegraphStatus";
|
|
2154
|
+
WebviewMessageType["GetAuthState"] = "getAuthState";
|
|
2155
|
+
WebviewMessageType["UpdateAuthState"] = "updateAuthState";
|
|
2156
|
+
WebviewMessageType["Login"] = "login";
|
|
2157
|
+
WebviewMessageType["Logout"] = "logout";
|
|
2158
|
+
WebviewMessageType["GetAccounts"] = "getAccounts";
|
|
2159
|
+
WebviewMessageType["UpdateAccounts"] = "updateAccounts";
|
|
2160
|
+
WebviewMessageType["SwitchAccount"] = "switchAccount";
|
|
2161
|
+
WebviewMessageType["GetUserProfile"] = "getUserProfile";
|
|
2162
|
+
WebviewMessageType["UpdateUserProfile"] = "updateUserProfile";
|
|
2163
|
+
WebviewMessageType["UserProfileUpdated"] = "userProfileUpdated";
|
|
2164
|
+
WebviewMessageType["AddExtraEmail"] = "addExtraEmail";
|
|
2165
|
+
WebviewMessageType["DeleteExtraEmail"] = "deleteExtraEmail";
|
|
2166
|
+
WebviewMessageType["GetCalendarHolidays"] = "getCalendarHolidays";
|
|
2167
|
+
WebviewMessageType["UpdateCalendarHolidays"] = "updateCalendarHolidays";
|
|
2168
|
+
WebviewMessageType["GetCalendarLeaves"] = "getCalendarLeaves";
|
|
2169
|
+
WebviewMessageType["UpdateCalendarLeaves"] = "updateCalendarLeaves";
|
|
2170
|
+
WebviewMessageType["CreateCalendarLeave"] = "createCalendarLeave";
|
|
2171
|
+
WebviewMessageType["DeleteCalendarLeave"] = "deleteCalendarLeave";
|
|
2172
|
+
WebviewMessageType["GetCalendarNotes"] = "getCalendarNotes";
|
|
2173
|
+
WebviewMessageType["UpdateCalendarNotes"] = "updateCalendarNotes";
|
|
2174
|
+
WebviewMessageType["SaveCalendarNote"] = "saveCalendarNote";
|
|
2175
|
+
WebviewMessageType["GetCalendarMonthSchedule"] = "getCalendarMonthSchedule";
|
|
2176
|
+
WebviewMessageType["GetScheduledTasks"] = "getScheduledTasks";
|
|
2177
|
+
WebviewMessageType["UpdateScheduledTasks"] = "updateScheduledTasks";
|
|
2178
|
+
WebviewMessageType["CreateScheduledTask"] = "createScheduledTask";
|
|
2179
|
+
WebviewMessageType["EditScheduledTask"] = "editScheduledTask";
|
|
2180
|
+
WebviewMessageType["DeleteScheduledTask"] = "deleteScheduledTask";
|
|
2181
|
+
WebviewMessageType["ToggleScheduledTask"] = "toggleScheduledTask";
|
|
2182
|
+
WebviewMessageType["TriggerScheduledTask"] = "triggerScheduledTask";
|
|
2183
|
+
WebviewMessageType["CancelTaskExecution"] = "cancelTaskExecution";
|
|
2184
|
+
WebviewMessageType["GetTaskExecutionLogs"] = "getTaskExecutionLogs";
|
|
2185
|
+
WebviewMessageType["ClearTaskExecutionLogs"] = "clearTaskExecutionLogs";
|
|
2186
|
+
WebviewMessageType["UpdateTaskExecutionLogs"] = "updateTaskExecutionLogs";
|
|
2187
|
+
WebviewMessageType["UpdateTaskExecutionState"] = "updateTaskExecutionState";
|
|
2188
|
+
WebviewMessageType["GetCurrentWorkspace"] = "getCurrentWorkspace";
|
|
2189
|
+
WebviewMessageType["UpdateCurrentWorkspace"] = "updateCurrentWorkspace";
|
|
2190
|
+
WebviewMessageType["GetProviders"] = "getProviders";
|
|
2191
|
+
WebviewMessageType["ProvidersResponse"] = "providersResponse";
|
|
2192
|
+
WebviewMessageType["AddProvider"] = "addProvider";
|
|
2193
|
+
WebviewMessageType["UpdateProvider"] = "updateProvider";
|
|
2194
|
+
WebviewMessageType["RemoveProvider"] = "removeProvider";
|
|
2195
|
+
WebviewMessageType["SetDefaultProvider"] = "setDefaultProvider";
|
|
2196
|
+
WebviewMessageType["SetProviderEnabled"] = "setProviderEnabled";
|
|
2197
|
+
WebviewMessageType["SetProviderOrder"] = "setProviderOrder";
|
|
2198
|
+
WebviewMessageType["TestProvider"] = "testProvider";
|
|
2199
|
+
WebviewMessageType["ProviderTestResultMessage"] = "providerTestResult";
|
|
2200
|
+
WebviewMessageType["TestProviderModel"] = "testProviderModel";
|
|
2201
|
+
WebviewMessageType["ProviderTestModelResultMessage"] = "providerTestModelResult";
|
|
2202
|
+
WebviewMessageType["DefaultProviderChanged"] = "defaultProviderChanged";
|
|
2203
|
+
WebviewMessageType["FetchProviderModels"] = "fetchProviderModels";
|
|
2204
|
+
WebviewMessageType["FetchProviderModelsResult"] = "fetchProviderModelsResult";
|
|
2205
|
+
WebviewMessageType["GetProviderUsage"] = "getProviderUsage";
|
|
2206
|
+
WebviewMessageType["ProviderUsageResponse"] = "providerUsageResponse";
|
|
2207
|
+
WebviewMessageType["SetCacheControlEnabled"] = "setCacheControlEnabled";
|
|
2208
|
+
WebviewMessageType["GetByomSettings"] = "getByomSettings";
|
|
2209
|
+
WebviewMessageType["ByomSettingsResponse"] = "byomSettingsResponse";
|
|
2210
|
+
WebviewMessageType["ListSkillRepoEntries"] = "listSkillRepoEntries";
|
|
2211
|
+
WebviewMessageType["GetSkillRepoEntry"] = "getSkillRepoEntry";
|
|
2212
|
+
WebviewMessageType["InstallSkillRepoEntry"] = "installSkillRepoEntry";
|
|
2213
|
+
WebviewMessageType["ConvertSkillRepoEntryToSymlink"] = "convertSkillRepoEntryToSymlink";
|
|
2214
|
+
WebviewMessageType["UninstallSkillRepoEntry"] = "uninstallSkillRepoEntry";
|
|
2215
|
+
WebviewMessageType["SetSkillRepoEntryEnabled"] = "setSkillRepoEntryEnabled";
|
|
2216
|
+
WebviewMessageType["ListLinkedSkills"] = "listLinkedSkills";
|
|
2217
|
+
WebviewMessageType["UpdateSkillRepoCatalog"] = "updateSkillRepoCatalog";
|
|
2218
|
+
WebviewMessageType["UpdateSkillRepoInstall"] = "updateSkillRepoInstall";
|
|
2219
|
+
WebviewMessageType["UpdateLinkedSkills"] = "updateLinkedSkills";
|
|
2220
|
+
WebviewMessageType["CreateSkillRepoDraft"] = "createSkillRepoDraft";
|
|
2221
|
+
WebviewMessageType["CommitSkillRepoDraft"] = "commitSkillRepoDraft";
|
|
2222
|
+
WebviewMessageType["ListSkillRepoDrafts"] = "listSkillRepoDrafts";
|
|
2223
|
+
WebviewMessageType["DeleteSkillRepoDraft"] = "deleteSkillRepoDraft";
|
|
2224
|
+
WebviewMessageType["UpdateSkillRepoDraft"] = "updateSkillRepoDraft";
|
|
2225
|
+
WebviewMessageType["ListRepositories"] = "listRepositories";
|
|
2226
|
+
WebviewMessageType["AddRepository"] = "addRepository";
|
|
2227
|
+
WebviewMessageType["UpdateRepository"] = "updateRepository";
|
|
2228
|
+
WebviewMessageType["RemoveRepository"] = "removeRepository";
|
|
2229
|
+
WebviewMessageType["EnableRepository"] = "enableRepository";
|
|
2230
|
+
WebviewMessageType["DisableRepository"] = "disableRepository";
|
|
2231
|
+
WebviewMessageType["SyncRepository"] = "syncRepository";
|
|
2232
|
+
WebviewMessageType["SyncAllRepositories"] = "syncAllRepositories";
|
|
2233
|
+
WebviewMessageType["ResetParseCache"] = "resetParseCache";
|
|
2234
|
+
WebviewMessageType["ListCopilotPlugins"] = "listCopilotPlugins";
|
|
2235
|
+
WebviewMessageType["RegisterCopilotPlugin"] = "registerCopilotPlugin";
|
|
2236
|
+
WebviewMessageType["UnregisterCopilotPlugin"] = "unregisterCopilotPlugin";
|
|
2237
|
+
WebviewMessageType["SetCopilotPluginEnabled"] = "setCopilotPluginEnabled";
|
|
2238
|
+
WebviewMessageType["GetCopilotContentStatus"] = "getCopilotContentStatus";
|
|
2239
|
+
WebviewMessageType["RestoreCopilotContent"] = "restoreCopilotContent";
|
|
2240
|
+
WebviewMessageType["ApproveCopilotContent"] = "approveCopilotContent";
|
|
2241
|
+
WebviewMessageType["UpdateRepositoryList"] = "updateRepositoryList";
|
|
2242
|
+
WebviewMessageType["UpdateRepositoryAdd"] = "updateRepositoryAdd";
|
|
2243
|
+
WebviewMessageType["UpdateRepositoryUpdate"] = "updateRepositoryUpdate";
|
|
2244
|
+
WebviewMessageType["UpdateRepositoryRemove"] = "updateRepositoryRemove";
|
|
2245
|
+
WebviewMessageType["UpdateRepositoryToggle"] = "updateRepositoryToggle";
|
|
2246
|
+
WebviewMessageType["UpdateRepositorySyncResult"] = "updateRepositorySyncResult";
|
|
2247
|
+
WebviewMessageType["UpdateCopilotContentStatus"] = "updateCopilotContentStatus";
|
|
2248
|
+
WebviewMessageType["GetCopilotCustomizations"] = "getCopilotCustomizations";
|
|
2249
|
+
WebviewMessageType["SyncCopilotCustomizations"] = "syncCopilotCustomizations";
|
|
2250
|
+
WebviewMessageType["UpdateCopilotCustomizations"] = "updateCopilotCustomizations";
|
|
2251
|
+
WebviewMessageType["InstallCopilotPackage"] = "installCopilotPackage";
|
|
2252
|
+
WebviewMessageType["MoveCopilotPackage"] = "moveCopilotPackage";
|
|
2253
|
+
WebviewMessageType["UninstallCopilotPackage"] = "uninstallCopilotPackage";
|
|
2254
|
+
WebviewMessageType["ListCopilotSources"] = "listCopilotSources";
|
|
2255
|
+
WebviewMessageType["RemoveCopilotSource"] = "removeCopilotSource";
|
|
2256
|
+
WebviewMessageType["PreviewCopilotUpdate"] = "previewCopilotUpdate";
|
|
2257
|
+
WebviewMessageType["UpdateCopilotPackage"] = "updateCopilotPackage";
|
|
2258
|
+
WebviewMessageType["PreviewCopilotLegacy"] = "previewCopilotLegacy";
|
|
2259
|
+
WebviewMessageType["MigrateCopilotLegacy"] = "migrateCopilotLegacy";
|
|
2260
|
+
WebviewMessageType["UpdateCopilotSources"] = "updateCopilotSources";
|
|
2261
|
+
WebviewMessageType["UpdateCopilotUpdatePreview"] = "updateCopilotUpdatePreview";
|
|
2262
|
+
WebviewMessageType["UpdateCopilotLegacyPreview"] = "updateCopilotLegacyPreview";
|
|
2263
|
+
WebviewMessageType["GetUtilityModels"] = "getUtilityModels";
|
|
2264
|
+
WebviewMessageType["UtilityModelsResponse"] = "utilityModelsResponse";
|
|
2265
|
+
WebviewMessageType["UpdateUtilityModels"] = "updateUtilityModels";
|
|
2266
|
+
WebviewMessageType["GetServerProxyState"] = "getServerProxyState";
|
|
2267
|
+
WebviewMessageType["ServerProxyStateResponse"] = "serverProxyStateResponse";
|
|
2268
|
+
WebviewMessageType["SetServerProxyEnabled"] = "setServerProxyEnabled";
|
|
2269
|
+
WebviewMessageType["SetServerProxyAllowOverride"] = "setServerProxyAllowOverride";
|
|
2270
|
+
WebviewMessageType["GetCachedServerUrl"] = "getCachedServerUrl";
|
|
2271
|
+
WebviewMessageType["CachedServerUrlResponse"] = "cachedServerUrlResponse";
|
|
2272
|
+
return WebviewMessageType;
|
|
2273
|
+
}({});
|
|
2274
|
+
/**
|
|
2275
|
+
* Top-level const aliases for the BYO Utility Models message-type
|
|
2276
|
+
* members above. Re-exported as `export const` (rather than just enum
|
|
2277
|
+
* members) because `@serviceme/devtools-shared` ships as CommonJS — bare
|
|
2278
|
+
* `import { GetUtilityModels } from "@serviceme/devtools-shared"` from an ESM
|
|
2279
|
+
* module resolves to `undefined` unless the binding is also exported
|
|
2280
|
+
* as a top-level const. The webview's vitest tests compare against
|
|
2281
|
+
* these by reference; without the const aliases, every
|
|
2282
|
+
* `c[0] === UpdateUtilityModels` check matches the mount-time
|
|
2283
|
+
* `vscode.post(GetUtilityModels)` call (because `undefined ===
|
|
2284
|
+
* undefined` is true). Keep both the enum members AND the consts in
|
|
2285
|
+
* sync; Task 5 (extension handler) uses the enum members, the webview
|
|
2286
|
+
* component + tests use the consts.
|
|
2287
|
+
*/
|
|
2288
|
+
const GetUtilityModels = "getUtilityModels";
|
|
2289
|
+
const UtilityModelsResponse = "utilityModelsResponse";
|
|
2290
|
+
const UpdateUtilityModels = "updateUtilityModels";
|
|
2291
|
+
const SetCacheControlEnabled = "setCacheControlEnabled";
|
|
2292
|
+
const GetByomSettings = "getByomSettings";
|
|
2293
|
+
const ByomSettingsResponse = "byomSettingsResponse";
|
|
2294
|
+
const GetServerProxyState = "getServerProxyState";
|
|
2295
|
+
const ServerProxyStateResponse = "serverProxyStateResponse";
|
|
2296
|
+
const SetServerProxyEnabled = "setServerProxyEnabled";
|
|
2297
|
+
const SetServerProxyAllowOverride = "setServerProxyAllowOverride";
|
|
2298
|
+
const GetCachedServerUrl = "getCachedServerUrl";
|
|
2299
|
+
const CachedServerUrlResponse = "cachedServerUrlResponse";
|
|
2300
|
+
//#endregion
|
|
2301
|
+
//#region src/utils/safe-cast.ts
|
|
2302
|
+
/**
|
|
2303
|
+
* Environment-agnostic cast helpers.
|
|
2304
|
+
*
|
|
2305
|
+
* These centralize the handful of `as unknown as` / `JSON.parse` fallbacks
|
|
2306
|
+
* that used to be scattered across the codebase. They are intentionally
|
|
2307
|
+
* thin wrappers that preserve the exact runtime behavior of the original
|
|
2308
|
+
* inline casts — they exist for consistency and discoverability, not to
|
|
2309
|
+
* change semantics. No `protocol`-level imports are used here so the helpers
|
|
2310
|
+
* stay usable from any package (extension, server, webview) without pulling
|
|
2311
|
+
* in transport types.
|
|
2312
|
+
*/
|
|
2313
|
+
/**
|
|
2314
|
+
* Narrow an unknown payload into a typed shape.
|
|
2315
|
+
*
|
|
2316
|
+
* Equivalent to `raw as T`. Retains the original "blind cast" semantics used
|
|
2317
|
+
* for scheduled-task payloads: callers own the contract and we do not validate
|
|
2318
|
+
* the runtime shape here. Keeping the cast in one place makes the intent
|
|
2319
|
+
* (and the assumption) explicit and grep-able.
|
|
2320
|
+
*/
|
|
2375
2321
|
function parsePayload(raw) {
|
|
2376
|
-
|
|
2322
|
+
return raw;
|
|
2377
2323
|
}
|
|
2324
|
+
/**
|
|
2325
|
+
* Best-effort extraction of an `AbortSignal` from an inbound request object.
|
|
2326
|
+
*
|
|
2327
|
+
* The original code read `req.signal` via `req as unknown as { signal?: AbortSignal }`,
|
|
2328
|
+
* which would return whatever sat on `.signal` — including a non-`AbortSignal`
|
|
2329
|
+
* value. To avoid leaking an invalid signal into downstream `fetch`/`undici`
|
|
2330
|
+
* calls (where a non-`AbortSignal` signal throws), we only return the value when
|
|
2331
|
+
* it is a genuine `AbortSignal` instance; otherwise we return `undefined`, which
|
|
2332
|
+
* is the same as "no signal". In practice the request signal is always a real
|
|
2333
|
+
* `AbortSignal`, so behavior is unchanged for every production path.
|
|
2334
|
+
*/
|
|
2378
2335
|
function asAbortSignal(input) {
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
}
|
|
2384
|
-
}
|
|
2385
|
-
return void 0;
|
|
2336
|
+
if (input !== null && typeof input === "object") {
|
|
2337
|
+
const candidate = input.signal;
|
|
2338
|
+
if (candidate instanceof AbortSignal) return candidate;
|
|
2339
|
+
}
|
|
2386
2340
|
}
|
|
2341
|
+
/**
|
|
2342
|
+
* Parse a JSON string, returning `fallback` when parsing fails.
|
|
2343
|
+
*
|
|
2344
|
+
* Equivalent to wrapping `JSON.parse(text)` in a try/catch. Used to replace
|
|
2345
|
+
* the previous `response.json().catch(() => ({}))` patterns (callers pair this
|
|
2346
|
+
* with their own `.catch` so that a body-read failure still yields the same
|
|
2347
|
+
* fallback as a malformed-body failure).
|
|
2348
|
+
*/
|
|
2387
2349
|
function safeJson(text, fallback) {
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2350
|
+
try {
|
|
2351
|
+
return JSON.parse(text);
|
|
2352
|
+
} catch {
|
|
2353
|
+
return fallback;
|
|
2354
|
+
}
|
|
2393
2355
|
}
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
});
|
|
2356
|
+
//#endregion
|
|
2357
|
+
exports.BUILTIN_PROVIDER_PRESETS = BUILTIN_PROVIDER_PRESETS;
|
|
2358
|
+
exports.ByomSettingsResponse = ByomSettingsResponse;
|
|
2359
|
+
exports.CERTIFICATE_BUNDLE_FORMATS = CERTIFICATE_BUNDLE_FORMATS;
|
|
2360
|
+
exports.CachedServerUrlResponse = CachedServerUrlResponse;
|
|
2361
|
+
exports.DeviceAuthHeaders = DeviceAuthHeaders;
|
|
2362
|
+
exports.GIT_REMOTE_HOST_ALIASES = GIT_REMOTE_HOST_ALIASES;
|
|
2363
|
+
exports.GetByomSettings = GetByomSettings;
|
|
2364
|
+
exports.GetCachedServerUrl = GetCachedServerUrl;
|
|
2365
|
+
exports.GetServerProxyState = GetServerProxyState;
|
|
2366
|
+
exports.GetUtilityModels = GetUtilityModels;
|
|
2367
|
+
exports.LISTABLE_PRESET_MODELS = LISTABLE_PRESET_MODELS;
|
|
2368
|
+
exports.LogLevel = LogLevel;
|
|
2369
|
+
exports.MEDALSOFT_NUGET_PRIVATE_SOURCE = MEDALSOFT_NUGET_PRIVATE_SOURCE;
|
|
2370
|
+
exports.MEDALSOFT_PRIVATE_GATEWAY_URL = MEDALSOFT_PRIVATE_GATEWAY_URL;
|
|
2371
|
+
exports.MODEL_METADATA = MODEL_METADATA;
|
|
2372
|
+
exports.NAMESPACE_ALIASES = NAMESPACE_ALIASES;
|
|
2373
|
+
exports.NAMESPACE_ALIAS_FAMILY = NAMESPACE_ALIAS_FAMILY;
|
|
2374
|
+
exports.PRESET_MODEL_FAMILIES = PRESET_MODEL_FAMILIES;
|
|
2375
|
+
exports.PROVIDER_BASE_URL_PRESETS = PROVIDER_BASE_URL_PRESETS;
|
|
2376
|
+
exports.PROVIDER_CACHE_CONTROL_METADATA = PROVIDER_CACHE_CONTROL_METADATA;
|
|
2377
|
+
exports.ServerProxyStateResponse = ServerProxyStateResponse;
|
|
2378
|
+
exports.SetCacheControlEnabled = SetCacheControlEnabled;
|
|
2379
|
+
exports.SetServerProxyAllowOverride = SetServerProxyAllowOverride;
|
|
2380
|
+
exports.SetServerProxyEnabled = SetServerProxyEnabled;
|
|
2381
|
+
exports.UpdateUtilityModels = UpdateUtilityModels;
|
|
2382
|
+
exports.UtilityModelsResponse = UtilityModelsResponse;
|
|
2383
|
+
exports.WebviewMessageType = WebviewMessageType;
|
|
2384
|
+
exports.__internal = __internal;
|
|
2385
|
+
exports.asAbortSignal = asAbortSignal;
|
|
2386
|
+
exports.buildGitHubLocalEmail = buildGitHubLocalEmail;
|
|
2387
|
+
exports.buildPresetModel = buildPresetModel;
|
|
2388
|
+
exports.checkGitHubOrgMembership = checkGitHubOrgMembership;
|
|
2389
|
+
exports.createConsoleLogger = createConsoleLogger;
|
|
2390
|
+
exports.createDeviceRequestSignature = createDeviceRequestSignature;
|
|
2391
|
+
exports.currencyForBaseUrl = currencyForBaseUrl;
|
|
2392
|
+
exports.effectiveAdapterType = effectiveAdapterType;
|
|
2393
|
+
exports.fetchGitHubUser = fetchGitHubUser;
|
|
2394
|
+
exports.getBuiltinProviderPreset = getBuiltinProviderPreset;
|
|
2395
|
+
exports.getGitHubOrgMembership = getGitHubOrgMembership;
|
|
2396
|
+
exports.getPresetModelDisplayName = getPresetModelDisplayName;
|
|
2397
|
+
exports.getProviderBaseUrlPresets = getProviderBaseUrlPresets;
|
|
2398
|
+
exports.isGitHubLocalEmail = isGitHubLocalEmail;
|
|
2399
|
+
exports.isProviderCacheControlAware = isProviderCacheControlAware;
|
|
2400
|
+
exports.isValidCanonicalSlug = isValidCanonicalSlug;
|
|
2401
|
+
exports.isWholePackageInstall = isWholePackageInstall;
|
|
2402
|
+
exports.listPresetModelGroups = listPresetModelGroups;
|
|
2403
|
+
exports.lookupModelMetadata = lookupModelMetadata;
|
|
2404
|
+
exports.normalizeCanonicalSlug = normalizeCanonicalSlug;
|
|
2405
|
+
exports.normalizeErrorForLog = normalizeErrorForLog;
|
|
2406
|
+
exports.normalizeGitUrl = normalizeGitUrl;
|
|
2407
|
+
exports.parsePayload = parsePayload;
|
|
2408
|
+
exports.protocolForBaseUrl = protocolForBaseUrl;
|
|
2409
|
+
exports.registrationIdFromPackageId = registrationIdFromPackageId;
|
|
2410
|
+
exports.resolvePrimaryEmail = resolvePrimaryEmail;
|
|
2411
|
+
exports.safeJson = safeJson;
|
|
2412
|
+
exports.unionProviderModelWithPreset = unionProviderModelWithPreset;
|