@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
|
@@ -2,6 +2,8 @@ import { existsSync, readFileSync } from 'node:fs';
|
|
|
2
2
|
import { homedir } from 'node:os';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { spawn, spawnSync } from 'node:child_process';
|
|
5
|
+
import { identifyModelProvider, normalizeProviderBaseUrl, thirdPartyProvider } from './provider-registry.js';
|
|
6
|
+
import { OfficialProviderSubscriptionService } from './official-provider-subscription.js';
|
|
5
7
|
import { JsonRpcProcess } from './json-rpc-process.js';
|
|
6
8
|
import { detectBinary } from './rpc-driver-utils.js';
|
|
7
9
|
/** 统一读取 Codex、Claude Code、OpenCode 订阅摘要。所有原始凭据都留在 Host。 */
|
|
@@ -11,15 +13,21 @@ export class ProviderSubscriptionService {
|
|
|
11
13
|
claudeCode;
|
|
12
14
|
opencode;
|
|
13
15
|
sub2api;
|
|
16
|
+
deepseek;
|
|
17
|
+
official;
|
|
14
18
|
constructor(options = {}) {
|
|
15
19
|
this.commandCode = options.commandCode;
|
|
16
20
|
this.codex = new CodexSubscriptionService(options.codex);
|
|
17
21
|
this.claudeCode = new ClaudeCodeSubscriptionService(options.claudeCode);
|
|
18
22
|
this.opencode = new OpenCodeSubscriptionService(options.opencode);
|
|
19
23
|
this.sub2api = new Sub2ApiUsageService(options.sub2api);
|
|
24
|
+
this.deepseek = new DeepseekSubscriptionService(options.deepseek);
|
|
25
|
+
this.official = new OfficialProviderSubscriptionService(options.official);
|
|
20
26
|
}
|
|
21
|
-
read(adapterId) {
|
|
22
|
-
if (adapterId === '
|
|
27
|
+
read(adapterId, providerId) {
|
|
28
|
+
if (adapterId === 'dsh')
|
|
29
|
+
return this.readDsh(providerId);
|
|
30
|
+
if (adapterId === 'codex' || adapterId === 'claude-code' || adapterId === 'grok' || adapterId === 'opencode') {
|
|
23
31
|
return this.readSub2ApiFirst(adapterId);
|
|
24
32
|
}
|
|
25
33
|
switch (adapterId) {
|
|
@@ -30,22 +38,103 @@ export class ProviderSubscriptionService {
|
|
|
30
38
|
default: return Promise.resolve(null);
|
|
31
39
|
}
|
|
32
40
|
}
|
|
41
|
+
async readDsh(providerId) {
|
|
42
|
+
const currentProviderId = providerId ?? resolveDshDefaultProvider();
|
|
43
|
+
const configuredSource = resolveDshProviderSource(currentProviderId);
|
|
44
|
+
const matchedProvider = identifyModelProvider({ name: currentProviderId, baseUrl: configuredSource?.baseUrl });
|
|
45
|
+
if (matchedProvider?.reader === 'openrouter-balance' || matchedProvider?.reader === 'minimax-usage' || matchedProvider?.reader === 'zai-usage' || matchedProvider?.reader === 'github-copilot-usage') {
|
|
46
|
+
return this.official.read(currentProviderId, configuredSource);
|
|
47
|
+
}
|
|
48
|
+
if (matchedProvider?.reader === 'deepseek-balance' && isOfficialDeepseekSource(currentProviderId, configuredSource))
|
|
49
|
+
return this.deepseek.read(configuredSource ?? undefined);
|
|
50
|
+
if (matchedProvider?.reader === 'sub2api')
|
|
51
|
+
return this.sub2api.read('dsh', currentProviderId);
|
|
52
|
+
// 已识别的官方提供商但没有可用读取器时,不尝试把官方 API 当成 Sub2API。
|
|
53
|
+
if (matchedProvider !== undefined && configuredSource !== null)
|
|
54
|
+
return null;
|
|
55
|
+
if (isThirdPartyDeepseekProvider(currentProviderId))
|
|
56
|
+
return this.sub2api.read('dsh', currentProviderId);
|
|
57
|
+
// 明确配置了第三方来源时,始终沿用 sub2api 适配器,避免把失败的上游误判为官方余额。
|
|
58
|
+
const hasThirdPartySource = this.sub2api.hasSource('dsh');
|
|
59
|
+
if (hasThirdPartySource)
|
|
60
|
+
return this.sub2api.read('dsh');
|
|
61
|
+
const official = await this.deepseek.read();
|
|
62
|
+
return official === null || matchedProvider === undefined ? official : withProvider(official, matchedProvider, configuredSource?.baseUrl);
|
|
63
|
+
}
|
|
33
64
|
async readSub2ApiFirst(adapterId) {
|
|
34
65
|
// 已配置第三方上游时,官方额度接口没有意义;即使 Sub2API 探测失败也必须隐藏,
|
|
35
66
|
// 不能把旧的官方订阅窗口误显示成当前上游的用量。
|
|
36
|
-
const hasThirdPartySource = this.sub2api.
|
|
37
|
-
const upstream = await this.sub2api.read(adapterId);
|
|
38
|
-
if (upstream !== null)
|
|
39
|
-
return upstream;
|
|
67
|
+
const hasThirdPartySource = this.sub2api.hasThirdPartySource(adapterId);
|
|
40
68
|
if (hasThirdPartySource)
|
|
41
|
-
return
|
|
69
|
+
return this.sub2api.read(adapterId);
|
|
42
70
|
if (adapterId === 'codex')
|
|
43
|
-
return this.codex.read();
|
|
71
|
+
return this.codex.read().then((usage) => usage === null ? null : withProvider(usage, identifyModelProvider({ name: 'openai-codex' }), ''));
|
|
44
72
|
if (adapterId === 'claude-code')
|
|
45
|
-
return this.claudeCode.read();
|
|
73
|
+
return this.claudeCode.read().then((usage) => usage === null ? null : withProvider(usage, identifyModelProvider({ name: 'anthropic' }), ''));
|
|
46
74
|
return null;
|
|
47
75
|
}
|
|
48
76
|
}
|
|
77
|
+
/** 读取官方 DeepSeek API 的余额;凭据只在 Host 内使用。 */
|
|
78
|
+
export class DeepseekSubscriptionService {
|
|
79
|
+
request;
|
|
80
|
+
timeoutMs;
|
|
81
|
+
configuredSources;
|
|
82
|
+
constructor(options = {}) {
|
|
83
|
+
this.request = options.fetch ?? fetch;
|
|
84
|
+
this.timeoutMs = options.timeoutMs ?? 8_000;
|
|
85
|
+
this.configuredSources = options.sources;
|
|
86
|
+
}
|
|
87
|
+
async read(source) {
|
|
88
|
+
const sources = source === undefined ? (this.configuredSources ?? resolveDeepseekSources()) : [source];
|
|
89
|
+
for (const candidate of sources) {
|
|
90
|
+
if (!isOfficialDeepseekUrl(candidate.baseUrl))
|
|
91
|
+
continue;
|
|
92
|
+
const result = await this.readSource(candidate);
|
|
93
|
+
if (result !== null)
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
async readSource(source) {
|
|
99
|
+
const baseUrl = deepseekApiRoot(source.baseUrl);
|
|
100
|
+
if (baseUrl === '' || source.apiKey.trim() === '')
|
|
101
|
+
return null;
|
|
102
|
+
const controller = new AbortController();
|
|
103
|
+
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
104
|
+
try {
|
|
105
|
+
const response = await this.request(`${baseUrl}/user/balance`, {
|
|
106
|
+
headers: {
|
|
107
|
+
Authorization: source.apiKey.trim().startsWith('Bearer ') ? source.apiKey.trim() : `Bearer ${source.apiKey.trim()}`,
|
|
108
|
+
Accept: 'application/json',
|
|
109
|
+
},
|
|
110
|
+
signal: controller.signal,
|
|
111
|
+
});
|
|
112
|
+
if (!response.ok)
|
|
113
|
+
return null;
|
|
114
|
+
const usage = normalizeDeepseekUsage(await response.json(), baseUrl);
|
|
115
|
+
if (usage === null)
|
|
116
|
+
return null;
|
|
117
|
+
return {
|
|
118
|
+
authenticated: true,
|
|
119
|
+
planType: null,
|
|
120
|
+
primary: null,
|
|
121
|
+
secondary: null,
|
|
122
|
+
monthly: null,
|
|
123
|
+
rateLimitReachedType: null,
|
|
124
|
+
resetCredits: null,
|
|
125
|
+
capturedAt: new Date().toISOString(),
|
|
126
|
+
provider: providerSummary(identifyModelProvider({ name: 'deepseek', baseUrl }), baseUrl, await readLogoDataUrl(identifyModelProvider({ name: 'deepseek', baseUrl })?.logoUrl ?? '', this.request, this.timeoutMs)),
|
|
127
|
+
deepseek: usage,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
finally {
|
|
134
|
+
clearTimeout(timer);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
49
138
|
/** 通过 Provider 的上游 base URL 检测 Sub2API;只返回脱敏后的统计摘要。 */
|
|
50
139
|
export class Sub2ApiUsageService {
|
|
51
140
|
request;
|
|
@@ -56,23 +145,31 @@ export class Sub2ApiUsageService {
|
|
|
56
145
|
this.timeoutMs = options.timeoutMs ?? 8_000;
|
|
57
146
|
this.configuredSources = options.sources;
|
|
58
147
|
}
|
|
59
|
-
hasSource(adapterId) {
|
|
148
|
+
hasSource(adapterId, providerId) {
|
|
60
149
|
const configured = this.configuredSources?.[adapterId];
|
|
61
150
|
if (configured !== undefined)
|
|
62
151
|
return Array.isArray(configured) ? configured.length > 0 : true;
|
|
63
|
-
return resolveSub2ApiSources(adapterId).length > 0;
|
|
152
|
+
return resolveSub2ApiSources(adapterId, providerId).length > 0;
|
|
64
153
|
}
|
|
65
|
-
|
|
154
|
+
/** 只判断是否存在第三方上游;官方 Codex/Claude 源必须允许回退原生订阅读取器。 */
|
|
155
|
+
hasThirdPartySource(adapterId, providerId) {
|
|
66
156
|
const configured = this.configuredSources?.[adapterId];
|
|
67
|
-
const sources = configured === undefined
|
|
157
|
+
const sources = configured === undefined
|
|
158
|
+
? resolveSub2ApiSources(adapterId, providerId)
|
|
159
|
+
: Array.isArray(configured) ? configured : [configured];
|
|
160
|
+
return sources.some((source) => !isOfficialAgentSource(adapterId, source));
|
|
161
|
+
}
|
|
162
|
+
async read(adapterId, providerId) {
|
|
163
|
+
const configured = this.configuredSources?.[adapterId];
|
|
164
|
+
const sources = configured === undefined ? resolveSub2ApiSources(adapterId, providerId) : Array.isArray(configured) ? configured : [configured];
|
|
68
165
|
for (const source of sources) {
|
|
69
|
-
const result = await this.readSource(source);
|
|
166
|
+
const result = await this.readSource(source, providerId);
|
|
70
167
|
if (result !== null)
|
|
71
168
|
return result;
|
|
72
169
|
}
|
|
73
170
|
return null;
|
|
74
171
|
}
|
|
75
|
-
async readSource(source) {
|
|
172
|
+
async readSource(source, providerId) {
|
|
76
173
|
const baseUrl = source.baseUrl.trim().replace(/\/+$/u, '');
|
|
77
174
|
const usagePath = /\/v1$/u.test(baseUrl) ? '/usage' : '/v1/usage';
|
|
78
175
|
const controller = new AbortController();
|
|
@@ -89,7 +186,9 @@ export class Sub2ApiUsageService {
|
|
|
89
186
|
return null;
|
|
90
187
|
const logoUrl = buildLogoUrl(baseUrl);
|
|
91
188
|
const logoDataUrl = logoUrl === '' ? '' : await readLogoDataUrl(logoUrl, this.request, this.timeoutMs);
|
|
92
|
-
|
|
189
|
+
const providerDefinition = identifyModelProvider({ name: providerId, baseUrl }) ?? thirdPartyProvider({ name: providerId, baseUrl });
|
|
190
|
+
const providerLogoDataUrl = logoDataUrl || await readLogoDataUrl(providerDefinition.logoUrl, this.request, this.timeoutMs);
|
|
191
|
+
return { authenticated: true, planType: usage.planName, primary: null, secondary: null, monthly: null, rateLimitReachedType: null, resetCredits: null, capturedAt: new Date().toISOString(), provider: providerSummary(providerDefinition, baseUrl, providerLogoDataUrl, 'sub2api'), sub2api: { ...usage, logoUrl, logoDataUrl } };
|
|
93
192
|
}
|
|
94
193
|
catch {
|
|
95
194
|
return null;
|
|
@@ -124,7 +223,7 @@ export class CodexSubscriptionService {
|
|
|
124
223
|
const controller = new AbortController();
|
|
125
224
|
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
126
225
|
try {
|
|
127
|
-
await rpc.request('initialize', { clientInfo: { name: 'codingns4dsh', version: '0.1.
|
|
226
|
+
await rpc.request('initialize', { clientInfo: { name: 'codingns4dsh', version: '0.1.1' }, capabilities: {} }, { signal: controller.signal });
|
|
128
227
|
rpc.notify('initialized', {});
|
|
129
228
|
const result = await rpc.request('account/rateLimits/read', {}, { signal: controller.signal });
|
|
130
229
|
return normalizeCodexSnapshot(result);
|
|
@@ -278,13 +377,10 @@ function hasSubscriptionWindow(value) {
|
|
|
278
377
|
return value !== null && (value.primary !== null || value.secondary !== null || value.monthly !== null);
|
|
279
378
|
}
|
|
280
379
|
function hasThirdPartyCodexConfig(homeDirectory) {
|
|
281
|
-
if (textValue(process.env.OPENAI_API_KEY) !== null)
|
|
282
|
-
return true;
|
|
283
380
|
const config = readText(join(homeDirectory, 'config.toml'));
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
return baseUrls.some((value) => !isOfficialOpenAiUrl(value));
|
|
381
|
+
const configuredBaseUrl = config === null ? null : readCodexBaseUrl(config);
|
|
382
|
+
const environmentBaseUrl = textValue(process.env.OPENAI_BASE_URL);
|
|
383
|
+
return [configuredBaseUrl, environmentBaseUrl].some((value) => value !== null && !isOfficialOpenAiUrl(value));
|
|
288
384
|
}
|
|
289
385
|
function hasThirdPartyClaudeConfig(homeDirectory) {
|
|
290
386
|
const baseUrl = textValue(process.env.ANTHROPIC_BASE_URL);
|
|
@@ -325,6 +421,23 @@ function readText(path) {
|
|
|
325
421
|
return null;
|
|
326
422
|
}
|
|
327
423
|
}
|
|
424
|
+
/** 只读取 DSH 凭据文档中的指定引用,不把凭据内容写入任何返回结构。 */
|
|
425
|
+
function readDshCredential(name) {
|
|
426
|
+
const dshHome = process.env.DSH_HOME ?? join(homedir(), '.dsh');
|
|
427
|
+
const paths = [join(dshHome, '.credentials.yaml'), join(dshHome, '.env'), join(process.cwd(), '.env')];
|
|
428
|
+
const pattern = new RegExp(`^\\s*${name}\\s*:\\s*(?:"([^"]*)"|'([^']*)'|([^#\\s]+))`, 'mu');
|
|
429
|
+
const envPattern = new RegExp(`^\\s*${name}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|([^#\\s]+))`, 'mu');
|
|
430
|
+
for (const path of paths) {
|
|
431
|
+
const source = readText(path);
|
|
432
|
+
if (source === null)
|
|
433
|
+
continue;
|
|
434
|
+
const match = path.endsWith('.env') ? envPattern.exec(source) : pattern.exec(source);
|
|
435
|
+
const value = match?.[1] ?? match?.[2] ?? match?.[3];
|
|
436
|
+
if (typeof value === 'string' && value.trim() !== '')
|
|
437
|
+
return value.trim();
|
|
438
|
+
}
|
|
439
|
+
return null;
|
|
440
|
+
}
|
|
328
441
|
function readJson(path) {
|
|
329
442
|
if (!existsSync(path))
|
|
330
443
|
return null;
|
|
@@ -350,7 +463,138 @@ function timestampValue(value) {
|
|
|
350
463
|
return null;
|
|
351
464
|
}
|
|
352
465
|
function clamp(value) { return Math.max(0, Math.min(100, value)); }
|
|
353
|
-
function
|
|
466
|
+
function normalizeDeepseekUsage(value, baseUrl) {
|
|
467
|
+
const root = recordValue(value);
|
|
468
|
+
if (root === null)
|
|
469
|
+
return null;
|
|
470
|
+
const rawBalances = root.balance_infos ?? root.balanceInfos ?? root.balances;
|
|
471
|
+
if (!Array.isArray(rawBalances))
|
|
472
|
+
return null;
|
|
473
|
+
const balances = rawBalances.flatMap((value) => {
|
|
474
|
+
const source = recordValue(value);
|
|
475
|
+
const currency = textValue(source?.currency);
|
|
476
|
+
const totalBalance = numberValue(source?.total_balance ?? source?.totalBalance);
|
|
477
|
+
if (currency === null || totalBalance === null)
|
|
478
|
+
return [];
|
|
479
|
+
return [{
|
|
480
|
+
currency,
|
|
481
|
+
totalBalance,
|
|
482
|
+
grantedBalance: numberValue(source?.granted_balance ?? source?.grantedBalance) ?? 0,
|
|
483
|
+
toppedUpBalance: numberValue(source?.topped_up_balance ?? source?.toppedUpBalance) ?? 0,
|
|
484
|
+
}];
|
|
485
|
+
});
|
|
486
|
+
if (balances.length === 0)
|
|
487
|
+
return null;
|
|
488
|
+
return {
|
|
489
|
+
upstreamUrl: sanitizeUpstreamUrl(baseUrl),
|
|
490
|
+
isAvailable: typeof root.is_available === 'boolean' ? root.is_available : typeof root.isAvailable === 'boolean' ? root.isAvailable : null,
|
|
491
|
+
balances,
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
function resolveDeepseekSources() {
|
|
495
|
+
const sources = [];
|
|
496
|
+
const add = (source) => {
|
|
497
|
+
if (source === null || source.baseUrl.trim() === '' || source.apiKey.trim() === '')
|
|
498
|
+
return;
|
|
499
|
+
if (!sources.some((item) => item.baseUrl === source.baseUrl && item.apiKey === source.apiKey))
|
|
500
|
+
sources.push(source);
|
|
501
|
+
};
|
|
502
|
+
const dshBaseUrl = textValue(process.env.DSH_BASE_URL);
|
|
503
|
+
const dshApiKey = textValue(process.env.DSH_API_KEY);
|
|
504
|
+
add(dshBaseUrl !== null && dshApiKey !== null ? { baseUrl: dshBaseUrl, apiKey: dshApiKey } : null);
|
|
505
|
+
const deepseekBaseUrl = textValue(process.env.DEEPSEEK_BASE_URL);
|
|
506
|
+
const deepseekApiKey = textValue(process.env.DEEPSEEK_API_KEY) ?? readDshCredential('DEEPSEEK_API_KEY');
|
|
507
|
+
add(deepseekApiKey === null ? null : { baseUrl: deepseekBaseUrl ?? 'https://api.deepseek.com', apiKey: deepseekApiKey });
|
|
508
|
+
add(envSource('DEEPSEEK_BASE_URL', 'DEEPSEEK_API_KEY'));
|
|
509
|
+
add(findConfigSource(readJson(join(homedir(), '.dsh', 'config.json'))));
|
|
510
|
+
return sources;
|
|
511
|
+
}
|
|
512
|
+
function resolveDshProviderSource(providerId) {
|
|
513
|
+
const normalized = providerId?.trim();
|
|
514
|
+
if (normalized === undefined || normalized === '')
|
|
515
|
+
return null;
|
|
516
|
+
const envPrefix = normalized.replace(/[^a-z0-9]+/giu, '_').toUpperCase();
|
|
517
|
+
const envBaseUrl = textValue(process.env[`${envPrefix}_BASE_URL`]);
|
|
518
|
+
const envApiKey = textValue(process.env[`${envPrefix}_API_KEY`]);
|
|
519
|
+
if (envBaseUrl !== null && envApiKey !== null)
|
|
520
|
+
return { baseUrl: envBaseUrl, apiKey: envApiKey };
|
|
521
|
+
const dshHome = process.env.DSH_HOME ?? join(homedir(), '.dsh');
|
|
522
|
+
for (const path of [join(dshHome, 'settings.yaml'), join(dshHome, 'settings.yaml.imported')]) {
|
|
523
|
+
const source = readDshProviderYaml(path, normalized);
|
|
524
|
+
if (source !== null)
|
|
525
|
+
return source;
|
|
526
|
+
}
|
|
527
|
+
return null;
|
|
528
|
+
}
|
|
529
|
+
function resolveDshDefaultProvider() {
|
|
530
|
+
const dshHome = process.env.DSH_HOME ?? join(homedir(), '.dsh');
|
|
531
|
+
for (const path of [join(dshHome, 'settings.yaml'), join(dshHome, 'settings.yaml.imported')]) {
|
|
532
|
+
const text = readText(path);
|
|
533
|
+
if (text === null)
|
|
534
|
+
continue;
|
|
535
|
+
const start = text.search(/^agent-default-model:\s*$/mu);
|
|
536
|
+
if (start < 0)
|
|
537
|
+
continue;
|
|
538
|
+
const rest = text.slice(start);
|
|
539
|
+
const endMatch = /^\S[^\n]*$/mu.exec(rest.slice(1));
|
|
540
|
+
const block = endMatch === null ? rest : rest.slice(0, endMatch.index + 1);
|
|
541
|
+
const provider = yamlScalar(block.match(/^\s+provider:\s*(.+)$/mu)?.[1]);
|
|
542
|
+
if (provider !== null)
|
|
543
|
+
return provider;
|
|
544
|
+
}
|
|
545
|
+
return undefined;
|
|
546
|
+
}
|
|
547
|
+
function readDshProviderYaml(path, providerId) {
|
|
548
|
+
const text = readText(path);
|
|
549
|
+
if (text === null)
|
|
550
|
+
return null;
|
|
551
|
+
const escaped = providerId.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&');
|
|
552
|
+
const start = text.search(new RegExp(`^ {4}${escaped}:\\s*$`, 'mu'));
|
|
553
|
+
if (start < 0)
|
|
554
|
+
return null;
|
|
555
|
+
const rest = text.slice(start);
|
|
556
|
+
const endMatch = /^ {4}\S[^\n]*$/mu.exec(rest.slice(1));
|
|
557
|
+
const block = endMatch === null ? rest : rest.slice(0, endMatch.index + 1);
|
|
558
|
+
const baseUrl = yamlScalar(block.match(/^\s+baseURL:\s*(.+)$/mu)?.[1]);
|
|
559
|
+
const keyRef = yamlScalar(block.match(/^\s+apiKeyEnv:\s*(.+)$/mu)?.[1]);
|
|
560
|
+
const apiKey = keyRef === null ? null : textValue(process.env[keyRef]) ?? readDshCredential(keyRef);
|
|
561
|
+
return baseUrl === null || apiKey === null ? null : { baseUrl, apiKey };
|
|
562
|
+
}
|
|
563
|
+
function yamlScalar(value) {
|
|
564
|
+
if (value === undefined)
|
|
565
|
+
return null;
|
|
566
|
+
const normalized = value.trim().replace(/^(['"])(.*)\1$/u, '$2');
|
|
567
|
+
return normalized === '' ? null : normalized;
|
|
568
|
+
}
|
|
569
|
+
function isOfficialDeepseekProvider(providerId) {
|
|
570
|
+
if (providerId === undefined)
|
|
571
|
+
return false;
|
|
572
|
+
return /^(?:deepseek(?:-official)?|official-deepseek)$/iu.test(providerId.trim());
|
|
573
|
+
}
|
|
574
|
+
/** 官方接口必须同时满足提供商名称和官方 baseURL,避免第三方代理冒充官方。 */
|
|
575
|
+
function isOfficialDeepseekSource(providerId, source) {
|
|
576
|
+
if (source === null || !isOfficialDeepseekUrl(source.baseUrl))
|
|
577
|
+
return false;
|
|
578
|
+
return providerId === undefined || isOfficialDeepseekProvider(providerId);
|
|
579
|
+
}
|
|
580
|
+
function isThirdPartyDeepseekProvider(providerId) {
|
|
581
|
+
return providerId !== undefined && providerId.trim() !== '' && !isOfficialDeepseekProvider(providerId);
|
|
582
|
+
}
|
|
583
|
+
function providerSummary(definition, baseUrl, logoDataUrl, capability) {
|
|
584
|
+
const resolved = definition ?? thirdPartyProvider({ baseUrl });
|
|
585
|
+
return {
|
|
586
|
+
id: resolved.id,
|
|
587
|
+
displayName: resolved.displayName,
|
|
588
|
+
baseUrl: sanitizeUpstreamUrl(baseUrl),
|
|
589
|
+
capability: capability ?? resolved.capability,
|
|
590
|
+
logoUrl: resolved.logoUrl,
|
|
591
|
+
...(logoDataUrl === '' ? {} : { logoDataUrl }),
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
function withProvider(usage, definition, baseUrl = '') {
|
|
595
|
+
return { ...usage, provider: providerSummary(definition, baseUrl, '') };
|
|
596
|
+
}
|
|
597
|
+
function resolveSub2ApiSources(adapterId, providerId) {
|
|
354
598
|
const sources = [];
|
|
355
599
|
const add = (source) => {
|
|
356
600
|
if (source === null || source.baseUrl.trim() === '' || source.apiKey.trim() === '')
|
|
@@ -361,9 +605,7 @@ function resolveSub2ApiSources(adapterId) {
|
|
|
361
605
|
if (adapterId === 'codex') {
|
|
362
606
|
const home = process.env.CODEX_HOME ?? join(homedir(), '.codex');
|
|
363
607
|
const config = readText(join(home, 'config.toml')) ?? '';
|
|
364
|
-
|
|
365
|
-
const key = textValue(readJsonValue(join(home, 'auth.json'), 'OPENAI_API_KEY'));
|
|
366
|
-
add(baseUrl === undefined || key === null ? null : { baseUrl, apiKey: key });
|
|
608
|
+
add(readCodexSource(home, config));
|
|
367
609
|
}
|
|
368
610
|
if (adapterId === 'claude-code') {
|
|
369
611
|
const home = process.env.CLAUDE_CONFIG_DIR ?? join(homedir(), '.claude');
|
|
@@ -385,10 +627,16 @@ function resolveSub2ApiSources(adapterId) {
|
|
|
385
627
|
add(findConfigSource(config));
|
|
386
628
|
}
|
|
387
629
|
if (adapterId === 'dsh') {
|
|
388
|
-
|
|
389
|
-
add(
|
|
630
|
+
const providerSource = resolveDshProviderSource(providerId);
|
|
631
|
+
add(providerSource);
|
|
632
|
+
// 已明确拿到当前提供商时只查询它的上游,不能把另一个配置项的余额冒充过来。
|
|
633
|
+
if (providerId !== undefined && !isOfficialDeepseekProvider(providerId))
|
|
634
|
+
return sources;
|
|
635
|
+
for (const source of resolveDeepseekSources()) {
|
|
636
|
+
if (!isOfficialDeepseekUrl(source.baseUrl))
|
|
637
|
+
add(source);
|
|
638
|
+
}
|
|
390
639
|
add(envSource('OPENAI_BASE_URL', 'OPENAI_API_KEY'));
|
|
391
|
-
add(findConfigSource(readJson(join(homedir(), '.dsh', 'config.json'))));
|
|
392
640
|
}
|
|
393
641
|
if (adapterId === 'grok') {
|
|
394
642
|
add(envSource('GROK_BASE_URL', 'GROK_API_KEY'));
|
|
@@ -398,6 +646,74 @@ function resolveSub2ApiSources(adapterId) {
|
|
|
398
646
|
}
|
|
399
647
|
return sources;
|
|
400
648
|
}
|
|
649
|
+
function readCodexSource(homeDirectory, config) {
|
|
650
|
+
const baseUrl = readCodexBaseUrl(config) ?? textValue(process.env.OPENAI_BASE_URL);
|
|
651
|
+
if (baseUrl === null)
|
|
652
|
+
return null;
|
|
653
|
+
const provider = tomlScalar(config, 'model_provider');
|
|
654
|
+
const section = provider === null ? '' : tomlSection(config, `model_providers.${provider}`);
|
|
655
|
+
const token = tomlScalar(section, 'experimental_bearer_token')
|
|
656
|
+
?? tomlScalar(section, 'api_key')
|
|
657
|
+
?? (() => {
|
|
658
|
+
const environmentName = tomlScalar(section, 'api_key_env');
|
|
659
|
+
return environmentName === null ? null : textValue(process.env[environmentName]);
|
|
660
|
+
})()
|
|
661
|
+
?? textValue(readJsonValue(join(homeDirectory, 'auth.json'), 'OPENAI_API_KEY'))
|
|
662
|
+
?? textValue(process.env.OPENAI_API_KEY);
|
|
663
|
+
return token === null ? null : { baseUrl, apiKey: token };
|
|
664
|
+
}
|
|
665
|
+
function readCodexBaseUrl(config) {
|
|
666
|
+
const provider = tomlScalar(config, 'model_provider');
|
|
667
|
+
const section = provider === null ? '' : tomlSection(config, `model_providers.${provider}`);
|
|
668
|
+
return tomlScalar(section, 'base_url') ?? tomlScalar(config, 'base_url');
|
|
669
|
+
}
|
|
670
|
+
function tomlSection(source, name) {
|
|
671
|
+
if (source === '')
|
|
672
|
+
return '';
|
|
673
|
+
const escaped = name.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&');
|
|
674
|
+
const start = source.search(new RegExp(`^\\[${escaped}\\]\\s*$`, 'mu'));
|
|
675
|
+
if (start < 0)
|
|
676
|
+
return '';
|
|
677
|
+
const rest = source.slice(start);
|
|
678
|
+
const next = /^\[[^\n]+\]\s*$/mu.exec(rest.slice(1));
|
|
679
|
+
return next === null ? rest : rest.slice(0, next.index + 1);
|
|
680
|
+
}
|
|
681
|
+
function tomlScalar(source, key) {
|
|
682
|
+
if (source === '')
|
|
683
|
+
return null;
|
|
684
|
+
const escaped = key.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&');
|
|
685
|
+
const match = new RegExp(`^\\s*${escaped}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|([^#\\s]+))`, 'mu').exec(source);
|
|
686
|
+
const value = match?.[1] ?? match?.[2] ?? match?.[3];
|
|
687
|
+
return textValue(value);
|
|
688
|
+
}
|
|
689
|
+
function isOfficialAgentSource(adapterId, source) {
|
|
690
|
+
if (adapterId === 'codex')
|
|
691
|
+
return isOfficialOpenAiUrl(source.baseUrl);
|
|
692
|
+
if (adapterId === 'claude-code')
|
|
693
|
+
return isOfficialAnthropicUrl(source.baseUrl);
|
|
694
|
+
return false;
|
|
695
|
+
}
|
|
696
|
+
function isOfficialDeepseekUrl(value) {
|
|
697
|
+
try {
|
|
698
|
+
const hostname = new URL(value).hostname.toLowerCase();
|
|
699
|
+
return hostname === 'api.deepseek.com' || hostname === 'api.deepseek.com.cn';
|
|
700
|
+
}
|
|
701
|
+
catch {
|
|
702
|
+
return false;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
function deepseekApiRoot(value) {
|
|
706
|
+
try {
|
|
707
|
+
const url = new URL(value);
|
|
708
|
+
url.pathname = url.pathname.replace(/\/v1\/?$/u, '').replace(/\/+$/u, '');
|
|
709
|
+
url.search = '';
|
|
710
|
+
url.hash = '';
|
|
711
|
+
return url.toString().replace(/\/$/u, '');
|
|
712
|
+
}
|
|
713
|
+
catch {
|
|
714
|
+
return '';
|
|
715
|
+
}
|
|
716
|
+
}
|
|
401
717
|
function defaultOpenCodeDataDirectory() {
|
|
402
718
|
if (process.platform === 'win32')
|
|
403
719
|
return join(process.env.APPDATA ?? join(homedir(), 'AppData', 'Roaming'), 'opencode');
|
|
@@ -28,6 +28,8 @@ export declare class CodingNsCliAdapterRegistry {
|
|
|
28
28
|
private readonly modelTimers;
|
|
29
29
|
private readonly modelGenerations;
|
|
30
30
|
private readonly requestedModelCatalogs;
|
|
31
|
+
/** 已切到下一个 DSH step、但仍在继续产出 Provider 流的驱动迭代器。 */
|
|
32
|
+
private readonly segmentedTurns;
|
|
31
33
|
private cacheGeneration;
|
|
32
34
|
private disposed;
|
|
33
35
|
constructor(drivers: readonly CodingNsCliDriver[], enabled?: Readonly<Record<string, boolean>>, options?: {
|
|
@@ -62,6 +64,8 @@ export declare class CodingNsCliAdapterRegistry {
|
|
|
62
64
|
private findRememberedPreference;
|
|
63
65
|
private rememberPreference;
|
|
64
66
|
getSession(sessionId: string): CodingNsCliSessionConfig;
|
|
67
|
+
/** 只有驱动自己维护 Provider turn 边界时,Host 才能把它映射到 DSH step。 */
|
|
68
|
+
supportsSegmentedTurns(adapterId: CodingNsCliAdapterId): boolean;
|
|
65
69
|
execute(input: CodingNsCliTurnInput & {
|
|
66
70
|
readonly adapterId: CodingNsCliAdapterId;
|
|
67
71
|
}): AsyncIterable<CodingNsAgentEvent>;
|
|
@@ -74,6 +78,8 @@ export declare class CodingNsCliAdapterRegistry {
|
|
|
74
78
|
respondQuestion(sessionId: string, response: CodingNsAgentQuestionResponse): Promise<void>;
|
|
75
79
|
steer(sessionId: string, prompt: string, followUp?: boolean): Promise<void>;
|
|
76
80
|
interrupt(sessionId: string): Promise<void>;
|
|
81
|
+
/** 丢弃等待下一步的 Provider 流,避免取消后的旧正文进入新回合。 */
|
|
82
|
+
discardSegmentedTurn(sessionId: string): void;
|
|
77
83
|
dispose(): Promise<void>;
|
|
78
84
|
private readDetection;
|
|
79
85
|
/** 同一适配器只允许一个探测;刷新失败时保留最后一次可用状态。 */
|