@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
@@ -1,4 +1,5 @@
1
- import { CODINGNS_SETTINGS_NAMESPACE } from '../shared/contracts/config.js';
1
+ import { CODINGNS_SETTINGS_NAMESPACE, isCodingNsSettingsEntryId } from '../shared/contracts/config.js';
2
+ import { debugInfo, debugWarn } from '../shared/debug.js';
2
3
  import { CodingNsRpcError } from './rpc-table.js';
3
4
  /**
4
5
  * 创建 Codingns4DSH Host RPC 主处理器。
@@ -9,16 +10,30 @@ import { CodingNsRpcError } from './rpc-table.js';
9
10
  * 凭据存储。
10
11
  */
11
12
  export function createCodingNsRpcHandler(table) {
12
- return async (endpoint, payload) => {
13
+ return async (endpoint, payload, signal) => {
14
+ debugInfo('codingns4dsh: host rpc request', { endpoint });
13
15
  const target = table.resolve(endpoint);
14
16
  if (target === null) {
17
+ debugWarn('codingns4dsh: host rpc endpoint not found', { endpoint });
15
18
  return failure('CODINGNS_RPC_NOT_FOUND', `未知 Codingns4DSH RPC: ${endpoint}`);
16
19
  }
17
20
  try {
18
- return success(await target.handler(target.action, payload));
21
+ const value = await target.handler(target.action, payload, { signal });
22
+ debugInfo('codingns4dsh: host rpc success', { endpoint });
23
+ return success(value);
19
24
  }
20
25
  catch (error) {
21
- return failure(errorCode(error), error instanceof Error ? error.message : String(error));
26
+ const code = errorCode(error);
27
+ const details = { endpoint, code, error };
28
+ if (code === 'CODINGNS_RPC_UNAUTHENTICATED') {
29
+ // 未登录是正常的业务状态,不能把它伪装成 Host 故障;RPC 仍返回失败,
30
+ // 让客户端根据稳定错误码决定是否等待登录。
31
+ console.warn('codingns4dsh: host rpc request rejected', details);
32
+ }
33
+ else {
34
+ console.error('codingns4dsh: host rpc handler failed', details);
35
+ }
36
+ return failure(code, error instanceof Error ? error.message : String(error));
22
37
  }
23
38
  };
24
39
  }
@@ -29,16 +44,27 @@ export function registerCodingNsRpc(ctx, table, settingsProvider) {
29
44
  // 注入的服务实例,挂载同协议的前缀路由,避免把 RPC 请求落到 SPA fallback。
30
45
  const webServer = ctx.webServer;
31
46
  const connection = ctx.connection;
47
+ debugInfo('codingns4dsh: host rpc registration begin', {
48
+ hasConnectionRpc: typeof connection.rpc?.handle === 'function',
49
+ endpointCount: CODINGNS_RPC_ENDPOINTS.length,
50
+ });
32
51
  ctx.effect(() => {
33
52
  const unregisterSettings = settingsProvider === undefined
34
53
  ? undefined
35
54
  : table.register('settings', createCodingNsSettingsRpcHandler(settingsProvider));
36
55
  const handler = createCodingNsRpcHandler(table);
56
+ // DSH 0.1.7 的 connection.rpc.handle() 会在当前插件 Fiber 中再次读取
57
+ // webServer;该 Fiber 没有 webServer 注入时会直接抛错。这里使用当前
58
+ // Host 已明确注入的 webServer 注册插件自有前缀,避免 Host RPC 装配中断。
37
59
  const unregisterChannel = webServer.register({
38
60
  kind: 'prefix',
39
61
  path: '/codingns',
40
62
  handler: (request, response) => handleChannelRequest(request, response, connection, handler),
41
63
  });
64
+ debugInfo('codingns4dsh: host rpc channel registered', {
65
+ transport: 'webServer.prefix',
66
+ channel: '/codingns',
67
+ });
42
68
  // 保留旧的精确 Fetch 路由,兼容早期 H5/桌面载体直接访问 `/api/codingns/*`
43
69
  // 的调用方。两条入口共享同一个 handler,不复制任何业务逻辑。
44
70
  const disposeFetch = CODINGNS_RPC_ENDPOINTS.map((endpoint) => ctx.connection.fetch.register({
@@ -47,11 +73,17 @@ export function registerCodingNsRpc(ctx, table, settingsProvider) {
47
73
  requestBody: 'buffered',
48
74
  fetch: async (request) => handleFetchRpc(request, endpoint, handler),
49
75
  }));
76
+ debugInfo('codingns4dsh: host rpc fetch routes registered', {
77
+ prefix: '/api/codingns/',
78
+ count: disposeFetch.length,
79
+ endpoints: CODINGNS_RPC_ENDPOINTS,
80
+ });
50
81
  return async () => {
51
82
  for (const dispose of disposeFetch.reverse())
52
83
  await dispose();
53
- unregisterChannel();
84
+ await unregisterChannel();
54
85
  unregisterSettings?.();
86
+ debugInfo('codingns4dsh: host rpc channel disposed');
55
87
  };
56
88
  }, 'codingns4dsh: Host RPC');
57
89
  }
@@ -146,6 +178,7 @@ const CODINGNS_RPC_ENDPOINTS = [
146
178
  'debug/config/get', 'debug/config/save', 'debug/config/update', 'debug/config/delete', 'debug/profile/list', 'debug/profile/launch',
147
179
  'debug/runtime/get', 'debug/runtime/list', 'debug/runtime/stop',
148
180
  'debug/port/check', 'debug/port/terminate', 'debug/port/kill', 'debug/proxy/get', 'debug/proxy/enable', 'debug/proxy/disable',
181
+ 'git/status', 'git/init', 'git/diff', 'git/stage', 'git/unstage', 'git/discard', 'git/commit', 'git/commit-diff', 'git/history', 'git/branches', 'git/switch', 'git/fetch', 'git/pull', 'git/push', 'git/undo',
149
182
  'lanAccessDsh/addresses', 'lanAccessDsh/detect', 'lanAccessDsh/get', 'lanAccessDsh/settings/get', 'lanAccessDsh/settings/set', 'lanAccessDsh/login/get', 'lanAccessDsh/login/set', 'lanAccessDsh/login/session/open', 'lanAccessDsh/start', 'lanAccessDsh/stop',
150
183
  'cli/catalog', 'cli/models', 'cli/adapter/set', 'cli/session/get', 'cli/session/set', 'cli/session/list', 'cli/session/adapter-map', 'cli/session/archive', 'cli/session/steer', 'cli/session/follow-up', 'cli/session/interrupt', 'cli/subscription',
151
184
  ];
@@ -158,22 +191,33 @@ export function createCodingNsSettingsRpcHandler(provider) {
158
191
  if (!provider.writable)
159
192
  throw new CodingNsRpcError('CODINGNS_SETTINGS_READ_ONLY', 'Host 设置提供器当前只读');
160
193
  const input = parseSettingsMutation(payload);
161
- await provider.mutate(CODINGNS_SETTINGS_NAMESPACE, input.ops, input.expectedRevision);
194
+ await provider.mutate(resolveCodingNsSettingsNamespace(provider), input.ops, input.expectedRevision);
162
195
  return readCodingNsSettings(provider);
163
196
  }
164
197
  throw new CodingNsRpcError('CODINGNS_RPC_NOT_FOUND', `未知 Codingns4DSH RPC: settings/${action}`);
165
198
  };
166
199
  }
