@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.
Files changed (213) hide show
  1. package/README.en.md +306 -0
  2. package/README.md +190 -993
  3. package/data/build/dist/client/account-bar.d.ts +2 -2
  4. package/data/build/dist/client/account-bar.js +96 -39
  5. package/data/build/dist/client/bundle.js +13722 -3611
  6. package/data/build/dist/client/cli-catalog.js +20 -3
  7. package/data/build/dist/client/cli-slots.d.ts +5 -0
  8. package/data/build/dist/client/cli-slots.js +57 -7
  9. package/data/build/dist/client/debug/ui.d.ts +1 -1
  10. package/data/build/dist/client/debug/ui.js +62 -32
  11. package/data/build/dist/client/dsh-runtime-version.d.ts +9 -2
  12. package/data/build/dist/client/dsh-runtime-version.js +24 -2
  13. package/data/build/dist/client/features/cli-adapters.d.ts +1 -1
  14. package/data/build/dist/client/features/cli-adapters.js +19 -16
  15. package/data/build/dist/client/features/debug.js +2 -2
  16. package/data/build/dist/client/features/index.d.ts +2 -1
  17. package/data/build/dist/client/features/index.js +3 -1
  18. package/data/build/dist/client/features/lan-access-panel.d.ts +1 -1
  19. package/data/build/dist/client/features/lan-access-panel.js +8 -10
  20. package/data/build/dist/client/features/lan-access.js +1 -1
  21. package/data/build/dist/client/features/login-protection-panel.d.ts +1 -1
  22. package/data/build/dist/client/features/login-protection-panel.js +7 -9
  23. package/data/build/dist/client/features/reverse-proxy-panel.d.ts +1 -1
  24. package/data/build/dist/client/features/reverse-proxy-panel.js +99 -11
  25. package/data/build/dist/client/features/reverse-proxy.d.ts +1 -1
  26. package/data/build/dist/client/features/reverse-proxy.js +19 -18
  27. package/data/build/dist/client/features/terminal-enhancement-panel.d.ts +1 -1
  28. package/data/build/dist/client/features/terminal-enhancement-panel.js +62 -55
  29. package/data/build/dist/client/features/terminal-enhancement.d.ts +1 -1
  30. package/data/build/dist/client/features/terminal-enhancement.js +3 -3
  31. package/data/build/dist/client/features/types.d.ts +14 -3
  32. package/data/build/dist/client/features/workspace-session-enhancement-panel.d.ts +1 -1
  33. package/data/build/dist/client/features/workspace-session-enhancement-panel.js +31 -9
  34. package/data/build/dist/client/features/workspace-session-enhancement.js +72 -11
  35. package/data/build/dist/client/git-management.d.ts +16 -0
  36. package/data/build/dist/client/git-management.js +757 -0
  37. package/data/build/dist/client/index.d.ts +3 -2
  38. package/data/build/dist/client/index.js +152 -27
  39. package/data/build/dist/client/locale.js +91 -29
  40. package/data/build/dist/client/quick-phrase-layout.d.ts +49 -0
  41. package/data/build/dist/client/quick-phrase-layout.js +95 -0
  42. package/data/build/dist/client/quick-phrase-slot.d.ts +7 -0
  43. package/data/build/dist/client/quick-phrase-slot.js +370 -0
  44. package/data/build/dist/client/remote-web-context.js +1 -1
  45. package/data/build/dist/client/settings-bridge.d.ts +9 -6
  46. package/data/build/dist/client/settings-bridge.js +51 -27
  47. package/data/build/dist/client/settings-section.d.ts +3 -2
  48. package/data/build/dist/client/settings-section.js +33 -19
  49. package/data/build/dist/client/subscription-slot.d.ts +3 -0
  50. package/data/build/dist/client/subscription-slot.js +108 -16
  51. package/data/build/dist/client/terminal/model.d.ts +14 -2
  52. package/data/build/dist/client/terminal/model.js +109 -21
  53. package/data/build/dist/client/terminal/recovery.d.ts +50 -0
  54. package/data/build/dist/client/terminal/recovery.js +101 -0
  55. package/data/build/dist/client/terminal/ui.d.ts +2 -2
  56. package/data/build/dist/client/terminal/ui.js +49 -11
  57. package/data/build/dist/client/terminal/xterm-view.d.ts +2 -2
  58. package/data/build/dist/client/terminal/xterm-view.js +2 -1
  59. package/data/build/dist/client/theme.d.ts +6 -1
  60. package/data/build/dist/client/theme.js +32 -1
  61. package/data/build/dist/client/workspace-session-archive-dom.d.ts +12 -0
  62. package/data/build/dist/client/workspace-session-archive-dom.js +27 -5
  63. package/data/build/dist/client/workspace-session-visibility-dom.d.ts +30 -0
  64. package/data/build/dist/client/workspace-session-visibility-dom.js +604 -0
  65. package/data/build/dist/dsh-capabilities/client/config-forms-adapter.d.ts +50 -0
  66. package/data/build/dist/dsh-capabilities/client/config-forms-adapter.js +139 -0
  67. package/data/build/dist/dsh-capabilities/client/conversation-adapter.d.ts +7 -0
  68. package/data/build/dist/dsh-capabilities/client/conversation-adapter.js +6 -0
  69. package/data/build/dist/dsh-capabilities/client/locale-adapter.d.ts +9 -0
  70. package/data/build/dist/dsh-capabilities/client/locale-adapter.js +4 -0
  71. package/data/build/dist/dsh-capabilities/client/primitives-adapter.d.ts +10 -0
  72. package/data/build/dist/dsh-capabilities/client/primitives-adapter.js +14 -0
  73. package/data/build/dist/dsh-capabilities/client/settings-scope-adapter.d.ts +8 -0
  74. package/data/build/dist/dsh-capabilities/client/settings-scope-adapter.js +16 -0
  75. package/data/build/dist/dsh-capabilities/client/sidebar-adapter.d.ts +7 -0
  76. package/data/build/dist/dsh-capabilities/client/sidebar-adapter.js +5 -0
  77. package/data/build/dist/dsh-capabilities/client/theme-adapter.d.ts +6 -0
  78. package/data/build/dist/dsh-capabilities/client/theme-adapter.js +4 -0
  79. package/data/build/dist/dsh-capabilities/client/typert-adapter.d.ts +5 -0
  80. package/data/build/dist/dsh-capabilities/client/typert-adapter.js +4 -0
  81. package/data/build/dist/dsh-capabilities/host/config-forms-adapter.d.ts +19 -0
  82. package/data/build/dist/dsh-capabilities/host/config-forms-adapter.js +45 -0
  83. package/data/build/dist/dsh-capabilities/host/connection-rpc-adapter.d.ts +19 -0
  84. package/data/build/dist/dsh-capabilities/host/connection-rpc-adapter.js +13 -0
  85. package/data/build/dist/dsh-capabilities/host/settings-scope-adapter.d.ts +8 -0
  86. package/data/build/dist/dsh-capabilities/host/settings-scope-adapter.js +47 -0
  87. package/data/build/dist/dsh-capabilities/host/typert-adapter.d.ts +6 -0
  88. package/data/build/dist/dsh-capabilities/host/typert-adapter.js +4 -0
  89. package/data/build/dist/dsh-capabilities/index.d.ts +6 -0
  90. package/data/build/dist/dsh-capabilities/index.js +6 -0
  91. package/data/build/dist/dsh-capabilities/matrix.d.ts +15 -0
  92. package/data/build/dist/dsh-capabilities/matrix.js +32 -0
  93. package/data/build/dist/dsh-capabilities/registry.d.ts +22 -0
  94. package/data/build/dist/dsh-capabilities/registry.js +196 -0
  95. package/data/build/dist/dsh-capabilities/routes.d.ts +10 -0
  96. package/data/build/dist/dsh-capabilities/routes.js +34 -0
  97. package/data/build/dist/dsh-capabilities/settings-store.d.ts +29 -0
  98. package/data/build/dist/dsh-capabilities/settings-store.js +5 -0
  99. package/data/build/dist/dsh-capabilities/types.d.ts +50 -0
  100. package/data/build/dist/dsh-capabilities/types.js +23 -0
  101. package/data/build/dist/features/registry.d.ts +8 -2
  102. package/data/build/dist/features/registry.js +104 -1
  103. package/data/build/dist/host/auth-session.d.ts +8 -1
  104. package/data/build/dist/host/auth-session.js +28 -6
  105. package/data/build/dist/host/cli-adapters/claude-driver.d.ts +2 -0
  106. package/data/build/dist/host/cli-adapters/claude-driver.js +19 -4
  107. package/data/build/dist/host/cli-adapters/claude-model-options.d.ts +14 -0
  108. package/data/build/dist/host/cli-adapters/claude-model-options.js +157 -0
  109. package/data/build/dist/host/cli-adapters/codex-driver.d.ts +11 -0
  110. package/data/build/dist/host/cli-adapters/codex-driver.js +166 -14
  111. package/data/build/dist/host/cli-adapters/driver.d.ts +2 -0
  112. package/data/build/dist/host/cli-adapters/dsh-message-projector.d.ts +15 -0
  113. package/data/build/dist/host/cli-adapters/dsh-message-projector.js +120 -29
  114. package/data/build/dist/host/cli-adapters/dsh-tool-history.d.ts +4 -1
  115. package/data/build/dist/host/cli-adapters/dsh-tool-history.js +25 -7
  116. package/data/build/dist/host/cli-adapters/feature.js +74 -8
  117. package/data/build/dist/host/cli-adapters/gemini-driver.js +2 -2
  118. package/data/build/dist/host/cli-adapters/grok-driver.js +2 -2
  119. package/data/build/dist/host/cli-adapters/json-rpc-process.d.ts +4 -2
  120. package/data/build/dist/host/cli-adapters/json-rpc-process.js +8 -9
  121. package/data/build/dist/host/cli-adapters/kimi-driver.js +1 -1
  122. package/data/build/dist/host/cli-adapters/legacy-session-adapter.d.ts +17 -0
  123. package/data/build/dist/host/cli-adapters/legacy-session-adapter.js +88 -0
  124. package/data/build/dist/host/cli-adapters/legacy-session-settings.d.ts +24 -0
  125. package/data/build/dist/host/cli-adapters/legacy-session-settings.js +162 -0
  126. package/data/build/dist/host/cli-adapters/official-provider-subscription.d.ts +29 -0
  127. package/data/build/dist/host/cli-adapters/official-provider-subscription.js +361 -0
  128. package/data/build/dist/host/cli-adapters/opencode-driver.d.ts +2 -0
  129. package/data/build/dist/host/cli-adapters/opencode-driver.js +97 -12
  130. package/data/build/dist/host/cli-adapters/pi-driver.js +69 -23
  131. package/data/build/dist/host/cli-adapters/provider-registry.d.ts +33 -0
  132. package/data/build/dist/host/cli-adapters/provider-registry.js +83 -0
  133. package/data/build/dist/host/cli-adapters/provider-subscription.d.ts +25 -3
  134. package/data/build/dist/host/cli-adapters/provider-subscription.js +346 -30
  135. package/data/build/dist/host/cli-adapters/registry.d.ts +6 -0
  136. package/data/build/dist/host/cli-adapters/registry.js +170 -13
  137. package/data/build/dist/host/cli-adapters/rpc-driver-utils.js +10 -0
  138. package/data/build/dist/host/cli-adapters/session-store.d.ts +7 -0
  139. package/data/build/dist/host/cli-adapters/session-store.js +39 -1
  140. package/data/build/dist/host/cli-adapters/standard-stream-driver.d.ts +3 -0
  141. package/data/build/dist/host/cli-adapters/stream-normalizer.d.ts +3 -0
  142. package/data/build/dist/host/cli-adapters/stream-normalizer.js +22 -2
  143. package/data/build/dist/host/control-api-client.d.ts +3 -3
  144. package/data/build/dist/host/control-api-client.js +2 -1
  145. package/data/build/dist/host/credential-store.js +11 -1
  146. package/data/build/dist/host/dsh-device-runtime.d.ts +4 -0
  147. package/data/build/dist/host/dsh-device-runtime.js +20 -9
  148. package/data/build/dist/host/features/auth.js +8 -5
  149. package/data/build/dist/host/features/debug.js +1 -1
  150. package/data/build/dist/host/features/git-management.d.ts +5 -0
  151. package/data/build/dist/host/features/git-management.js +290 -0
  152. package/data/build/dist/host/features/host-status.js +1 -1
  153. package/data/build/dist/host/features/index.d.ts +1 -0
  154. package/data/build/dist/host/features/index.js +3 -0
  155. package/data/build/dist/host/features/terminal-process.js +1 -1
  156. package/data/build/dist/host/features/terminal-status.d.ts +3 -1
  157. package/data/build/dist/host/features/terminal-status.js +3 -1
  158. package/data/build/dist/host/index.d.ts +7 -3
  159. package/data/build/dist/host/index.js +76 -11
  160. package/data/build/dist/host/lan-access-dsh.js +18 -5
  161. package/data/build/dist/host/native-session-bridge.d.ts +23 -1
  162. package/data/build/dist/host/native-session-bridge.js +157 -10
  163. package/data/build/dist/host/rpc-table.d.ts +1 -1
  164. package/data/build/dist/host/rpc.js +60 -10
  165. package/data/build/dist/host/session-migration-repair.d.ts +19 -0
  166. package/data/build/dist/host/session-migration-repair.js +395 -0
  167. package/data/build/dist/host/settings.d.ts +8 -1
  168. package/data/build/dist/host/settings.js +94 -3
  169. package/data/build/dist/host/terminal/backends/tmux-backend.d.ts +1 -0
  170. package/data/build/dist/host/terminal/backends/tmux-backend.js +4 -2
  171. package/data/build/dist/host/terminal/controller-factory.d.ts +4 -0
  172. package/data/build/dist/host/terminal/controller-factory.js +26 -7
  173. package/data/build/dist/host/terminal/runtime-manager.d.ts +2 -1
  174. package/data/build/dist/host/terminal/runtime-manager.js +3 -0
  175. package/data/build/dist/host/terminal/startup.d.ts +2 -0
  176. package/data/build/dist/host/terminal/startup.js +2 -1
  177. package/data/build/dist/host/terminal/terminal-controller.d.ts +10 -3
  178. package/data/build/dist/host/terminal/terminal-controller.js +101 -15
  179. package/data/build/dist/host/terminal/terminal-process-service.js +3 -1
  180. package/data/build/dist/host/terminal/terminal-service.d.ts +2 -0
  181. package/data/build/dist/host/terminal/terminal-service.js +10 -0
  182. package/data/build/dist/index.d.ts +6 -2
  183. package/data/build/dist/index.js +8 -2
  184. package/data/build/dist/shared/contracts/cli-adapter.d.ts +14 -0
  185. package/data/build/dist/shared/contracts/config.d.ts +31 -1
  186. package/data/build/dist/shared/contracts/config.js +25 -0
  187. package/data/build/dist/shared/contracts/dsh-device.d.ts +13 -1
  188. package/data/build/dist/shared/contracts/errors.d.ts +1 -1
  189. package/data/build/dist/shared/contracts/feature.d.ts +6 -0
  190. package/data/build/dist/shared/contracts/git.d.ts +82 -0
  191. package/data/build/dist/shared/contracts/git.js +3 -0
  192. package/data/build/dist/shared/contracts/subscription.d.ts +46 -0
  193. package/data/build/dist/shared/contracts/terminal.d.ts +4 -1
  194. package/data/build/dist/shared/contracts/version.d.ts +2 -2
  195. package/data/build/dist/shared/contracts/version.js +4 -6
  196. package/data/build/dist/shared/debug.d.ts +14 -0
  197. package/data/build/dist/shared/debug.js +53 -0
  198. package/data/build/dist/shared/index.d.ts +7 -4
  199. package/data/build/dist/shared/index.js +2 -1
  200. package/data/build/dist/transport/debug.d.ts +1 -1
  201. package/data/build/dist/transport/debug.js +3 -30
  202. package/data/build/dist/transport/dsh-session.js +3 -3
  203. package/data/build/dist/typert.host.js +4 -2
  204. package/data/build/dist/typert.remote-client.d.ts +11 -0
  205. package/data/build/dist/typert.remote-client.js +7 -0
  206. package/dsh.bundle.patch +1 -1
  207. package/package.json +17 -5
  208. package/profile/README.md +4 -4
  209. package/profile/package.json +6 -4
  210. package/profile/scripts/check-dsh-install.mjs +2 -2
  211. package/profile/version.json +2 -2
  212. package/scripts/check-dsh-install.mjs +2 -2
  213. package/version.json +2 -2
