@jingyi0605/codingns4dsh 0.1.0 → 0.1.1
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/dsh-runtime-version.d.ts +3 -2
- package/data/build/dist/host/dsh-runtime-version.js +71 -2
- 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 +79 -15
- 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.d.ts +1 -1
- package/data/build/dist/typert.host.js +6 -3
- 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 +13 -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
|
@@ -1,22 +1,38 @@
|
|
|
1
1
|
import { CODINGNS_RPC_CHANNEL } from '../shared/contracts/transport.js';
|
|
2
|
+
import { debugInfo, debugWarn } from '../shared/debug.js';
|
|
2
3
|
/** Client 侧访问 Host CLI 命名空间的统一入口。 */
|
|
3
4
|
export async function callCliRpc(rpc, action, payload) {
|
|
4
5
|
let response;
|
|
5
6
|
try {
|
|
7
|
+
debugInfo('codingns4dsh: cli rpc request', { channel: CODINGNS_RPC_CHANNEL, action });
|
|
6
8
|
response = await rpc.call(CODINGNS_RPC_CHANNEL, `cli/${action}`, payload);
|
|
7
9
|
}
|
|
8
10
|
catch (error) {
|
|
9
11
|
const message = error instanceof Error ? error.message : String(error);
|
|
10
12
|
if (!/HTTP (?:404|405)\b/u.test(message))
|
|
11
13
|
throw error;
|
|
12
|
-
|
|
14
|
+
debugWarn('codingns4dsh: cli rpc fallback', { action, error: message });
|
|
15
|
+
try {
|
|
16
|
+
response = await rpc.call('/api', `codingns/cli/${action}`, payload);
|
|
17
|
+
}
|
|
18
|
+
catch (fallbackError) {
|
|
19
|
+
console.error('codingns4dsh: cli rpc fallback failed', { action, error: fallbackError });
|
|
20
|
+
throw fallbackError;
|
|
21
|
+
}
|
|
13
22
|
}
|
|
14
|
-
if (!response.ok)
|
|
23
|
+
if (!response.ok) {
|
|
24
|
+
console.error('codingns4dsh: cli rpc response error', { action, error: response.error });
|
|
15
25
|
throw new Error(response.error.message);
|
|
26
|
+
}
|
|
27
|
+
debugInfo('codingns4dsh: cli rpc response success', { action });
|
|
16
28
|
return response.value;
|
|
17
29
|
}
|
|
18
30
|
export function adapterCatalogWithDsh(catalog) {
|
|
19
|
-
|
|
31
|
+
// 对话框只展示实际可用的适配器;未安装或已停用的适配器由设置页管理。
|
|
32
|
+
return [
|
|
33
|
+
{ id: 'dsh', name: 'DeepSeek Harness', installed: true, enabled: true, version: null, command: null },
|
|
34
|
+
...catalog.filter((adapter) => adapter.installed && adapter.enabled),
|
|
35
|
+
];
|
|
20
36
|
}
|
|
21
37
|
export function firstModel(catalog) {
|
|
22
38
|
for (const group of catalog.groups) {
|
|
@@ -58,6 +74,7 @@ export async function restoreCliSession(rpc, record) {
|
|
|
58
74
|
adapterId: record.adapterId,
|
|
59
75
|
...(record.modelId ? { modelId: record.modelId } : {}),
|
|
60
76
|
...(record.effortId ? { effortId: record.effortId } : {}),
|
|
77
|
+
...(record.providerId ? { providerId: record.providerId } : {}),
|
|
61
78
|
...(record.providerSessionId ? { providerSessionId: record.providerSessionId } : {}),
|
|
62
79
|
});
|
|
63
80
|
navigateToDshSession(record.dshSessionId);
|
|
@@ -4,6 +4,7 @@ import type { SlotRegistry } from '@deepseek-ai/dsh-client-ui-renderer/client';
|
|
|
4
4
|
import { type CodingNsLocale } from './locale.js';
|
|
5
5
|
interface SessionSnapshot {
|
|
6
6
|
readonly sessionId?: string;
|
|
7
|
+
readonly modelSelection?: unknown;
|
|
7
8
|
readonly blank?: boolean;
|
|
8
9
|
readonly promptAttempted?: boolean;
|
|
9
10
|
readonly running?: boolean;
|
|
@@ -13,6 +14,10 @@ type SessionSelector = <Selected>(selector: (session: SessionSnapshot) => Select
|
|
|
13
14
|
/** DSH Web 当前版本的对话工具栏 Slot 契约。Slot 包没有预声明这些业务名称,插件在此补齐类型。 */
|
|
14
15
|
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
15
16
|
interface SlotMap {
|
|
17
|
+
'conversation.input.left': {
|
|
18
|
+
kind: 'list';
|
|
19
|
+
scope: 'session';
|
|
20
|
+
};
|
|
16
21
|
'conversation.input.right': {
|
|
17
22
|
kind: 'list';
|
|
18
23
|
scope: 'session';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createElement, useEffect, useState } from 'react';
|
|
1
|
+
import { createElement, useEffect, useRef, useState } from 'react';
|
|
2
2
|
import { adapterCatalogWithDsh, callCliRpc, findModel, firstModel } from './cli-catalog.js';
|
|
3
3
|
import { providerIconUrl } from './provider-icons.js';
|
|
4
4
|
import { publishSessionAdapter } from './session-adapter-cache.js';
|
|
@@ -15,12 +15,23 @@ function installComposerStyles() {
|
|
|
15
15
|
style.textContent = [
|
|
16
16
|
'html[data-codingns-agent]:not([data-codingns-agent="dsh"]) [data-slot="conversation.input.model"],',
|
|
17
17
|
'body[data-codingns-agent]:not([data-codingns-agent="dsh"]) [data-slot="conversation.input.model"]{display:none!important}',
|
|
18
|
+
// DSH 原生输入栏默认允许工具行换行;选择器过长时保持单行并让右侧区域收缩。
|
|
19
|
+
'[data-composer-card] > div:has([data-slot="conversation.input.right"]){flex-wrap:nowrap!important}',
|
|
20
|
+
'[data-composer-card] > div:has([data-slot="conversation.input.right"]) > div:has(> [data-slot="conversation.input.right"]){min-width:0;width:0;flex:1 1 0}',
|
|
21
|
+
'[data-composer-card] > div:has([data-slot="conversation.input.right"]) [data-slot="conversation.input.right"] > .codingns4dsh-model-root{min-width:0;max-width:min(360px,45cqw);flex:1 1 min(360px,45cqw)}',
|
|
22
|
+
'[data-composer-card] > div:has([data-slot="conversation.input.right"]) [data-slot="conversation.input.model"] > select{width:100%;min-width:0;max-width:min(150px,45cqw);flex:1 1 min(150px,45cqw);overflow:hidden;white-space:nowrap}',
|
|
23
|
+
'[data-composer-card] > div:has([data-slot="conversation.input.right"]) [data-slot="conversation.input.model"] > button{width:100%;min-width:0;max-width:min(360px,45cqw);overflow:hidden;white-space:nowrap}',
|
|
24
|
+
// ContextMeter 在 pressure 尚未合并时会暂时返回 null;dock 保留同样的行高,数值回来时只更新内容。
|
|
25
|
+
'[data-composer-card] + div{box-sizing:border-box;min-height:26px;align-items:center}',
|
|
26
|
+
'[data-composer-card] + div svg[viewBox="0 0 14 14"] circle:last-child{transition:stroke-dasharray .18s ease,stroke .18s ease}',
|
|
18
27
|
'@keyframes codingns4dsh-cli-spin{to{transform:rotate(360deg)}}',
|
|
28
|
+
'@keyframes codingns4dsh-cli-model-scroll{0%,18%{transform:translateX(0)}82%,100%{transform:translateX(var(--codingns4dsh-model-scroll-offset))}}',
|
|
19
29
|
'.codingns4dsh-cli-spinner{animation:codingns4dsh-cli-spin .8s linear infinite}',
|
|
30
|
+
'.codingns4dsh-model-name[data-overflow="true"]>span{animation:codingns4dsh-cli-model-scroll 6s ease-in-out infinite alternate;will-change:transform}',
|
|
20
31
|
'.codingns4dsh-agent-trigger:hover:not(:disabled){background:color-mix(in srgb,currentColor 7%,transparent)}',
|
|
21
32
|
'.codingns4dsh-agent-option:hover:not(:disabled){background:color-mix(in srgb,currentColor 7%,transparent)!important}',
|
|
22
33
|
'.codingns4dsh-agent-option[data-selected="true"]{background:color-mix(in srgb,currentColor 10%,transparent)!important}',
|
|
23
|
-
'@media (prefers-reduced-motion:reduce){.codingns4dsh-cli-spinner{animation-duration:1.6s}}',
|
|
34
|
+
'@media (prefers-reduced-motion:reduce){.codingns4dsh-cli-spinner{animation-duration:1.6s}.codingns4dsh-model-name[data-overflow="true"]>span{animation:none;transform:translateX(0)}}',
|
|
24
35
|
].join('');
|
|
25
36
|
document.head.appendChild(style);
|
|
26
37
|
}
|
|
@@ -174,10 +185,10 @@ function NativeDropdownChevron({ open, locked = false }) {
|
|
|
174
185
|
fill: 'currentColor',
|
|
175
186
|
}));
|
|
176
187
|
}
|
|
177
|
-
const agentRootStyle = { position: 'relative', minWidth: 0, display: 'inline-flex' };
|
|
178
|
-
const agentTriggerStyle = { height: 30,
|
|
188
|
+
const agentRootStyle = { position: 'relative', minWidth: 0, flex: '0 0 auto', display: 'inline-flex' };
|
|
189
|
+
const agentTriggerStyle = { height: 30, minWidth: 0, color: dshThemeColor.labelPrimary, border: 0, borderRadius: 8, padding: '0 6px', background: 'transparent', display: 'inline-flex', alignItems: 'center', gap: 7, fontSize: 14, lineHeight: '20px', whiteSpace: 'nowrap' };
|
|
179
190
|
const agentTriggerIconStyle = { width: 20, height: 20, flex: '0 0 20px', objectFit: 'contain' };
|
|
180
|
-
const agentTriggerLabelStyle = {
|
|
191
|
+
const agentTriggerLabelStyle = { flex: '0 0 auto', whiteSpace: 'nowrap' };
|
|
181
192
|
const nativeDropdownChevronStyle = { display: 'block', flex: '0 0 14px', color: dshThemeColor.labelCaption, transformOrigin: 'center' };
|
|
182
193
|
const agentMenuStyle = { ...dshPopupSurfaceStyle, position: 'absolute', zIndex: 1100, bottom: 'calc(100% + 8px)', left: 0, minWidth: 238, maxWidth: 'min(320px, calc(100vw - 32px))', maxHeight: 'min(400px, calc(100vh - 96px))', overflowY: 'auto', padding: 5, border: 0, borderRadius: 8 };
|
|
183
194
|
const agentOptionStyle = { width: '100%', minHeight: 40, color: 'inherit', border: 0, borderRadius: 6, padding: '5px 8px 5px 4px', background: 'transparent', display: 'flex', alignItems: 'center', gap: 8, textAlign: 'left', fontSize: 14, lineHeight: '20px' };
|
|
@@ -186,6 +197,44 @@ const agentOptionIconStyle = { width: 22, height: 22, flex: '0 0 22px', objectFi
|
|
|
186
197
|
const agentOptionLabelStyle = { flex: '1 1 auto', minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' };
|
|
187
198
|
const agentStatusStyle = { flex: '0 0 auto', color: dshThemeColor.labelTertiary, fontSize: 12, whiteSpace: 'nowrap' };
|
|
188
199
|
const agentFallbackIconStyle = { flexGrow: 0, flexShrink: 0, borderRadius: 5, color: '#fff', background: dshThemeColor.labelTertiary, display: 'inline-flex', alignItems: 'center', justifyContent: 'center', fontSize: 11, fontWeight: 600, lineHeight: 1 };
|
|
200
|
+
/** 模型名称超出 150px 时,在固定视口内往返滚动展示完整文本。 */
|
|
201
|
+
function ModelName({ label, loading }) {
|
|
202
|
+
const viewportRef = useRef(null);
|
|
203
|
+
const contentRef = useRef(null);
|
|
204
|
+
const [scrollDistance, setScrollDistance] = useState(0);
|
|
205
|
+
useEffect(() => {
|
|
206
|
+
const updateOverflow = () => {
|
|
207
|
+
const viewport = viewportRef.current;
|
|
208
|
+
const content = contentRef.current;
|
|
209
|
+
if (viewport === null || content === null)
|
|
210
|
+
return;
|
|
211
|
+
const nextDistance = Math.max(0, Math.ceil(content.getBoundingClientRect().width - viewport.clientWidth));
|
|
212
|
+
setScrollDistance((current) => current === nextDistance ? current : nextDistance);
|
|
213
|
+
};
|
|
214
|
+
updateOverflow();
|
|
215
|
+
if (typeof ResizeObserver === 'undefined')
|
|
216
|
+
return;
|
|
217
|
+
const observer = new ResizeObserver(updateOverflow);
|
|
218
|
+
if (viewportRef.current !== null)
|
|
219
|
+
observer.observe(viewportRef.current);
|
|
220
|
+
if (contentRef.current !== null)
|
|
221
|
+
observer.observe(contentRef.current);
|
|
222
|
+
return () => observer.disconnect();
|
|
223
|
+
}, [label]);
|
|
224
|
+
const contentStyle = {
|
|
225
|
+
display: 'inline-block',
|
|
226
|
+
whiteSpace: 'nowrap',
|
|
227
|
+
'--codingns4dsh-model-scroll-offset': `-${scrollDistance}px`,
|
|
228
|
+
};
|
|
229
|
+
return createElement('span', {
|
|
230
|
+
ref: viewportRef,
|
|
231
|
+
className: 'codingns4dsh-model-name',
|
|
232
|
+
'data-overflow': String(scrollDistance > 0),
|
|
233
|
+
role: loading ? 'status' : undefined,
|
|
234
|
+
'aria-live': loading ? 'polite' : undefined,
|
|
235
|
+
style: modelNameStyle,
|
|
236
|
+
}, createElement('span', { ref: contentRef, style: contentStyle }, label));
|
|
237
|
+
}
|
|
189
238
|
function ModelSlot(props) {
|
|
190
239
|
// 语言词典中的中文值仍保留“正在加载模型列表…”语义,切换语言时由 t() 取值。
|
|
191
240
|
const t = useCodingNsTranslator(props.locale);
|
|
@@ -275,9 +324,10 @@ function ModelSlot(props) {
|
|
|
275
324
|
createElement('div', { key: 'title', style: nativeGroupTitleStyle }, `${t('cli.thinking')}(${modelLabel})`),
|
|
276
325
|
...(efforts.length > 0 ? efforts : ['default']).map((effort) => createElement('button', { key: effort, type: 'button', role: 'menuitemradio', 'aria-checked': effort === effortValue, onClick: () => chooseEffort(effort), style: nativeOptionStyle }, createElement('span', { style: { flex: '1 1 auto' } }, effort === 'default' ? 'Default' : effort), effort === effortValue && createElement('span', { 'aria-hidden': true }, '✓'))),
|
|
277
326
|
];
|
|
278
|
-
return createElement('div', { style: { position: 'relative', minWidth: 0, display: 'inline-flex' } }, createElement('button', { type: 'button', disabled: triggerDisabled, 'aria-label': t('cli.chooseModel', { model: modelLabel, effort: effortLabel }), 'aria-busy': loading, 'aria-haspopup': 'menu', 'aria-expanded': open, onClick: () => { setPane('root'); setOpen((value) => !value); }, style: nativeTriggerStyle }, loading && createElement('span', { className: 'codingns4dsh-cli-spinner', 'aria-hidden': true, style: modelSpinnerStyle }), createElement(
|
|
327
|
+
return createElement('div', { className: 'codingns4dsh-model-root', style: { position: 'relative', minWidth: 0, maxWidth: '100%', flex: '1 1 min(360px, 45cqw)', display: 'inline-flex' } }, createElement('button', { type: 'button', disabled: triggerDisabled, 'aria-label': t('cli.chooseModel', { model: modelLabel, effort: effortLabel }), 'aria-busy': loading, 'aria-haspopup': 'menu', 'aria-expanded': open, onClick: () => { setPane('root'); setOpen((value) => !value); }, style: nativeTriggerStyle }, loading && createElement('span', { className: 'codingns4dsh-cli-spinner', 'aria-hidden': true, style: modelSpinnerStyle }), createElement(ModelName, { label: modelLabel, loading }), !loading && createElement('span', { style: { color: dshThemeColor.labelCaption, whiteSpace: 'nowrap' } }, effortLabel), !loading && createElement(NativeDropdownChevron, { open })), open && createElement('div', { role: 'menu', 'aria-label': t('cli.chooseModelMenu'), style: nativeMenuStyle }, ...menu));
|
|
279
328
|
}
|
|
280
|
-
const nativeTriggerStyle = { minWidth: 0, maxWidth: 'min(360px, 45cqw)', height: 28, color: dshThemeColor.labelSecondary, cursor: 'pointer', background: 'transparent', border: 0, borderRadius: 24, padding: '0 4px 0 8px', display: 'inline-flex', alignItems: 'center', gap: 4, fontSize: 13, lineHeight: '20px' };
|
|
329
|
+
const nativeTriggerStyle = { width: '100%', minWidth: 0, maxWidth: 'min(360px, 45cqw)', height: 28, color: dshThemeColor.labelSecondary, cursor: 'pointer', background: 'transparent', border: 0, borderRadius: 24, padding: '0 4px 0 8px', display: 'inline-flex', alignItems: 'center', gap: 4, fontSize: 13, lineHeight: '20px' };
|
|
330
|
+
const modelNameStyle = { minWidth: 0, maxWidth: 150, flex: '0 1 150px', display: 'block', overflow: 'hidden', whiteSpace: 'nowrap' };
|
|
281
331
|
const nativeMenuStyle = { ...dshPopupSurfaceStyle, position: 'absolute', zIndex: 1100, right: 0, bottom: 'calc(100% + 8px)', minWidth: 240, maxWidth: 'min(420px, calc(100vw - 32px))', maxHeight: 'min(360px, calc(100vh - 96px))', overflowY: 'auto', padding: 4, border: 0, borderRadius: 20 };
|
|
282
332
|
const nativeMenuCellStyle = { width: '100%', minHeight: 40, color: 'inherit', cursor: 'pointer', background: 'transparent', border: 0, borderRadius: 10, padding: '0 10px', display: 'flex', alignItems: 'center', gap: 8, textAlign: 'left', fontSize: 14, lineHeight: '22px' };
|
|
283
333
|
const nativeMenuLabelStyle = { flex: 'none', whiteSpace: 'nowrap' };
|
|
@@ -3,7 +3,7 @@ import type { CodingNsRpcClient } from '../features/types.js';
|
|
|
3
3
|
export declare const DEBUG_KIND = "debug";
|
|
4
4
|
export declare const DEBUG_PROVIDER_ID = "codingns4dsh/debug";
|
|
5
5
|
/** 注册最小 Debug 页面;页面只负责展示和发送用户意图。 */
|
|
6
|
-
export declare function registerDebugUi(ctx: Context, rpc: CodingNsRpcClient, remote: unknown): () => void;
|
|
6
|
+
export declare function registerDebugUi(ctx: Context, rpc: CodingNsRpcClient, remote: unknown, terminalRemote?: () => unknown): () => void;
|
|
7
7
|
declare module '@deepseek-ai/dsh-client-ui-sidebar-right/client' {
|
|
8
8
|
interface SidebarRightTabParamsMap {
|
|
9
9
|
debug: undefined;
|
|
@@ -1,28 +1,43 @@
|
|
|
1
1
|
import { createElement, useEffect, useState } from 'react';
|
|
2
2
|
import { CODINGNS_RPC_CHANNEL } from '../../shared/contracts/transport.js';
|
|
3
|
+
import { debugWarn } from '../../shared/debug.js';
|
|
3
4
|
import { dshButtonStyle, dshFieldStyle, dshFormRootStyle, dshThemeColor } from '../theme.js';
|
|
4
5
|
export const DEBUG_KIND = 'debug';
|
|
5
6
|
export const DEBUG_PROVIDER_ID = 'codingns4dsh/debug';
|
|
6
7
|
const PORT_CHECK_INTERVAL_MS = 5_000;
|
|
7
8
|
/** 注册最小 Debug 页面;页面只负责展示和发送用户意图。 */
|
|
8
|
-
export function registerDebugUi(ctx, rpc, remote) {
|
|
9
|
+
export function registerDebugUi(ctx, rpc, remote, terminalRemote) {
|
|
9
10
|
const disposers = [];
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
11
|
+
try {
|
|
12
|
+
disposers.push(ctx.sidebarRightTabs.register({
|
|
13
|
+
id: DEBUG_PROVIDER_ID,
|
|
14
|
+
kind: DEBUG_KIND,
|
|
15
|
+
multiple: false,
|
|
16
|
+
priority: 'extension',
|
|
17
|
+
title: () => '调试',
|
|
18
|
+
guide: [{ id: 'debug', order: 30, title: () => '调试', description: () => '启动工作区命令、检查端口并访问服务', icon: DebugIcon }],
|
|
19
|
+
}));
|
|
20
|
+
disposers.push(ctx.slots.inject('sidebar.right.pane.tab', () => ctx.slots.register({
|
|
21
|
+
name: 'sidebar.right.pane.tab', key: DEBUG_PROVIDER_ID,
|
|
22
|
+
inject: () => ({ rpc, remote, terminalRemote, sidebarRight: ctx.sidebarRight }),
|
|
23
|
+
}, DebugBody)));
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
for (const dispose of disposers.reverse())
|
|
27
|
+
dispose();
|
|
28
|
+
if (isDuplicateDebugRegistration(error)) {
|
|
29
|
+
debugWarn(`codingns4dsh: 调试 Sidebar 已注册,跳过重复注册: ${DEBUG_PROVIDER_ID}`);
|
|
30
|
+
return () => { };
|
|
31
|
+
}
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
22
34
|
return () => { for (const dispose of disposers.reverse())
|
|
23
35
|
dispose(); };
|
|
24
36
|
}
|
|
25
|
-
function
|
|
37
|
+
function isDuplicateDebugRegistration(error) {
|
|
38
|
+
return error instanceof Error && /sidebarRight: (?:tab type id|tab kind) .* already registered/u.test(error.message);
|
|
39
|
+
}
|
|
40
|
+
function DebugBody({ sessionId, rpc, remote, terminalRemote, sidebarRight }) {
|
|
26
41
|
const [workspaceId, setWorkspaceId] = useState(null);
|
|
27
42
|
const [config, setConfig] = useState(null);
|
|
28
43
|
const [instances, setInstances] = useState([]);
|
|
@@ -32,10 +47,13 @@ function DebugBody({ sessionId, rpc, remote, sidebarRight }) {
|
|
|
32
47
|
const [draft, setDraft] = useState(null);
|
|
33
48
|
const [message, setMessage] = useState('正在读取工作区…');
|
|
34
49
|
const [busy, setBusy] = useState(false);
|
|
35
|
-
const load = async (currentWorkspaceId) => {
|
|
50
|
+
const load = async (currentWorkspaceId, isCurrent) => {
|
|
36
51
|
const scope = { sessionId: String(sessionId), workspaceId: currentWorkspaceId, generation: 0 };
|
|
37
52
|
const next = await call(rpc, 'debug/config/get', scope);
|
|
38
53
|
const running = await call(rpc, 'debug/runtime/list', scope);
|
|
54
|
+
// Session 切换后,旧请求的结果不能覆盖新 Session 的面板状态。
|
|
55
|
+
if (isCurrent?.() === false)
|
|
56
|
+
return;
|
|
39
57
|
setConfig(next);
|
|
40
58
|
setInstances(running);
|
|
41
59
|
setMessage('');
|
|
@@ -43,7 +61,7 @@ function DebugBody({ sessionId, rpc, remote, sidebarRight }) {
|
|
|
43
61
|
useEffect(() => {
|
|
44
62
|
let disposed = false;
|
|
45
63
|
setPortChecks({});
|
|
46
|
-
const terminal =
|
|
64
|
+
const terminal = terminalRemote?.();
|
|
47
65
|
void call(rpc, 'terminal/status', {}).then((status) => {
|
|
48
66
|
if (!disposed)
|
|
49
67
|
setTerminalStatus(status);
|
|
@@ -57,7 +75,7 @@ function DebugBody({ sessionId, rpc, remote, sidebarRight }) {
|
|
|
57
75
|
if (disposed)
|
|
58
76
|
return;
|
|
59
77
|
setWorkspaceId(id);
|
|
60
|
-
await load(id);
|
|
78
|
+
await load(id, () => !disposed);
|
|
61
79
|
}
|
|
62
80
|
catch (error) {
|
|
63
81
|
if (!disposed)
|
|
@@ -115,7 +133,7 @@ function DebugBody({ sessionId, rpc, remote, sidebarRight }) {
|
|
|
115
133
|
if (workspaceId === null)
|
|
116
134
|
return createElement('section', { style: panelStyle }, createElement('div', { style: loadingStyle }, createElement('span', { style: loadingDotStyle }), message));
|
|
117
135
|
const profiles = config?.profiles ?? [];
|
|
118
|
-
return createElement('section', { style: panelStyle }, createElement('header', { style: headerStyle }, createElement('div', { style: titleBlockStyle }, createElement('div', { style: eyebrowStyle }, '工作区工具'), createElement('h2', { style: titleStyle }, '工作区调试'), createElement('div', { style: workspaceStyle, title: workspaceId }, createElement('span', { style: workspaceDotStyle }), workspaceId)), createElement('div', { style: headerActionsStyle }, createElement('span', { style: countStyle }, `${profiles.length} 个配置`), createElement('button', { type: 'button', 'aria-label': '添加启动配置', disabled: busy || draft !== null, onClick: () => setDraft(createProfileDraft()), style: primaryButtonStyle }, createElement('span', { 'aria-hidden': true }, '+'), ' 添加配置'))), message && createElement('div', { role: 'status', 'aria-live': 'polite', style: statusStyle(statusTone(message)) }, createElement('span', { style: statusIconStyle }, statusTone(message) === 'success' ? '✓' : statusTone(message) === 'error' ? '!' : 'i'), message), draft === null ? null : createProfileForm(draft), profiles.length === 0 && draft === null ? createElement('div', { style: emptyStyle }, createElement('div', { style: emptyIconStyle }, createElement(DebugIcon, { size: 22 })), createElement('strong', { style: emptyTitleStyle }, '还没有启动配置'), createElement('p', { style: emptyTextStyle }, '为当前 Workspace 添加一个命令,即可从这里启动终端、检查端口和访问服务。'), createElement('button', { type: 'button', 'aria-label': '添加启动配置', disabled: busy, onClick: () => setDraft(createProfileDraft()), style: secondaryButtonStyle }, '+ 添加第一个配置')) : null, profiles.map((profile) => {
|
|
136
|
+
return createElement('section', { style: panelStyle }, createElement('header', { style: headerStyle }, createElement('div', { style: titleBlockStyle }, createElement('div', { style: eyebrowStyle }, '工作区工具'), createElement('h2', { style: titleStyle }, '工作区调试'), createElement('div', { style: workspaceStyle, title: workspaceId }, createElement('span', { style: workspaceDotStyle }), workspaceId)), createElement('div', { style: headerActionsStyle }, createElement('span', { style: countStyle }, `${profiles.length} 个配置`), createElement('button', { type: 'button', 'aria-label': '添加启动配置', disabled: busy || draft !== null, onClick: () => setDraft(createProfileDraft()), style: primaryButtonStyle }, createElement('span', { 'aria-hidden': true }, '+'), ' 添加配置'))), message && createElement('div', { role: 'status', 'aria-live': 'polite', style: statusStyle(statusTone(message)) }, createElement('span', { style: statusIconStyle }, statusTone(message) === 'success' ? '✓' : statusTone(message) === 'error' ? '!' : 'i'), message), terminalStatus?.runtimeWarning === undefined ? null : createElement('div', { role: 'alert', style: warningStatusStyle }, createElement('span', { style: statusIconStyle }, '!'), terminalStatus.runtimeWarning), draft === null ? null : createProfileForm(draft), profiles.length === 0 && draft === null ? createElement('div', { style: emptyStyle }, createElement('div', { style: emptyIconStyle }, createElement(DebugIcon, { size: 22 })), createElement('strong', { style: emptyTitleStyle }, '还没有启动配置'), createElement('p', { style: emptyTextStyle }, '为当前 Workspace 添加一个命令,即可从这里启动终端、检查端口和访问服务。'), createElement('button', { type: 'button', 'aria-label': '添加启动配置', disabled: busy, onClick: () => setDraft(createProfileDraft()), style: secondaryButtonStyle }, '+ 添加第一个配置')) : null, profiles.map((profile) => {
|
|
119
137
|
const running = runningInstances(profile, instances);
|
|
120
138
|
const portCheck = portChecks[profile.id];
|
|
121
139
|
return createElement('article', { key: profile.id, style: itemStyle }, createElement('div', { style: itemHeaderStyle }, createElement('div', { style: itemTitleBlockStyle }, createElement('strong', { style: itemTitleStyle }, profile.name), createElement('span', { style: itemCommandStyle }, formatCommand(profile))), createElement('span', { style: running > 0 ? runningBadgeStyle : stoppedBadgeStyle }, running > 0 ? `${running} 个运行中` : '未运行')), createElement('div', { style: metaStyle }, createElement('span', { style: metaItemStyle }, createElement('span', { style: metaKeyStyle }, '目录'), profile.cwdRelative || '.'), profile.port === null ? null : createElement('span', { style: metaItemStyle }, createElement('span', { style: metaKeyStyle }, '端口'), String(profile.port)), createElement('span', { style: metaItemStyle }, createElement('span', { style: metaKeyStyle }, '运行方式'), profile.runtimeType), profile.proxy.enabled ? createElement('span', { style: proxyBadgeStyle }, '代理已启用') : null), profile.port === null ? null : createElement('div', { style: portStatusStyle }, portCheck === undefined
|
|
@@ -150,7 +168,7 @@ function DebugBody({ sessionId, rpc, remote, sidebarRight }) {
|
|
|
150
168
|
style: inputStyle,
|
|
151
169
|
}));
|
|
152
170
|
const selectField = (label, key, options) => createElement('label', { style: selectFieldStyle }, createElement('span', { style: fieldLabelStyle }, label), createElement('select', { value: value[key], onChange: (event) => setDraft({ ...value, [key]: event.currentTarget.value }), style: inputStyle }, ...options.map((option) => createElement('option', { key: option.value, value: option.value }, option.label))));
|
|
153
|
-
return createElement('div', { role: 'dialog', 'aria-modal': true, 'aria-labelledby': 'debug-shortcut-title', style: formOverlayStyle }, createElement('form', { style: formStyle, onSubmit: (event) => { event.preventDefault(); void saveProfile(value); } }, createElement('div', { style: formHeaderStyle }, createElement('div', undefined, createElement('h3', { id: 'debug-shortcut-title', style: formTitleStyle }, value.id === null ? '添加快捷启动项' : '编辑快捷启动项'), createElement('p', { style: formHintStyle }, '保存一组可重复使用的启动参数,之后可以一键打开终端。')), createElement('button', { type: 'button', disabled: busy, onClick: () => setDraft(null), style: closeButtonStyle, 'aria-label': '关闭快捷启动项' }, '×')), createElement('section', { style: formSectionStyle }, createElement('div', { style: formSectionHeaderStyle }, createElement('strong', { style: formSectionTitleStyle }, '启动入口'), createElement('span', { style: formSectionHintStyle }, '直接输入完整命令,例如 pnpm run dev --host 0.0.0.0。')), createElement('div', { style: formGridStyle }, field('名称', 'name', 'text', '例如:前端开发'), field('启动目录(Workspace 内相对路径)', 'cwdRelative', 'text', '留空使用根目录'), field('完整启动命令', 'commandLine', 'text', '例如:pnpm run dev --host 0.0.0.0', { gridColumn: '1 / -1' }))), createElement('section', { style: formSectionStyle }, createElement('div', { style: formSectionHeaderStyle }, createElement('strong', { style: formSectionTitleStyle }, '执行环境(可选)'), createElement('span', { style: formSectionHintStyle }, '只显示 Host 实际检测到的 Shell。')), createElement('div', { style: formGridStyle }, selectField('终端 Shell', 'shellProfileId', shellOptions(terminalStatus))), createElement('p', { style: runtimeHintStyle }, `运行方式由 Host
|
|
171
|
+
return createElement('div', { role: 'dialog', 'aria-modal': true, 'aria-labelledby': 'debug-shortcut-title', style: formOverlayStyle }, createElement('form', { style: formStyle, onSubmit: (event) => { event.preventDefault(); void saveProfile(value); } }, createElement('div', { style: formHeaderStyle }, createElement('div', undefined, createElement('h3', { id: 'debug-shortcut-title', style: formTitleStyle }, value.id === null ? '添加快捷启动项' : '编辑快捷启动项'), createElement('p', { style: formHintStyle }, '保存一组可重复使用的启动参数,之后可以一键打开终端。')), createElement('button', { type: 'button', disabled: busy, onClick: () => setDraft(null), style: closeButtonStyle, 'aria-label': '关闭快捷启动项' }, '×')), createElement('section', { style: formSectionStyle }, createElement('div', { style: formSectionHeaderStyle }, createElement('strong', { style: formSectionTitleStyle }, '启动入口'), createElement('span', { style: formSectionHintStyle }, '直接输入完整命令,例如 pnpm run dev --host 0.0.0.0。')), createElement('div', { style: formGridStyle }, field('名称', 'name', 'text', '例如:前端开发'), field('启动目录(Workspace 内相对路径)', 'cwdRelative', 'text', '留空使用根目录'), field('完整启动命令', 'commandLine', 'text', '例如:pnpm run dev --host 0.0.0.0', { gridColumn: '1 / -1' }))), createElement('section', { style: formSectionStyle }, createElement('div', { style: formSectionHeaderStyle }, createElement('strong', { style: formSectionTitleStyle }, '执行环境(可选)'), createElement('span', { style: formSectionHintStyle }, '只显示 Host 实际检测到的 Shell。')), createElement('div', { style: formGridStyle }, selectField('终端 Shell', 'shellProfileId', shellOptions(terminalStatus))), createElement('p', { style: runtimeHintStyle }, `运行方式由 Host 平台自动选择(实际能力:${terminalRuntimeLabel(terminalStatus)})。`), terminalStatus?.runtimeWarning === undefined ? null : createElement('p', { style: warningHintStyle }, terminalStatus.runtimeWarning)), createElement('section', { style: formSectionStyle }, createElement('div', { style: formSectionHeaderStyle }, createElement('strong', { style: formSectionTitleStyle }, '服务检查'), createElement('span', { style: formSectionHintStyle }, '端口每 5 秒自动检查,也可手动刷新。')), createElement('div', { style: formGridStyle }, field('监听端口(可选)', 'port', 'number', '1 - 65535'), createElement('label', { style: proxyFieldStyle }, createElement('span', { style: fieldLabelStyle }, '反向代理'), createElement('span', { style: proxyToggleStyle }, createElement('span', undefined, '允许访问已确认的服务'), createElement('input', { type: 'checkbox', 'aria-label': '启用服务代理', checked: value.proxyEnabled, onChange: (event) => setDraft({ ...value, proxyEnabled: event.currentTarget.checked }), style: { accentColor: dshThemeColor.accent } }))))), createElement('div', { style: formActionsStyle }, createElement('button', { type: 'button', disabled: busy, onClick: () => setDraft(null), style: secondaryButtonStyle }, '关闭'), createElement('button', { type: 'submit', 'aria-label': value.id === null ? '保存配置并创建快捷启动项' : '保存快捷启动项修改', disabled: busy || value.commandLine.trim() === '', style: primaryButtonStyle }, busy ? '保存中…' : value.id === null ? '保存为快捷启动' : '保存修改'))));
|
|
154
172
|
}
|
|
155
173
|
async function saveProfile(value) {
|
|
156
174
|
await withBusy(async () => {
|
|
@@ -171,7 +189,7 @@ function DebugBody({ sessionId, rpc, remote, sidebarRight }) {
|
|
|
171
189
|
args: commandParts.slice(1),
|
|
172
190
|
env: {},
|
|
173
191
|
shell,
|
|
174
|
-
runtimeType: runtimeTypeFor(terminalStatus
|
|
192
|
+
runtimeType: runtimeTypeFor(terminalStatus, shell.profileId),
|
|
175
193
|
port,
|
|
176
194
|
proxy: { enabled: value.proxyEnabled },
|
|
177
195
|
};
|
|
@@ -331,6 +349,8 @@ const selectFieldStyle = { ...fieldStyle };
|
|
|
331
349
|
const fieldLabelStyle = { color: dshThemeColor.labelSecondary, fontSize: 12, fontWeight: 500 };
|
|
332
350
|
const inputStyle = { ...dshFieldStyle, width: '100%', boxSizing: 'border-box', minHeight: 32, padding: '6px 8px', borderRadius: 6, fontSize: 12 };
|
|
333
351
|
const runtimeHintStyle = { margin: '-5px 0 0', color: dshThemeColor.labelTertiary, fontSize: 11 };
|
|
352
|
+
const warningHintStyle = { margin: '-5px 0 0', color: dshThemeColor.error, fontSize: 11 };
|
|
353
|
+
const warningStatusStyle = { ...statusStyle('error'), marginBottom: 0 };
|
|
334
354
|
const formGridStyle = { display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: 14 };
|
|
335
355
|
const proxyFieldStyle = { ...fieldStyle };
|
|
336
356
|
const proxyToggleStyle = { display: 'flex', alignItems: 'center', justifyContent: 'space-between', height: 32, boxSizing: 'border-box', padding: '0 2px', border: 0, borderRadius: 0, color: dshThemeColor.labelSecondary, background: 'transparent', fontSize: 12, lineHeight: '18px' };
|
|
@@ -386,23 +406,33 @@ function shellArgsFor(profileId) {
|
|
|
386
406
|
function defaultShellForPlatform(platform) {
|
|
387
407
|
return platform === 'win32' ? 'powershell' : 'zsh';
|
|
388
408
|
}
|
|
389
|
-
function runtimeTypeFor(
|
|
390
|
-
if (
|
|
391
|
-
throw new Error('Host
|
|
392
|
-
|
|
409
|
+
function runtimeTypeFor(status, shellProfileId) {
|
|
410
|
+
if (status === null || status === undefined)
|
|
411
|
+
throw new Error('尚未获取 Host 终端能力');
|
|
412
|
+
const runtimeTypes = status.runtimeTypes ?? (status.effectiveEnabled ? ['tmux'] : ['local-pty']);
|
|
413
|
+
if (runtimeTypes.includes('tmux'))
|
|
393
414
|
return 'tmux';
|
|
394
|
-
if (
|
|
415
|
+
if (status.platform !== 'win32')
|
|
416
|
+
return 'local-pty';
|
|
417
|
+
if (shellProfileId === 'cmd' && runtimeTypes.includes('conpty-cmd'))
|
|
395
418
|
return 'conpty-cmd';
|
|
396
|
-
if (shellProfileId === 'git-bash')
|
|
419
|
+
if (shellProfileId === 'git-bash' && runtimeTypes.includes('conpty-git-bash'))
|
|
397
420
|
return 'conpty-git-bash';
|
|
398
|
-
|
|
421
|
+
if (runtimeTypes.includes('conpty-powershell'))
|
|
422
|
+
return 'conpty-powershell';
|
|
423
|
+
if (runtimeTypes.includes('local-pty'))
|
|
424
|
+
return 'local-pty';
|
|
425
|
+
throw new Error('Host 没有可用的终端 backend');
|
|
399
426
|
}
|
|
400
|
-
function terminalRuntimeLabel(
|
|
401
|
-
|
|
427
|
+
function terminalRuntimeLabel(status) {
|
|
428
|
+
const runtimeTypes = status?.runtimeTypes ?? (status?.effectiveEnabled ? ['tmux'] : ['local-pty']);
|
|
429
|
+
if (runtimeTypes.includes('tmux'))
|
|
430
|
+
return status?.platform === 'darwin' ? 'macOS 使用 tmux' : 'Linux 使用 tmux';
|
|
431
|
+
if (runtimeTypes.includes('conpty-powershell') || runtimeTypes.includes('conpty-cmd') || runtimeTypes.includes('conpty-git-bash'))
|
|
402
432
|
return 'Windows 使用 ConPTY';
|
|
403
|
-
if (
|
|
404
|
-
return
|
|
405
|
-
return '等待 Host
|
|
433
|
+
if (runtimeTypes.includes('local-pty'))
|
|
434
|
+
return '使用 local-pty(进程内 PTY)';
|
|
435
|
+
return '等待 Host 终端能力状态';
|
|
406
436
|
}
|
|
407
437
|
function splitCommandLine(value) {
|
|
408
438
|
const parts = [];
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
2
|
+
/**
|
|
3
|
+
* 读取 Host 注入的真实 DSH 版本,并在无法读取时拒绝启用 Client。
|
|
4
|
+
*
|
|
5
|
+
* DSH 0.1.7 的设置服务迁移会让旧 Host 启动页注入钩子无法执行;此时
|
|
6
|
+
* `configForms` 是新版 Client 唯一可靠的运行时标志,使用其最低现代版本
|
|
7
|
+
* 作为能力路由版本,避免因为旧注入链失败而误判整个 Client 不兼容。
|
|
8
|
+
*/
|
|
9
|
+
export declare function assertInjectedDshVersion(ctx?: Context): string;
|
|
3
10
|
//# sourceMappingURL=dsh-runtime-version.d.ts.map
|
|
@@ -1,11 +1,33 @@
|
|
|
1
1
|
import { assertSupportedDshVersion, CODINGNS_DSH_ERROR_CODES, CODINGNS_DSH_VERSION_GLOBAL, CodingNsDshError, } from '../shared/index.js';
|
|
2
|
-
/**
|
|
3
|
-
|
|
2
|
+
/**
|
|
3
|
+
* 读取 Host 注入的真实 DSH 版本,并在无法读取时拒绝启用 Client。
|
|
4
|
+
*
|
|
5
|
+
* DSH 0.1.7 的设置服务迁移会让旧 Host 启动页注入钩子无法执行;此时
|
|
6
|
+
* `configForms` 是新版 Client 唯一可靠的运行时标志,使用其最低现代版本
|
|
7
|
+
* 作为能力路由版本,避免因为旧注入链失败而误判整个 Client 不兼容。
|
|
8
|
+
*/
|
|
9
|
+
export function assertInjectedDshVersion(ctx) {
|
|
4
10
|
const value = globalThis[CODINGNS_DSH_VERSION_GLOBAL];
|
|
5
11
|
if (typeof value !== 'string' || value.trim() === '') {
|
|
12
|
+
if (hasModernConfigForms(ctx)) {
|
|
13
|
+
const modernVersion = '0.1.7-rc.2';
|
|
14
|
+
assertSupportedDshVersion(modernVersion);
|
|
15
|
+
return modernVersion;
|
|
16
|
+
}
|
|
6
17
|
throw new CodingNsDshError(CODINGNS_DSH_ERROR_CODES.DSH_VERSION_UNSUPPORTED, '无法读取当前 DSH 版本;为避免 Client API 不兼容,已拒绝启用 codingns4dsh');
|
|
7
18
|
}
|
|
8
19
|
assertSupportedDshVersion(value);
|
|
9
20
|
return value;
|
|
10
21
|
}
|
|
22
|
+
function hasModernConfigForms(ctx) {
|
|
23
|
+
if (ctx === undefined)
|
|
24
|
+
return false;
|
|
25
|
+
try {
|
|
26
|
+
const forms = ctx.get('configForms');
|
|
27
|
+
return typeof forms?.get === 'function';
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
11
33
|
//# sourceMappingURL=dsh-runtime-version.js.map
|
|
@@ -3,5 +3,5 @@ import type { FeaturePanelProps, CodingNsClientFeatureModule } from './types.js'
|
|
|
3
3
|
/** 外部 Agent 集成模块。Agent 进程在 Host 运行,浏览器只读取目录和状态。 */
|
|
4
4
|
export declare const cliAdaptersFeature: CodingNsClientFeatureModule;
|
|
5
5
|
/** 设置页中的 Agent 列表和详情模态框。 */
|
|
6
|
-
export declare function CliAdaptersPanel({ services, enabled }: FeaturePanelProps): ReactElement;
|
|
6
|
+
export declare function CliAdaptersPanel({ services, enabled, notify }: FeaturePanelProps): ReactElement;
|
|
7
7
|
//# sourceMappingURL=cli-adapters.d.ts.map
|
|
@@ -7,7 +7,7 @@ import { registerExternalToolStreamUi } from '../external-tool-stream.js';
|
|
|
7
7
|
export const cliAdaptersFeature = {
|
|
8
8
|
descriptor: {
|
|
9
9
|
name: 'cliAdapters',
|
|
10
|
-
version: '0.1.
|
|
10
|
+
version: '0.1.1',
|
|
11
11
|
enabledByDefault: true,
|
|
12
12
|
dependencies: [],
|
|
13
13
|
runtime: 'client',
|
|
@@ -33,7 +33,7 @@ export const cliAdaptersFeature = {
|
|
|
33
33
|
settingsPanel: CliAdaptersPanel,
|
|
34
34
|
};
|
|
35
35
|
/** 设置页中的 Agent 列表和详情模态框。 */
|
|
36
|
-
export function CliAdaptersPanel({ services, enabled }) {
|
|
36
|
+
export function CliAdaptersPanel({ services, enabled, notify }) {
|
|
37
37
|
const t = useCodingNsTranslator(services.locale);
|
|
38
38
|
const [catalog, setCatalog] = useState([]);
|
|
39
39
|
const [selected, setSelected] = useState(null);
|
|
@@ -44,7 +44,7 @@ export function CliAdaptersPanel({ services, enabled }) {
|
|
|
44
44
|
const [sessionsLoading, setSessionsLoading] = useState(false);
|
|
45
45
|
const [restoringSessionId, setRestoringSessionId] = useState(null);
|
|
46
46
|
const [archivingSessionId, setArchivingSessionId] = useState(null);
|
|
47
|
-
const [
|
|
47
|
+
const [modelsError, setModelsError] = useState('');
|
|
48
48
|
const disabled = !enabled;
|
|
49
49
|
useEffect(() => {
|
|
50
50
|
if (disabled)
|
|
@@ -55,7 +55,7 @@ export function CliAdaptersPanel({ services, enabled }) {
|
|
|
55
55
|
.then((value) => { if (active)
|
|
56
56
|
setCatalog(value); })
|
|
57
57
|
.catch((error) => { if (active)
|
|
58
|
-
|
|
58
|
+
notify({ kind: 'error', message: errorMessage(error) }); })
|
|
59
59
|
.finally(() => { if (active)
|
|
60
60
|
setLoading(false); });
|
|
61
61
|
return () => { active = false; };
|
|
@@ -71,7 +71,7 @@ export function CliAdaptersPanel({ services, enabled }) {
|
|
|
71
71
|
.then((value) => { if (active)
|
|
72
72
|
setSessions(value); })
|
|
73
73
|
.catch((error) => { if (active)
|
|
74
|
-
|
|
74
|
+
notify({ kind: 'error', message: errorMessage(error) }); })
|
|
75
75
|
.finally(() => { if (active)
|
|
76
76
|
setSessionsLoading(false); });
|
|
77
77
|
return () => { active = false; };
|
|
@@ -83,26 +83,29 @@ export function CliAdaptersPanel({ services, enabled }) {
|
|
|
83
83
|
}
|
|
84
84
|
let active = true;
|
|
85
85
|
setModels(null);
|
|
86
|
-
|
|
86
|
+
setModelsError('');
|
|
87
87
|
void callCliRpc(services.rpc, 'models', { adapterId: selected.id })
|
|
88
88
|
.then((value) => { if (active)
|
|
89
89
|
setModels(value); })
|
|
90
|
-
.catch((error) => { if (active)
|
|
91
|
-
|
|
90
|
+
.catch((error) => { if (active) {
|
|
91
|
+
const message = errorMessage(error);
|
|
92
|
+
setModelsError(message);
|
|
93
|
+
notify({ kind: 'error', message });
|
|
94
|
+
} });
|
|
92
95
|
return () => { active = false; };
|
|
93
96
|
}, [disabled, selected, services.rpc]);
|
|
94
97
|
const rowStyle = dshSettingsListRowStyle;
|
|
95
98
|
const buttonStyle = { ...dshSettingsButtonStyle, cursor: disabled ? 'not-allowed' : 'pointer' };
|
|
96
99
|
const toggleAdapter = async (adapter, next) => {
|
|
97
100
|
setBusyAdapterId(adapter.id);
|
|
98
|
-
setMessage('');
|
|
99
101
|
try {
|
|
100
102
|
await callCliRpc(services.rpc, 'adapter/set', { adapterId: adapter.id, enabled: next });
|
|
101
103
|
const refreshed = await callCliRpc(services.rpc, 'catalog', {});
|
|
102
104
|
setCatalog(refreshed);
|
|
105
|
+
notify({ kind: 'success', message: next ? `已启用 ${adapter.name}` : `已停用 ${adapter.name}` });
|
|
103
106
|
}
|
|
104
107
|
catch (error) {
|
|
105
|
-
|
|
108
|
+
notify({ kind: 'error', message: errorMessage(error) });
|
|
106
109
|
}
|
|
107
110
|
finally {
|
|
108
111
|
setBusyAdapterId(null);
|
|
@@ -110,12 +113,12 @@ export function CliAdaptersPanel({ services, enabled }) {
|
|
|
110
113
|
};
|
|
111
114
|
const restoreSession = async (record) => {
|
|
112
115
|
setRestoringSessionId(record.dshSessionId);
|
|
113
|
-
setMessage('');
|
|
114
116
|
try {
|
|
115
117
|
await restoreCliSession(services.rpc, record);
|
|
118
|
+
notify({ kind: 'success', message: `已打开 ${record.title ?? record.adapterId} 会话` });
|
|
116
119
|
}
|
|
117
120
|
catch (error) {
|
|
118
|
-
|
|
121
|
+
notify({ kind: 'error', message: errorMessage(error) });
|
|
119
122
|
}
|
|
120
123
|
finally {
|
|
121
124
|
setRestoringSessionId(null);
|
|
@@ -123,13 +126,13 @@ export function CliAdaptersPanel({ services, enabled }) {
|
|
|
123
126
|
};
|
|
124
127
|
const archiveSession = async (record) => {
|
|
125
128
|
setArchivingSessionId(record.dshSessionId);
|
|
126
|
-
setMessage('');
|
|
127
129
|
try {
|
|
128
130
|
await archiveCliSession(services.rpc, record.dshSessionId);
|
|
129
131
|
setSessions((current) => current.filter((item) => item.dshSessionId !== record.dshSessionId));
|
|
132
|
+
notify({ kind: 'success', message: `已移除 ${record.title ?? record.adapterId} 会话` });
|
|
130
133
|
}
|
|
131
134
|
catch (error) {
|
|
132
|
-
|
|
135
|
+
notify({ kind: 'error', message: errorMessage(error) });
|
|
133
136
|
}
|
|
134
137
|
finally {
|
|
135
138
|
setArchivingSessionId(null);
|
|
@@ -148,10 +151,10 @@ export function CliAdaptersPanel({ services, enabled }) {
|
|
|
148
151
|
onRestore: (record) => { void restoreSession(record); },
|
|
149
152
|
onArchive: (record) => { void archiveSession(record); },
|
|
150
153
|
t,
|
|
151
|
-
}),
|
|
154
|
+
}), selected !== null && createElement(AdapterDetailsDialog, {
|
|
152
155
|
adapter: selected,
|
|
153
156
|
models,
|
|
154
|
-
loading: selected.installed && selected.enabled && models === null &&
|
|
157
|
+
loading: selected.installed && selected.enabled && models === null && modelsError === '',
|
|
155
158
|
onClose: () => setSelected(null),
|
|
156
159
|
buttonStyle,
|
|
157
160
|
t,
|
|
@@ -3,7 +3,7 @@ import { registerDebugUi } from '../debug/ui.js';
|
|
|
3
3
|
export const debugFeature = {
|
|
4
4
|
descriptor: {
|
|
5
5
|
name: 'debug',
|
|
6
|
-
version: '0.1.
|
|
6
|
+
version: '0.1.1',
|
|
7
7
|
enabledByDefault: true,
|
|
8
8
|
dependencies: [],
|
|
9
9
|
runtime: 'client',
|
|
@@ -18,7 +18,7 @@ export const debugFeature = {
|
|
|
18
18
|
const uiContext = context.services.uiContext;
|
|
19
19
|
if (uiContext === undefined)
|
|
20
20
|
throw new Error('工作区调试模块缺少 DSH UI 上下文');
|
|
21
|
-
context.resources.add(registerDebugUi(uiContext, context.services.rpc, context.services.remote));
|
|
21
|
+
context.resources.add(registerDebugUi(uiContext, context.services.rpc, context.services.remote, context.services.terminalRemote));
|
|
22
22
|
},
|
|
23
23
|
};
|
|
24
24
|
//# sourceMappingURL=debug.js.map
|
|
@@ -7,6 +7,7 @@ import { cliAdaptersFeature } from './cli-adapters.js';
|
|
|
7
7
|
import { terminalEnhancementFeature } from './terminal-enhancement.js';
|
|
8
8
|
import { workspaceSessionEnhancementFeature } from './workspace-session-enhancement.js';
|
|
9
9
|
import { debugFeature } from './debug.js';
|
|
10
|
+
import { gitManagementFeature } from '../git-management.js';
|
|
10
11
|
import type { CodingNsClientFeatureModule, CodingNsClientServices } from './types.js';
|
|
11
12
|
/** Client 侧功能模块清单:新增模块在这里登记一行,不需要改动设置页和入口。 */
|
|
12
13
|
export declare const CLIENT_FEATURES: readonly CodingNsClientFeatureModule[];
|
|
@@ -21,7 +22,7 @@ export interface CodingNsSettingsModule {
|
|
|
21
22
|
* 归属 Host 的模块由 Host 侧配置承载,这里跳过;没有 ui 描述的模块不显示。
|
|
22
23
|
*/
|
|
23
24
|
export declare function settingsModules(registry: FeatureRegistry<CodingNsClientServices, CodingNsClientFeatureModule>): readonly CodingNsSettingsModule[];
|
|
24
|
-
export { lanAccessFeature, loginProtectionFeature, reverseProxyFeature, cliAdaptersFeature, workspaceSessionEnhancementFeature, terminalEnhancementFeature, debugFeature };
|
|
25
|
+
export { lanAccessFeature, loginProtectionFeature, reverseProxyFeature, cliAdaptersFeature, workspaceSessionEnhancementFeature, terminalEnhancementFeature, debugFeature, gitManagementFeature };
|
|
25
26
|
export { startBrowserRelayConnection } from './reverse-proxy.js';
|
|
26
27
|
export { LanAccessPanel } from './lan-access-panel.js';
|
|
27
28
|
export { LoginProtectionPanel } from './login-protection-panel.js';
|