@jingyi0605/codingns4dsh 0.1.0 → 0.1.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/README.en.md +306 -0
- package/README.md +190 -993
- package/data/build/dist/client/account-bar.d.ts +2 -2
- package/data/build/dist/client/account-bar.js +96 -39
- package/data/build/dist/client/bundle.js +13722 -3611
- package/data/build/dist/client/cli-catalog.js +20 -3
- package/data/build/dist/client/cli-slots.d.ts +5 -0
- package/data/build/dist/client/cli-slots.js +57 -7
- package/data/build/dist/client/debug/ui.d.ts +1 -1
- package/data/build/dist/client/debug/ui.js +62 -32
- package/data/build/dist/client/dsh-runtime-version.d.ts +9 -2
- package/data/build/dist/client/dsh-runtime-version.js +24 -2
- package/data/build/dist/client/features/cli-adapters.d.ts +1 -1
- package/data/build/dist/client/features/cli-adapters.js +19 -16
- package/data/build/dist/client/features/debug.js +2 -2
- package/data/build/dist/client/features/index.d.ts +2 -1
- package/data/build/dist/client/features/index.js +3 -1
- package/data/build/dist/client/features/lan-access-panel.d.ts +1 -1
- package/data/build/dist/client/features/lan-access-panel.js +8 -10
- package/data/build/dist/client/features/lan-access.js +1 -1
- package/data/build/dist/client/features/login-protection-panel.d.ts +1 -1
- package/data/build/dist/client/features/login-protection-panel.js +7 -9
- package/data/build/dist/client/features/reverse-proxy-panel.d.ts +1 -1
- package/data/build/dist/client/features/reverse-proxy-panel.js +99 -11
- package/data/build/dist/client/features/reverse-proxy.d.ts +1 -1
- package/data/build/dist/client/features/reverse-proxy.js +19 -18
- package/data/build/dist/client/features/terminal-enhancement-panel.d.ts +1 -1
- package/data/build/dist/client/features/terminal-enhancement-panel.js +62 -55
- package/data/build/dist/client/features/terminal-enhancement.d.ts +1 -1
- package/data/build/dist/client/features/terminal-enhancement.js +3 -3
- package/data/build/dist/client/features/types.d.ts +14 -3
- package/data/build/dist/client/features/workspace-session-enhancement-panel.d.ts +1 -1
- package/data/build/dist/client/features/workspace-session-enhancement-panel.js +31 -9
- package/data/build/dist/client/features/workspace-session-enhancement.js +72 -11
- package/data/build/dist/client/git-management.d.ts +16 -0
- package/data/build/dist/client/git-management.js +757 -0
- package/data/build/dist/client/index.d.ts +3 -2
- package/data/build/dist/client/index.js +152 -27
- package/data/build/dist/client/locale.js +91 -29
- package/data/build/dist/client/quick-phrase-layout.d.ts +49 -0
- package/data/build/dist/client/quick-phrase-layout.js +95 -0
- package/data/build/dist/client/quick-phrase-slot.d.ts +7 -0
- package/data/build/dist/client/quick-phrase-slot.js +370 -0
- package/data/build/dist/client/remote-web-context.js +1 -1
- package/data/build/dist/client/settings-bridge.d.ts +9 -6
- package/data/build/dist/client/settings-bridge.js +51 -27
- package/data/build/dist/client/settings-section.d.ts +3 -2
- package/data/build/dist/client/settings-section.js +33 -19
- package/data/build/dist/client/subscription-slot.d.ts +3 -0
- package/data/build/dist/client/subscription-slot.js +108 -16
- package/data/build/dist/client/terminal/model.d.ts +14 -2
- package/data/build/dist/client/terminal/model.js +109 -21
- package/data/build/dist/client/terminal/recovery.d.ts +50 -0
- package/data/build/dist/client/terminal/recovery.js +101 -0
- package/data/build/dist/client/terminal/ui.d.ts +2 -2
- package/data/build/dist/client/terminal/ui.js +49 -11
- package/data/build/dist/client/terminal/xterm-view.d.ts +2 -2
- package/data/build/dist/client/terminal/xterm-view.js +2 -1
- package/data/build/dist/client/theme.d.ts +6 -1
- package/data/build/dist/client/theme.js +32 -1
- package/data/build/dist/client/workspace-session-archive-dom.d.ts +12 -0
- package/data/build/dist/client/workspace-session-archive-dom.js +27 -5
- package/data/build/dist/client/workspace-session-visibility-dom.d.ts +30 -0
- package/data/build/dist/client/workspace-session-visibility-dom.js +604 -0
- package/data/build/dist/dsh-capabilities/client/config-forms-adapter.d.ts +50 -0
- package/data/build/dist/dsh-capabilities/client/config-forms-adapter.js +139 -0
- package/data/build/dist/dsh-capabilities/client/conversation-adapter.d.ts +7 -0
- package/data/build/dist/dsh-capabilities/client/conversation-adapter.js +6 -0
- package/data/build/dist/dsh-capabilities/client/locale-adapter.d.ts +9 -0
- package/data/build/dist/dsh-capabilities/client/locale-adapter.js +4 -0
- package/data/build/dist/dsh-capabilities/client/primitives-adapter.d.ts +10 -0
- package/data/build/dist/dsh-capabilities/client/primitives-adapter.js +14 -0
- package/data/build/dist/dsh-capabilities/client/settings-scope-adapter.d.ts +8 -0
- package/data/build/dist/dsh-capabilities/client/settings-scope-adapter.js +16 -0
- package/data/build/dist/dsh-capabilities/client/sidebar-adapter.d.ts +7 -0
- package/data/build/dist/dsh-capabilities/client/sidebar-adapter.js +5 -0
- package/data/build/dist/dsh-capabilities/client/theme-adapter.d.ts +6 -0
- package/data/build/dist/dsh-capabilities/client/theme-adapter.js +4 -0
- package/data/build/dist/dsh-capabilities/client/typert-adapter.d.ts +5 -0
- package/data/build/dist/dsh-capabilities/client/typert-adapter.js +4 -0
- package/data/build/dist/dsh-capabilities/host/config-forms-adapter.d.ts +19 -0
- package/data/build/dist/dsh-capabilities/host/config-forms-adapter.js +45 -0
- package/data/build/dist/dsh-capabilities/host/connection-rpc-adapter.d.ts +19 -0
- package/data/build/dist/dsh-capabilities/host/connection-rpc-adapter.js +13 -0
- package/data/build/dist/dsh-capabilities/host/settings-scope-adapter.d.ts +8 -0
- package/data/build/dist/dsh-capabilities/host/settings-scope-adapter.js +47 -0
- package/data/build/dist/dsh-capabilities/host/typert-adapter.d.ts +6 -0
- package/data/build/dist/dsh-capabilities/host/typert-adapter.js +4 -0
- package/data/build/dist/dsh-capabilities/index.d.ts +6 -0
- package/data/build/dist/dsh-capabilities/index.js +6 -0
- package/data/build/dist/dsh-capabilities/matrix.d.ts +15 -0
- package/data/build/dist/dsh-capabilities/matrix.js +32 -0
- package/data/build/dist/dsh-capabilities/registry.d.ts +22 -0
- package/data/build/dist/dsh-capabilities/registry.js +196 -0
- package/data/build/dist/dsh-capabilities/routes.d.ts +10 -0
- package/data/build/dist/dsh-capabilities/routes.js +34 -0
- package/data/build/dist/dsh-capabilities/settings-store.d.ts +29 -0
- package/data/build/dist/dsh-capabilities/settings-store.js +5 -0
- package/data/build/dist/dsh-capabilities/types.d.ts +50 -0
- package/data/build/dist/dsh-capabilities/types.js +23 -0
- package/data/build/dist/features/registry.d.ts +8 -2
- package/data/build/dist/features/registry.js +104 -1
- package/data/build/dist/host/auth-session.d.ts +8 -1
- package/data/build/dist/host/auth-session.js +28 -6
- package/data/build/dist/host/cli-adapters/claude-driver.d.ts +2 -0
- package/data/build/dist/host/cli-adapters/claude-driver.js +19 -4
- package/data/build/dist/host/cli-adapters/claude-model-options.d.ts +14 -0
- package/data/build/dist/host/cli-adapters/claude-model-options.js +157 -0
- package/data/build/dist/host/cli-adapters/codex-driver.d.ts +11 -0
- package/data/build/dist/host/cli-adapters/codex-driver.js +166 -14
- package/data/build/dist/host/cli-adapters/driver.d.ts +2 -0
- package/data/build/dist/host/cli-adapters/dsh-message-projector.d.ts +15 -0
- package/data/build/dist/host/cli-adapters/dsh-message-projector.js +120 -29
- package/data/build/dist/host/cli-adapters/dsh-tool-history.d.ts +4 -1
- package/data/build/dist/host/cli-adapters/dsh-tool-history.js +25 -7
- package/data/build/dist/host/cli-adapters/feature.js +74 -8
- package/data/build/dist/host/cli-adapters/gemini-driver.js +2 -2
- package/data/build/dist/host/cli-adapters/grok-driver.js +2 -2
- package/data/build/dist/host/cli-adapters/json-rpc-process.d.ts +4 -2
- package/data/build/dist/host/cli-adapters/json-rpc-process.js +8 -9
- package/data/build/dist/host/cli-adapters/kimi-driver.js +1 -1
- package/data/build/dist/host/cli-adapters/legacy-session-adapter.d.ts +17 -0
- package/data/build/dist/host/cli-adapters/legacy-session-adapter.js +88 -0
- package/data/build/dist/host/cli-adapters/legacy-session-settings.d.ts +24 -0
- package/data/build/dist/host/cli-adapters/legacy-session-settings.js +162 -0
- package/data/build/dist/host/cli-adapters/official-provider-subscription.d.ts +29 -0
- package/data/build/dist/host/cli-adapters/official-provider-subscription.js +361 -0
- package/data/build/dist/host/cli-adapters/opencode-driver.d.ts +2 -0
- package/data/build/dist/host/cli-adapters/opencode-driver.js +97 -12
- package/data/build/dist/host/cli-adapters/pi-driver.js +69 -23
- package/data/build/dist/host/cli-adapters/provider-registry.d.ts +33 -0
- package/data/build/dist/host/cli-adapters/provider-registry.js +83 -0
- package/data/build/dist/host/cli-adapters/provider-subscription.d.ts +25 -3
- package/data/build/dist/host/cli-adapters/provider-subscription.js +346 -30
- package/data/build/dist/host/cli-adapters/registry.d.ts +6 -0
- package/data/build/dist/host/cli-adapters/registry.js +170 -13
- package/data/build/dist/host/cli-adapters/rpc-driver-utils.js +10 -0
- package/data/build/dist/host/cli-adapters/session-store.d.ts +7 -0
- package/data/build/dist/host/cli-adapters/session-store.js +39 -1
- package/data/build/dist/host/cli-adapters/standard-stream-driver.d.ts +3 -0
- package/data/build/dist/host/cli-adapters/stream-normalizer.d.ts +3 -0
- package/data/build/dist/host/cli-adapters/stream-normalizer.js +22 -2
- package/data/build/dist/host/control-api-client.d.ts +3 -3
- package/data/build/dist/host/control-api-client.js +2 -1
- package/data/build/dist/host/credential-store.js +11 -1
- package/data/build/dist/host/dsh-device-runtime.d.ts +4 -0
- package/data/build/dist/host/dsh-device-runtime.js +20 -9
- package/data/build/dist/host/features/auth.js +8 -5
- package/data/build/dist/host/features/debug.js +1 -1
- package/data/build/dist/host/features/git-management.d.ts +5 -0
- package/data/build/dist/host/features/git-management.js +290 -0
- package/data/build/dist/host/features/host-status.js +1 -1
- package/data/build/dist/host/features/index.d.ts +1 -0
- package/data/build/dist/host/features/index.js +3 -0
- package/data/build/dist/host/features/terminal-process.js +1 -1
- package/data/build/dist/host/features/terminal-status.d.ts +3 -1
- package/data/build/dist/host/features/terminal-status.js +3 -1
- package/data/build/dist/host/index.d.ts +7 -3
- package/data/build/dist/host/index.js +76 -11
- package/data/build/dist/host/lan-access-dsh.js +18 -5
- package/data/build/dist/host/native-session-bridge.d.ts +23 -1
- package/data/build/dist/host/native-session-bridge.js +157 -10
- package/data/build/dist/host/rpc-table.d.ts +1 -1
- package/data/build/dist/host/rpc.js +60 -10
- package/data/build/dist/host/session-migration-repair.d.ts +19 -0
- package/data/build/dist/host/session-migration-repair.js +395 -0
- package/data/build/dist/host/settings.d.ts +8 -1
- package/data/build/dist/host/settings.js +94 -3
- package/data/build/dist/host/terminal/backends/tmux-backend.d.ts +1 -0
- package/data/build/dist/host/terminal/backends/tmux-backend.js +4 -2
- package/data/build/dist/host/terminal/controller-factory.d.ts +4 -0
- package/data/build/dist/host/terminal/controller-factory.js +26 -7
- package/data/build/dist/host/terminal/runtime-manager.d.ts +2 -1
- package/data/build/dist/host/terminal/runtime-manager.js +3 -0
- package/data/build/dist/host/terminal/startup.d.ts +2 -0
- package/data/build/dist/host/terminal/startup.js +2 -1
- package/data/build/dist/host/terminal/terminal-controller.d.ts +10 -3
- package/data/build/dist/host/terminal/terminal-controller.js +101 -15
- package/data/build/dist/host/terminal/terminal-process-service.js +3 -1
- package/data/build/dist/host/terminal/terminal-service.d.ts +2 -0
- package/data/build/dist/host/terminal/terminal-service.js +10 -0
- package/data/build/dist/index.d.ts +6 -2
- package/data/build/dist/index.js +8 -2
- package/data/build/dist/shared/contracts/cli-adapter.d.ts +14 -0
- package/data/build/dist/shared/contracts/config.d.ts +31 -1
- package/data/build/dist/shared/contracts/config.js +25 -0
- package/data/build/dist/shared/contracts/dsh-device.d.ts +13 -1
- package/data/build/dist/shared/contracts/errors.d.ts +1 -1
- package/data/build/dist/shared/contracts/feature.d.ts +6 -0
- package/data/build/dist/shared/contracts/git.d.ts +82 -0
- package/data/build/dist/shared/contracts/git.js +3 -0
- package/data/build/dist/shared/contracts/subscription.d.ts +46 -0
- package/data/build/dist/shared/contracts/terminal.d.ts +4 -1
- package/data/build/dist/shared/contracts/version.d.ts +2 -2
- package/data/build/dist/shared/contracts/version.js +4 -6
- package/data/build/dist/shared/debug.d.ts +14 -0
- package/data/build/dist/shared/debug.js +53 -0
- package/data/build/dist/shared/index.d.ts +7 -4
- package/data/build/dist/shared/index.js +2 -1
- package/data/build/dist/transport/debug.d.ts +1 -1
- package/data/build/dist/transport/debug.js +3 -30
- package/data/build/dist/transport/dsh-session.js +3 -3
- package/data/build/dist/typert.host.js +4 -2
- package/data/build/dist/typert.remote-client.d.ts +11 -0
- package/data/build/dist/typert.remote-client.js +7 -0
- package/dsh.bundle.patch +1 -1
- package/package.json +17 -5
- package/profile/README.md +4 -4
- package/profile/package.json +6 -4
- package/profile/scripts/check-dsh-install.mjs +2 -2
- package/profile/version.json +2 -2
- package/scripts/check-dsh-install.mjs +2 -2
- package/version.json +2 -2
|
@@ -24,6 +24,7 @@ export class OpenCodeDriver {
|
|
|
24
24
|
/** Provider 会话的实际工作目录;目录变化时禁止复用旧会话。 */
|
|
25
25
|
sessionCwds = new Map();
|
|
26
26
|
interactionTargets = new Map();
|
|
27
|
+
modelContextWindows = new Map();
|
|
27
28
|
constructor(options = {}) {
|
|
28
29
|
this.binaries = options.binaries ?? DEFAULT_BINARIES;
|
|
29
30
|
this.serverUrls = options.serverUrls ?? (process.env.OPENCODE_SERVER_URL ? [process.env.OPENCODE_SERVER_URL] : DEFAULT_URLS);
|
|
@@ -51,6 +52,7 @@ export class OpenCodeDriver {
|
|
|
51
52
|
const response = await this.http.json(`${server}${path}`);
|
|
52
53
|
if (!response.data || response.status < 200 || response.status >= 300)
|
|
53
54
|
continue;
|
|
55
|
+
rememberModelContextWindows(this.modelContextWindows, response.data);
|
|
54
56
|
const catalog = parseModelCatalog(response.data);
|
|
55
57
|
if (catalog.groups.length > 0)
|
|
56
58
|
return catalog;
|
|
@@ -106,6 +108,7 @@ export class OpenCodeDriver {
|
|
|
106
108
|
sessionId = await this.createSession(server, input);
|
|
107
109
|
this.sessions.set(input.sessionId, sessionId);
|
|
108
110
|
}
|
|
111
|
+
const contextWindow = await this.resolveModelContextWindow(server, input.modelId);
|
|
109
112
|
if (input.cwd?.trim())
|
|
110
113
|
this.sessionCwds.set(sessionId, input.cwd.trim());
|
|
111
114
|
this.interactionTargets.set(input.sessionId, server);
|
|
@@ -164,7 +167,7 @@ export class OpenCodeDriver {
|
|
|
164
167
|
const pending = pendingMessageParts.get(messageRole.id) ?? [];
|
|
165
168
|
pendingMessageParts.delete(messageRole.id);
|
|
166
169
|
for (const pendingPart of pending) {
|
|
167
|
-
const chunk = eventToChunk(pendingPart, cumulative, assistantMessageIds, partTypes);
|
|
170
|
+
const chunk = eventToChunk(pendingPart, cumulative, assistantMessageIds, partTypes, contextWindow);
|
|
168
171
|
if (chunk !== null) {
|
|
169
172
|
emitted = true;
|
|
170
173
|
yield chunk;
|
|
@@ -174,6 +177,15 @@ export class OpenCodeDriver {
|
|
|
174
177
|
else {
|
|
175
178
|
pendingMessageParts.delete(messageRole.id);
|
|
176
179
|
}
|
|
180
|
+
// message.updated 同时承载 assistant 的最终 tokens。角色识别后
|
|
181
|
+
// 不能直接跳过,否则 OpenCode 的 token/context 统计会被吞掉。
|
|
182
|
+
if (messageRole.role === 'assistant') {
|
|
183
|
+
const chunk = eventToChunk(parsed, cumulative, assistantMessageIds, partTypes, contextWindow);
|
|
184
|
+
if (chunk !== null) {
|
|
185
|
+
emitted = true;
|
|
186
|
+
yield chunk;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
177
189
|
continue;
|
|
178
190
|
}
|
|
179
191
|
const messageId = eventMessageId(parsed);
|
|
@@ -183,7 +195,7 @@ export class OpenCodeDriver {
|
|
|
183
195
|
// assistant 消息结算后才投影,否则原生工具节点会被追加到正文底部。
|
|
184
196
|
// 用户消息没有 tool 字段,因此仍然暂存并等待 role 校验。
|
|
185
197
|
if (isOpenCodeToolEvent(parsed)) {
|
|
186
|
-
const chunk = eventToChunk(parsed, cumulative, undefined, partTypes);
|
|
198
|
+
const chunk = eventToChunk(parsed, cumulative, undefined, partTypes, contextWindow);
|
|
187
199
|
if (chunk !== null) {
|
|
188
200
|
emitted = true;
|
|
189
201
|
yield chunk;
|
|
@@ -197,7 +209,7 @@ export class OpenCodeDriver {
|
|
|
197
209
|
}
|
|
198
210
|
if (messageId !== undefined && !assistantMessageIds.has(messageId))
|
|
199
211
|
continue;
|
|
200
|
-
const chunk = eventToChunk(parsed, cumulative, assistantMessageIds, partTypes);
|
|
212
|
+
const chunk = eventToChunk(parsed, cumulative, assistantMessageIds, partTypes, contextWindow);
|
|
201
213
|
if (chunk !== null) {
|
|
202
214
|
emitted = true;
|
|
203
215
|
yield chunk;
|
|
@@ -218,7 +230,7 @@ export class OpenCodeDriver {
|
|
|
218
230
|
const response = await send;
|
|
219
231
|
if (sendError !== null && !aborted)
|
|
220
232
|
throw sendError;
|
|
221
|
-
for (const chunk of responseChunks(response, cumulative, partTypes)) {
|
|
233
|
+
for (const chunk of responseChunks(response, cumulative, partTypes, contextWindow)) {
|
|
222
234
|
emitted = true;
|
|
223
235
|
yield chunk;
|
|
224
236
|
}
|
|
@@ -264,6 +276,7 @@ export class OpenCodeDriver {
|
|
|
264
276
|
dispose() {
|
|
265
277
|
this.sessions.clear();
|
|
266
278
|
this.sessionCwds.clear();
|
|
279
|
+
this.modelContextWindows.clear();
|
|
267
280
|
for (const managed of this.managedServers.values())
|
|
268
281
|
terminateChildProcess(managed.child);
|
|
269
282
|
this.managedServers.clear();
|
|
@@ -324,6 +337,23 @@ export class OpenCodeDriver {
|
|
|
324
337
|
}
|
|
325
338
|
catch { /* 取消请求尽力而为 */ }
|
|
326
339
|
}
|
|
340
|
+
async resolveModelContextWindow(server, modelId) {
|
|
341
|
+
if (modelId === undefined)
|
|
342
|
+
return undefined;
|
|
343
|
+
const known = this.modelContextWindows.get(modelId);
|
|
344
|
+
if (known !== undefined)
|
|
345
|
+
return known;
|
|
346
|
+
try {
|
|
347
|
+
const response = await this.http.json(`${server}/config/providers`);
|
|
348
|
+
if (response.status < 200 || response.status >= 300)
|
|
349
|
+
return undefined;
|
|
350
|
+
rememberModelContextWindows(this.modelContextWindows, response.data);
|
|
351
|
+
return this.modelContextWindows.get(modelId);
|
|
352
|
+
}
|
|
353
|
+
catch {
|
|
354
|
+
return undefined;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
327
357
|
async ensureServer(probeOnly, cwd) {
|
|
328
358
|
const workspace = cwd ?? process.cwd();
|
|
329
359
|
const managed = this.managedServers.get(workspace);
|
|
@@ -423,6 +453,43 @@ function parseOpenCodeModel(modelId) {
|
|
|
423
453
|
return null;
|
|
424
454
|
return { providerID: modelId.slice(0, separator), modelID: modelId.slice(separator + 1) };
|
|
425
455
|
}
|
|
456
|
+
/** 从 OpenCode provider 配置缓存每个模型的上下文窗口。 */
|
|
457
|
+
function rememberModelContextWindows(target, value) {
|
|
458
|
+
const root = asRecord(value);
|
|
459
|
+
const rawProviders = root?.providers;
|
|
460
|
+
const providers = [];
|
|
461
|
+
if (Array.isArray(rawProviders)) {
|
|
462
|
+
for (const raw of rawProviders) {
|
|
463
|
+
const provider = asRecord(raw);
|
|
464
|
+
if (provider !== null && typeof provider.id === 'string')
|
|
465
|
+
providers.push([provider.id, provider]);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
else {
|
|
469
|
+
const providerMap = asRecord(rawProviders) ?? root;
|
|
470
|
+
if (providerMap !== null) {
|
|
471
|
+
for (const [id, raw] of Object.entries(providerMap)) {
|
|
472
|
+
const provider = asRecord(raw);
|
|
473
|
+
if (provider !== null)
|
|
474
|
+
providers.push([id, provider]);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
for (const [providerId, provider] of providers) {
|
|
479
|
+
const models = asRecord(provider.models);
|
|
480
|
+
if (models === null)
|
|
481
|
+
continue;
|
|
482
|
+
for (const [modelId, rawModel] of Object.entries(models)) {
|
|
483
|
+
const model = asRecord(rawModel);
|
|
484
|
+
const limit = asRecord(model?.limit);
|
|
485
|
+
const context = limit?.context ?? model?.contextWindow ?? model?.context_window;
|
|
486
|
+
if (typeof context !== 'number' || !Number.isFinite(context) || context <= 0)
|
|
487
|
+
continue;
|
|
488
|
+
target.set(`${providerId}/${modelId}`, context);
|
|
489
|
+
target.set(modelId, context);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
426
493
|
function readProviderDirectory(record) {
|
|
427
494
|
const value = record?.directory ?? record?.cwd;
|
|
428
495
|
return typeof value === 'string' && value.trim() !== '' ? value.trim() : undefined;
|
|
@@ -454,7 +521,7 @@ function parseEvent(event) {
|
|
|
454
521
|
return null;
|
|
455
522
|
}
|
|
456
523
|
}
|
|
457
|
-
function eventToChunk(event, cumulative, assistantMessageIds, partTypes) {
|
|
524
|
+
function eventToChunk(event, cumulative, assistantMessageIds, partTypes, contextWindow) {
|
|
458
525
|
const type = typeof event.type === 'string' ? event.type : '';
|
|
459
526
|
const properties = asRecord(event.properties);
|
|
460
527
|
const part = asRecord(event.part) ?? asRecord(properties?.part) ?? properties ?? event;
|
|
@@ -536,24 +603,42 @@ function eventToChunk(event, cumulative, assistantMessageIds, partTypes) {
|
|
|
536
603
|
...(detail !== undefined ? { detail } : {}),
|
|
537
604
|
};
|
|
538
605
|
}
|
|
539
|
-
const usage = asRecord(event.usage) ?? asRecord(part.usage) ?? readOpenCodeTokenUsage(properties);
|
|
606
|
+
const usage = asRecord(event.usage) ?? asRecord(part.usage) ?? readOpenCodeTokenUsage(properties, contextWindow);
|
|
540
607
|
if (usage !== null)
|
|
541
608
|
return usageChunk(usage);
|
|
542
609
|
return null;
|
|
543
610
|
}
|
|
544
611
|
/** OpenCode 将最终用量放在 message.updated.properties.info.tokens。 */
|
|
545
|
-
function readOpenCodeTokenUsage(properties) {
|
|
612
|
+
function readOpenCodeTokenUsage(properties, contextWindow) {
|
|
546
613
|
const info = asRecord(properties?.info);
|
|
547
614
|
const tokens = asRecord(info?.tokens);
|
|
548
615
|
if (tokens === null)
|
|
549
616
|
return null;
|
|
550
617
|
const cache = asRecord(tokens.cache);
|
|
618
|
+
const model = asRecord(info?.model);
|
|
619
|
+
const modelLimit = asRecord(model?.limit);
|
|
620
|
+
const input = tokens.input ?? tokens.input_tokens;
|
|
621
|
+
const cacheRead = cache?.read ?? tokens.cache_read_tokens ?? tokens.cacheReadTokens;
|
|
622
|
+
const cacheWrite = cache?.write ?? tokens.cache_write_tokens ?? tokens.cacheWriteTokens;
|
|
623
|
+
const embeddedContextWindow = modelLimit?.context
|
|
624
|
+
?? model?.contextWindow
|
|
625
|
+
?? info?.contextWindow
|
|
626
|
+
?? tokens.contextWindow
|
|
627
|
+
?? tokens.context_window;
|
|
628
|
+
const contextTokens = tokens.contextTokens
|
|
629
|
+
?? tokens.context_tokens
|
|
630
|
+
?? (typeof input === 'number'
|
|
631
|
+
? input + (typeof cacheRead === 'number' ? cacheRead : 0) + (typeof cacheWrite === 'number' ? cacheWrite : 0)
|
|
632
|
+
: undefined);
|
|
551
633
|
return {
|
|
552
|
-
input_tokens:
|
|
634
|
+
input_tokens: input,
|
|
553
635
|
output_tokens: tokens.output ?? tokens.output_tokens,
|
|
554
|
-
cache_read_tokens:
|
|
555
|
-
cache_creation_tokens:
|
|
636
|
+
cache_read_tokens: cacheRead,
|
|
637
|
+
cache_creation_tokens: cacheWrite,
|
|
556
638
|
total_tokens: tokens.total ?? tokens.total_tokens ?? tokens.totalTokens,
|
|
639
|
+
...(embeddedContextWindow === undefined && contextWindow === undefined ? {} : { context_window: embeddedContextWindow ?? contextWindow }),
|
|
640
|
+
...(contextTokens === undefined ? {} : { context_tokens: contextTokens }),
|
|
641
|
+
...(typeof tokens.contextUsageRatio === 'number' ? { context_usage_ratio: tokens.contextUsageRatio } : {}),
|
|
557
642
|
};
|
|
558
643
|
}
|
|
559
644
|
function isFinishedEvent(event, emitted) {
|
|
@@ -566,11 +651,11 @@ function isFinishedEvent(event, emitted) {
|
|
|
566
651
|
const status = typeof rawStatus === 'string' ? rawStatus.toLowerCase() : typeof statusRecord?.type === 'string' ? statusRecord.type.toLowerCase() : '';
|
|
567
652
|
return status === 'idle' || status === 'completed' || status === 'success';
|
|
568
653
|
}
|
|
569
|
-
function responseChunks(value, cumulative, partTypes) {
|
|
654
|
+
function responseChunks(value, cumulative, partTypes, contextWindow) {
|
|
570
655
|
const record = asRecord(value);
|
|
571
656
|
if (record === null)
|
|
572
657
|
return [];
|
|
573
|
-
const chunk = eventToChunk(record, cumulative, undefined, partTypes);
|
|
658
|
+
const chunk = eventToChunk(record, cumulative, undefined, partTypes, contextWindow);
|
|
574
659
|
return chunk === null ? [] : [chunk];
|
|
575
660
|
}
|
|
576
661
|
function readMessageRole(event) {
|
|
@@ -40,7 +40,7 @@ export class PiAgentDriver {
|
|
|
40
40
|
const controller = new AbortController();
|
|
41
41
|
const timer = setTimeout(() => controller.abort(), 12_000);
|
|
42
42
|
try {
|
|
43
|
-
const response = await rpc.request('get_available_models', {}, { signal: controller.signal });
|
|
43
|
+
const response = await rpc.request('get_available_models', {}, { signal: controller.signal, wireFormat: 'pi' });
|
|
44
44
|
const catalog = parsePiCatalog(response);
|
|
45
45
|
if (catalog.groups.length > 0)
|
|
46
46
|
return catalog;
|
|
@@ -73,23 +73,34 @@ export class PiAgentDriver {
|
|
|
73
73
|
const command = this.cachedBinary ?? (await this.detect()).command;
|
|
74
74
|
if (command === null)
|
|
75
75
|
throw new Error('Pi Agent 未安装');
|
|
76
|
-
const session = this.getSession(input.sessionId, command, input.cwd, input.providerSessionId);
|
|
76
|
+
const session = this.getSession(input.sessionId, command, input.cwd, input.providerSessionId, input.rawStoreRef);
|
|
77
77
|
const rpc = session.rpc;
|
|
78
78
|
try {
|
|
79
|
-
//
|
|
80
|
-
if (session.
|
|
79
|
+
// Pi 的 sessionId 由进程创建;先读状态,避免把 DSH sessionId 错当成 Provider 会话标识。
|
|
80
|
+
if (!session.stateLoaded) {
|
|
81
81
|
try {
|
|
82
|
-
await rpc.request('
|
|
83
|
-
|
|
82
|
+
const state = await rpc.request('get_state', {}, { signal: input.signal, killOnAbort: false, wireFormat: 'pi' });
|
|
83
|
+
const discoveredId = readPiStateSessionId(state);
|
|
84
|
+
if (discoveredId)
|
|
85
|
+
session.providerSessionId = discoveredId;
|
|
84
86
|
}
|
|
85
|
-
catch { /* 旧版 Pi
|
|
86
|
-
session.
|
|
87
|
+
catch { /* 旧版 Pi 不提供 get_state 时保留 Host 侧回退标识。 */ }
|
|
88
|
+
session.stateLoaded = true;
|
|
87
89
|
}
|
|
88
90
|
if (!isProviderDefaultModel(input.modelId)) {
|
|
91
|
+
const model = parsePiModelId(input.modelId);
|
|
92
|
+
if (model !== null) {
|
|
93
|
+
try {
|
|
94
|
+
await rpc.request('set_model', model, { signal: input.signal, killOnAbort: false, wireFormat: 'pi' });
|
|
95
|
+
}
|
|
96
|
+
catch { /* 模型目录或旧版 Pi 不支持切换时继续使用当前模型。 */ }
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (input.effortId && input.effortId !== 'default') {
|
|
89
100
|
try {
|
|
90
|
-
await rpc.request('
|
|
101
|
+
await rpc.request('set_thinking_level', { level: input.effortId }, { signal: input.signal, killOnAbort: false, wireFormat: 'pi' });
|
|
91
102
|
}
|
|
92
|
-
catch { /*
|
|
103
|
+
catch { /* 旧版 Pi 不支持单独设置思考等级时使用当前等级。 */ }
|
|
93
104
|
}
|
|
94
105
|
yield { type: 'session-binding', providerSessionId: session.providerSessionId };
|
|
95
106
|
const stream = streamPiPrompt(rpc, input.prompt, input.signal);
|
|
@@ -121,14 +132,14 @@ export class PiAgentDriver {
|
|
|
121
132
|
const session = this.sessions.get(sessionId);
|
|
122
133
|
if (session === undefined)
|
|
123
134
|
throw new Error('Pi 会话不存在');
|
|
124
|
-
await session.rpc.request('steer', { message: prompt }, { killOnAbort: false });
|
|
135
|
+
await session.rpc.request('steer', { message: prompt }, { killOnAbort: false, wireFormat: 'pi' });
|
|
125
136
|
}
|
|
126
137
|
/** 向运行中的 Pi 会话追加 follow-up。 */
|
|
127
138
|
async followUp(sessionId, prompt) {
|
|
128
139
|
const session = this.sessions.get(sessionId);
|
|
129
140
|
if (session === undefined)
|
|
130
141
|
throw new Error('Pi 会话不存在');
|
|
131
|
-
await session.rpc.request('follow_up', { message: prompt }, { killOnAbort: false });
|
|
142
|
+
await session.rpc.request('follow_up', { message: prompt }, { killOnAbort: false, wireFormat: 'pi' });
|
|
132
143
|
}
|
|
133
144
|
/** 中断当前 turn,但保留 Pi 进程供同一会话继续使用。 */
|
|
134
145
|
async interrupt(sessionId) {
|
|
@@ -136,11 +147,11 @@ export class PiAgentDriver {
|
|
|
136
147
|
if (session === undefined)
|
|
137
148
|
return;
|
|
138
149
|
try {
|
|
139
|
-
await session.rpc.request('
|
|
150
|
+
await session.rpc.request('abort', {}, { killOnAbort: false, wireFormat: 'pi' });
|
|
140
151
|
}
|
|
141
152
|
catch {
|
|
142
153
|
try {
|
|
143
|
-
session.rpc.notify('abort', {});
|
|
154
|
+
session.rpc.notify('abort', {}, 'pi');
|
|
144
155
|
}
|
|
145
156
|
catch { /* 进程可能已退出 */ }
|
|
146
157
|
}
|
|
@@ -152,18 +163,27 @@ export class PiAgentDriver {
|
|
|
152
163
|
this.processes.clear();
|
|
153
164
|
this.cachedBinary = null;
|
|
154
165
|
}
|
|
155
|
-
getSession(sessionId, command, cwd, providerSessionId) {
|
|
166
|
+
getSession(sessionId, command, cwd, providerSessionId, rawStoreRef) {
|
|
156
167
|
const previous = this.sessions.get(sessionId);
|
|
157
|
-
if (previous !== undefined
|
|
168
|
+
if (previous !== undefined
|
|
169
|
+
&& previous.cwd === cwd
|
|
170
|
+
&& (providerSessionId === undefined || providerSessionId === previous.providerSessionId)
|
|
171
|
+
&& (rawStoreRef === undefined || rawStoreRef === previous.rawStoreRef))
|
|
158
172
|
return previous;
|
|
159
173
|
previous?.rpc.dispose();
|
|
160
|
-
const
|
|
174
|
+
const args = ['--mode', 'rpc'];
|
|
175
|
+
if (rawStoreRef?.trim())
|
|
176
|
+
args.push('--session', rawStoreRef.trim());
|
|
177
|
+
else if (providerSessionId?.trim())
|
|
178
|
+
args.push('--session-id', providerSessionId.trim());
|
|
179
|
+
const rpc = new JsonRpcProcess({ command, args, cwd, spawn: this.runSpawn });
|
|
161
180
|
this.processes.add(rpc);
|
|
162
181
|
const session = {
|
|
163
182
|
rpc,
|
|
164
183
|
cwd,
|
|
184
|
+
rawStoreRef,
|
|
165
185
|
providerSessionId: providerSessionId ?? sessionId,
|
|
166
|
-
|
|
186
|
+
stateLoaded: false,
|
|
167
187
|
};
|
|
168
188
|
this.sessions.set(sessionId, session);
|
|
169
189
|
return session;
|
|
@@ -182,10 +202,10 @@ async function* streamPiPrompt(rpc, prompt, signal) {
|
|
|
182
202
|
const listener = (message) => {
|
|
183
203
|
queue.push(message);
|
|
184
204
|
const type = piEventType(message);
|
|
185
|
-
if (type === '
|
|
186
|
-
terminalReason = 'stop';
|
|
187
|
-
else if (type === 'error' || type === 'agent_error' || type === 'agent_failed')
|
|
205
|
+
if (type === 'error' || type === 'agent_error' || type === 'agent_failed' || piEventHasError(message))
|
|
188
206
|
terminalReason = 'error';
|
|
207
|
+
else if (type === 'agent_settled' && terminalReason === null)
|
|
208
|
+
terminalReason = 'stop';
|
|
189
209
|
notify();
|
|
190
210
|
};
|
|
191
211
|
const removeListener = rpc.addNotificationListener(listener);
|
|
@@ -194,7 +214,7 @@ async function* streamPiPrompt(rpc, prompt, signal) {
|
|
|
194
214
|
onAbort();
|
|
195
215
|
else
|
|
196
216
|
signal?.addEventListener('abort', onAbort, { once: true });
|
|
197
|
-
void rpc.request('prompt', { message: prompt }, { signal, killOnAbort: false }).then(() => { promptSettled = true; notify(); }, () => {
|
|
217
|
+
void rpc.request('prompt', { message: prompt }, { signal, killOnAbort: false, wireFormat: 'pi' }).then(() => { promptSettled = true; notify(); }, () => {
|
|
198
218
|
promptSettled = true;
|
|
199
219
|
terminalReason = signal?.aborted ? 'cancel' : 'error';
|
|
200
220
|
notify();
|
|
@@ -218,6 +238,12 @@ function piEventType(message) {
|
|
|
218
238
|
const params = isRecord(message.params) ? message.params : message;
|
|
219
239
|
return typeof params.type === 'string' ? params.type.toLowerCase() : '';
|
|
220
240
|
}
|
|
241
|
+
function piEventHasError(message) {
|
|
242
|
+
const params = isRecord(message.params) ? message.params : message;
|
|
243
|
+
const event = isRecord(params.message) ? params.message : params;
|
|
244
|
+
const stopReason = textValue(event.stopReason ?? params.stopReason);
|
|
245
|
+
return stopReason === 'error' || stopReason === 'failed' || typeof event.errorMessage === 'string' && event.errorMessage.trim() !== '';
|
|
246
|
+
}
|
|
221
247
|
function parsePiCliCatalog(output) {
|
|
222
248
|
const models = [];
|
|
223
249
|
const levels = ['off', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'];
|
|
@@ -266,7 +292,13 @@ function piMessageToChunk(message) {
|
|
|
266
292
|
const assistantEvent = isRecord(params.assistantMessageEvent) ? params.assistantMessageEvent : null;
|
|
267
293
|
const completedMessage = isRecord(params.message) ? params.message : null;
|
|
268
294
|
const rootType = typeof event.type === 'string' ? event.type : typeof params.event === 'string' ? params.event : '';
|
|
269
|
-
if (rootType === '
|
|
295
|
+
if (rootType === 'agent_start'
|
|
296
|
+
|| rootType === 'agent_settled'
|
|
297
|
+
|| rootType === 'agent_end'
|
|
298
|
+
|| rootType === 'turn_start'
|
|
299
|
+
|| rootType === 'turn_end'
|
|
300
|
+
|| rootType === 'message_start'
|
|
301
|
+
|| rootType === 'message_end')
|
|
270
302
|
return null;
|
|
271
303
|
const type = assistantEvent !== null && typeof assistantEvent.type === 'string' ? assistantEvent.type : rootType;
|
|
272
304
|
const text = textValue(assistantEvent?.delta ?? params.delta ?? params.text ?? params.content ?? params.message);
|
|
@@ -309,4 +341,18 @@ function readSessionId(message) {
|
|
|
309
341
|
return params[key].trim();
|
|
310
342
|
return null;
|
|
311
343
|
}
|
|
344
|
+
function readPiStateSessionId(value) {
|
|
345
|
+
if (!isRecord(value))
|
|
346
|
+
return null;
|
|
347
|
+
const data = isRecord(value.data) ? value.data : value;
|
|
348
|
+
return textValue(data.sessionId ?? data.session_id) ?? null;
|
|
349
|
+
}
|
|
350
|
+
function parsePiModelId(value) {
|
|
351
|
+
if (!value)
|
|
352
|
+
return null;
|
|
353
|
+
const separator = value.indexOf('/');
|
|
354
|
+
if (separator <= 0 || separator === value.length - 1)
|
|
355
|
+
return null;
|
|
356
|
+
return { provider: value.slice(0, separator), modelId: value.slice(separator + 1) };
|
|
357
|
+
}
|
|
312
358
|
//# sourceMappingURL=pi-driver.js.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 模型提供商注册表。
|
|
3
|
+
*
|
|
4
|
+
* Agent 只是运行入口,订阅归属属于模型提供商。这里集中维护提供商名称、
|
|
5
|
+
* 官方地址、余额能力和 Logo,调用方只传入 DSH 的 provider 名称与 baseURL。
|
|
6
|
+
*/
|
|
7
|
+
export type ProviderSubscriptionCapability = 'official-balance' | 'official-usage' | 'subscription-window' | 'sub2api' | 'unsupported';
|
|
8
|
+
export type ProviderSubscriptionReader = 'deepseek-balance' | 'openrouter-balance' | 'minimax-usage' | 'zai-usage' | 'github-copilot-usage' | 'sub2api' | 'none';
|
|
9
|
+
export interface ProviderIdentity {
|
|
10
|
+
readonly name?: string | undefined;
|
|
11
|
+
readonly baseUrl?: string | undefined;
|
|
12
|
+
}
|
|
13
|
+
export interface ProviderDefinition {
|
|
14
|
+
readonly id: string;
|
|
15
|
+
readonly displayName: string;
|
|
16
|
+
readonly aliases: readonly string[];
|
|
17
|
+
readonly officialHosts: readonly string[];
|
|
18
|
+
readonly capability: ProviderSubscriptionCapability;
|
|
19
|
+
readonly reader: ProviderSubscriptionReader;
|
|
20
|
+
/** 浏览器可直接加载的公开 Logo 地址;Host 会尽量内联为 data URL。 */
|
|
21
|
+
readonly logoUrl: string;
|
|
22
|
+
}
|
|
23
|
+
/** DSH 内置模型提供商的统一订阅能力登记。 */
|
|
24
|
+
export declare const MODEL_PROVIDER_DEFINITIONS: readonly ProviderDefinition[];
|
|
25
|
+
/** 规范化名称,避免大小写、下划线和连字符造成重复提供商。 */
|
|
26
|
+
export declare function normalizeProviderName(value: string | undefined): string;
|
|
27
|
+
/** 去掉 path、查询参数和凭据,只保留可用于身份判断的 origin。 */
|
|
28
|
+
export declare function normalizeProviderBaseUrl(value: string | undefined): string;
|
|
29
|
+
/** 按名称和 baseURL 联合识别;同名第三方代理不会冒充官方提供商。 */
|
|
30
|
+
export declare function identifyModelProvider(identity: ProviderIdentity): ProviderDefinition | undefined;
|
|
31
|
+
/** 返回一个不依赖名称的第三方上游定义,供 Sub2API 统计使用。 */
|
|
32
|
+
export declare function thirdPartyProvider(identity: ProviderIdentity): ProviderDefinition;
|
|
33
|
+
//# sourceMappingURL=provider-registry.d.ts.map
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
const simpleIcon = (slug) => `https://cdn.simpleicons.org/${slug}`;
|
|
2
|
+
/** DSH 内置模型提供商的统一订阅能力登记。 */
|
|
3
|
+
export const MODEL_PROVIDER_DEFINITIONS = [
|
|
4
|
+
provider('amazon-bedrock', 'Amazon Bedrock', ['amazon-bedrock'], ['bedrock-runtime.us-east-1.amazonaws.com'], 'unsupported', 'amazonaws'),
|
|
5
|
+
provider('ant-ling', 'Ant Ling', ['ant-ling'], [], 'sub2api', 'antdesign'),
|
|
6
|
+
provider('anthropic', 'Anthropic', ['anthropic', 'claude'], ['api.anthropic.com'], 'official-usage', 'anthropic'),
|
|
7
|
+
provider('azure-openai-responses', 'Azure OpenAI', ['azure-openai-responses', 'azure-openai'], [], 'unsupported', 'microsoftazure'),
|
|
8
|
+
provider('baseten', 'Baseten', ['baseten'], ['inference.baseten.co'], 'official-usage', 'baseten'),
|
|
9
|
+
provider('cerebras', 'Cerebras', ['cerebras'], ['api.cerebras.ai'], 'unsupported', 'cerebras'),
|
|
10
|
+
provider('cloudflare-ai-gateway', 'Cloudflare AI Gateway', ['cloudflare-ai-gateway'], ['api.cloudflare.com'], 'official-usage', 'cloudflare'),
|
|
11
|
+
provider('cloudflare-workers-ai', 'Cloudflare Workers AI', ['cloudflare-workers-ai'], ['api.cloudflare.com'], 'unsupported', 'cloudflare'),
|
|
12
|
+
provider('deepseek', 'DeepSeek', ['deepseek', 'deepseek-official', 'official-deepseek'], ['api.deepseek.com', 'api.deepseek.com.cn'], 'official-balance', 'deepseek', 'deepseek-balance'),
|
|
13
|
+
provider('fireworks', 'Fireworks AI', ['fireworks'], ['api.fireworks.ai'], 'official-usage', 'fireworks'),
|
|
14
|
+
provider('github-copilot', 'GitHub Copilot', ['github-copilot'], ['api.githubcopilot.com', 'api.github.com'], 'subscription-window', 'github', 'github-copilot-usage'),
|
|
15
|
+
provider('google', 'Google Gemini', ['google', 'gemini'], ['generativelanguage.googleapis.com'], 'unsupported', 'googlegemini'),
|
|
16
|
+
provider('google-vertex', 'Google Vertex AI', ['google-vertex', 'vertex'], ['aiplatform.googleapis.com'], 'unsupported', 'googlecloud'),
|
|
17
|
+
provider('groq', 'Groq', ['groq'], ['api.groq.com'], 'unsupported', 'groq'),
|
|
18
|
+
provider('huggingface', 'Hugging Face', ['huggingface', 'hf'], ['api-inference.huggingface.co'], 'unsupported', 'huggingface'),
|
|
19
|
+
provider('kimi-coding', 'Kimi Coding', ['kimi-coding'], ['api.kimi.com'], 'official-usage', 'moonshot'),
|
|
20
|
+
provider('minimax', 'MiniMax', ['minimax'], ['api.minimax.io'], 'official-balance', 'minimax', 'minimax-usage'),
|
|
21
|
+
provider('minimax-cn', 'MiniMax 中国', ['minimax-cn'], ['api.minimaxi.com'], 'official-balance', 'minimax', 'minimax-usage'),
|
|
22
|
+
provider('mistral', 'Mistral AI', ['mistral'], ['api.mistral.ai'], 'official-usage', 'mistral'),
|
|
23
|
+
provider('moonshotai', 'Moonshot AI', ['moonshotai', 'moonshot'], ['api.moonshot.ai'], 'official-balance', 'moonshot'),
|
|
24
|
+
provider('moonshotai-cn', 'Moonshot AI 中国', ['moonshotai-cn'], ['api.moonshot.cn'], 'official-balance', 'moonshot'),
|
|
25
|
+
provider('nvidia', 'NVIDIA NIM', ['nvidia'], ['integrate.api.nvidia.com'], 'unsupported', 'nvidia'),
|
|
26
|
+
provider('openai', 'OpenAI', ['openai'], ['api.openai.com'], 'official-usage', 'openai'),
|
|
27
|
+
provider('openai-codex', 'OpenAI Codex', ['openai-codex'], ['chatgpt.com', 'api.openai.com'], 'subscription-window', 'openai'),
|
|
28
|
+
provider('opencode', 'OpenCode', ['opencode'], [], 'sub2api', 'opencode'),
|
|
29
|
+
provider('opencode-go', 'OpenCode Go', ['opencode-go'], [], 'sub2api', 'opencode'),
|
|
30
|
+
provider('openrouter', 'OpenRouter', ['openrouter'], ['openrouter.ai'], 'official-balance', 'openrouter', 'openrouter-balance'),
|
|
31
|
+
provider('qwen-token-plan', 'Qwen Token Plan', ['qwen-token-plan', 'qwen-token-plan-individual'], ['dashscope.aliyuncs.com'], 'unsupported', 'alibabacloud'),
|
|
32
|
+
provider('qwen-token-plan-cn', 'Qwen Token Plan 中国', ['qwen-token-plan-cn'], ['dashscope.aliyuncs.com.cn'], 'unsupported', 'alibabacloud'),
|
|
33
|
+
provider('together', 'Together AI', ['together'], ['api.together.xyz'], 'unsupported', 'together'),
|
|
34
|
+
provider('vercel-ai-gateway', 'Vercel AI Gateway', ['vercel-ai-gateway'], ['ai-gateway.vercel.sh'], 'official-usage', 'vercel'),
|
|
35
|
+
provider('xai', 'xAI', ['xai', 'grok'], ['api.x.ai'], 'unsupported', 'x'),
|
|
36
|
+
provider('xiaomi', 'Xiaomi MiMo', ['xiaomi', 'xiaomi-token-plan-ams', 'xiaomi-token-plan-cn', 'xiaomi-token-plan-sgp'], ['api.xiaomimimo.com'], 'unsupported', 'xiaomi'),
|
|
37
|
+
provider('zai', 'Z.ai', ['zai'], ['api.z.ai'], 'official-balance', 'zhipu', 'zai-usage'),
|
|
38
|
+
provider('zai-coding-cn', 'Z.ai Coding 中国', ['zai-coding-cn'], ['open.bigmodel.cn'], 'official-balance', 'zhipu', 'zai-usage'),
|
|
39
|
+
];
|
|
40
|
+
function provider(id, displayName, aliases, officialHosts, capability, logoSlug, reader = capability === 'sub2api' ? 'sub2api' : 'none') {
|
|
41
|
+
return { id, displayName, aliases, officialHosts, capability, reader, logoUrl: simpleIcon(logoSlug) };
|
|
42
|
+
}
|
|
43
|
+
/** 规范化名称,避免大小写、下划线和连字符造成重复提供商。 */
|
|
44
|
+
export function normalizeProviderName(value) {
|
|
45
|
+
return (value ?? '').trim().toLowerCase().replace(/[_\s]+/gu, '-');
|
|
46
|
+
}
|
|
47
|
+
/** 去掉 path、查询参数和凭据,只保留可用于身份判断的 origin。 */
|
|
48
|
+
export function normalizeProviderBaseUrl(value) {
|
|
49
|
+
if (value === undefined || value.trim() === '')
|
|
50
|
+
return '';
|
|
51
|
+
try {
|
|
52
|
+
const url = new URL(value.trim());
|
|
53
|
+
return url.origin.toLowerCase();
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return '';
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function providerMatches(definition, identity) {
|
|
60
|
+
const name = normalizeProviderName(identity.name);
|
|
61
|
+
const nameMatches = name !== '' && definition.aliases.some((alias) => normalizeProviderName(alias) === name);
|
|
62
|
+
const host = normalizeProviderBaseUrl(identity.baseUrl);
|
|
63
|
+
const hostMatches = host !== '' && definition.officialHosts.some((officialHost) => normalizeProviderBaseUrl(`https://${officialHost}`) === host);
|
|
64
|
+
return nameMatches && (host === '' || hostMatches);
|
|
65
|
+
}
|
|
66
|
+
/** 按名称和 baseURL 联合识别;同名第三方代理不会冒充官方提供商。 */
|
|
67
|
+
export function identifyModelProvider(identity) {
|
|
68
|
+
return MODEL_PROVIDER_DEFINITIONS.find((definition) => providerMatches(definition, identity));
|
|
69
|
+
}
|
|
70
|
+
/** 返回一个不依赖名称的第三方上游定义,供 Sub2API 统计使用。 */
|
|
71
|
+
export function thirdPartyProvider(identity) {
|
|
72
|
+
const name = identity.name?.trim();
|
|
73
|
+
return {
|
|
74
|
+
id: name === undefined || name === '' ? 'custom' : normalizeProviderName(name),
|
|
75
|
+
displayName: name === undefined || name === '' ? '自定义提供商' : name,
|
|
76
|
+
aliases: [],
|
|
77
|
+
officialHosts: [],
|
|
78
|
+
capability: 'sub2api',
|
|
79
|
+
reader: 'sub2api',
|
|
80
|
+
logoUrl: '',
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=provider-registry.js.map
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { spawn, spawnSync } from 'node:child_process';
|
|
2
2
|
import type { CliSubscriptionUsage } from '../../shared/contracts/subscription.js';
|
|
3
|
+
import { OfficialProviderSubscriptionService, type OfficialProviderSubscriptionOptions } from './official-provider-subscription.js';
|
|
3
4
|
type FetchLike = typeof fetch;
|
|
4
5
|
/** 统一读取 Codex、Claude Code、OpenCode 订阅摘要。所有原始凭据都留在 Host。 */
|
|
5
6
|
export declare class ProviderSubscriptionService {
|
|
@@ -8,8 +9,11 @@ export declare class ProviderSubscriptionService {
|
|
|
8
9
|
readonly claudeCode: ClaudeCodeSubscriptionService;
|
|
9
10
|
readonly opencode: OpenCodeSubscriptionService;
|
|
10
11
|
readonly sub2api: Sub2ApiUsageService;
|
|
12
|
+
readonly deepseek: DeepseekSubscriptionService;
|
|
13
|
+
readonly official: OfficialProviderSubscriptionService;
|
|
11
14
|
constructor(options?: ProviderSubscriptionOptions);
|
|
12
|
-
read(adapterId: string): Promise<CliSubscriptionUsage | null>;
|
|
15
|
+
read(adapterId: string, providerId?: string): Promise<CliSubscriptionUsage | null>;
|
|
16
|
+
private readDsh;
|
|
13
17
|
private readSub2ApiFirst;
|
|
14
18
|
}
|
|
15
19
|
export interface ProviderSubscriptionOptions {
|
|
@@ -18,6 +22,8 @@ export interface ProviderSubscriptionOptions {
|
|
|
18
22
|
readonly claudeCode?: ClaudeCodeSubscriptionOptions;
|
|
19
23
|
readonly opencode?: OpenCodeSubscriptionOptions;
|
|
20
24
|
readonly sub2api?: Sub2ApiUsageOptions;
|
|
25
|
+
readonly deepseek?: DeepseekSubscriptionOptions;
|
|
26
|
+
readonly official?: OfficialProviderSubscriptionOptions;
|
|
21
27
|
}
|
|
22
28
|
export interface SubscriptionReader {
|
|
23
29
|
read(): Promise<CliSubscriptionUsage | null>;
|
|
@@ -26,6 +32,20 @@ export interface Sub2ApiSource {
|
|
|
26
32
|
readonly baseUrl: string;
|
|
27
33
|
readonly apiKey: string;
|
|
28
34
|
}
|
|
35
|
+
export interface DeepseekSubscriptionOptions {
|
|
36
|
+
readonly fetch?: FetchLike;
|
|
37
|
+
readonly timeoutMs?: number;
|
|
38
|
+
readonly sources?: readonly Sub2ApiSource[];
|
|
39
|
+
}
|
|
40
|
+
/** 读取官方 DeepSeek API 的余额;凭据只在 Host 内使用。 */
|
|
41
|
+
export declare class DeepseekSubscriptionService {
|
|
42
|
+
private readonly request;
|
|
43
|
+
private readonly timeoutMs;
|
|
44
|
+
private readonly configuredSources;
|
|
45
|
+
constructor(options?: DeepseekSubscriptionOptions);
|
|
46
|
+
read(source?: Sub2ApiSource): Promise<CliSubscriptionUsage | null>;
|
|
47
|
+
private readSource;
|
|
48
|
+
}
|
|
29
49
|
export interface Sub2ApiUsageOptions {
|
|
30
50
|
readonly fetch?: FetchLike;
|
|
31
51
|
readonly timeoutMs?: number;
|
|
@@ -37,8 +57,10 @@ export declare class Sub2ApiUsageService {
|
|
|
37
57
|
private readonly timeoutMs;
|
|
38
58
|
private readonly configuredSources;
|
|
39
59
|
constructor(options?: Sub2ApiUsageOptions);
|
|
40
|
-
hasSource(adapterId: string): boolean;
|
|
41
|
-
|
|
60
|
+
hasSource(adapterId: string, providerId?: string): boolean;
|
|
61
|
+
/** 只判断是否存在第三方上游;官方 Codex/Claude 源必须允许回退原生订阅读取器。 */
|
|
62
|
+
hasThirdPartySource(adapterId: string, providerId?: string): boolean;
|
|
63
|
+
read(adapterId: string, providerId?: string): Promise<CliSubscriptionUsage | null>;
|
|
42
64
|
private readSource;
|
|
43
65
|
}
|
|
44
66
|
export interface CodexSubscriptionOptions {
|