claude-coder 1.8.2 → 1.8.3

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 (47) hide show
  1. package/README.md +167 -167
  2. package/bin/cli.js +172 -172
  3. package/package.json +52 -52
  4. package/src/commands/auth.js +290 -240
  5. package/src/commands/setup-modules/helpers.js +99 -99
  6. package/src/commands/setup-modules/index.js +25 -25
  7. package/src/commands/setup-modules/mcp.js +94 -94
  8. package/src/commands/setup-modules/provider.js +260 -260
  9. package/src/commands/setup-modules/safety.js +61 -61
  10. package/src/commands/setup-modules/simplify.js +52 -52
  11. package/src/commands/setup.js +172 -172
  12. package/src/common/assets.js +236 -236
  13. package/src/common/config.js +125 -125
  14. package/src/common/constants.js +55 -55
  15. package/src/common/indicator.js +222 -222
  16. package/src/common/interaction.js +170 -170
  17. package/src/common/logging.js +77 -77
  18. package/src/common/sdk.js +50 -50
  19. package/src/common/tasks.js +88 -88
  20. package/src/common/utils.js +161 -161
  21. package/src/core/coding.js +55 -55
  22. package/src/core/context.js +117 -117
  23. package/src/core/go.js +310 -310
  24. package/src/core/harness.js +484 -484
  25. package/src/core/hooks.js +533 -533
  26. package/src/core/init.js +171 -171
  27. package/src/core/plan.js +325 -325
  28. package/src/core/prompts.js +227 -227
  29. package/src/core/query.js +49 -49
  30. package/src/core/repair.js +46 -46
  31. package/src/core/runner.js +195 -195
  32. package/src/core/scan.js +89 -89
  33. package/src/core/session.js +56 -56
  34. package/src/core/simplify.js +53 -52
  35. package/templates/bash-process.md +12 -12
  36. package/templates/codingSystem.md +65 -65
  37. package/templates/codingUser.md +17 -17
  38. package/templates/coreProtocol.md +29 -29
  39. package/templates/goSystem.md +130 -130
  40. package/templates/guidance.json +52 -52
  41. package/templates/planSystem.md +78 -78
  42. package/templates/planUser.md +8 -8
  43. package/templates/playwright.md +16 -16
  44. package/templates/requirements.example.md +57 -57
  45. package/templates/scanSystem.md +120 -120
  46. package/templates/scanUser.md +10 -10
  47. package/templates/test_rule.md +194 -194
