@jingyi0605/codingns 0.6.0 → 0.6.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.
Files changed (170) hide show
  1. package/bin/codingns.mjs +15 -2
  2. package/dist/public/assets/{AdaptiveButlerPage-uFwDdN-F.js → AdaptiveButlerPage-Dw72U3hG.js} +3 -3
  3. package/dist/public/assets/{App-BZvapsi8.js → App-Dsf3ooXU.js} +3 -3
  4. package/dist/public/assets/{BootstrapPage-gHSoa4JN.js → BootstrapPage-CE0m1qSR.js} +1 -1
  5. package/dist/public/assets/ConversationPage-8wOY7SX-.js +4 -0
  6. package/dist/public/assets/{DesktopDetachPreviewPage-4eMRxiBW.js → DesktopDetachPreviewPage-Dxarr_Wf.js} +1 -1
  7. package/dist/public/assets/DesktopWindowPage-VytPwJ4c.js +2 -0
  8. package/dist/public/assets/FileContextPanel-DwFzLsOp.js +1 -0
  9. package/dist/public/assets/GitSidebar-CH6WqTrM.js +6 -0
  10. package/dist/public/assets/MobileCreateSessionSheet-DcxKM00P.js +1 -0
  11. package/dist/public/assets/{MobileTopHeaderFrame-Bwv8Ovm_.js → MobileTopHeaderFrame-C5rIKQT6.js} +1 -1
  12. package/dist/public/assets/MobileWorkspaceSwitcherHeader-CfUnHgv_.js +1 -0
  13. package/dist/public/assets/{RelayConnectEntryPage-D_4YL-YH.js → RelayConnectEntryPage-CgMvVZwa.js} +1 -1
  14. package/dist/public/assets/{ServerSettingsModal-CMSm3BZU.js → ServerSettingsModal-CFul__z1.js} +1 -1
  15. package/dist/public/assets/SessionIndexPage-B-tRhBXC.js +1 -0
  16. package/dist/public/assets/SettingsPage-C9LGxSQZ.js +1 -0
  17. package/dist/public/assets/TerminalManagerPanel-BbORd-ee.js +1 -0
  18. package/dist/public/assets/{TerminalPage-DaooFaJ4.js → TerminalPage-DWHv6mlu.js} +1 -1
  19. package/dist/public/assets/TerminalRuntimeFallbackModal-B29YxbQe.js +1 -0
  20. package/dist/public/assets/{ToolFilesPage-CGxBvYG0.js → ToolFilesPage-Dx9cv9hu.js} +1 -1
  21. package/dist/public/assets/ToolGitPage-D7H3vAia.js +1 -0
  22. package/dist/public/assets/ToolProcessesPage-PqQWxsy-.js +1 -0
  23. package/dist/public/assets/ToolsHomePage-CX05Pe_4.js +1 -0
  24. package/dist/public/assets/WorkbenchLandingPage-CchkAC75.js +1 -0
  25. package/dist/public/assets/WorkbenchLayout-pOZvEqp7.js +3 -0
  26. package/dist/public/assets/{WorkbenchModal-0tPIIhca.js → WorkbenchModal-ColqvV6a.js} +1 -1
  27. package/dist/public/assets/WorkbenchShellRoute-C0_h4lP6.js +1 -0
  28. package/dist/public/assets/WorkbenchShellRoute-RGZpA0_J.css +1 -0
  29. package/dist/public/assets/WorkspaceDebugDetailPage-Deqy2_pO.js +1 -0
  30. package/dist/public/assets/WorkspaceDetailPage-Cvf-ZdlB.js +1 -0
  31. package/dist/public/assets/WorkspaceHomePage-Dsyvqyk1.js +1 -0
  32. package/dist/public/assets/{client-runtime-manager-BZpL17fc.js → client-runtime-manager-DROQJ9v3.js} +1 -1
  33. package/dist/public/assets/{file-tree-icon-Db5LXC8h.js → file-tree-icon-Bp3Ntt7u.js} +1 -1
  34. package/dist/public/assets/index-B84Po2NA.css +1 -0
  35. package/dist/public/assets/index-C-0oeG_5.js +42 -0
  36. package/dist/public/assets/legna-code-6TqgZ4Ls.png +0 -0
  37. package/dist/public/assets/{login-direct-candidate-resolver-1mxe_Oh8.js → login-direct-candidate-resolver-DotM530R.js} +1 -1
  38. package/dist/public/assets/model-switch-api-Bh9nYslz.js +1 -0
  39. package/dist/public/assets/{preferences-service-DWnzl5a0.js → preferences-service-BG6GKG29.js} +1 -1
  40. package/dist/public/assets/{relay-entry-C5_Iay0I.js → relay-entry-pmr-c42O.js} +1 -1
  41. package/dist/public/assets/session-runtime-machine-YN84QBlr.js +21 -0
  42. package/dist/public/assets/{terminal-runtime-meta-cdtWVfCm.js → terminal-runtime-meta-8_uRZf7h.js} +1 -1
  43. package/dist/public/assets/useRegisteredDebugTemplates-DWX7LXQu.js +1 -0
  44. package/dist/public/index.html +2 -2
  45. package/dist/server/config/env.d.ts +2 -0
  46. package/dist/server/config/env.js +7 -0
  47. package/dist/server/config/env.js.map +1 -1
  48. package/dist/server/modules/model-switch/cc-switch-adapter.d.ts +7 -0
  49. package/dist/server/modules/model-switch/cc-switch-adapter.js +17 -0
  50. package/dist/server/modules/model-switch/cc-switch-adapter.js.map +1 -1
  51. package/dist/server/modules/parallel-sessions/parallel-session-controller.d.ts +4 -0
  52. package/dist/server/modules/parallel-sessions/parallel-session-controller.js +7 -0
  53. package/dist/server/modules/parallel-sessions/parallel-session-controller.js.map +1 -1
  54. package/dist/server/modules/parallel-sessions/parallel-session-group-service.d.ts +6 -1
  55. package/dist/server/modules/parallel-sessions/parallel-session-group-service.js +36 -2
  56. package/dist/server/modules/parallel-sessions/parallel-session-group-service.js.map +1 -1
  57. package/dist/server/modules/provider/opencode-model-options.d.ts +1 -0
  58. package/dist/server/modules/provider/opencode-model-options.js +54 -12
  59. package/dist/server/modules/provider/opencode-model-options.js.map +1 -1
  60. package/dist/server/modules/provider/provider-controller.d.ts +6 -1
  61. package/dist/server/modules/provider/provider-controller.js +24 -2
  62. package/dist/server/modules/provider/provider-controller.js.map +1 -1
  63. package/dist/server/modules/provider/provider-discovery-helper-client.d.ts +2 -0
  64. package/dist/server/modules/provider/provider-discovery-helper-client.js.map +1 -1
  65. package/dist/server/modules/provider/provider-discovery-helper-process.js +1 -1
  66. package/dist/server/modules/provider/provider-discovery-helper-process.js.map +1 -1
  67. package/dist/server/modules/provider/provider-discovery-runtime.js +5 -1
  68. package/dist/server/modules/provider/provider-discovery-runtime.js.map +1 -1
  69. package/dist/server/modules/sessions/claude-runtime-helper-client.d.ts +1 -0
  70. package/dist/server/modules/sessions/claude-runtime-helper-client.js +14 -0
  71. package/dist/server/modules/sessions/claude-runtime-helper-client.js.map +1 -1
  72. package/dist/server/modules/sessions/codex-app-server-helper-client.d.ts +1 -0
  73. package/dist/server/modules/sessions/codex-app-server-helper-client.js +10 -0
  74. package/dist/server/modules/sessions/codex-app-server-helper-client.js.map +1 -1
  75. package/dist/server/modules/sessions/provider-session-delete-cli.js +2 -0
  76. package/dist/server/modules/sessions/provider-session-delete-cli.js.map +1 -1
  77. package/dist/server/modules/sessions/session-controller.d.ts +7 -0
  78. package/dist/server/modules/sessions/session-controller.js +22 -0
  79. package/dist/server/modules/sessions/session-controller.js.map +1 -1
  80. package/dist/server/modules/sessions/session-history-service.d.ts +12 -2
  81. package/dist/server/modules/sessions/session-history-service.js +284 -16
  82. package/dist/server/modules/sessions/session-history-service.js.map +1 -1
  83. package/dist/server/modules/sessions/session-live-runtime-service.d.ts +15 -2
  84. package/dist/server/modules/sessions/session-live-runtime-service.js +265 -50
  85. package/dist/server/modules/sessions/session-live-runtime-service.js.map +1 -1
  86. package/dist/server/modules/sessions/session-message-attachment-service.js +2 -2
  87. package/dist/server/modules/sessions/session-message-attachment-service.js.map +1 -1
  88. package/dist/server/modules/sessions/session-provider-config-service.d.ts +66 -0
  89. package/dist/server/modules/sessions/session-provider-config-service.js +821 -0
  90. package/dist/server/modules/sessions/session-provider-config-service.js.map +1 -0
  91. package/dist/server/modules/sessions/session-provider-error-mapper.d.ts +2 -0
  92. package/dist/server/modules/sessions/session-provider-error-mapper.js +42 -0
  93. package/dist/server/modules/sessions/session-provider-error-mapper.js.map +1 -1
  94. package/dist/server/server/create-server.js +11 -8
  95. package/dist/server/server/create-server.js.map +1 -1
  96. package/dist/server/storage/repositories/session-binding-repository.js +44 -5
  97. package/dist/server/storage/repositories/session-binding-repository.js.map +1 -1
  98. package/dist/server/storage/repositories/session-index-repository.js +6 -0
  99. package/dist/server/storage/repositories/session-index-repository.js.map +1 -1
  100. package/dist/server/storage/sqlite/client.js +19 -0
  101. package/dist/server/storage/sqlite/client.js.map +1 -1
  102. package/dist/server/storage/sqlite/schema.sql +5 -0
  103. package/dist/server/types/domain.d.ts +6 -0
  104. package/node_modules/@codingns/session-sync-core/dist/claude-message-utils.d.ts +5 -2
  105. package/node_modules/@codingns/session-sync-core/dist/claude-message-utils.js +40 -8
  106. package/node_modules/@codingns/session-sync-core/dist/claude-message-utils.js.map +1 -1
  107. package/node_modules/@codingns/session-sync-core/dist/index.d.ts +2 -0
  108. package/node_modules/@codingns/session-sync-core/dist/index.js +2 -0
  109. package/node_modules/@codingns/session-sync-core/dist/index.js.map +1 -1
  110. package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.d.ts +10 -1
  111. package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.js +110 -35
  112. package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.js.map +1 -1
  113. package/node_modules/@codingns/session-sync-core/dist/providers/claude-session-store.d.ts +11 -0
  114. package/node_modules/@codingns/session-sync-core/dist/providers/claude-session-store.js +105 -0
  115. package/node_modules/@codingns/session-sync-core/dist/providers/claude-session-store.js.map +1 -0
  116. package/node_modules/@codingns/session-sync-core/dist/providers/gemini.js +131 -39
  117. package/node_modules/@codingns/session-sync-core/dist/providers/gemini.js.map +1 -1
  118. package/node_modules/@codingns/session-sync-core/dist/providers/legna-code.d.ts +9 -0
  119. package/node_modules/@codingns/session-sync-core/dist/providers/legna-code.js +17 -0
  120. package/node_modules/@codingns/session-sync-core/dist/providers/legna-code.js.map +1 -0
  121. package/node_modules/@codingns/session-sync-core/dist/providers/opencode-shared.d.ts +8 -1
  122. package/node_modules/@codingns/session-sync-core/dist/providers/opencode-shared.js +19 -6
  123. package/node_modules/@codingns/session-sync-core/dist/providers/opencode-shared.js.map +1 -1
  124. package/node_modules/@codingns/session-sync-core/dist/providers/opencode.d.ts +1 -0
  125. package/node_modules/@codingns/session-sync-core/dist/providers/opencode.js +13 -8
  126. package/node_modules/@codingns/session-sync-core/dist/providers/opencode.js.map +1 -1
  127. package/node_modules/@codingns/session-sync-core/dist/runtime/claude-runtime.d.ts +5 -1
  128. package/node_modules/@codingns/session-sync-core/dist/runtime/claude-runtime.js +103 -51
  129. package/node_modules/@codingns/session-sync-core/dist/runtime/claude-runtime.js.map +1 -1
  130. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.d.ts +2 -1
  131. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js +41 -21
  132. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js.map +1 -1
  133. package/node_modules/@codingns/session-sync-core/dist/runtime/gemini-runtime.js +32 -8
  134. package/node_modules/@codingns/session-sync-core/dist/runtime/gemini-runtime.js.map +1 -1
  135. package/node_modules/@codingns/session-sync-core/dist/runtime/legna-runtime.d.ts +10 -0
  136. package/node_modules/@codingns/session-sync-core/dist/runtime/legna-runtime.js +16 -0
  137. package/node_modules/@codingns/session-sync-core/dist/runtime/legna-runtime.js.map +1 -0
  138. package/node_modules/@codingns/session-sync-core/dist/runtime/opencode-runtime.js +167 -10
  139. package/node_modules/@codingns/session-sync-core/dist/runtime/opencode-runtime.js.map +1 -1
  140. package/node_modules/@codingns/session-sync-core/dist/runtime/types.d.ts +2 -0
  141. package/node_modules/@codingns/session-sync-core/dist/types.d.ts +1 -1
  142. package/node_modules/@codingns/session-sync-core/dist/types.js +1 -1
  143. package/node_modules/@codingns/session-sync-core/dist/types.js.map +1 -1
  144. package/package.json +1 -1
  145. package/dist/public/assets/ConversationPage-z3sXtKZ7.js +0 -4
  146. package/dist/public/assets/DesktopWindowPage-CZcoGApB.js +0 -2
  147. package/dist/public/assets/FileContextPanel-C3qex8bb.js +0 -1
  148. package/dist/public/assets/GitSidebar-BK6H16XU.js +0 -6
  149. package/dist/public/assets/MobileCreateSessionSheet-BYfbvK8o.js +0 -1
  150. package/dist/public/assets/MobileSheet-Ckug8hTb.js +0 -1
  151. package/dist/public/assets/MobileWorkspaceSwitcherHeader-RqWrBdn2.js +0 -1
  152. package/dist/public/assets/SessionIndexPage-DuK10DL5.js +0 -1
  153. package/dist/public/assets/SettingsPage-fyD-xaHL.js +0 -1
  154. package/dist/public/assets/TerminalManagerPanel-CCLr1Ypk.js +0 -1
  155. package/dist/public/assets/TerminalRuntimeFallbackModal-aUzjEBwP.js +0 -1
  156. package/dist/public/assets/ToolGitPage-C264yjS9.js +0 -1
  157. package/dist/public/assets/ToolProcessesPage-BOP4A1cb.js +0 -1
  158. package/dist/public/assets/ToolsHomePage-CQxGiKQA.js +0 -1
  159. package/dist/public/assets/WorkbenchLandingPage-CvAY68ca.js +0 -1
  160. package/dist/public/assets/WorkbenchLayout-DGm8Tc5M.js +0 -3
  161. package/dist/public/assets/WorkbenchShellRoute-BF0nHWOk.css +0 -1
  162. package/dist/public/assets/WorkbenchShellRoute-DBBOsJo9.js +0 -1
  163. package/dist/public/assets/WorkspaceDebugDetailPage-CDerFYd2.js +0 -1
  164. package/dist/public/assets/WorkspaceDetailPage-BlJc1CHE.js +0 -1
  165. package/dist/public/assets/WorkspaceHomePage-BUsKJ3lv.js +0 -1
  166. package/dist/public/assets/default-session-permission-mode-DT4SGiwp.js +0 -1
  167. package/dist/public/assets/index-BZLcEHW3.js +0 -42
  168. package/dist/public/assets/index-BbspQPC2.css +0 -1
  169. package/dist/public/assets/session-runtime-machine-DdLeDqQr.js +0 -17
  170. package/dist/public/assets/useRegisteredDebugTemplates-oFAQNIqh.js +0 -1