167
200
  function readCodingNsSettings(provider) {
168
- const descriptor = provider.describe({ redactSecrets: true }).find((item) => item.ns === CODINGNS_SETTINGS_NAMESPACE);
201
+ const descriptor = findCodingNsSettingsDescriptor(provider);
169
202
  if (descriptor === undefined)
170
203
  throw new CodingNsRpcError('CODINGNS_SETTINGS_UNAVAILABLE', 'Codingns4DSH 设置尚未注册');
171
- const value = provider.get(CODINGNS_SETTINGS_NAMESPACE);
204
+ // 0.1.7 的 ConfigForm namespace 由 Bundle entry id 决定。包名改为 scoped 后,
205
+ // descriptor.ns 可能是 `@jingyi0605/codingns4dsh`,不能继续固定读取旧的 `codingns`。
206
+ const providerValue = typeof provider.get === 'function'
207
+ ? provider.get(descriptor.ns)
208
+ : undefined;
209
+ const value = providerValue ?? descriptor.value;
172
210
  // cliSessions 是 Host-only 索引,包含 providerSessionId/rawStoreRef,不能通过设置 RPC
173
211
  // 暴露给浏览器。外部会话列表必须走 cli/session/list,由 Host 按需返回摘要。
174
212
  const { cliSessions: _cliSessions, ...clientValue } = value;
175
213
  return { value: clientValue, revision: descriptor.revision };
176
214
  }