@@ -1,261 +1,261 @@
1
- 'use strict';
2
-
3
- const { ask, askChoice, askApiKey } = require('./helpers');
4
- const { log, COLOR, updateEnvVar } = require('../../common/config');
5
-
6
- // ── 提供商菜单 ──
7
-
8
- const PROVIDER_MENU = `
9
- 请选择模型提供商:
10
-
11
- 1) 默认 Claude 官方模型,使用系统登录态
12
- 2) Coding Plan 自建 API,使用推荐的多模型路由配置
13
- 3) API DeepSeek 或其他 Anthropic 兼容 API
14
- `;
15
-
16
- // ── 提供商配置函数 ──
17
-
18
- async function configureDefault() {
19
- return {
20
- lines: [
21
- '# Claude Coder 模型配置',
22
- '# 提供商: Claude 官方',
23
- '',
24
- 'MODEL_PROVIDER=claude',
25
- 'API_TIMEOUT_MS=3000000',
26
- ],
27
- summary: 'Claude 官方模型',
28
- };
29
- }
30
-
31
- async function configureCodingPlan(rl, existing) {
32
- // 1. 选择或输入 BASE_URL
33
- console.log('请选择或输入 BASE_URL:');
34
- console.log('');
35
- console.log(' 1) 智谱 GLM https://open.bigmodel.cn/api/anthropic');
36
- console.log(' 2) Z.AI https://api.z.ai/api/anthropic');
37
- console.log(' 3) 阿里云百炼 https://coding.dashscope.aliyuncs.com/apps/anthropic');
38
- console.log(' 4) 其他(手动输入)');
39
- console.log('');
40
-
41
- const urlChoice = await askChoice(rl, '选择 [1-4,默认 1]: ', 1, 4, 1);
42
- let finalUrl = '';
43
-
44
- if (urlChoice === 4) {
45
- const defaultUrl = existing.ANTHROPIC_BASE_URL || '';
46
- console.log('');
47
- let baseUrl = await ask(rl, ` BASE_URL${defaultUrl ? ` (回车保留: ${defaultUrl})` : ''}: `);
48
- finalUrl = baseUrl.trim() || defaultUrl;
49
- } else {
50
- const urlMap = {
51
- 1: 'https://open.bigmodel.cn/api/anthropic',
52
- 2: 'https://api.z.ai/api/anthropic',
53
- 3: 'https://coding.dashscope.aliyuncs.com/apps/anthropic',
54
- };
55
- finalUrl = urlMap[urlChoice];
56
- }
57
-
58
- if (!finalUrl) {
59
- console.error('BASE_URL 不能为空');
60
- process.exit(1);
61
- }
62
-
63
- // 2. 输入 API_KEY(提示获取地址)
64
- const apiUrlMap = {
65
- 'open.bigmodel.cn': 'https://open.bigmodel.cn/usercenter/proj-mgmt/apikeys',
66
- 'api.z.ai': 'https://z.ai/manage-apikey/apikey-list',
67
- 'dashscope.aliyuncs.com': 'https://bailian.console.aliyun.com/?tab=model#/api-key',
68
- };
69
-
70
- let apiUrlHint = '';
71
- for (const [host, url] of Object.entries(apiUrlMap)) {
72
- if (finalUrl.includes(host)) {
73
- apiUrlHint = url;
74
- break;
75
- }
76
- }
77
-
78
- console.log('');
79
- if (apiUrlHint) {
80
- console.log(` ${COLOR.blue}API Key 获取地址: ${apiUrlHint}${COLOR.reset}`);
81
- }
82
- const apiKey = await askApiKey(rl, 'Coding Plan', '', existing.ANTHROPIC_API_KEY);
83
-
84
- // 3. 返回配置(使用「长时间自运行Agent」推荐配置)
85
- return {
86
- lines: [
87
- '# Claude Coder 模型配置',
88
- '# 提供商: Coding Plan',
89
- '',
90
- 'MODEL_PROVIDER=coding-plan',
91
- `ANTHROPIC_BASE_URL=${finalUrl}`,
92
- `ANTHROPIC_API_KEY=${apiKey}`,
93
- '',
94
- 'CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1',
95
- '',
96
- '# 模型路由配置(可在 .claude-coder/.env 修改)',
97
- 'ANTHROPIC_DEFAULT_OPUS_MODEL=glm-5',
98
- 'ANTHROPIC_DEFAULT_SONNET_MODEL=qwen3-coder-next',
99
- 'ANTHROPIC_DEFAULT_HAIKU_MODEL=qwen3-coder-plus',
100
- 'ANTHROPIC_MODEL=kimi-k2.5',
101
- '',
102
- 'API_TIMEOUT_MS=3000000',
103
- ],
104
- summary: `Coding Plan (${finalUrl})`,
105
- };
106
- }
107
-
108
- async function configureAPI(rl, existing) {
109
- console.log('请选择 API 模式:');
110
- console.log('');
111
- console.log(' 1) DeepSeek Chat (V3) - 速度快成本低 [推荐]');
112
- console.log(' 2) DeepSeek Reasoner (R1) - 全链路推理');
113
- console.log(' 3) DeepSeek Hybrid (R1+V3) - 规划用R1,执行用V3');
114
- console.log(' 4) 自定义 - 输入其他 API');
115
- console.log('');
116
- const choice = await askChoice(rl, '选择 [1-4,默认 1]: ', 1, 4, 1);
117
-
118
- if (choice === 4) {
119
- return await configureCustomAPI(rl, existing);
120
- }
121
-
122
- return await configureDeepSeekMode(rl, existing, choice);
123
- }
124
-
125
- async function configureDeepSeekMode(rl, existing, choice) {
126
- const existingKey = existing.MODEL_PROVIDER === 'deepseek' ? existing.ANTHROPIC_API_KEY : '';
127
- const apiKey = await askApiKey(rl, 'DeepSeek', 'https://platform.deepseek.com/api_keys', existingKey);
128
-
129
- const dsModel = ['deepseek-chat', 'deepseek-reasoner', 'deepseek-hybrid'][choice - 1];
130
-
131
- const lines = [
132
- '# Claude Coder 模型配置',
133
- `# 提供商: DeepSeek`,
134
- `# 模型: ${dsModel} | API_TIMEOUT_MS=600000 防止长输出超时(10分钟)`,
135
- '',
136
- 'MODEL_PROVIDER=deepseek',
137
- 'ANTHROPIC_BASE_URL=https://api.deepseek.com/anthropic',
138
- `ANTHROPIC_API_KEY=${apiKey}`,
139
- `ANTHROPIC_AUTH_TOKEN=${apiKey}`,
140
- '',
141
- 'CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1',
142
- ];
143
-
144
- if (dsModel === 'deepseek-chat') {
145
- lines.push(
146
- '# [DeepSeek Chat 降本策略]',
147
- 'ANTHROPIC_MODEL=deepseek-chat',
148
- 'ANTHROPIC_DEFAULT_OPUS_MODEL=deepseek-chat',
149
- 'ANTHROPIC_DEFAULT_SONNET_MODEL=deepseek-chat',
150
- 'ANTHROPIC_DEFAULT_HAIKU_MODEL=deepseek-chat'
151
- );
152
- } else if (dsModel === 'deepseek-reasoner') {
153
- lines.push(
154
- '# [DeepSeek Pure Reasoner 模式]',
155
- 'ANTHROPIC_MODEL=deepseek-reasoner',
156
- 'ANTHROPIC_DEFAULT_OPUS_MODEL=deepseek-reasoner',
157
- 'ANTHROPIC_DEFAULT_SONNET_MODEL=deepseek-reasoner',
158
- 'ANTHROPIC_DEFAULT_HAIKU_MODEL=deepseek-reasoner'
159
- );
160
- } else {
161
- lines.push(
162
- '# [DeepSeek Hybrid 混合模式]',
163
- 'ANTHROPIC_MODEL=deepseek-reasoner',
164
- 'ANTHROPIC_DEFAULT_OPUS_MODEL=deepseek-reasoner',
165
- 'ANTHROPIC_DEFAULT_SONNET_MODEL=deepseek-chat',
166
- 'ANTHROPIC_DEFAULT_HAIKU_MODEL=deepseek-chat'
167
- );
168
- }
169
-
170
- lines.push('API_TIMEOUT_MS=600000');
171
-
172
- return { lines, summary: `DeepSeek (${dsModel})` };
173
- }
174
-
175
- async function configureCustomAPI(rl, existing) {
176
- const defaultUrl = existing.MODEL_PROVIDER === 'custom' ? existing.ANTHROPIC_BASE_URL || '' : '';
177
- console.log('请输入 Anthropic 兼容的 BASE_URL:');
178
- let baseUrl = await ask(rl, ` URL${defaultUrl ? ` (回车保留: ${defaultUrl})` : ''}: `);
179
- baseUrl = baseUrl.trim() || defaultUrl;
180
-
181
- if (!baseUrl) {
182
- console.error('BASE_URL 不能为空');
183
- process.exit(1);
184
- }
185
-
186
- const existingKey = existing.MODEL_PROVIDER === 'custom' ? existing.ANTHROPIC_API_KEY : '';
187
- const apiKey = await askApiKey(rl, '自定义 API', '', existingKey);
188
-
189
- console.log('');
190
- const modelInput = await ask(rl, '默认模型名称(回车跳过): ');
191
- const model = modelInput.trim();
192
-
193
- const lines = [
194
- '# Claude Coder 模型配置',
195
- '# 提供商: 自定义 API',
196
- '',
197
- 'MODEL_PROVIDER=custom',
198
- `ANTHROPIC_BASE_URL=${baseUrl}`,
199
- `ANTHROPIC_API_KEY=${apiKey}`,
200
- '',
201
- 'CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1',
202
- ];
203
-
204
- if (model) {
205
- lines.push(`ANTHROPIC_MODEL=${model}`);
206
- }
207
-
208
- lines.push('API_TIMEOUT_MS=3000000');
209
-
210
- return { lines, summary: `自定义 API (${baseUrl})` };
211
- }
212
-
213
- // ── 提供商选择 ──
214
-
215
- const PROVIDER_CONFIG = [configureDefault, configureCodingPlan, configureAPI];
216
-
217
- async function selectProvider(rl, existing, showHeader = true) {
218
- if (showHeader) console.log(PROVIDER_MENU);
219
- const choice = await askChoice(rl, '选择 [1-3]: ', 1, 3);
220
- console.log('');
221
- return PROVIDER_CONFIG[choice - 1](rl, existing);
222
- }
223
-
224
- // ── 更新 API Key ──
225
-
226
- async function updateApiKeyOnly(rl, existing) {
227
- const provider = existing.MODEL_PROVIDER;
228
- if (!provider || provider === 'claude') {
229
- log('warn', 'Claude 官方无需配置 API Key(使用系统登录态)');
230
- return;
231
- }
232
-
233
- const apiUrlMap = {
234
- 'coding-plan': '',
235
- 'deepseek': 'https://platform.deepseek.com/api_keys',
236
- 'custom': '',
237
- };
238
-
239
- const apiKey = await askApiKey(rl, provider, apiUrlMap[provider] || '', existing.ANTHROPIC_API_KEY);
240
- if (apiKey === null) {
241
- log('info', '已取消,返回菜单');
242
- return;
243
- }
244
- updateEnvVar('ANTHROPIC_API_KEY', apiKey);
245
- if (provider === 'deepseek') {
246
- updateEnvVar('ANTHROPIC_AUTH_TOKEN', apiKey);
247
- }
248
- log('ok', 'API Key 已更新');
249
- }
250
-
251
- module.exports = {
252
- PROVIDER_MENU,
253
- PROVIDER_CONFIG,
254
- configureDefault,
255
- configureCodingPlan,
256
- configureAPI,
257
- configureDeepSeekMode,
258
- configureCustomAPI,
259
- selectProvider,
260
- updateApiKeyOnly,
1
+ 'use strict';
2
+
3
+ const { ask, askChoice, askApiKey } = require('./helpers');
4
+ const { log, COLOR, updateEnvVar } = require('../../common/config');
5
+
6
+ // ── 提供商菜单 ──
7
+
8
+ const PROVIDER_MENU = `
9
+ 请选择模型提供商:
10
+
11
+ 1) 默认 Claude 官方模型,使用系统登录态
12
+ 2) Coding Plan 自建 API,使用推荐的多模型路由配置
13
+ 3) API DeepSeek 或其他 Anthropic 兼容 API
14
+ `;
15
+
16
+ // ── 提供商配置函数 ──
17
+
18
+ async function configureDefault() {
19
+ return {
20
+ lines: [
21
+ '# Claude Coder 模型配置',
22
+ '# 提供商: Claude 官方',
23
+ '',
24
+ 'MODEL_PROVIDER=claude',
25
+ 'API_TIMEOUT_MS=3000000',
26
+ ],
27
+ summary: 'Claude 官方模型',
28
+ };
29
+ }
30
+
31
+ async function configureCodingPlan(rl, existing) {
32
+ // 1. 选择或输入 BASE_URL
33
+ console.log('请选择或输入 BASE_URL:');
34
+ console.log('');
35
+ console.log(' 1) 智谱 GLM https://open.bigmodel.cn/api/anthropic');
36
+ console.log(' 2) Z.AI https://api.z.ai/api/anthropic');
37
+ console.log(' 3) 阿里云百炼 https://coding.dashscope.aliyuncs.com/apps/anthropic');
38
+ console.log(' 4) 其他(手动输入)');
39
+ console.log('');
40
+
41
+ const urlChoice = await askChoice(rl, '选择 [1-4,默认 1]: ', 1, 4, 1);
42
+ let finalUrl = '';
43
+
44
+ if (urlChoice === 4) {
45
+ const defaultUrl = existing.ANTHROPIC_BASE_URL || '';
46
+ console.log('');
47
+ let baseUrl = await ask(rl, ` BASE_URL${defaultUrl ? ` (回车保留: ${defaultUrl})` : ''}: `);
48
+ finalUrl = baseUrl.trim() || defaultUrl;
49
+ } else {
50
+ const urlMap = {
51
+ 1: 'https://open.bigmodel.cn/api/anthropic',
52
+ 2: 'https://api.z.ai/api/anthropic',
53
+ 3: 'https://coding.dashscope.aliyuncs.com/apps/anthropic',
54
+ };
55
+ finalUrl = urlMap[urlChoice];
56
+ }
57
+
58
+ if (!finalUrl) {
59
+ console.error('BASE_URL 不能为空');
60
+ process.exit(1);
61
+ }
62
+
63
+ // 2. 输入 API_KEY(提示获取地址)
64
+ const apiUrlMap = {
65
+ 'open.bigmodel.cn': 'https://open.bigmodel.cn/usercenter/proj-mgmt/apikeys',
66
+ 'api.z.ai': 'https://z.ai/manage-apikey/apikey-list',
67
+ 'dashscope.aliyuncs.com': 'https://bailian.console.aliyun.com/?tab=model#/api-key',
68
+ };
69
+
70
+ let apiUrlHint = '';
71
+ for (const [host, url] of Object.entries(apiUrlMap)) {
72
+ if (finalUrl.includes(host)) {
73
+ apiUrlHint = url;
74
+ break;
75
+ }
76
+ }
77
+
78
+ console.log('');
79
+ if (apiUrlHint) {
80
+ console.log(` ${COLOR.blue}API Key 获取地址: ${apiUrlHint}${COLOR.reset}`);
81
+ }
82
+ const apiKey = await askApiKey(rl, 'Coding Plan', '', existing.ANTHROPIC_API_KEY);
83
+
84
+ // 3. 返回配置(使用「长时间自运行Agent」推荐配置)
85
+ return {
86
+ lines: [
87
+ '# Claude Coder 模型配置',
88
+ '# 提供商: Coding Plan',
89
+ '',
90
+ 'MODEL_PROVIDER=coding-plan',
91
+ `ANTHROPIC_BASE_URL=${finalUrl}`,
92
+ `ANTHROPIC_API_KEY=${apiKey}`,
93
+ '',
94
+ 'CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1',
95
+ '',
96
+ '# 模型路由配置(可在 .claude-coder/.env 修改)',
97
+ 'ANTHROPIC_DEFAULT_OPUS_MODEL=glm-5',
98
+ 'ANTHROPIC_DEFAULT_SONNET_MODEL=qwen3-coder-next',
99
+ 'ANTHROPIC_DEFAULT_HAIKU_MODEL=qwen3-coder-plus',
100
+ 'ANTHROPIC_MODEL=kimi-k2.5',
101
+ '',
102
+ 'API_TIMEOUT_MS=3000000',
103
+ ],
104
+ summary: `Coding Plan (${finalUrl})`,
105
+ };
106
+ }
107
+
108
+ async function configureAPI(rl, existing) {
109
+ console.log('请选择 API 模式:');
110
+ console.log('');
111
+ console.log(' 1) DeepSeek Chat (V3) - 速度快成本低 [推荐]');
112
+ console.log(' 2) DeepSeek Reasoner (R1) - 全链路推理');
113
+ console.log(' 3) DeepSeek Hybrid (R1+V3) - 规划用R1,执行用V3');
114
+ console.log(' 4) 自定义 - 输入其他 API');
115
+ console.log('');
116
+ const choice = await askChoice(rl, '选择 [1-4,默认 1]: ', 1, 4, 1);
117
+
118
+ if (choice === 4) {
119
+ return await configureCustomAPI(rl, existing);
120
+ }
121
+
122
+ return await configureDeepSeekMode(rl, existing, choice);
123
+ }
124
+
125
+ async function configureDeepSeekMode(rl, existing, choice) {
126
+ const existingKey = existing.MODEL_PROVIDER === 'deepseek' ? existing.ANTHROPIC_API_KEY : '';
127
+ const apiKey = await askApiKey(rl, 'DeepSeek', 'https://platform.deepseek.com/api_keys', existingKey);
128
+
129
+ const dsModel = ['deepseek-chat', 'deepseek-reasoner', 'deepseek-hybrid'][choice - 1];
130
+
131
+ const lines = [
132
+ '# Claude Coder 模型配置',
133
+ `# 提供商: DeepSeek`,
134
+ `# 模型: ${dsModel} | API_TIMEOUT_MS=600000 防止长输出超时(10分钟)`,
135
+ '',
136
+ 'MODEL_PROVIDER=deepseek',
137
+ 'ANTHROPIC_BASE_URL=https://api.deepseek.com/anthropic',
138
+ `ANTHROPIC_API_KEY=${apiKey}`,
139
+ `ANTHROPIC_AUTH_TOKEN=${apiKey}`,
140
+ '',
141
+ 'CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1',
142
+ ];
143
+
144
+ if (dsModel === 'deepseek-chat') {
145
+ lines.push(
146
+ '# [DeepSeek Chat 降本策略]',
147
+ 'ANTHROPIC_MODEL=deepseek-chat',
148
+ 'ANTHROPIC_DEFAULT_OPUS_MODEL=deepseek-chat',
149
+ 'ANTHROPIC_DEFAULT_SONNET_MODEL=deepseek-chat',
150
+ 'ANTHROPIC_DEFAULT_HAIKU_MODEL=deepseek-chat'
151
+ );
152
+ } else if (dsModel === 'deepseek-reasoner') {
153
+ lines.push(
154
+ '# [DeepSeek Pure Reasoner 模式]',
155
+ 'ANTHROPIC_MODEL=deepseek-reasoner',
156
+ 'ANTHROPIC_DEFAULT_OPUS_MODEL=deepseek-reasoner',
157
+ 'ANTHROPIC_DEFAULT_SONNET_MODEL=deepseek-reasoner',
158
+ 'ANTHROPIC_DEFAULT_HAIKU_MODEL=deepseek-reasoner'
159
+ );
160
+ } else {
161
+ lines.push(
162
+ '# [DeepSeek Hybrid 混合模式]',
163
+ 'ANTHROPIC_MODEL=deepseek-reasoner',
164
+ 'ANTHROPIC_DEFAULT_OPUS_MODEL=deepseek-reasoner',
165
+ 'ANTHROPIC_DEFAULT_SONNET_MODEL=deepseek-chat',
166
+ 'ANTHROPIC_DEFAULT_HAIKU_MODEL=deepseek-chat'
167
+ );
168
+ }
169
+
170
+ lines.push('API_TIMEOUT_MS=600000');
171
+
172
+ return { lines, summary: `DeepSeek (${dsModel})` };
173
+ }
174
+
175
+ async function configureCustomAPI(rl, existing) {
176
+ const defaultUrl = existing.MODEL_PROVIDER === 'custom' ? existing.ANTHROPIC_BASE_URL || '' : '';
177
+ console.log('请输入 Anthropic 兼容的 BASE_URL:');
178
+ let baseUrl = await ask(rl, ` URL${defaultUrl ? ` (回车保留: ${defaultUrl})` : ''}: `);
179
+ baseUrl = baseUrl.trim() || defaultUrl;
180
+
181
+ if (!baseUrl) {
182
+ console.error('BASE_URL 不能为空');
183
+ process.exit(1);
184
+ }
185
+
186
+ const existingKey = existing.MODEL_PROVIDER === 'custom' ? existing.ANTHROPIC_API_KEY : '';
187
+ const apiKey = await askApiKey(rl, '自定义 API', '', existingKey);
188
+
189
+ console.log('');
190
+ const modelInput = await ask(rl, '默认模型名称(回车跳过): ');
191
+ const model = modelInput.trim();
192
+
193
+ const lines = [
194
+ '# Claude Coder 模型配置',
195
+ '# 提供商: 自定义 API',
196
+ '',
197
+ 'MODEL_PROVIDER=custom',
198
+ `ANTHROPIC_BASE_URL=${baseUrl}`,
199
+ `ANTHROPIC_API_KEY=${apiKey}`,
200
+ '',
201
+ 'CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1',
202
+ ];
203
+
204
+ if (model) {
205
+ lines.push(`ANTHROPIC_MODEL=${model}`);
206
+ }
207
+
208
+ lines.push('API_TIMEOUT_MS=3000000');
209
+
210
+ return { lines, summary: `自定义 API (${baseUrl})` };
211
+ }
212
+
213
+ // ── 提供商选择 ──
214
+
215
+ const PROVIDER_CONFIG = [configureDefault, configureCodingPlan, configureAPI];
216
+
217
+ async function selectProvider(rl, existing, showHeader = true) {
218
+ if (showHeader) console.log(PROVIDER_MENU);
219
+ const choice = await askChoice(rl, '选择 [1-3]: ', 1, 3);
220
+ console.log('');
221
+ return PROVIDER_CONFIG[choice - 1](rl, existing);
222
+ }
223
+
224
+ // ── 更新 API Key ──
225
+
226
+ async function updateApiKeyOnly(rl, existing) {
227
+ const provider = existing.MODEL_PROVIDER;
228
+ if (!provider || provider === 'claude') {
229
+ log('warn', 'Claude 官方无需配置 API Key(使用系统登录态)');
230
+ return;
231
+ }
232
+
233
+ const apiUrlMap = {
234
+ 'coding-plan': '',
235
+ 'deepseek': 'https://platform.deepseek.com/api_keys',
236
+ 'custom': '',
237
+ };
238
+
239
+ const apiKey = await askApiKey(rl, provider, apiUrlMap[provider] || '', existing.ANTHROPIC_API_KEY);
240
+ if (apiKey === null) {
241
+ log('info', '已取消,返回菜单');
242
+ return;
243
+ }
244
+ updateEnvVar('ANTHROPIC_API_KEY', apiKey);
245
+ if (provider === 'deepseek') {
246
+ updateEnvVar('ANTHROPIC_AUTH_TOKEN', apiKey);
247
+ }
248
+ log('ok', 'API Key 已更新');
249
+ }
250
+
251
+ module.exports = {
252
+ PROVIDER_MENU,
253
+ PROVIDER_CONFIG,
254
+ configureDefault,
255
+ configureCodingPlan,
256
+ configureAPI,
257
+ configureDeepSeekMode,
258
+ configureCustomAPI,
259
+ selectProvider,
260
+ updateApiKeyOnly,
261
261
  };
@@ -1,62 +1,62 @@
1
- 'use strict';
2
-
3
- const { ask } = require('./helpers');
4
- const { log, COLOR, updateEnvVar } = require('../../common/config');
5
-
6
- // ── 安全限制配置 ──
7
-
8
- async function updateSafetyLimits(rl, existing) {
9
- const currentStall = existing.SESSION_STALL_TIMEOUT || '600';
10
- const currentCompletion = existing.SESSION_COMPLETION_TIMEOUT || '300';
11
- const currentTurns = existing.SESSION_MAX_TURNS || '0';
12
-
13
- console.log(`${COLOR.blue}当前安全限制:${COLOR.reset}`);
14
- console.log(` 停顿超时: ${currentStall} 秒 (${Math.floor(parseInt(currentStall) / 60)} 分钟)`);
15
- console.log(` 完成检测超时: ${currentCompletion} 秒 (${Math.ceil(parseInt(currentCompletion) / 60)} 分钟)`);
16
- console.log(` 最大工具轮次: ${currentTurns === '0' ? '无限制' : currentTurns}`);
17
- console.log('');
18
- console.log(`${COLOR.yellow}说明:${COLOR.reset}`);
19
- console.log(' 完成检测 — 模型写入 session_result.json 后缩短等待,解决"完成但不退出"');
20
- console.log(' 停顿超时 — 长时间无工具调用时自动中断(通用兜底)');
21
- console.log(' 最大轮次 — 限制总轮次,仅 CI 推荐(默认 0 = 无限制)');
22
- console.log('');
23
-
24
- const stallInput = await ask(rl, `停顿超时秒数(回车保留 ${currentStall}): `);
25
- if (stallInput.trim()) {
26
- const seconds = parseInt(stallInput.trim(), 10);
27
- if (isNaN(seconds) || seconds < 60) {
28
- log('warn', '停顿超时需 >= 60 秒,跳过');
29
- } else {
30
- updateEnvVar('SESSION_STALL_TIMEOUT', String(seconds));
31
- log('ok', `停顿超时已设置为 ${seconds} 秒 (${Math.floor(seconds / 60)} 分钟)`);
32
- }
33
- }
34
-
35
- console.log('');
36
- const compInput = await ask(rl, `完成检测超时秒数(回车保留 ${currentCompletion}): `);
37
- if (compInput.trim()) {
38
- const seconds = parseInt(compInput.trim(), 10);
39
- if (isNaN(seconds) || seconds < 30) {
40
- log('warn', '完成检测超时需 >= 30 秒,跳过');
41
- } else {
42
- updateEnvVar('SESSION_COMPLETION_TIMEOUT', String(seconds));
43
- log('ok', `完成检测超时已设置为 ${seconds} 秒`);
44
- }
45
- }
46
-
47
- console.log('');
48
- const turnsInput = await ask(rl, `最大工具轮次(回车保留 ${currentTurns === '0' ? '无限制' : currentTurns},输入 0 = 无限制): `);
49
- if (turnsInput.trim()) {
50
- const turns = parseInt(turnsInput.trim(), 10);
51
- if (isNaN(turns) || turns < 0) {
52
- log('warn', '请输入 >= 0 的整数,跳过');
53
- } else {
54
- updateEnvVar('SESSION_MAX_TURNS', String(turns));
55
- log('ok', `最大工具轮次已设置为 ${turns === 0 ? '无限制' : turns}`);
56
- }
57
- }
58
- }
59
-
60
- module.exports = {
61
- updateSafetyLimits,
1
+ 'use strict';
2
+
3
+ const { ask } = require('./helpers');
4
+ const { log, COLOR, updateEnvVar } = require('../../common/config');
5
+
6
+ // ── 安全限制配置 ──
7
+
8
+ async function updateSafetyLimits(rl, existing) {
9
+ const currentStall = existing.SESSION_STALL_TIMEOUT || '600';
10
+ const currentCompletion = existing.SESSION_COMPLETION_TIMEOUT || '300';
11
+ const currentTurns = existing.SESSION_MAX_TURNS || '0';
12
+
13
+ console.log(`${COLOR.blue}当前安全限制:${COLOR.reset}`);
14
+ console.log(` 停顿超时: ${currentStall} 秒 (${Math.floor(parseInt(currentStall) / 60)} 分钟)`);
15
+ console.log(` 完成检测超时: ${currentCompletion} 秒 (${Math.ceil(parseInt(currentCompletion) / 60)} 分钟)`);
16
+ console.log(` 最大工具轮次: ${currentTurns === '0' ? '无限制' : currentTurns}`);
17
+ console.log('');
18
+ console.log(`${COLOR.yellow}说明:${COLOR.reset}`);
19
+ console.log(' 完成检测 — 模型写入 session_result.json 后缩短等待,解决"完成但不退出"');
20
+ console.log(' 停顿超时 — 长时间无工具调用时自动中断(通用兜底)');
21
+ console.log(' 最大轮次 — 限制总轮次,仅 CI 推荐(默认 0 = 无限制)');
22
+ console.log('');
23
+
24
+ const stallInput = await ask(rl, `停顿超时秒数(回车保留 ${currentStall}): `);
25
+ if (stallInput.trim()) {
26
+ const seconds = parseInt(stallInput.trim(), 10);
27
+ if (isNaN(seconds) || seconds < 60) {
28
+ log('warn', '停顿超时需 >= 60 秒,跳过');
29
+ } else {
30
+ updateEnvVar('SESSION_STALL_TIMEOUT', String(seconds));
31
+ log('ok', `停顿超时已设置为 ${seconds} 秒 (${Math.floor(seconds / 60)} 分钟)`);
32
+ }
33
+ }
34
+
35
+ console.log('');
36
+ const compInput = await ask(rl, `完成检测超时秒数(回车保留 ${currentCompletion}): `);
37
+ if (compInput.trim()) {
38
+ const seconds = parseInt(compInput.trim(), 10);
39
+ if (isNaN(seconds) || seconds < 30) {
40
+ log('warn', '完成检测超时需 >= 30 秒,跳过');
41
+ } else {
42
+ updateEnvVar('SESSION_COMPLETION_TIMEOUT', String(seconds));
43
+ log('ok', `完成检测超时已设置为 ${seconds} 秒`);
44
+ }
45
+ }
46
+
47
+ console.log('');
48
+ const turnsInput = await ask(rl, `最大工具轮次(回车保留 ${currentTurns === '0' ? '无限制' : currentTurns},输入 0 = 无限制): `);
49
+ if (turnsInput.trim()) {
50
+ const turns = parseInt(turnsInput.trim(), 10);
51
+ if (isNaN(turns) || turns < 0) {
52
+ log('warn', '请输入 >= 0 的整数,跳过');
53
+ } else {
54
+ updateEnvVar('SESSION_MAX_TURNS', String(turns));
55
+ log('ok', `最大工具轮次已设置为 ${turns === 0 ? '无限制' : turns}`);
56
+ }
57
+ }
58
+ }
59
+
60
+ module.exports = {
61
+ updateSafetyLimits,
62
62
  };