@@ -0,0 +1,821 @@
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { AppError } from "../../shared/errors/app-error.js";
5
+ const SESSION_RUNTIME_METADATA_FILE = ".codingns-provider-runtime.json";
6
+ const PROVIDER_DEFAULT_MODEL_ID = "provider-default";
7
+ const SUPPORTED_SESSION_PRESET_PROVIDERS = new Set([
8
+ "claude-code",
9
+ "codex",
10
+ "gemini"
11
+ ]);
12
+ const CLAUDE_STANDARD_MODEL_IDS = new Set([
13
+ "sonnet",
14
+ "opus",
15
+ "haiku"
16
+ ]);
17
+ const CLAUDE_RUNTIME_STATE_FILES = [
18
+ ".claude.json",
19
+ "history.jsonl",
20
+ "stats-cache.json"
21
+ ];
22
+ const CLAUDE_RUNTIME_STATE_DIRECTORIES = [
23
+ "plans",
24
+ "session-env",
25
+ "sessions",
26
+ "shell-snapshots",
27
+ "todos"
28
+ ];
29
+ const CLAUDE_STANDARD_MODEL_PREFIX = "claude-";
30
+ const CLAUDE_MODEL_ALIASES = [
31
+ {
32
+ id: "sonnet",
33
+ label: "Sonnet",
34
+ envKey: "ANTHROPIC_DEFAULT_SONNET_MODEL"
35
+ },
36
+ {
37
+ id: "opus",
38
+ label: "Opus",
39
+ envKey: "ANTHROPIC_DEFAULT_OPUS_MODEL"
40
+ },
41
+ {
42
+ id: "haiku",
43
+ label: "Haiku",
44
+ envKey: "ANTHROPIC_DEFAULT_HAIKU_MODEL"
45
+ }
46
+ ];
47
+ export class SessionProviderConfigService {
48
+ config;
49
+ ccSwitchAdapter;
50
+ constructor(config, ccSwitchAdapter) {
51
+ this.config = config;
52
+ this.ccSwitchAdapter = ccSwitchAdapter;
53
+ }
54
+ prepareSessionBinding(input) {
55
+ const selection = this.resolveRequestedSelection({
56
+ providerConfigMode: input.providerConfigMode ?? undefined,
57
+ providerPresetId: input.providerPresetId ?? undefined
58
+ });
59
+ if (selection.providerConfigMode === "global-default") {
60
+ return {
61
+ providerConfigMode: selection.providerConfigMode,
62
+ providerPresetId: null,
63
+ runtimeHomeDir: null
64
+ };
65
+ }
66
+ this.assertProviderSupportsSessionPreset(input.provider);
67
+ const presetId = selection.providerPresetId;
68
+ const app = mapProviderToModelSwitchApp(input.provider);
69
+ const preset = this.ccSwitchAdapter.readPresetRuntimeConfig(app, presetId);
70
+ if (!preset) {
71
+ throw new AppError({
72
+ statusCode: 404,
73
+ errorCode: "MODEL_PRESET_NOT_FOUND",
74
+ detail: `未找到 provider preset:${presetId}`,
75
+ field: "providerPresetId"
76
+ });
77
+ }
78
+ const runtimeHomeDir = this.resolveRuntimeHomeDir(input.provider, input.sessionId);
79
+ this.materializeRuntimeHome(input.provider, runtimeHomeDir, preset);
80
+ return {
81
+ providerConfigMode: selection.providerConfigMode,
82
+ providerPresetId: selection.providerPresetId,
83
+ runtimeHomeDir
84
+ };
85
+ }
86
+ resolveSessionBinding(input) {
87
+ const existingSelection = input.existingBinding
88
+ ? {
89
+ providerConfigMode: input.existingBinding.providerConfigMode,
90
+ providerPresetId: input.existingBinding.providerPresetId
91
+ }
92
+ : null;
93
+ const selection = this.resolveRequestedSelection({
94
+ providerConfigMode: input.providerConfigMode ?? undefined,
95
+ providerPresetId: input.providerPresetId ?? undefined,
96
+ fallback: existingSelection
97
+ });
98
+ if (selection.providerConfigMode === "global-default") {
99
+ return {
100
+ providerConfigMode: "global-default",
101
+ providerPresetId: null,
102
+ runtimeHomeDir: null
103
+ };
104
+ }
105
+ const existingRuntimeHomeDir = input.existingBinding?.runtimeHomeDir?.trim() ?? "";
106
+ if (input.existingBinding?.providerConfigMode === "cc-switch-preset"
107
+ && input.existingBinding.providerPresetId === selection.providerPresetId
108
+ && existingRuntimeHomeDir.length > 0) {
109
+ return {
110
+ providerConfigMode: "cc-switch-preset",
111
+ providerPresetId: selection.providerPresetId,
112
+ runtimeHomeDir: existingRuntimeHomeDir
113
+ };
114
+ }
115
+ const preparedBinding = this.prepareSessionBinding({
116
+ sessionId: input.sessionId,
117
+ provider: input.provider,
118
+ providerConfigMode: selection.providerConfigMode,
119
+ providerPresetId: selection.providerPresetId
120
+ });
121
+ this.syncProviderRuntimeStateToPreparedHome(input.provider, input.existingBinding, preparedBinding.runtimeHomeDir);
122
+ return preparedBinding;
123
+ }
124
+ resolveCapabilities(input) {
125
+ const selection = this.resolveRequestedSelection({
126
+ providerConfigMode: input.providerConfigMode ?? undefined,
127
+ providerPresetId: input.providerPresetId ?? undefined
128
+ });
129
+ if (selection.providerConfigMode === "global-default") {
130
+ return input.baseCapabilities;
131
+ }
132
+ this.assertProviderSupportsSessionPreset(input.provider);
133
+ const presetId = selection.providerPresetId;
134
+ const app = mapProviderToModelSwitchApp(input.provider);
135
+ const preset = this.ccSwitchAdapter.readPresetRuntimeConfig(app, presetId);
136
+ if (!preset) {
137
+ throw new AppError({
138
+ statusCode: 404,
139
+ errorCode: "MODEL_PRESET_NOT_FOUND",
140
+ detail: `未找到 provider preset:${presetId}`,
141
+ field: "providerPresetId"
142
+ });
143
+ }
144
+ switch (input.provider) {
145
+ case "claude-code":
146
+ return buildClaudePresetCapabilities(input.baseCapabilities, preset.settingsConfig);
147
+ case "codex":
148
+ return buildCodexPresetCapabilities(input.baseCapabilities, preset.settingsConfig);
149
+ case "gemini":
150
+ return buildGeminiPresetCapabilities(input.baseCapabilities, preset.settingsConfig);
151
+ default:
152
+ return input.baseCapabilities;
153
+ }
154
+ }
155
+ resolveLaunchContext(binding) {
156
+ if (binding.providerConfigMode !== "cc-switch-preset") {
157
+ return {
158
+ runtimeHomeDir: null,
159
+ runtimeEnv: {}
160
+ };
161
+ }
162
+ const runtimeHomeDir = binding.runtimeHomeDir?.trim() ?? "";
163
+ if (!runtimeHomeDir) {
164
+ throw new AppError({
165
+ statusCode: 409,
166
+ errorCode: "SESSION_PROVIDER_RUNTIME_HOME_MISSING",
167
+ detail: "当前会话绑定了 cc-switch preset,但缺少运行目录"
168
+ });
169
+ }
170
+ const metadata = this.readRuntimeMetadata(runtimeHomeDir, binding.provider, binding.providerPresetId);
171
+ return {
172
+ runtimeHomeDir,
173
+ runtimeEnv: metadata.runtimeEnv
174
+ };
175
+ }
176
+ describeBinding(binding) {
177
+ const summary = {
178
+ provider: binding.provider,
179
+ providerConfigMode: binding.providerConfigMode,
180
+ providerPresetId: binding.providerPresetId,
181
+ providerPresetName: null,
182
+ runtimeHomeDir: binding.runtimeHomeDir ?? null,
183
+ modelProvider: null,
184
+ model: null,
185
+ baseUrl: null,
186
+ authEnvKeys: []
187
+ };
188
+ if (binding.providerConfigMode !== "cc-switch-preset") {
189
+ return summary;
190
+ }
191
+ try {
192
+ const app = mapProviderToModelSwitchApp(binding.provider);
193
+ const preset = binding.providerPresetId
194
+ ? this.ccSwitchAdapter.readPresetRuntimeConfig(app, binding.providerPresetId)
195
+ : null;
196
+ summary.providerPresetName = preset?.name ?? null;
197
+ }
198
+ catch {
199
+ return summary;
200
+ }
201
+ const runtimeHomeDir = binding.runtimeHomeDir?.trim() ?? "";
202
+ if (!runtimeHomeDir) {
203
+ return summary;
204
+ }
205
+ const configPath = path.join(runtimeHomeDir, "config.toml");
206
+ if (fs.existsSync(configPath) && fs.statSync(configPath).isFile()) {
207
+ const document = parseSimpleTomlDocument(fs.readFileSync(configPath, "utf8"));
208
+ const modelProvider = decodeSimpleTomlStringValue(document.rootValues.get("model_provider"));
209
+ const model = decodeSimpleTomlStringValue(document.rootValues.get("model"));
210
+ summary.modelProvider = modelProvider;
211
+ summary.model = model;
212
+ if (modelProvider) {
213
+ const providerTable = document.tables.get(`model_providers.${modelProvider}`);
214
+ summary.baseUrl = decodeSimpleTomlStringValue(providerTable?.values.get("base_url"));
215
+ }
216
+ }
217
+ try {
218
+ const metadata = this.readRuntimeMetadata(runtimeHomeDir, binding.provider, binding.providerPresetId);
219
+ summary.authEnvKeys = Object.keys(metadata.runtimeEnv).sort();
220
+ }
221
+ catch {
222
+ return summary;
223
+ }
224
+ return summary;
225
+ }
226
+ assertProviderSupportsSessionPreset(provider) {
227
+ if (SUPPORTED_SESSION_PRESET_PROVIDERS.has(provider)) {
228
+ return;
229
+ }
230
+ throw new AppError({
231
+ statusCode: 400,
232
+ errorCode: "PROVIDER_PRESET_NOT_SUPPORTED",
233
+ detail: `${provider} 当前不支持会话级 cc-switch preset`,
234
+ field: "provider"
235
+ });
236
+ }
237
+ resolveRequestedSelection(input) {
238
+ const normalizedPresetId = input.providerPresetId?.trim() || null;
239
+ if (input.providerConfigMode === undefined && input.providerPresetId === undefined) {
240
+ return input.fallback ?? {
241
+ providerConfigMode: "global-default",
242
+ providerPresetId: null
243
+ };
244
+ }
245
+ const providerConfigMode = input.providerConfigMode ?? (normalizedPresetId ? "cc-switch-preset" : "global-default");
246
+ if (providerConfigMode === "global-default") {
247
+ return {
248
+ providerConfigMode,
249
+ providerPresetId: null
250
+ };
251
+ }
252
+ if (!normalizedPresetId) {
253
+ throw new AppError({
254
+ statusCode: 400,
255
+ errorCode: "INVALID_INPUT",
256
+ detail: "使用 cc-switch preset 时必须提供 providerPresetId",
257
+ field: "providerPresetId"
258
+ });
259
+ }
260
+ return {
261
+ providerConfigMode,
262
+ providerPresetId: normalizedPresetId
263
+ };
264
+ }
265
+ resolveRuntimeHomeDir(provider, sessionId) {
266
+ return path.resolve(path.dirname(this.config.databasePath), "session-provider-runtime", provider, sessionId);
267
+ }
268
+ materializeRuntimeHome(provider, runtimeHomeDir, preset) {
269
+ fs.mkdirSync(runtimeHomeDir, { recursive: true });
270
+ const runtimeEnv = normalizeRuntimeEnv(preset.settingsConfig);
271
+ this.writeRuntimeMetadata(runtimeHomeDir, {
272
+ provider,
273
+ providerPresetId: preset.id,
274
+ runtimeEnv
275
+ });
276
+ switch (provider) {
277
+ case "claude-code":
278
+ this.materializeClaudeRuntimeHome(runtimeHomeDir, preset.settingsConfig);
279
+ return;
280
+ case "codex":
281
+ this.materializeCodexRuntimeHome(runtimeHomeDir, preset.settingsConfig);
282
+ return;
283
+ case "gemini":
284
+ this.materializeGeminiRuntimeHome(runtimeHomeDir);
285
+ return;
286
+ default:
287
+ throw new AppError({
288
+ statusCode: 400,
289
+ errorCode: "PROVIDER_PRESET_NOT_SUPPORTED",
290
+ detail: `${provider} 当前不支持会话级 cc-switch preset`
291
+ });
292
+ }
293
+ }
294
+ materializeClaudeRuntimeHome(runtimeHomeDir, settingsConfig) {
295
+ const sourceHomeDir = path.resolve(this.config.claudeCodeHomeDir);
296
+ const mergedSettings = mergeJsonObjects(readJsonObject(path.join(sourceHomeDir, "settings.json")), readJsonObject(path.join(sourceHomeDir, "settings.local.json")), settingsConfig);
297
+ syncOptionalFile(path.join(sourceHomeDir, "config.json"), path.join(runtimeHomeDir, "config.json"));
298
+ syncOptionalFile(path.join(sourceHomeDir, "project-config.json"), path.join(runtimeHomeDir, "project-config.json"));
299
+ syncOptionalDirectory(path.join(sourceHomeDir, "plugins"), path.join(runtimeHomeDir, "plugins"));
300
+ syncOptionalDirectory(path.join(sourceHomeDir, "skills"), path.join(runtimeHomeDir, "skills"));
301
+ writeJsonFile(path.join(runtimeHomeDir, "settings.json"), mergedSettings);
302
+ removeFileIfExists(path.join(runtimeHomeDir, "settings.local.json"));
303
+ }
304
+ materializeCodexRuntimeHome(runtimeHomeDir, settingsConfig) {
305
+ const sourceHomeDir = resolveCodexSourceHomeDir(this.config.codexHomeDir, runtimeHomeDir);
306
+ const sourceConfigPath = path.join(sourceHomeDir, "config.toml");
307
+ const configuredText = normalizeText(settingsConfig.config) ?? normalizeText(settingsConfig.toml) ?? "";
308
+ const sourceConfigText = sourceHomeDir !== runtimeHomeDir && fs.existsSync(sourceConfigPath) && fs.statSync(sourceConfigPath).isFile()
309
+ ? fs.readFileSync(sourceConfigPath, "utf8")
310
+ : "";
311
+ syncOptionalFile(path.join(sourceHomeDir, "auth.json"), path.join(runtimeHomeDir, "auth.json"));
312
+ syncOptionalDirectory(path.join(sourceHomeDir, "skills"), path.join(runtimeHomeDir, "skills"));
313
+ writeTextFile(path.join(runtimeHomeDir, "config.toml"), `${composeCodexConfigContent(sourceConfigText, configuredText)}\n`);
314
+ }
315
+ materializeGeminiRuntimeHome(runtimeHomeDir) {
316
+ fs.mkdirSync(path.join(runtimeHomeDir, "tmp"), { recursive: true });
317
+ }
318
+ syncProviderRuntimeStateToPreparedHome(provider, existingBinding, targetRuntimeHomeDir) {
319
+ if (!existingBinding || !targetRuntimeHomeDir) {
320
+ return;
321
+ }
322
+ switch (provider) {
323
+ case "claude-code":
324
+ this.syncClaudeSessionRuntimeState(existingBinding, targetRuntimeHomeDir);
325
+ return;
326
+ default:
327
+ return;
328
+ }
329
+ }
330
+ syncClaudeSessionRuntimeState(existingBinding, targetRuntimeHomeDir) {
331
+ const sourceSessionFilePath = this.resolveClaudeSessionFilePath(existingBinding);
332
+ const sourceRuntimeHomeDir = this.resolveClaudeSourceHomeDir(existingBinding, sourceSessionFilePath);
333
+ if (sourceRuntimeHomeDir) {
334
+ this.syncClaudeRuntimeStateEntries(sourceRuntimeHomeDir, targetRuntimeHomeDir);
335
+ }
336
+ if (!sourceSessionFilePath) {
337
+ return;
338
+ }
339
+ const sourceSessionDirectory = path.dirname(sourceSessionFilePath);
340
+ const targetSessionDirectory = resolveClaudeSessionTargetDirectory(sourceSessionDirectory, targetRuntimeHomeDir);
341
+ if (path.resolve(sourceSessionDirectory) === path.resolve(targetSessionDirectory)) {
342
+ return;
343
+ }
344
+ syncOptionalDirectory(sourceSessionDirectory, targetSessionDirectory);
345
+ }
346
+ resolveClaudeSessionFilePath(binding) {
347
+ const rawStoreRef = binding.rawStoreRef?.trim() ?? "";
348
+ if (rawStoreRef && fs.existsSync(rawStoreRef) && fs.statSync(rawStoreRef).isFile()) {
349
+ return rawStoreRef;
350
+ }
351
+ const providerSessionId = binding.providerSessionId?.trim() ?? "";
352
+ if (!providerSessionId) {
353
+ return null;
354
+ }
355
+ const candidateHomes = this.listClaudeCandidateHomes(binding);
356
+ for (const homeDir of candidateHomes) {
357
+ const matched = findClaudeSessionFileInHome(homeDir, providerSessionId);
358
+ if (matched) {
359
+ return matched;
360
+ }
361
+ }
362
+ return null;
363
+ }
364
+ resolveClaudeSourceHomeDir(binding, sourceSessionFilePath) {
365
+ const candidateHomes = this.listClaudeCandidateHomes(binding);
366
+ const normalizedSessionFilePath = sourceSessionFilePath ? path.resolve(sourceSessionFilePath) : null;
367
+ if (normalizedSessionFilePath) {
368
+ for (const homeDir of candidateHomes) {
369
+ const projectsRoot = path.join(homeDir, "projects");
370
+ const normalizedProjectsRoot = path.resolve(projectsRoot);
371
+ if (normalizedSessionFilePath === normalizedProjectsRoot
372
+ || normalizedSessionFilePath.startsWith(`${normalizedProjectsRoot}${path.sep}`)) {
373
+ return homeDir;
374
+ }
375
+ }
376
+ }
377
+ const existingRuntimeHomeDir = binding.runtimeHomeDir?.trim() ?? "";
378
+ if (existingRuntimeHomeDir.length > 0
379
+ && fs.existsSync(existingRuntimeHomeDir)
380
+ && fs.statSync(existingRuntimeHomeDir).isDirectory()) {
381
+ return path.resolve(existingRuntimeHomeDir);
382
+ }
383
+ return candidateHomes[0] ?? null;
384
+ }
385
+ listClaudeCandidateHomes(binding) {
386
+ return [
387
+ binding.runtimeHomeDir?.trim() ?? "",
388
+ path.resolve(this.config.claudeCodeHomeDir)
389
+ ].filter((value, index, values) => value.length > 0 && values.indexOf(value) === index);
390
+ }
391
+ syncClaudeRuntimeStateEntries(sourceHomeDir, targetRuntimeHomeDir) {
392
+ if (path.resolve(sourceHomeDir) === path.resolve(targetRuntimeHomeDir)) {
393
+ return;
394
+ }
395
+ CLAUDE_RUNTIME_STATE_FILES.forEach((entry) => {
396
+ syncOptionalFile(path.join(sourceHomeDir, entry), path.join(targetRuntimeHomeDir, entry));
397
+ });
398
+ CLAUDE_RUNTIME_STATE_DIRECTORIES.forEach((entry) => {
399
+ syncOptionalDirectory(path.join(sourceHomeDir, entry), path.join(targetRuntimeHomeDir, entry));
400
+ });
401
+ }
402
+ writeRuntimeMetadata(runtimeHomeDir, metadata) {
403
+ writeJsonFile(path.join(runtimeHomeDir, SESSION_RUNTIME_METADATA_FILE), metadata);
404
+ }
405
+ readRuntimeMetadata(runtimeHomeDir, provider, providerPresetId) {
406
+ const filePath = path.join(runtimeHomeDir, SESSION_RUNTIME_METADATA_FILE);
407
+ if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile()) {
408
+ throw new AppError({
409
+ statusCode: 409,
410
+ errorCode: "SESSION_PROVIDER_RUNTIME_METADATA_MISSING",
411
+ detail: "当前会话的 provider 运行配置已经丢失,请重新创建会话"
412
+ });
413
+ }
414
+ try {
415
+ const parsed = JSON.parse(fs.readFileSync(filePath, "utf8"));
416
+ if (!parsed || parsed.provider !== provider) {
417
+ throw new Error("PROVIDER_MISMATCH");
418
+ }
419
+ if ((parsed.providerPresetId ?? "").trim() !== (providerPresetId ?? "").trim()) {
420
+ throw new Error("PRESET_MISMATCH");
421
+ }
422
+ return {
423
+ provider,
424
+ providerPresetId: parsed.providerPresetId,
425
+ runtimeEnv: normalizeRuntimeEnv({
426
+ env: parsed.runtimeEnv
427
+ })
428
+ };
429
+ }
430
+ catch {
431
+ throw new AppError({
432
+ statusCode: 409,
433
+ errorCode: "SESSION_PROVIDER_RUNTIME_METADATA_INVALID",
434
+ detail: "当前会话的 provider 运行配置已损坏,请重新创建会话"
435
+ });
436
+ }
437
+ }
438
+ }
439
+ function mapProviderToModelSwitchApp(provider) {
440
+ switch (provider) {
441
+ case "claude-code":
442
+ case "codex":
443
+ case "gemini":
444
+ return provider;
445
+ default:
446
+ throw new Error(`UNSUPPORTED_MODEL_SWITCH_PROVIDER:${provider}`);
447
+ }
448
+ }
449
+ function buildClaudePresetCapabilities(baseCapabilities, settingsConfig) {
450
+ const env = normalizeRuntimeEnv(settingsConfig);
451
+ const defaultModel = normalizeText(env.ANTHROPIC_MODEL);
452
+ const aliasTargets = CLAUDE_MODEL_ALIASES.map((alias) => ({
453
+ ...alias,
454
+ target: normalizeText(env[alias.envKey])
455
+ }));
456
+ const customModels = new Set();
457
+ if (defaultModel && isClaudeCustomModel(defaultModel)) {
458
+ customModels.add(defaultModel);
459
+ }
460
+ aliasTargets.forEach((alias) => {
461
+ if (alias.target && isClaudeCustomModel(alias.target)) {
462
+ customModels.add(alias.target);
463
+ }
464
+ });
465
+ return {
466
+ ...baseCapabilities,
467
+ modelOptions: [
468
+ buildPresetDefaultModelOption(defaultModel),
469
+ ...aliasTargets.map((alias) => ({
470
+ id: alias.id,
471
+ name: alias.target ? `${alias.label}(当前:${alias.target})` : alias.label
472
+ })),
473
+ ...Array.from(customModels).map((modelId) => ({
474
+ id: modelId,
475
+ name: modelId
476
+ }))
477
+ ]
478
+ };
479
+ }
480
+ function buildCodexPresetCapabilities(baseCapabilities, settingsConfig) {
481
+ const configuredText = normalizeText(settingsConfig.config)
482
+ ?? normalizeText(settingsConfig.toml)
483
+ ?? "";
484
+ const currentModel = readTomlStringValue(configuredText, "model");
485
+ const currentReasoningLevel = readTomlStringValue(configuredText, "model_reasoning_effort");
486
+ const modelOptions = appendMissingModelOption(withPresetDefaultModelOption(baseCapabilities.modelOptions, currentModel), currentModel);
487
+ return {
488
+ ...baseCapabilities,
489
+ modelOptions,
490
+ defaultReasoningLevel: normalizeReasoningLevel(currentReasoningLevel) ?? baseCapabilities.defaultReasoningLevel ?? null
491
+ };
492
+ }
493
+ function buildGeminiPresetCapabilities(baseCapabilities, settingsConfig) {
494
+ const currentModel = resolveGeminiPresetModel(settingsConfig);
495
+ return {
496
+ ...baseCapabilities,
497
+ modelOptions: appendMissingModelOption(withPresetDefaultModelOption(baseCapabilities.modelOptions, currentModel), currentModel)
498
+ };
499
+ }
500
+ function withPresetDefaultModelOption(options, currentModel) {
501
+ const preserved = (options ?? []).filter((option) => option.id !== PROVIDER_DEFAULT_MODEL_ID);
502
+ return [
503
+ buildPresetDefaultModelOption(currentModel),
504
+ ...preserved
505
+ ];
506
+ }
507
+ function appendMissingModelOption(options, currentModel) {
508
+ if (!currentModel || options.some((option) => option.id === currentModel)) {
509
+ return options;
510
+ }
511
+ return [
512
+ ...options,
513
+ {
514
+ id: currentModel,
515
+ name: currentModel
516
+ }
517
+ ];
518
+ }
519
+ function buildPresetDefaultModelOption(currentModel) {
520
+ return {
521
+ id: PROVIDER_DEFAULT_MODEL_ID,
522
+ name: currentModel ? `跟随配置文件默认模型(当前:${currentModel})` : "跟随配置文件默认模型",
523
+ usesProviderDefault: true
524
+ };
525
+ }
526
+ function isClaudeCustomModel(modelId) {
527
+ if (CLAUDE_STANDARD_MODEL_IDS.has(modelId)) {
528
+ return false;
529
+ }
530
+ if (modelId.startsWith(CLAUDE_STANDARD_MODEL_PREFIX)) {
531
+ return false;
532
+ }
533
+ return true;
534
+ }
535
+ function readTomlStringValue(content, key) {
536
+ const matcher = new RegExp(`(^|\\n)\\s*${escapeRegExp(key)}\\s*=\\s*["']([^"']+)["']`, "i");
537
+ const match = matcher.exec(content);
538
+ return normalizeText(match?.[2]);
539
+ }
540
+ function resolveGeminiPresetModel(settingsConfig) {
541
+ const env = normalizeRuntimeEnv(settingsConfig);
542
+ const configRecord = asRecord(settingsConfig.config);
543
+ return normalizeText(env.GEMINI_MODEL)
544
+ ?? normalizeText(env.GOOGLE_MODEL)
545
+ ?? normalizeText(env.GEMINI_DEFAULT_MODEL)
546
+ ?? normalizeText(configRecord?.model)
547
+ ?? normalizeText(configRecord?.defaultModel);
548
+ }
549
+ function normalizeReasoningLevel(value) {
550
+ if (value === "low" || value === "medium" || value === "high" || value === "xhigh") {
551
+ return value;
552
+ }
553
+ return null;
554
+ }
555
+ function escapeRegExp(value) {
556
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
557
+ }
558
+ function normalizeRuntimeEnv(settingsConfig) {
559
+ return {
560
+ ...normalizeStringRecord(asRecord(settingsConfig.env)),
561
+ ...normalizeStringRecord(asRecord(settingsConfig.auth))
562
+ };
563
+ }
564
+ function readJsonObject(filePath) {
565
+ if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile()) {
566
+ return {};
567
+ }
568
+ try {
569
+ const parsed = JSON.parse(fs.readFileSync(filePath, "utf8"));
570
+ return asRecord(parsed) ?? {};
571
+ }
572
+ catch {
573
+ return {};
574
+ }
575
+ }
576
+ function mergeJsonObjects(...records) {
577
+ return records.reduce((current, record) => deepMergeRecord(current, record), {});
578
+ }
579
+ function deepMergeRecord(left, right) {
580
+ const next = {
581
+ ...left
582
+ };
583
+ for (const [key, value] of Object.entries(right)) {
584
+ const leftValue = next[key];
585
+ if (asRecord(leftValue) && asRecord(value)) {
586
+ next[key] = deepMergeRecord(asRecord(leftValue) ?? {}, asRecord(value) ?? {});
587
+ continue;
588
+ }
589
+ next[key] = value;
590
+ }
591
+ return next;
592
+ }
593
+ function composeCodexConfigContent(sourceConfigContent, presetConfigContent) {
594
+ const merged = mergeCodexTomlDocuments(parseSimpleTomlDocument(sourceConfigContent), parseSimpleTomlDocument(presetConfigContent));
595
+ return serializeSimpleTomlDocument(merged);
596
+ }
597
+ function resolveCodexSourceHomeDir(sourceCodexHomeDir, targetHomeDir) {
598
+ const resolvedConfiguredSource = path.resolve(sourceCodexHomeDir);
599
+ if (resolvedConfiguredSource !== targetHomeDir) {
600
+ return resolvedConfiguredSource;
601
+ }
602
+ const fallbackHomeDir = path.resolve(path.join(os.homedir(), ".codex"));
603
+ if (fallbackHomeDir !== targetHomeDir) {
604
+ return fallbackHomeDir;
605
+ }
606
+ return targetHomeDir;
607
+ }
608
+ function writeJsonFile(filePath, value) {
609
+ writeTextFile(filePath, `${JSON.stringify(value, null, 2)}\n`);
610
+ }
611
+ function writeTextFile(filePath, content) {
612
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
613
+ if (fs.existsSync(filePath) && fs.readFileSync(filePath, "utf8") === content) {
614
+ return;
615
+ }
616
+ fs.writeFileSync(filePath, content, "utf8");
617
+ }
618
+ function syncOptionalFile(sourcePath, targetPath) {
619
+ if (!fs.existsSync(sourcePath) || !fs.statSync(sourcePath).isFile()) {
620
+ return;
621
+ }
622
+ writeTextFile(targetPath, fs.readFileSync(sourcePath, "utf8"));
623
+ }
624
+ function syncOptionalDirectory(sourcePath, targetPath) {
625
+ if (!fs.existsSync(sourcePath) || !fs.statSync(sourcePath).isDirectory()) {
626
+ return;
627
+ }
628
+ if (path.resolve(sourcePath) === path.resolve(targetPath)) {
629
+ return;
630
+ }
631
+ fs.rmSync(targetPath, { recursive: true, force: true });
632
+ fs.mkdirSync(path.dirname(targetPath), { recursive: true });
633
+ fs.cpSync(sourcePath, targetPath, { recursive: true });
634
+ }
635
+ function findClaudeSessionFileInHome(homeDir, providerSessionId) {
636
+ const projectsDir = path.join(homeDir, "projects");
637
+ if (!fs.existsSync(projectsDir) || !fs.statSync(projectsDir).isDirectory()) {
638
+ return null;
639
+ }
640
+ const projectDirectories = fs.readdirSync(projectsDir, { withFileTypes: true })
641
+ .filter((entry) => entry.isDirectory())
642
+ .map((entry) => path.join(projectsDir, entry.name, `${providerSessionId}.jsonl`));
643
+ for (const candidatePath of projectDirectories) {
644
+ if (fs.existsSync(candidatePath) && fs.statSync(candidatePath).isFile()) {
645
+ return candidatePath;
646
+ }
647
+ }
648
+ return null;
649
+ }
650
+ function resolveClaudeSessionTargetDirectory(sourceSessionDirectory, targetRuntimeHomeDir) {
651
+ const normalizedSourceDirectory = path.resolve(sourceSessionDirectory);
652
+ const projectsMarker = `${path.sep}projects${path.sep}`;
653
+ const markerIndex = normalizedSourceDirectory.lastIndexOf(projectsMarker);
654
+ const relativeProjectPath = markerIndex >= 0
655
+ ? normalizedSourceDirectory.slice(markerIndex + projectsMarker.length)
656
+ : path.basename(normalizedSourceDirectory);
657
+ return path.join(targetRuntimeHomeDir, "projects", relativeProjectPath);
658
+ }
659
+ function removeFileIfExists(filePath) {
660
+ if (!fs.existsSync(filePath)) {
661
+ return;
662
+ }
663
+ fs.rmSync(filePath, { force: true });
664
+ }
665
+ function asRecord(value) {
666
+ return value && typeof value === "object" && !Array.isArray(value)
667
+ ? value
668
+ : null;
669
+ }
670
+ function normalizeStringRecord(record) {
671
+ if (!record) {
672
+ return {};
673
+ }
674
+ return Object.fromEntries(Object.entries(record)
675
+ .filter((entry) => typeof entry[1] === "string" && entry[1].trim().length > 0)
676
+ .map(([key, value]) => [key.trim(), value.trim()])
677
+ .filter(([key, value]) => key.length > 0 && value.length > 0));
678
+ }
679
+ function normalizeText(value) {
680
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
681
+ }
682
+ function decodeSimpleTomlStringValue(value) {
683
+ const normalizedValue = value?.trim();
684
+ if (!normalizedValue) {
685
+ return null;
686
+ }
687
+ if ((normalizedValue.startsWith("\"") && normalizedValue.endsWith("\""))
688
+ || (normalizedValue.startsWith("'") && normalizedValue.endsWith("'"))) {
689
+ return normalizedValue.slice(1, -1).trim() || null;
690
+ }
691
+ return normalizedValue;
692
+ }
693
+ function parseSimpleTomlDocument(content) {
694
+ const document = {
695
+ rootOrder: [],
696
+ rootValues: new Map(),
697
+ tableOrder: [],
698
+ tables: new Map()
699
+ };
700
+ let currentTableName = null;
701
+ for (const line of content.split(/\r?\n/u)) {
702
+ const trimmed = line.trim();
703
+ if (!trimmed || trimmed.startsWith("#")) {
704
+ continue;
705
+ }
706
+ const tableMatch = /^\[([^\]]+)\]$/u.exec(trimmed);
707
+ if (tableMatch) {
708
+ currentTableName = tableMatch[1]?.trim() || null;
709
+ if (currentTableName && !document.tables.has(currentTableName)) {
710
+ document.tableOrder.push(currentTableName);
711
+ document.tables.set(currentTableName, {
712
+ order: [],
713
+ values: new Map()
714
+ });
715
+ }
716
+ continue;
717
+ }
718
+ const delimiterIndex = trimmed.indexOf("=");
719
+ if (delimiterIndex <= 0) {
720
+ continue;
721
+ }
722
+ const key = trimmed.slice(0, delimiterIndex).trim();
723
+ const value = trimmed.slice(delimiterIndex + 1).trim();
724
+ if (!key || !value) {
725
+ continue;
726
+ }
727
+ if (!currentTableName) {
728
+ if (!document.rootValues.has(key)) {
729
+ document.rootOrder.push(key);
730
+ }
731
+ document.rootValues.set(key, value);
732
+ continue;
733
+ }
734
+ const currentTable = document.tables.get(currentTableName);
735
+ if (!currentTable) {
736
+ continue;
737
+ }
738
+ if (!currentTable.values.has(key)) {
739
+ currentTable.order.push(key);
740
+ }
741
+ currentTable.values.set(key, value);
742
+ }
743
+ return document;
744
+ }
745
+ function mergeCodexTomlDocuments(source, preset) {
746
+ const merged = {
747
+ rootOrder: [...source.rootOrder],
748
+ rootValues: new Map(source.rootValues),
749
+ tableOrder: [...source.tableOrder],
750
+ tables: new Map(Array.from(source.tables.entries()).map(([tableName, table]) => [
751
+ tableName,
752
+ {
753
+ order: [...table.order],
754
+ values: new Map(table.values)
755
+ }
756
+ ]))
757
+ };
758
+ for (const key of preset.rootOrder) {
759
+ if (!merged.rootValues.has(key)) {
760
+ merged.rootOrder.push(key);
761
+ }
762
+ const value = preset.rootValues.get(key);
763
+ if (value) {
764
+ merged.rootValues.set(key, value);
765
+ }
766
+ }
767
+ for (const tableName of preset.tableOrder) {
768
+ const presetTable = preset.tables.get(tableName);
769
+ if (!presetTable) {
770
+ continue;
771
+ }
772
+ if (!merged.tables.has(tableName)) {
773
+ merged.tableOrder.push(tableName);
774
+ merged.tables.set(tableName, {
775
+ order: [],
776
+ values: new Map()
777
+ });
778
+ }
779
+ const mergedTable = merged.tables.get(tableName);
780
+ for (const key of presetTable.order) {
781
+ if (!mergedTable.values.has(key)) {
782
+ mergedTable.order.push(key);
783
+ }
784
+ const value = presetTable.values.get(key);
785
+ if (value) {
786
+ mergedTable.values.set(key, value);
787
+ }
788
+ }
789
+ }
790
+ return merged;
791
+ }
792
+ function serializeSimpleTomlDocument(document) {
793
+ const lines = ["# 会话级 Codex 配置(系统自动生成)"];
794
+ if (document.rootOrder.length > 0) {
795
+ lines.push("");
796
+ for (const key of document.rootOrder) {
797
+ const value = document.rootValues.get(key);
798
+ if (!value) {
799
+ continue;
800
+ }
801
+ lines.push(`${key} = ${value}`);
802
+ }
803
+ }
804
+ for (const tableName of document.tableOrder) {
805
+ const table = document.tables.get(tableName);
806
+ if (!table || table.order.length === 0) {
807
+ continue;
808
+ }
809
+ lines.push("");
810
+ lines.push(`[${tableName}]`);
811
+ for (const key of table.order) {
812
+ const value = table.values.get(key);
813
+ if (!value) {
814
+ continue;
815
+ }
816
+ lines.push(`${key} = ${value}`);
817
+ }
818
+ }
819
+ return lines.join("\n");
820
+ }
821
+ //# sourceMappingURL=session-provider-config-service.js.map