215
+ function findCodingNsSettingsDescriptor(provider) {
216
+ return provider.describe({ redactSecrets: true }).find((item) => isCodingNsSettingsEntryId(item.ns));
217
+ }
218
+ function resolveCodingNsSettingsNamespace(provider) {
219
+ return findCodingNsSettingsDescriptor(provider)?.ns ?? CODINGNS_SETTINGS_NAMESPACE;
220
+ }
177
221
  function parseSettingsMutation(value) {
178
222
  if (!isRecord(value) || !Array.isArray(value.ops) || value.ops.length === 0 || value.ops.length > 8) {
179
223
  throw new TypeError('settings/set 参数必须包含 1 到 8 个 ops');
@@ -205,9 +249,9 @@ function isAllowedSettingsPath(path) {
205
249
  if (path.length === 1)
206
250
  return ['controlBaseUrl', 'controlBaseUrls', 'terminalEnhancement', 'workspaceSessionEnhancement'].includes(path[0] ?? '');
207
251
  if (path[0] === 'modules')
208
- return path.length === 2 && ['lanAccess', 'reverseProxy', 'cliAdapters', 'terminalEnhancement', 'workspaceSessionEnhancement', 'debug'].includes(path[1] ?? '');
252
+ return path.length === 2 && ['lanAccess', 'reverseProxy', 'cliAdapters', 'terminalEnhancement', 'workspaceSessionEnhancement', 'debug', 'gitManagement'].includes(path[1] ?? '');
209
253
  if (path[0] === 'workspaceSessionEnhancement') {
210
- return path.length === 2 && ['showAdapterLogo', 'showArchivedSessions', 'showSubscriptionUsage'].includes(path[1] ?? '');
254
+ return path.length === 2 && ['showAdapterLogo', 'showArchivedSessions', 'showWorkspaceHiding', 'hiddenWorkspaceIds', 'showSubscriptionUsage', 'showQuickPhrases', 'quickPhrases', 'quickPhrasesSeeded'].includes(path[1] ?? '');
211
255
  }
212
256
  return path[0] === 'lanAccessDsh' && path.length === 2 && ['autoStart', 'listenHost', 'listenPort', 'dshPort'].includes(path[1] ?? '');
213
257
  }
@@ -215,6 +259,11 @@ function isRecord(value) {
215
259
  return typeof value === 'object' && value !== null && !Array.isArray(value);
216
260
  }
217
261
  async function handleFetchRpc(request, endpoint, handler) {
262
+ debugInfo('codingns4dsh: host fetch rpc request', {
263
+ endpoint,
264
+ method: request.method,
265
+ path: new URL(request.url).pathname,
266
+ });
218
267
  let body;
219
268
  try {
220
269
  body = await request.json();
@@ -230,6 +279,7 @@ async function handleFetchRpc(request, endpoint, handler) {
230
279
  return new Response('invalid RPC envelope', { status: 400 });
231
280
  }
232
281
  const result = await handler(endpoint, envelope.payload, request.signal);
282
+ debugInfo('codingns4dsh: host fetch rpc response', { endpoint, ok: result.ok });
233
283
  return Response.json({ type: 'server-response', rpcId: envelope.rpcId, result });
234
284
  }
235
285
  function success(value) {
@@ -0,0 +1,19 @@
1
+ export interface LegacySessionRepairOptions {
2
+ readonly root: string;
3
+ readonly signal?: AbortSignal;
4
+ readonly logger?: (message: string, error?: unknown) => void;
5
+ }
6
+ export interface LegacySessionRepairReport {
7
+ readonly scanned: number;
8
+ readonly repaired: number;
9
+ readonly skipped: number;
10
+ readonly failed: number;
11
+ }
12
+ /**
13
+ * 在官方 v3->v4 迁移前修复外部 Agent 写入的裸 tool/call。
14
+ * 每个文件独立处理,单文件失败不会影响其他历史会话。
15
+ */
16
+ export declare function repairLegacySessionLogs(options: LegacySessionRepairOptions): Promise<LegacySessionRepairReport>;
17
+ /** 修复一个 v3/v4 日志;返回 true 表示已发布修复文件。 */
18
+ export declare function repairLegacySessionLog(path: string, signal?: AbortSignal): Promise<boolean>;
19
+ //# sourceMappingURL=session-migration-repair.d.ts.map
@@ -0,0 +1,395 @@
1
+ import { copyFile, mkdtemp, readFile, readdir, rename, rm, writeFile } from 'node:fs/promises';
2
+ import { basename, join } from 'node:path';
3
+ import { tmpdir } from 'node:os';
4
+ import { zstdCompressSync, zstdDecompressSync } from 'node:zlib';
5
+ const ZSTD_MAGIC = Buffer.from([0x28, 0xb5, 0x2f, 0xfd]);
6
+ // v4 文件也可能是由旧版插件直接写入的裸 tool/call;DSH 0.1.7
7
+ // 会在恢复时直接拒绝它,因此必须在 open 边界前一并修复。
8
+ const LEGACY_FILE = /^session\.v[34]\.(jsonl|jsonl\.zstd)$/u;
9
+ const REFERENCE_KEYS = new Set([
10
+ 'sourceEventSeq',
11
+ 'sourceEventSeqs',
12
+ 'headerSeq',
13
+ 'throughSeq',
14
+ 'messageSeqs',
15
+ 'shadowedSeqs',
16
+ 'startSeq',
17
+ 'endSeq',
18
+ ]);
19
+ /**
20
+ * 在官方 v3->v4 迁移前修复外部 Agent 写入的裸 tool/call。
21
+ * 每个文件独立处理,单文件失败不会影响其他历史会话。
22
+ */
23
+ export async function repairLegacySessionLogs(options) {
24
+ const files = await findLegacyLogs(options.root, options.signal);
25
+ let repaired = 0;
26
+ let skipped = 0;
27
+ let failed = 0;
28
+ for (const path of files) {
29
+ throwIfAborted(options.signal);
30
+ try {
31
+ const result = await repairLegacySessionLog(path, options.signal);
32
+ if (result)
33
+ repaired += 1;
34
+ else
35
+ skipped += 1;
36
+ }
37
+ catch (error) {
38
+ failed += 1;
39
+ options.logger?.(`历史会话修复失败:${path}`, error);
40
+ }
41
+ }
42
+ return { scanned: files.length, repaired, skipped, failed };
43
+ }
44
+ /** 修复一个 v3/v4 日志;返回 true 表示已发布修复文件。 */
45
+ export async function repairLegacySessionLog(path, signal) {
46
+ const source = await readFileBytes(path);
47
+ throwIfAborted(signal);
48
+ const parsed = decodeLog(path, source);
49
+ const repaired = repairEvents(parsed.events, isV4Log(path, parsed.header));
50
+ if (repaired === null)
51
+ return false;
52
+ throwIfAborted(signal);
53
+ await publishRepair(path, parsed.header, repaired, parsed.compression, source, signal);
54
+ return true;
55
+ }
56
+ async function findLegacyLogs(root, signal) {
57
+ const result = [];
58
+ const queue = [root];
59
+ while (queue.length > 0) {
60
+ throwIfAborted(signal);
61
+ const current = queue.shift();
62
+ let entries;
63
+ try {
64
+ entries = await readdir(current, { withFileTypes: true });
65
+ }
66
+ catch (error) {
67
+ if (errorCode(error) === 'ENOENT')
68
+ continue;
69
+ throw error;
70
+ }
71
+ for (const entry of entries) {
72
+ const path = join(current, entry.name);
73
+ if (entry.isDirectory())
74
+ queue.push(path);
75
+ else if (entry.isFile() && LEGACY_FILE.test(entry.name))
76
+ result.push(path);
77
+ }
78
+ }
79
+ return result;
80
+ }
81
+ async function readFileBytes(path) {
82
+ const readBinary = readFile;
83
+ return readBinary(path);
84
+ }
85
+ function decodeLog(path, bytes) {
86
+ const compression = path.endsWith('.zstd') ? 'zstd' : 'none';
87
+ const text = compression === 'zstd' ? decodeZstd(bytes) : bytes.toString('utf8');
88
+ const rows = text.split('\n').filter((line) => line.trim() !== '').map((line, index) => {
89
+ const value = JSON.parse(line);
90
+ if (!isRecord(value))
91
+ throw new Error(`历史会话第 ${String(index + 1)} 行不是对象`);
92
+ return value;
93
+ });
94
+ const header = rows.shift();
95
+ if (header === undefined || header.type !== 'session')
96
+ throw new Error('历史会话缺少 session 头');
97
+ const events = rows.map((row, index) => {
98
+ if (typeof row.seq !== 'number' || row.seq !== index)
99
+ throw new Error(`历史会话 seq 不连续:期望 ${String(index)},得到 ${String(row.seq)}`);
100
+ if (typeof row.type !== 'string')
101
+ throw new Error(`历史会话事件 ${String(index)} 缺少 type`);
102
+ return row;
103
+ });
104
+ return { header, events, compression };
105
+ }
106
+ function decodeZstd(bytes) {
107
+ const chunks = [];
108
+ let offset = 0;
109
+ while (offset < bytes.length) {
110
+ const next = bytes.indexOf(ZSTD_MAGIC, offset + 1);
111
+ const frame = bytes.subarray(offset, next === -1 ? bytes.length : next);
112
+ chunks.push(zstdDecompressSync(frame));
113
+ offset = next === -1 ? bytes.length : next;
114
+ }
115
+ return Buffer.concat(chunks).toString('utf8');
116
+ }
117
+ function repairEvents(events, canonicalizeV4Results = false) {
118
+ const mapping = [];
119
+ const insertBefore = new Map();
120
+ const pending = new Set();
121
+ const normalizedAssistantMessages = new Map();
122
+ let canonicalized = false;
123
+ for (const event of events) {
124
+ const oldSeq = event.seq;
125
+ if (event.type === 'assistant/message')
126
+ registerDeclaredCalls(event, pending);
127
+ if (event.type === 'tool/call') {
128
+ const call = toolCallData(event);
129
+ if (call !== null && !pending.has(call.callId)) {
130
+ insertBefore.set(oldSeq, call);
131
+ pending.add(call.callId);
132
+ }
133
+ if (call !== null)
134
+ pending.add(call.callId);
135
+ }
136
+ if (event.type === 'tool/result') {
137
+ const callId = toolResultCallId(event);
138
+ if (callId !== null)
139
+ pending.delete(callId);
140
+ }
141
+ }
142
+ const declaredExternalCalls = new Set();
143
+ for (const event of events) {
144
+ if (event.type === 'step/start' || event.type === 'turn/start')
145
+ declaredExternalCalls.clear();
146
+ const normalized = canonicalizeExternalAssistantMessage(event, declaredExternalCalls);
147
+ if (normalized !== event) {
148
+ normalizedAssistantMessages.set(event.seq, normalized);
149
+ canonicalized = true;
150
+ }
151
+ if (event.type === 'step/end' || event.type === 'turn/end')
152
+ declaredExternalCalls.clear();
153
+ }
154
+ if (insertBefore.size === 0 && !canonicalizeV4Results && !canonicalized)
155
+ return null;
156
+ if (canonicalizeV4Results) {
157
+ for (const event of events) {
158
+ if (canonicalizeV4ToolResult(event) !== event) {
159
+ canonicalized = true;
160
+ break;
161
+ }
162
+ }
163
+ }
164
+ if (insertBefore.size === 0 && !canonicalized)
165
+ return null;
166
+ let nextSeq = 0;
167
+ for (const event of events) {
168
+ const oldSeq = event.seq;
169
+ if (insertBefore.has(oldSeq))
170
+ nextSeq += 1;
171
+ mapping[oldSeq] = nextSeq;
172
+ nextSeq += 1;
173
+ }
174
+ const output = [];
175
+ for (const event of events) {
176
+ const oldSeq = event.seq;
177
+ const call = insertBefore.get(oldSeq);
178
+ if (call !== undefined)
179
+ output.push(createDeclaration(event, call, mapping[oldSeq] - 1));
180
+ const deduplicated = normalizedAssistantMessages.get(oldSeq) ?? event;
181
+ const normalized = canonicalizeV4Results ? canonicalizeV4ToolResult(deduplicated) : deduplicated;
182
+ output.push(remapEvent(normalized, mapping[oldSeq], mapping));
183
+ }
184
+ return output;
185
+ }
186
+ function isV4Log(path, header) {
187
+ return header.version === 4 || /\.v4\.jsonl(?:\.zstd)?$/u.test(path);
188
+ }
189
+ /** 将插件旧版在 V4 文件中留下的 V3 tool-result wrapper 提升为 tool-role 消息。 */
190
+ function canonicalizeV4ToolResult(event) {
191
+ if (event.type !== 'tool/result')
192
+ return event;
193
+ const data = isRecord(event.data) ? event.data : undefined;
194
+ const message = data && isRecord(data.message) ? data.message : undefined;
195
+ if (message?.role !== 'user')
196
+ return event;
197
+ const source = isRecord(message.source) ? message.source : undefined;
198
+ const callId = source?.kind === 'tool' && typeof source.callId === 'string' ? source.callId : undefined;
199
+ const content = message.content;
200
+ if (callId === undefined || !Array.isArray(content) || content.length !== 1)
201
+ return event;
202
+ const wrapper = content[0];
203
+ if (!isRecord(wrapper) || wrapper.type !== 'tool-result' || wrapper.toolCallId !== callId || !Array.isArray(wrapper.content))
204
+ return event;
205
+ if (wrapper.isError !== undefined && typeof wrapper.isError !== 'boolean')
206
+ return event;
207
+ const { content: _legacyContent, ...rest } = message;
208
+ const normalizedMessage = {
209
+ ...rest,
210
+ role: 'tool',
211
+ toolCallId: callId,
212
+ content: wrapper.content,
213
+ ...(wrapper.isError === undefined ? {} : { isError: wrapper.isError }),
214
+ };
215
+ return { ...event, data: { ...data, message: normalizedMessage } };
216
+ }
217
+ /** 删除外部适配器累积消息中的历史 tool-call,保留当前消息新增的声明。 */
218
+ function canonicalizeExternalAssistantMessage(event, declared) {
219
+ if (event.type !== 'assistant/message')
220
+ return event;
221
+ const data = isRecord(event.data) ? event.data : undefined;
222
+ const message = data && isRecord(data.message) ? data.message : undefined;
223
+ if (!message || !Array.isArray(message.content) || !Array.isArray(data?.stream) || data.stream.length !== 0)
224
+ return event;
225
+ let changed = false;
226
+ const content = message.content.filter((block) => {
227
+ if (!isRecord(block) || block.type !== 'tool-call' || typeof block.id !== 'string')
228
+ return true;
229
+ if (declared.has(block.id)) {
230
+ changed = true;
231
+ return false;
232
+ }
233
+ declared.add(block.id);
234
+ return true;
235
+ });
236
+ return changed ? { ...event, data: { ...data, message: { ...message, content } } } : event;
237
+ }
238
+ function registerDeclaredCalls(event, pending) {
239
+ const data = isRecord(event.data) ? event.data : undefined;
240
+ const message = data && isRecord(data.message) ? data.message : undefined;
241
+ const content = message?.content;
242
+ if (!Array.isArray(content))
243
+ return;
244
+ for (const block of content) {
245
+ if (!isRecord(block) || block.type !== 'tool-call' || typeof block.id !== 'string')
246
+ continue;
247
+ pending.add(block.id);
248
+ }
249
+ }
250
+ function toolCallData(event) {
251
+ const data = isRecord(event.data) ? event.data : undefined;
252
+ if (!data || typeof data.callId !== 'string' || typeof data.name !== 'string' || typeof data.arguments !== 'string')
253
+ return null;
254
+ if (!Number.isSafeInteger(data.turn) || !Number.isSafeInteger(data.step))
255
+ return null;
256
+ return { callId: data.callId, name: data.name, arguments: data.arguments, turn: data.turn, step: data.step };
257
+ }
258
+ function toolResultCallId(event) {
259
+ const data = isRecord(event.data) ? event.data : undefined;
260
+ const message = data && isRecord(data.message) ? data.message : undefined;
261
+ if (typeof message?.toolCallId === 'string')
262
+ return message.toolCallId;
263
+ const content = message?.content;
264
+ if (!Array.isArray(content) || content.length !== 1)
265
+ return null;
266
+ const wrapper = content[0];
267
+ return isRecord(wrapper) && wrapper.type === 'tool-result' && typeof wrapper.toolCallId === 'string'
268
+ ? wrapper.toolCallId
269
+ : null;
270
+ }
271
+ function createDeclaration(event, call, seq) {
272
+ return {
273
+ type: 'assistant/message',
274
+ seq,
275
+ ...(typeof event.time === 'number' ? { time: event.time } : {}),
276
+ data: {
277
+ turn: call.turn,
278
+ step: call.step,
279
+ message: {
280
+ id: `external-tool-${call.callId}-${call.turn}-${call.step}`,
281
+ role: 'assistant',
282
+ content: [{ type: 'tool-call', id: call.callId, name: call.name, arguments: call.arguments }],
283
+ source: { kind: 'model', provider: 'codingns-external', model: 'external-agent' },
284
+ },
285
+ stream: [],
286
+ },
287
+ surfaceOp: 'append',
288
+ };
289
+ }
290
+ function remapEvent(event, seq, mapping) {
291
+ if (seq === event.seq)
292
+ return event;
293
+ const data = remapReferences(event.data, mapping);
294
+ const surfaceOp = remapReferences(event.surfaceOp, mapping);
295
+ const sourceEventSeqs = event.sourceEventSeqs === undefined ? undefined : event.sourceEventSeqs.map((value) => mapReference(value, mapping));
296
+ return {
297
+ ...event,
298
+ seq,
299
+ ...(data === event.data ? {} : { data }),
300
+ ...(surfaceOp === event.surfaceOp ? {} : { surfaceOp }),
301
+ ...(sourceEventSeqs === undefined ? {} : { sourceEventSeqs }),
302
+ };
303
+ }
304
+ function remapReferences(value, mapping, key, range = false, targetSeq = false) {
305
+ if (typeof value === 'number' && key !== undefined && (REFERENCE_KEYS.has(key) || range && (key === 'start' || key === 'end') || targetSeq && key === 'seq'))
306
+ return mapReference(value, mapping);
307
+ if (Array.isArray(value))
308
+ return value.map((item) => remapReferences(item, mapping, key, range, targetSeq));
309
+ if (!isRecord(value))
310
+ return value;
311
+ let changed = false;
312
+ const copy = {};
313
+ for (const [childKey, child] of Object.entries(value)) {
314
+ const mapped = remapReferences(child, mapping, childKey, key === 'shadowedRange', key === 'targets' || targetSeq);
315
+ copy[childKey] = mapped;
316
+ changed ||= mapped !== child;
317
+ }
318
+ return changed ? copy : value;
319
+ }
320
+ function mapReference(value, mapping) {
321
+ const mapped = mapping[value];
322
+ if (mapped === undefined)
323
+ throw new Error(`历史会话引用了不存在的 seq:${String(value)}`);
324
+ return mapped;
325
+ }
326
+ async function publishRepair(path, header, events, compression, original, signal) {
327
+ const tempDir = await mkdtemp(join(tmpdir(), 'codingns-session-repair-'));
328
+ const tempPath = join(tempDir, basename(path));
329
+ const backupPath = `${path}.codingns-repair-backup`;
330
+ try {
331
+ const body = [header, ...events].map((value) => `${JSON.stringify(value)}\n`).join('');
332
+ const encoded = compression === 'zstd'
333
+ ? Buffer.concat([zstdCompressSync(Buffer.from(`${JSON.stringify(header)}\n`)), zstdCompressSync(Buffer.from(events.map((event) => `${JSON.stringify(event)}\n`).join('')))])
334
+ : Buffer.from(body);
335
+ const writeBinary = writeFile;
336
+ await writeBinary(tempPath, encoded, { flag: 'wx', mode: 0o600 });
337
+ throwIfAborted(signal);
338
+ if (!(await sameBytes(path, original)))
339
+ throw new Error('历史会话在修复期间发生变化');
340
+ try {
341
+ await copyFile(path, backupPath, 1);
342
+ }
343
+ catch (error) {
344
+ if (errorCode(error) !== 'EEXIST')
345
+ throw error;
346
+ }
347
+ await replaceFile(tempPath, path, `${path}.codingns-repair-displaced-${basename(tempDir)}`);
348
+ }
349
+ finally {
350
+ await rm(tempDir, { recursive: true, force: true });
351
+ }
352
+ }
353
+ /** POSIX rename 可以直接替换;Windows 在目标存在时先暂存旧文件再发布。 */
354
+ async function replaceFile(tempPath, targetPath, displacedPath) {
355
+ try {
356
+ await rename(tempPath, targetPath);
357
+ return;
358
+ }
359
+ catch (error) {
360
+ if (!['EEXIST', 'EPERM', 'ENOTEMPTY'].includes(errorCode(error) ?? ''))
361
+ throw error;
362
+ }
363
+ await rename(targetPath, displacedPath);
364
+ try {
365
+ await rename(tempPath, targetPath);
366
+ }
367
+ catch (error) {
368
+ try {
369
+ await rename(displacedPath, targetPath);
370
+ }
371
+ catch { }
372
+ throw error;
373
+ }
374
+ await rm(displacedPath, { force: true });
375
+ }
376
+ async function sameBytes(path, expected) {
377
+ try {
378
+ const current = await readFileBytes(path);
379
+ return current.equals(expected);
380
+ }
381
+ catch {
382
+ return false;
383
+ }
384
+ }
385
+ function throwIfAborted(signal) {
386
+ if (signal?.aborted)
387
+ throw signal.reason instanceof Error ? signal.reason : new Error('历史会话修复已取消');
388
+ }
389
+ function errorCode(error) {
390
+ return isRecord(error) && typeof error.code === 'string' ? error.code : undefined;
391
+ }
392
+ function isRecord(value) {
393
+ return typeof value === 'object' && value !== null;
394
+ }
395
+ //# sourceMappingURL=session-migration-repair.js.map
@@ -1,7 +1,7 @@
1
1
  import type { Context } from '@deepseek-ai/cordis';
2
2
  import z from '@deepseek-ai/schemastery';
3
3
  import type { SettingsScope } from '@deepseek-ai/dsh-settings';
4
- import { type CodingNsSettings } from '../shared/contracts/config.js';
4
+ import { type CodingNsConfig, type CodingNsSettings } from '../shared/contracts/config.js';
5
5
  /**
6
6
  * DSH 设置服务使用的 Codingns4DSH namespace schema。
7
7
  *
@@ -9,6 +9,13 @@ import { type CodingNsSettings } from '../shared/contracts/config.js';
9
9
  * 也不需要改 CodingNsSettings 接口。
10
10
  */
11
11
  export declare const CodingNsSettingsSchema: z<CodingNsSettings>;
12
+ /**
13
+ * DSH 0.1.7 只会把 `volatile` 配置投影成可编辑 ConfigForm。
14
+ *
15
+ * 整个根节点标记为 volatile,保留 Host 侧 `cliSessions` 的持久化能力;
16
+ * Client 适配器会在镜像配置时剔除这个 Host-only 字段,避免会话索引进入浏览器。
17
+ */
18
+ export declare const CodingNsConfigSchema: z<CodingNsConfig>;
12
19
  /**
13
20
  * 在 Host 设置文档中注册 Codingns4DSH 的持久化选项。
14
21
  *