@@ -8,30 +8,77 @@ import { createCodingNsNativeSessionBridge } from './native-session-bridge.js';
8
8
  import { installTerminalController } from './terminal/startup.js';
9
9
  import { DebugWorkspaceService } from './debug.js';
10
10
  import { detectRuntimeDshVersion, DSH_VERSION_INJECTION_NAME } from './dsh-runtime-version.js';
11
+ import { createDshCapabilityRegistry } from '../dsh-capabilities/index.js';
12
+ import { debugInfo } from '../shared/debug.js';
13
+ import { repairLegacySessionLogs } from './session-migration-repair.js';
11
14
  export function apply(ctx) {
12
15
  if (ctx === undefined)
13
16
  return;
14
17
  const dshVersion = detectRuntimeDshVersion();
15
- console.error('codingns4dsh: host apply entered');
18
+ debugInfo('codingns4dsh: host apply entered', { dshVersion });
19
+ // DSH 0.1.7 的官方 v3->v4 迁移器要求每个 tool/call 先有 assistant/message
20
+ // 声明。旧版外部 Agent 曾直接写入 tool/call,必须在任何会话 open 前修复。
21
+ ctx.inject(['sessionPersistence'], async (sessionCtx) => {
22
+ const persistence = sessionCtx.get('sessionPersistence');
23
+ if (!isRecord(persistence) || typeof persistence.root !== 'string')
24
+ return;
25
+ const open = persistence.open;
26
+ if (typeof open !== 'function')
27
+ return;
28
+ let repair;
29
+ const repairBeforeOpen = () => {
30
+ repair ??= repairLegacySessionLogs({
31
+ root: persistence.root,
32
+ logger: (message, error) => console.warn('codingns4dsh:', message, error),
33
+ }).catch((error) => {
34
+ console.warn('codingns4dsh: 历史会话扫描失败', error);
35
+ return undefined;
36
+ });
37
+ return repair;
38
+ };
39
+ // DSH 的 v3->v4 转换发生在 persistence.open 内部。只在启动时异步扫描
40
+ // 会晚于第一次点击历史会话,因此必须把修复挂到真正的读取边界之前。
41
+ try {
42
+ persistence.open = async function (...args) {
43
+ await repairBeforeOpen();
44
+ return open.apply(this, args);
45
+ };
46
+ }
47
+ catch (error) {
48
+ // 某些 Host 会冻结 Service 实例;启动扫描仍然可修复磁盘上的旧日志。
49
+ console.warn('codingns4dsh: 无法包装 sessionPersistence.open', error);
50
+ }
51
+ const report = await repairBeforeOpen();
52
+ debugInfo('codingns4dsh: legacy session repair finished', report);
53
+ });
16
54
  ctx.inject(['settings', 'connection', 'webServer'], async (hostCtx) => {
17
- console.error('codingns4dsh: host inject ready', {
55
+ debugInfo('codingns4dsh: host inject ready', {
18
56
  hasConnection: hostCtx.connection !== undefined,
57
+ hasSettings: hostCtx.settings !== undefined,
58
+ hasWebServer: hostCtx.webServer !== undefined,
19
59
  });
20
60
  const webServerPort = hostCtx.webServer.port;
21
61
  // LAN 入口和中继 Web 页面可能使用非 loopback URL,但它们都已经经过
22
62
  // DSH Host 的认证边界并回到本机 Host。通过启动页注入 transport 所有权,
23
63
  // 让 DSH 原生 ui-settings 保持 host 模式,而不是错误降级为 memory 模式。
24
64
  const indexInjectionEvents = hostCtx;
65
+ debugInfo('codingns4dsh: host index injection registration begin');
25
66
  indexInjectionEvents.on('webserver/index-inject', (table) => {
26
67
  table.push({ kind: 'global', name: '__DSH_TRANSPORT__', value: { ownsHost: true } });
27
68
  table.push({ kind: 'global', name: DSH_VERSION_INJECTION_NAME, value: dshVersion });
28
69
  });
70
+ debugInfo('codingns4dsh: host index injection registration ready');
71
+ debugInfo('codingns4dsh: host settings registration begin');
29
72
  const settings = registerCodingNsSettings(hostCtx);
73
+ debugInfo('codingns4dsh: host settings registered');
74
+ const workspaceRoots = new Map();
30
75
  // controller 必须在功能模块和浏览器 Client 开始消费状态前完成装配。
31
76
  // 工厂在本次启动只读取一次开关,设置 watcher 不会热切同名 service。
32
77
  const terminal = await installTerminalController(hostCtx, settings, hostCtx.settings, {
33
- resolveWorkspaceRoot: (workspaceId) => resolveWorkspaceRoot(hostCtx, workspaceId),
78
+ resolveWorkspaceRoot: (workspaceId) => workspaceRoots.get(workspaceId) ?? resolveWorkspaceRoot(hostCtx, workspaceId),
79
+ registerWorkspaceRoot: (workspaceId, cwd) => workspaceRoots.set(workspaceId, cwd),
34
80
  });
81
+ debugInfo('codingns4dsh: host terminal controller ready', { mode: terminal.mode });
35
82
  const services = {
36
83
  rpc: new CodingNsRpcTable(),
37
84
  dshVersion,
@@ -40,9 +87,9 @@ export function apply(ctx) {
40
87
  dshWebPort: webServerPort,
41
88
  dshWebAuthenticatedUrl: hostCtx.connection.authenticatedUrl(`http://127.0.0.1:${String(webServerPort)}`),
42
89
  events: { on: hostCtx.on.bind(hostCtx) },
43
- nativeSessions: createCodingNsNativeSessionBridge(hostCtx),
90
+ nativeSessions: createCodingNsNativeSessionBridge(hostCtx, dshVersion),
44
91
  terminalProcesses: terminal.processService,
45
- resolveWorkspaceRoot: (workspaceId) => resolveWorkspaceRoot(hostCtx, workspaceId),
92
+ resolveWorkspaceRoot: (workspaceId) => workspaceRoots.get(workspaceId) ?? resolveWorkspaceRoot(hostCtx, workspaceId),
46
93
  registerDebugProxyRoute: (handler) => hostCtx.connection.fetch.register({
47
94
  path: '/api/codingns/debug-proxy',
48
95
  methods: ['GET', 'HEAD', 'POST'],
@@ -51,22 +98,31 @@ export function apply(ctx) {
51
98
  }),
52
99
  };
53
100
  const debug = new DebugWorkspaceService({
54
- resolveWorkspaceRoot: (workspaceId) => resolveWorkspaceRoot(hostCtx, workspaceId),
101
+ resolveWorkspaceRoot: (workspaceId) => workspaceRoots.get(workspaceId) ?? resolveWorkspaceRoot(hostCtx, workspaceId),
55
102
  terminalProcesses: terminal.processService,
56
103
  });
57
104
  const servicesWithDebug = { ...services, debug };
58
- const registry = new FeatureRegistry(servicesWithDebug);
105
+ const capabilityProfile = createDshCapabilityRegistry(dshVersion, 'host', hostCtx).getProfile(hostCtx);
106
+ debugInfo('codingns4dsh: host capabilities resolved', {
107
+ dshVersion,
108
+ capabilities: [...capabilityProfile.capabilities.entries()].map(([capability, resolution]) => ({ capability, status: resolution.status, route: resolution.routeId, reason: resolution.reason ?? null })),
109
+ diagnostics: capabilityProfile.diagnostics,
110
+ });
111
+ const registry = new FeatureRegistry(servicesWithDebug, capabilityProfile);
59
112
  registry.registerMany(createHostFeatures({
60
113
  terminalStatus: {
61
114
  controllerMode: terminal.mode,
62
115
  effectiveEnabled: terminal.mode === 'enhanced',
116
+ runtimeTypes: terminal.runtimeTypes,
117
+ ...(terminal.runtimeWarning === undefined ? {} : { runtimeWarning: terminal.runtimeWarning }),
63
118
  },
64
119
  }));
65
120
  registry.validate();
121
+ debugInfo('codingns4dsh: host feature registry ready', { features: registry.descriptors().map((item) => item.name) });
66
122
  const restartStates = captureRestartFeatureStates(registry.descriptors(), settings.get(), dshVersion);
67
123
  try {
68
124
  registerCodingNsRpc(hostCtx, services.rpc, services.settingsProvider);
69
- console.error('codingns4dsh: host RPC registration requested');
125
+ debugInfo('codingns4dsh: host RPC registration requested');
70
126
  }
71
127
  catch (error) {
72
128
  console.error('codingns4dsh: host RPC registration failed', error);
@@ -74,8 +130,10 @@ export function apply(ctx) {
74
130
  }
75
131
  hostCtx.effect(() => {
76
132
  const sync = () => {
133
+ const enabled = enabledFeatureNames(registry.descriptors(), settings.get(), restartStates, dshVersion);
134
+ debugInfo('codingns4dsh: host feature sync', { enabled, states: registry.list() });
77
135
  void registry
78
- .reconcile(enabledFeatureNames(registry.descriptors(), settings.get(), restartStates, dshVersion))
136
+ .reconcile(enabled)
79
137
  .catch((error) => {
80
138
  console.error('codingns4dsh: 功能模块状态同步失败', error);
81
139
  });
@@ -85,6 +143,9 @@ export function apply(ctx) {
85
143
  }, 'codingns4dsh: 功能模块启停同步');
86
144
  });
87
145
  }
146
+ function isRecord(value) {
147
+ return typeof value === 'object' && value !== null;
148
+ }
88
149
  function resolveWorkspaceRoot(ctx, workspaceId) {
89
150
  try {
90
151
  const registry = ctx.get('workspaceRegistry');
@@ -102,16 +163,20 @@ function resolveWorkspaceRoot(ctx, workspaceId) {
102
163
  }
103
164
  return null;
104
165
  }
105
- export { HOST_FEATURES, createAuthFeature, createLanAccessDshFeature, createTerminalStatusFeature, createHostFeatures, createCliAdaptersFeature, createTerminalProcessFeature, } from './features/index.js';
166
+ export { HOST_FEATURES, createAuthFeature, createLanAccessDshFeature, createTerminalStatusFeature, createHostFeatures, createCliAdaptersFeature, createTerminalProcessFeature, createGitManagementFeature, } from './features/index.js';
106
167
  export { CodingNsSettingsSchema, registerCodingNsSettings } from './settings.js';
107
168
  export { createCodingNsRpcHandler, createCodingNsSettingsRpcHandler, registerCodingNsRpc } from './rpc.js';
108
169
  export { DebugWorkspaceService, NodeDebugPortInspector, } from './debug.js';
109
170
  export { CodingNsRpcError, CodingNsRpcTable, } from './rpc-table.js';
110
171
  export { CodingNsAuthSession } from './auth-session.js';
111
172
  export { createCodingNsNativeSessionBridge, } from './native-session-bridge.js';
173
+ export { repairLegacySessionLog, repairLegacySessionLogs, } from './session-migration-repair.js';
174
+ export { defaultLegacySettingsPath, parseLegacyImportedSessionRecords, readLegacyImportedSessionRecords, } from './cli-adapters/legacy-session-settings.js';
112
175
  export { CommandCodeDriver } from './cli-adapters/command-code-driver.js';
113
176
  export { CommandCodeSubscriptionService, readCommandCodeApiKey } from './cli-adapters/command-code-subscription.js';
114
- export { ProviderSubscriptionService, CodexSubscriptionService, ClaudeCodeSubscriptionService, Sub2ApiUsageService, OpenCodeSubscriptionService, } from './cli-adapters/provider-subscription.js';
177
+ export { ProviderSubscriptionService, CodexSubscriptionService, ClaudeCodeSubscriptionService, DeepseekSubscriptionService, Sub2ApiUsageService, OpenCodeSubscriptionService, } from './cli-adapters/provider-subscription.js';
178
+ export { OfficialProviderSubscriptionService } from './cli-adapters/official-provider-subscription.js';
179
+ export { MODEL_PROVIDER_DEFINITIONS, identifyModelProvider, normalizeProviderBaseUrl, normalizeProviderName, thirdPartyProvider, } from './cli-adapters/provider-registry.js';
115
180
  export { ClaudeCodeDriver } from './cli-adapters/claude-driver.js';
116
181
  export { KimiCliDriver } from './cli-adapters/kimi-driver.js';
117
182
  export { GeminiCliDriver } from './cli-adapters/gemini-driver.js';
@@ -266,6 +266,15 @@ export class LanAccessDshProxy {
266
266
  }
267
267
  authorize(request, localAddress) {
268
268
  const config = this.loginConfig;
269
+ if (request.path === '/__codingns/session' && request.method === 'GET') {
270
+ const token = readCookie(request.headers.cookie, 'dsh_codingns_session');
271
+ const authenticated = !localAddress && config !== null && config.enabled && config.scopes.lan
272
+ && token !== undefined && !this.revokedSessions.has(token)
273
+ && verifySignedSessionToken(token, config, 'lan');
274
+ return loginJsonResponse(200, authenticated
275
+ ? { authenticated: true, username: config.username }
276
+ : { authenticated: false });
277
+ }
269
278
  if (localAddress || config === null || !config.enabled || !config.scopes.lan)
270
279
  return 'pass';
271
280
  if (request.path === '/__codingns/login' && request.method === 'POST') {
@@ -280,16 +289,16 @@ export class LanAccessDshProxy {
280
289
  return loginResponse(303, '', { 'Set-Cookie': sessionCookie(token, config.timeoutSeconds), Location: '/' });
281
290
  }
282
291
  if (request.path === '/__codingns/logout') {
283
- const token = readCookie(request.headers.cookie, 'codingns4dsh_session');
292
+ const token = readCookie(request.headers.cookie, 'dsh_codingns_session');
284
293
  if (token !== undefined && verifySignedSessionToken(token, config, 'lan'))
285
294
  this.revokedSessions.add(token);
286
- return loginResponse(303, '', { 'Set-Cookie': 'codingns4dsh_session=; Path=/; HttpOnly; SameSite=Strict; Max-Age=0', Location: '/' });
295
+ return loginResponse(303, '', { 'Set-Cookie': 'dsh_codingns_session=; Path=/; HttpOnly; SameSite=Strict; Max-Age=0', Location: '/' });
287
296
  }
288
297
  // 浏览器会在页面刷新时独立请求 Web App manifest;它只包含静态元数据,
289
298
  // 不应因为登录保护缺少 Cookie 而产生 401 控制台噪声。
290
299
  if (request.path === '/manifest.webmanifest' && request.method === 'GET')
291
300
  return 'pass';
292
- const token = readCookie(request.headers.cookie, 'codingns4dsh_session');
301
+ const token = readCookie(request.headers.cookie, 'dsh_codingns_session');
293
302
  if (token !== undefined && !this.revokedSessions.has(token) && verifySignedSessionToken(token, config, 'lan')) {
294
303
  return 'pass';
295
304
  }
@@ -389,7 +398,7 @@ function readCookie(value, name) {
389
398
  return undefined;
390
399
  }
391
400
  function sessionCookie(token, timeoutSeconds) {
392
- return `codingns4dsh_session=${token}; Path=/; HttpOnly; SameSite=Strict; Max-Age=${timeoutSeconds}`;
401
+ return `dsh_codingns_session=${token}; Path=/; HttpOnly; SameSite=Strict; Max-Age=${timeoutSeconds}`;
393
402
  }
394
403
  function getSetCookie(headers) {
395
404
  const extended = headers;
@@ -398,7 +407,8 @@ function getSetCookie(headers) {
398
407
  }
399
408
  function loginResponse(status, body, extra = {}) {
400
409
  const isHtml = body.startsWith('<!doctype html>');
401
- const content = new TextEncoder().encode(isHtml ? body : escapeHtml(body));
410
+ const isJson = extra['Content-Type']?.startsWith('application/json') === true;
411
+ const content = new TextEncoder().encode(isHtml || isJson ? body : escapeHtml(body));
402
412
  const headers = {
403
413
  'Content-Type': isHtml ? 'text/html; charset=utf-8' : 'text/plain; charset=utf-8',
404
414
  'Content-Length': String(content.length),
@@ -413,6 +423,9 @@ function loginResponse(status, body, extra = {}) {
413
423
  const head = encodeLatin1(`HTTP/1.1 ${status} ${statusText(status)}\r\n${Object.entries(headers).map(([key, value]) => `${key}: ${value}`).join('\r\n')}\r\n\r\n`);
414
424
  return concatBytes(head, content);
415
425
  }
426
+ function loginJsonResponse(status, value) {
427
+ return loginResponse(status, JSON.stringify(value), { 'Content-Type': 'application/json; charset=utf-8' });
428
+ }
416
429
  function loginPage() {
417
430
  // 视觉规则直接复用 codingns4dsh-h5/styles.css 的 Cyber 登录区,只把 Connect
418
431
  // 账号字段替换成本地用户名/密码;不能依赖外部 CSS,避免局域网入口离线时失效。
@@ -17,6 +17,8 @@ export interface CodingNsNativeToolCall {
17
17
  readonly callId: string;
18
18
  readonly name: string;
19
19
  readonly arguments: string;
20
+ /** 外部适配器 ID;旧调用方缺省时保持通用来源标记。 */
21
+ readonly adapterId?: string;
20
22
  }
21
23
  /** 一次工具调用在 DSH Session 中的稳定位置。 */
22
24
  export interface CodingNsNativeToolCallHandle {
@@ -42,6 +44,7 @@ export interface CodingNsNativeExternalToolEvent {
42
44
  readonly status: 'running' | 'completed' | 'failed';
43
45
  readonly output?: string;
44
46
  readonly error?: string;
47
+ readonly adapterId?: string;
45
48
  }
46
49
  export type CodingNsNativeApprovalOutcome = 'allowed-once' | 'rejected' | 'cancelled' | 'unavailable';
47
50
  /** 公共消息投影层交给 DSH 原生权限服务的请求。 */
@@ -64,6 +67,19 @@ export interface CodingNsNativeRequestContext {
64
67
  readonly model: string;
65
68
  readonly contextWindow?: number;
66
69
  }
70
+ /** 外部 Provider 已报告的用量采样;写入 assistant/attempt,不加入模型可见 surface。 */
71
+ export interface CodingNsNativeUsageSample {
72
+ readonly inputTokens: number;
73
+ readonly outputTokens: number;
74
+ readonly cacheReadTokens?: number;
75
+ readonly cacheWriteTokens?: number;
76
+ readonly uncachedInputTokens?: number;
77
+ readonly totalTokens?: number;
78
+ readonly cacheHitRate?: number;
79
+ readonly contextWindow?: number;
80
+ readonly contextTokens?: number;
81
+ readonly contextUsageRatio?: number;
82
+ }
67
83
  export interface CodingNsNativeSessionController {
68
84
  create?(request: {
69
85
  readonly sessionId?: string;
@@ -112,6 +128,12 @@ export interface CodingNsNativeSessionBridge {
112
128
  appendExternalToolEvent?(sessionId: string, event: CodingNsNativeExternalToolEvent): boolean;
113
129
  /** 写入当前原生步骤的路由上下文元数据,不携带凭据或消息正文。 */
114
130
  appendRequestContext?(sessionId: string, context: CodingNsNativeRequestContext): boolean;
131
+ /** 在当前步骤即时记录外部 Provider 用量;该事件不进入模型可见 surface。 */
132
+ appendUsageSample?(sessionId: string, usage: CodingNsNativeUsageSample): boolean;
133
+ /** 判断指定会话是否真的具备下一步注入能力。 */
134
+ canInjectNextStep?(sessionId: string): boolean;
135
+ /** 在当前 Agent turn 的下一个合法 step 注入插件上下文,不唤醒空闲 Agent。 */
136
+ injectNextStep?(sessionId: string, summary?: string): boolean;
115
137
  /** 使用 DSH 原生 approval 组件请求一次权限决定;服务不可用时拒绝。 */
116
138
  requestApproval?(sessionId: string, request: CodingNsNativeApprovalRequest): Promise<CodingNsNativeApprovalOutcome>;
117
139
  /** 使用 DSH 原生 userQuestions 组件提问;服务不可用或取消时返回 null。 */
@@ -126,5 +148,5 @@ export interface CodingNsNativeSessionBridge {
126
148
  readonly onFlush?: (session: unknown) => void | Promise<void>;
127
149
  }): () => void;
128
150
  }
129
- export declare function createCodingNsNativeSessionBridge(ctx: Context): CodingNsNativeSessionBridge;
151
+ export declare function createCodingNsNativeSessionBridge(ctx: Context, dshVersion?: string): CodingNsNativeSessionBridge;
130
152
  //# sourceMappingURL=native-session-bridge.d.ts.map
@@ -1,4 +1,5 @@
1
- export function createCodingNsNativeSessionBridge(ctx) {
1
+ import { randomUUID } from 'node:crypto';
2
+ export function createCodingNsNativeSessionBridge(ctx, dshVersion) {
2
3
  // Cordis Context 是运行时代理,直接读取未在 inject 中声明的可选服务会抛错。
3
4
  // get() 专门用于无强制依赖的服务探测,精简 Host 缺少服务时会返回 undefined。
4
5
  const storeValue = ctx.get('sessions');
@@ -15,11 +16,36 @@ export function createCodingNsNativeSessionBridge(ctx) {
15
16
  ? ctx.on.bind(ctx)
16
17
  : undefined;
17
18
  const externalHandles = new Map();
19
+ let injectedStepSequence = 0;
20
+ const modernInjectedSource = isModernDshVersion(dshVersion);
18
21
  const appendNativeToolCall = (sessionId, call) => {
19
22
  const session = appendableSession(store?.get(sessionId));
20
23
  const position = session === null ? null : activeStep(session);
21
24
  if (session === null || position === null)
22
25
  return null;
26
+ // DSH 的工具生命周期由 assistant/message 先声明,再由 tool/call 开始。
27
+ // 外部 Agent 已经在自身进程执行完请求,这条消息只用于持久化声明,绝不触发 DSH 执行器。
28
+ session.append('assistant/message', {
29
+ turn: position.turn,
30
+ step: position.step,
31
+ message: {
32
+ id: `external-tool-${call.callId}-${position.turn}-${position.step}`,
33
+ role: 'assistant',
34
+ content: [{
35
+ type: 'tool-call',
36
+ id: call.callId,
37
+ name: call.name,
38
+ arguments: call.arguments,
39
+ }],
40
+ source: {
41
+ kind: 'model',
42
+ plugin: 'codingns4dsh',
43
+ provider: call.adapterId?.trim() || 'codingns-external',
44
+ model: call.adapterId?.trim() || 'external-agent',
45
+ },
46
+ },
47
+ stream: [],
48
+ }, { surfaceOp: 'append' });
23
49
  const event = session.append('tool/call', {
24
50
  turn: position.turn,
25
51
  step: position.step,
@@ -40,14 +66,11 @@ export function createCodingNsNativeSessionBridge(ctx) {
40
66
  step: handle.step,
41
67
  message: {
42
68
  id: `${handle.callId}-result-${handle.turn}-${handle.step}`,
43
- role: 'user',
44
- content: [{
45
- type: 'tool-result',
46
- toolCallId: handle.callId,
47
- content: [{ type: 'text', text: result.output }],
48
- ...(result.isError ? { isError: true } : {}),
49
- }],
69
+ role: 'tool',
70
+ toolCallId: handle.callId,
71
+ content: [{ type: 'text', text: result.output }],
50
72
  source: { kind: 'tool', callId: handle.callId },
73
+ ...(result.isError ? { isError: true } : {}),
51
74
  },
52
75
  ...(result.isError
53
76
  ? { error: { name: 'ExternalToolError', code: 'EXTERNAL_TOOL_FAILED', ...(error ? { reason: error } : {}) } }
@@ -64,10 +87,69 @@ export function createCodingNsNativeSessionBridge(ctx) {
64
87
  if (session === null || context.provider.trim() === '' || context.model.trim() === '')
65
88
  return false;
66
89
  try {
90
+ // request/context 是 token-meter 的容量来源。相同路由重复追加会让
91
+ // 投影先清空旧 pressure,再等待下一条 usage,ContextMeter 因而闪烁。
92
+ // 保留已有容量并跳过等价事件,让用量更新直接落到同一个组件上。
93
+ const previous = latestRequestContext(session);
94
+ if (previous !== null
95
+ && previous.provider === context.provider
96
+ && previous.model === context.model
97
+ && (context.contextWindow === undefined || previous.contextWindow === context.contextWindow))
98
+ return true;
99
+ // Registry 在一轮开始时只能提供适配器身份,不能提供 Provider 容量。
100
+ // 继承上一条已知容量,避免先写无容量事件导致 ContextMeter 卸载;
101
+ // 真正的 usage 到达后,投影器会用 Provider 的最新容量覆盖它。
102
+ const contextWindow = context.contextWindow ?? previous?.contextWindow;
67
103
  session.append('request/context', {
68
104
  provider: context.provider,
69
105
  model: context.model,
70
- ...(context.contextWindow === undefined ? {} : { contextWindow: context.contextWindow }),
106
+ ...(contextWindow === undefined ? {} : { contextWindow }),
107
+ });
108
+ return true;
109
+ }
110
+ catch {
111
+ return false;
112
+ }
113
+ };
114
+ const appendNativeUsageSample = (sessionId, usage) => {
115
+ const session = appendableSession(store?.get(sessionId));
116
+ const position = session === null ? null : activeStep(session);
117
+ if (session === null || position === null)
118
+ return false;
119
+ try {
120
+ session.append('assistant/attempt', {
121
+ turn: position.turn,
122
+ step: position.step,
123
+ stream: [{
124
+ type: 'chunk',
125
+ time: Date.now(),
126
+ chunk: {
127
+ type: 'usage',
128
+ usage: compactUsage(usage),
129
+ },
130
+ }],
131
+ });
132
+ return true;
133
+ }
134
+ catch {
135
+ return false;
136
+ }
137
+ };
138
+ const injectNativeNextStep = (sessionId, summary = '外部工具已完成,继续处理当前任务。') => {
139
+ const agent = nativeAgent(ctx, sessionId);
140
+ if (agent === null || typeof agent.inject !== 'function')
141
+ return false;
142
+ injectedStepSequence += 1;
143
+ const boundedSummary = summary.trim().slice(0, 120) || '外部工具已完成,继续处理当前任务。';
144
+ try {
145
+ ;
146
+ agent.inject({
147
+ id: `codingns-external-step-${injectedStepSequence}-${randomUUID()}`,
148
+ role: 'user',
149
+ content: [{ type: 'text', text: boundedSummary }],
150
+ source: modernInjectedSource
151
+ ? { kind: 'model-selection', form: 'notice', summary: boundedSummary }
152
+ : { kind: 'plugin', plugin: 'codingns4dsh', form: 'notice', summary: boundedSummary },
71
153
  });
72
154
  return true;
73
155
  }
@@ -75,6 +157,10 @@ export function createCodingNsNativeSessionBridge(ctx) {
75
157
  return false;
76
158
  }
77
159
  };
160
+ const canInjectNativeNextStep = (sessionId) => {
161
+ const agent = nativeAgent(ctx, sessionId);
162
+ return agent !== null && typeof agent.inject === 'function';
163
+ };
78
164
  return {
79
165
  get available() {
80
166
  return store !== undefined || controller !== undefined || currentWorkspaceController() !== undefined;
@@ -133,9 +219,18 @@ export function createCodingNsNativeSessionBridge(ctx) {
133
219
  appendRequestContext(sessionId, context) {
134
220
  return appendNativeRequestContext(sessionId, context);
135
221
  },
222
+ appendUsageSample(sessionId, usage) {
223
+ return appendNativeUsageSample(sessionId, usage);
224
+ },
225
+ canInjectNextStep(sessionId) {
226
+ return canInjectNativeNextStep(sessionId);
227
+ },
228
+ injectNextStep(sessionId, summary) {
229
+ return injectNativeNextStep(sessionId, summary);
230
+ },
136
231
  appendExternalToolEvent(sessionId, externalTool) {
137
232
  try {
138
- // 兼容旧调用方,但仍然写入 DSH 原生工具事件,绝不能伪造 assistant/attempt。
233
+ // 兼容旧调用方;声明 assistant 工具生命周期,但绝不伪造 assistant/attempt 结算。
139
234
  const key = `${sessionId}:${externalTool.callId}`;
140
235
  if (externalTool.phase === 'start') {
141
236
  if (externalHandles.has(key))
@@ -144,6 +239,7 @@ export function createCodingNsNativeSessionBridge(ctx) {
144
239
  callId: externalTool.callId,
145
240
  name: externalTool.name,
146
241
  arguments: externalTool.arguments,
242
+ ...(externalTool.adapterId === undefined ? {} : { adapterId: externalTool.adapterId }),
147
243
  });
148
244
  if (handle === null)
149
245
  return false;
@@ -240,6 +336,32 @@ function appendableSession(value) {
240
336
  return null;
241
337
  return value;
242
338
  }
339
+ function latestRequestContext(session) {
340
+ let events;
341
+ try {
342
+ events = session.snapshotEvents();
343
+ }
344
+ catch {
345
+ return null;
346
+ }
347
+ for (let index = events.length - 1; index >= 0; index -= 1) {
348
+ const candidate = events[index];
349
+ const event = isRecord(candidate) ? candidate : null;
350
+ if (event?.type !== 'request/context')
351
+ continue;
352
+ const data = isRecord(event.data) ? event.data : null;
353
+ if (typeof data?.provider !== 'string' || typeof data.model !== 'string')
354
+ return null;
355
+ return {
356
+ provider: data.provider,
357
+ model: data.model,
358
+ ...(typeof data.contextWindow === 'number' && Number.isFinite(data.contextWindow) && data.contextWindow > 0
359
+ ? { contextWindow: data.contextWindow }
360
+ : {}),
361
+ };
362
+ }
363
+ return null;
364
+ }
243
365
  function activeStep(session) {
244
366
  let events;
245
367
  try {
@@ -271,6 +393,20 @@ function eventSeq(value) {
271
393
  function finiteInteger(value) {
272
394
  return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0 ? value : null;
273
395
  }
396
+ function compactUsage(usage) {
397
+ return {
398
+ inputTokens: usage.inputTokens,
399
+ outputTokens: usage.outputTokens,
400
+ ...(usage.cacheReadTokens === undefined ? {} : { cacheReadTokens: usage.cacheReadTokens }),
401
+ ...(usage.cacheWriteTokens === undefined ? {} : { cacheWriteTokens: usage.cacheWriteTokens }),
402
+ ...(usage.uncachedInputTokens === undefined ? {} : { uncachedInputTokens: usage.uncachedInputTokens }),
403
+ ...(usage.totalTokens === undefined ? {} : { totalTokens: usage.totalTokens }),
404
+ ...(usage.cacheHitRate === undefined ? {} : { cacheHitRate: usage.cacheHitRate }),
405
+ ...(usage.contextWindow === undefined ? {} : { contextWindow: usage.contextWindow }),
406
+ ...(usage.contextTokens === undefined ? {} : { contextTokens: usage.contextTokens }),
407
+ ...(usage.contextUsageRatio === undefined ? {} : { contextUsageRatio: usage.contextUsageRatio }),
408
+ };
409
+ }
274
410
  function isSessionStore(value) {
275
411
  return isRecord(value) && typeof value.get === 'function' && typeof value.list === 'function';
276
412
  }
@@ -283,6 +419,17 @@ function isWorkspaceController(value) {
283
419
  function isRecord(value) {
284
420
  return typeof value === 'object' && value !== null;
285
421
  }
422
+ function isModernDshVersion(value) {
423
+ if (value === undefined)
424
+ return false;
425
+ const match = /^(\d+)\.(\d+)\.(\d+)/u.exec(value.trim());
426
+ if (match === null)
427
+ return false;
428
+ const major = Number(match[1]);
429
+ const minor = Number(match[2]);
430
+ const patch = Number(match[3]);
431
+ return major > 0 || minor > 1 || minor === 1 && patch >= 7;
432
+ }
286
433
  function nativeAgent(ctx, sessionId) {
287
434
  const value = ctx.get('agents');
288
435
  if (!isRecord(value) || typeof value.get !== 'function')
@@ -4,7 +4,7 @@
4
4
  * 每个功能模块在启动时登记自己的命名空间,主 handler 只做一次 `namespace/action`
5
5
  * 前缀解析。新增模块只登记新命名空间,不需要修改中心分发代码。
6
6
  */
7
- export type CodingNsRpcHandler = (action: string, payload: unknown) => unknown | Promise<unknown>;
7
+ export type CodingNsRpcHandler = (action: string, payload: unknown, context?: unknown) => unknown | Promise<unknown>;
8
8
  export interface CodingNsRpcTarget {
9
9
  readonly handler: CodingNsRpcHandler;
10
10
  readonly action: string;