ccem 2.0.0-beta → 2.0.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +916 -314
- package/package.json +14 -14
package/dist/index.js
CHANGED
|
@@ -7,28 +7,435 @@ import inquirer from "inquirer";
|
|
|
7
7
|
import chalk7 from "chalk";
|
|
8
8
|
import Table3 from "cli-table3";
|
|
9
9
|
import { spawn as spawn3 } from "child_process";
|
|
10
|
-
import * as
|
|
11
|
-
import * as
|
|
12
|
-
import { fileURLToPath } from "url";
|
|
13
|
-
|
|
10
|
+
import * as fs8 from "fs";
|
|
11
|
+
import * as path6 from "path";
|
|
12
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
13
|
+
|
|
14
|
+
// ../../packages/core/dist/chunk-6STWB3RD.js
|
|
15
|
+
var TIER_MODEL_ALIASES = /* @__PURE__ */ new Set(["opus", "sonnet", "haiku"]);
|
|
16
|
+
function normalizeEnvConfig(envConfig, defaultRuntimeModel = "opus") {
|
|
17
|
+
const hasTierDefaults = Boolean(envConfig.ANTHROPIC_DEFAULT_OPUS_MODEL) || Boolean(envConfig.ANTHROPIC_DEFAULT_SONNET_MODEL) || Boolean(envConfig.ANTHROPIC_DEFAULT_HAIKU_MODEL);
|
|
18
|
+
const defaultOpusModel = envConfig.ANTHROPIC_DEFAULT_OPUS_MODEL ?? (hasTierDefaults ? void 0 : envConfig.ANTHROPIC_MODEL);
|
|
19
|
+
const defaultSonnetModel = envConfig.ANTHROPIC_DEFAULT_SONNET_MODEL ?? defaultOpusModel ?? (hasTierDefaults ? void 0 : envConfig.ANTHROPIC_MODEL);
|
|
20
|
+
const defaultHaikuModel = envConfig.ANTHROPIC_DEFAULT_HAIKU_MODEL ?? envConfig.ANTHROPIC_SMALL_FAST_MODEL;
|
|
21
|
+
return {
|
|
22
|
+
...envConfig.ANTHROPIC_BASE_URL && {
|
|
23
|
+
ANTHROPIC_BASE_URL: envConfig.ANTHROPIC_BASE_URL
|
|
24
|
+
},
|
|
25
|
+
...(envConfig.ANTHROPIC_AUTH_TOKEN ?? envConfig.ANTHROPIC_API_KEY) && {
|
|
26
|
+
ANTHROPIC_AUTH_TOKEN: envConfig.ANTHROPIC_AUTH_TOKEN ?? envConfig.ANTHROPIC_API_KEY
|
|
27
|
+
},
|
|
28
|
+
...defaultOpusModel && {
|
|
29
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: defaultOpusModel
|
|
30
|
+
},
|
|
31
|
+
...defaultSonnetModel && {
|
|
32
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: defaultSonnetModel
|
|
33
|
+
},
|
|
34
|
+
...defaultHaikuModel && {
|
|
35
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: defaultHaikuModel
|
|
36
|
+
},
|
|
37
|
+
ANTHROPIC_MODEL: hasTierDefaults ? envConfig.ANTHROPIC_MODEL ?? defaultRuntimeModel : defaultRuntimeModel,
|
|
38
|
+
...envConfig.CLAUDE_CODE_SUBAGENT_MODEL && {
|
|
39
|
+
CLAUDE_CODE_SUBAGENT_MODEL: envConfig.CLAUDE_CODE_SUBAGENT_MODEL
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function shouldRecoverTierModel(model) {
|
|
44
|
+
return !model || TIER_MODEL_ALIASES.has(model);
|
|
45
|
+
}
|
|
46
|
+
function recoverEnvConfigFromLegacy(currentEnvConfig, legacyEnvConfig) {
|
|
47
|
+
const current = normalizeEnvConfig(currentEnvConfig);
|
|
48
|
+
const legacy = normalizeEnvConfig(legacyEnvConfig);
|
|
49
|
+
return {
|
|
50
|
+
...current,
|
|
51
|
+
...!current.ANTHROPIC_AUTH_TOKEN && legacy.ANTHROPIC_AUTH_TOKEN && {
|
|
52
|
+
ANTHROPIC_AUTH_TOKEN: legacy.ANTHROPIC_AUTH_TOKEN
|
|
53
|
+
},
|
|
54
|
+
...shouldRecoverTierModel(current.ANTHROPIC_DEFAULT_OPUS_MODEL) && legacy.ANTHROPIC_DEFAULT_OPUS_MODEL && {
|
|
55
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: legacy.ANTHROPIC_DEFAULT_OPUS_MODEL
|
|
56
|
+
},
|
|
57
|
+
...shouldRecoverTierModel(current.ANTHROPIC_DEFAULT_SONNET_MODEL) && legacy.ANTHROPIC_DEFAULT_SONNET_MODEL && {
|
|
58
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: legacy.ANTHROPIC_DEFAULT_SONNET_MODEL
|
|
59
|
+
},
|
|
60
|
+
...shouldRecoverTierModel(current.ANTHROPIC_DEFAULT_HAIKU_MODEL) && legacy.ANTHROPIC_DEFAULT_HAIKU_MODEL && {
|
|
61
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: legacy.ANTHROPIC_DEFAULT_HAIKU_MODEL
|
|
62
|
+
},
|
|
63
|
+
...!current.CLAUDE_CODE_SUBAGENT_MODEL && legacy.CLAUDE_CODE_SUBAGENT_MODEL && {
|
|
64
|
+
CLAUDE_CODE_SUBAGENT_MODEL: legacy.CLAUDE_CODE_SUBAGENT_MODEL
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
var ENV_PRESETS = {
|
|
69
|
+
"GLM": {
|
|
70
|
+
ANTHROPIC_BASE_URL: "https://open.bigmodel.cn/api/anthropic",
|
|
71
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: "glm-5",
|
|
72
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: "glm-5",
|
|
73
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: "glm-4.5-air",
|
|
74
|
+
ANTHROPIC_MODEL: "opus"
|
|
75
|
+
},
|
|
76
|
+
"KIMI": {
|
|
77
|
+
ANTHROPIC_BASE_URL: "https://api.moonshot.cn/anthropic",
|
|
78
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: "kimi-k2.5",
|
|
79
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: "kimi-k2.5",
|
|
80
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: "kimi-k2.5",
|
|
81
|
+
ANTHROPIC_MODEL: "opus"
|
|
82
|
+
},
|
|
83
|
+
"KimiCodePlan": {
|
|
84
|
+
ANTHROPIC_BASE_URL: "https://api.kimi.com/coding/",
|
|
85
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: "kimi-for-coding",
|
|
86
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: "kimi-for-coding",
|
|
87
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: "kimi-for-coding",
|
|
88
|
+
ANTHROPIC_MODEL: "opus"
|
|
89
|
+
},
|
|
90
|
+
"MiniMax": {
|
|
91
|
+
ANTHROPIC_BASE_URL: "https://api.minimaxi.com/anthropic",
|
|
92
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: "MiniMax-M2.7",
|
|
93
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: "MiniMax-M2.7",
|
|
94
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: "MiniMax-M2.7-highspeed",
|
|
95
|
+
ANTHROPIC_MODEL: "opus"
|
|
96
|
+
},
|
|
97
|
+
"DeepSeek": {
|
|
98
|
+
ANTHROPIC_BASE_URL: "https://api.deepseek.com/anthropic",
|
|
99
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: "deepseek-chat",
|
|
100
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: "deepseek-chat",
|
|
101
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: "deepseek-chat",
|
|
102
|
+
ANTHROPIC_MODEL: "opus"
|
|
103
|
+
},
|
|
104
|
+
"Bailian": {
|
|
105
|
+
ANTHROPIC_BASE_URL: "https://dashscope.aliyuncs.com/api/v2/apps/claude-code-proxy",
|
|
106
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: "qwen3-coder-plus",
|
|
107
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: "qwen3-coder-plus",
|
|
108
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: "qwen3-coder-flash",
|
|
109
|
+
ANTHROPIC_MODEL: "opus"
|
|
110
|
+
},
|
|
111
|
+
"BailianCodePlan": {
|
|
112
|
+
ANTHROPIC_BASE_URL: "https://coding.dashscope.aliyuncs.com/api/v2/apps/claude-code-proxy",
|
|
113
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: "qwen3-coder-plus",
|
|
114
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: "qwen3-coder-plus",
|
|
115
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: "qwen3-coder-plus",
|
|
116
|
+
ANTHROPIC_MODEL: "opus"
|
|
117
|
+
},
|
|
118
|
+
"OpenRouter": {
|
|
119
|
+
ANTHROPIC_BASE_URL: "https://openrouter.ai/api/v1",
|
|
120
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: "anthropic/claude-opus-4-1",
|
|
121
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: "anthropic/claude-opus-4-1",
|
|
122
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: "anthropic/claude-3.5-haiku",
|
|
123
|
+
ANTHROPIC_MODEL: "opus"
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
var PERMISSION_PRESETS = {
|
|
127
|
+
"yolo": {
|
|
128
|
+
name: "YOLO \u6A21\u5F0F",
|
|
129
|
+
description: "\u5168\u90E8\u653E\u5F00\uFF0C\u65E0\u4EFB\u4F55\u9650\u5236",
|
|
130
|
+
permissionMode: "bypassPermissions",
|
|
131
|
+
permissions: {
|
|
132
|
+
allow: [
|
|
133
|
+
"Bash(*)",
|
|
134
|
+
"Read(*)",
|
|
135
|
+
"Edit(*)",
|
|
136
|
+
"Write(*)",
|
|
137
|
+
"WebFetch(*)",
|
|
138
|
+
"WebSearch(*)",
|
|
139
|
+
"Glob(*)",
|
|
140
|
+
"Grep(*)",
|
|
141
|
+
"LSP(*)",
|
|
142
|
+
"NotebookEdit(*)"
|
|
143
|
+
],
|
|
144
|
+
deny: []
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
"dev": {
|
|
148
|
+
name: "\u5F00\u53D1\u6A21\u5F0F",
|
|
149
|
+
description: "\u65E5\u5E38\u5F00\u53D1\u6743\u9650\uFF0C\u4FDD\u62A4\u654F\u611F\u6587\u4EF6",
|
|
150
|
+
permissionMode: "acceptEdits",
|
|
151
|
+
permissions: {
|
|
152
|
+
allow: [
|
|
153
|
+
"Read(*)",
|
|
154
|
+
"Edit(*)",
|
|
155
|
+
"Write(*)",
|
|
156
|
+
"Glob(*)",
|
|
157
|
+
"Grep(*)",
|
|
158
|
+
"LSP(*)",
|
|
159
|
+
"NotebookEdit(*)",
|
|
160
|
+
"Bash(npm:*)",
|
|
161
|
+
"Bash(pnpm:*)",
|
|
162
|
+
"Bash(yarn:*)",
|
|
163
|
+
"Bash(bun:*)",
|
|
164
|
+
"Bash(node:*)",
|
|
165
|
+
"Bash(npx:*)",
|
|
166
|
+
"Bash(git:*)",
|
|
167
|
+
"Bash(tsc:*)",
|
|
168
|
+
"Bash(tsx:*)",
|
|
169
|
+
"Bash(eslint:*)",
|
|
170
|
+
"Bash(prettier:*)",
|
|
171
|
+
"Bash(jest:*)",
|
|
172
|
+
"Bash(vitest:*)",
|
|
173
|
+
"Bash(cargo:*)",
|
|
174
|
+
"Bash(python:*)",
|
|
175
|
+
"Bash(pip:*)",
|
|
176
|
+
"Bash(go:*)",
|
|
177
|
+
"Bash(make:*)",
|
|
178
|
+
"Bash(cmake:*)",
|
|
179
|
+
"Bash(ls:*)",
|
|
180
|
+
"Bash(cat:*)",
|
|
181
|
+
"Bash(head:*)",
|
|
182
|
+
"Bash(tail:*)",
|
|
183
|
+
"Bash(find:*)",
|
|
184
|
+
"Bash(wc:*)",
|
|
185
|
+
"Bash(mkdir:*)",
|
|
186
|
+
"Bash(cp:*)",
|
|
187
|
+
"Bash(mv:*)",
|
|
188
|
+
"Bash(touch:*)",
|
|
189
|
+
"WebSearch"
|
|
190
|
+
],
|
|
191
|
+
deny: [
|
|
192
|
+
"Read(.env)",
|
|
193
|
+
"Read(.env.*)",
|
|
194
|
+
"Read(**/secrets/**)",
|
|
195
|
+
"Read(**/*.pem)",
|
|
196
|
+
"Read(**/*.key)",
|
|
197
|
+
"Read(**/*credential*)",
|
|
198
|
+
"Bash(rm -rf:*)",
|
|
199
|
+
"Bash(sudo:*)",
|
|
200
|
+
"Bash(chmod:*)",
|
|
201
|
+
"Bash(chown:*)"
|
|
202
|
+
]
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
"readonly": {
|
|
206
|
+
name: "\u53EA\u8BFB\u6A21\u5F0F",
|
|
207
|
+
description: "\u4EC5\u5141\u8BB8\u8BFB\u53D6\u548C\u641C\u7D22\uFF0C\u7981\u6B62\u4EFB\u4F55\u4FEE\u6539",
|
|
208
|
+
permissionMode: "plan",
|
|
209
|
+
permissions: {
|
|
210
|
+
allow: [
|
|
211
|
+
"Read(*)",
|
|
212
|
+
"Glob(*)",
|
|
213
|
+
"Grep(*)",
|
|
214
|
+
"LSP(*)",
|
|
215
|
+
"Bash(git status:*)",
|
|
216
|
+
"Bash(git log:*)",
|
|
217
|
+
"Bash(git diff:*)",
|
|
218
|
+
"Bash(git branch:*)",
|
|
219
|
+
"Bash(git show:*)",
|
|
220
|
+
"Bash(ls:*)",
|
|
221
|
+
"Bash(cat:*)",
|
|
222
|
+
"Bash(head:*)",
|
|
223
|
+
"Bash(tail:*)",
|
|
224
|
+
"Bash(find:*)",
|
|
225
|
+
"Bash(wc:*)",
|
|
226
|
+
"Bash(file:*)",
|
|
227
|
+
"WebSearch"
|
|
228
|
+
],
|
|
229
|
+
deny: [
|
|
230
|
+
"Edit(*)",
|
|
231
|
+
"Write(*)",
|
|
232
|
+
"NotebookEdit(*)",
|
|
233
|
+
"Bash(rm:*)",
|
|
234
|
+
"Bash(mv:*)",
|
|
235
|
+
"Bash(cp:*)",
|
|
236
|
+
"Bash(mkdir:*)",
|
|
237
|
+
"Bash(touch:*)",
|
|
238
|
+
"Bash(git add:*)",
|
|
239
|
+
"Bash(git commit:*)",
|
|
240
|
+
"Bash(git push:*)",
|
|
241
|
+
"Bash(git checkout:*)",
|
|
242
|
+
"Bash(git reset:*)",
|
|
243
|
+
"Bash(npm install:*)",
|
|
244
|
+
"Bash(pnpm install:*)",
|
|
245
|
+
"Bash(yarn add:*)"
|
|
246
|
+
]
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
"safe": {
|
|
250
|
+
name: "\u5B89\u5168\u6A21\u5F0F",
|
|
251
|
+
description: "\u4FDD\u5B88\u6743\u9650\uFF0C\u9002\u5408\u4E0D\u719F\u6089\u7684\u4EE3\u7801\u5E93",
|
|
252
|
+
permissionMode: "default",
|
|
253
|
+
permissions: {
|
|
254
|
+
allow: [
|
|
255
|
+
"Read(*)",
|
|
256
|
+
"Glob(*)",
|
|
257
|
+
"Grep(*)",
|
|
258
|
+
"LSP(*)",
|
|
259
|
+
"Bash(git status:*)",
|
|
260
|
+
"Bash(git log:*)",
|
|
261
|
+
"Bash(git diff:*)",
|
|
262
|
+
"Bash(ls:*)",
|
|
263
|
+
"Bash(cat:*)",
|
|
264
|
+
"Bash(head:*)",
|
|
265
|
+
"Bash(tail:*)",
|
|
266
|
+
"Bash(find:*)",
|
|
267
|
+
"Bash(wc:*)"
|
|
268
|
+
],
|
|
269
|
+
deny: [
|
|
270
|
+
"Read(.env)",
|
|
271
|
+
"Read(.env.*)",
|
|
272
|
+
"Read(**/secrets/**)",
|
|
273
|
+
"Read(**/*.pem)",
|
|
274
|
+
"Read(**/*.key)",
|
|
275
|
+
"Read(**/*credential*)",
|
|
276
|
+
"Read(**/*password*)",
|
|
277
|
+
"Edit(*)",
|
|
278
|
+
"Write(*)",
|
|
279
|
+
"NotebookEdit(*)",
|
|
280
|
+
"Bash(curl:*)",
|
|
281
|
+
"Bash(wget:*)",
|
|
282
|
+
"Bash(ssh:*)",
|
|
283
|
+
"Bash(scp:*)",
|
|
284
|
+
"Bash(rm:*)",
|
|
285
|
+
"Bash(mv:*)",
|
|
286
|
+
"WebFetch(*)"
|
|
287
|
+
]
|
|
288
|
+
}
|
|
289
|
+
},
|
|
290
|
+
"ci": {
|
|
291
|
+
name: "CI/CD \u6A21\u5F0F",
|
|
292
|
+
description: "\u9002\u5408\u81EA\u52A8\u5316\u6D41\u6C34\u7EBF\u7684\u6743\u9650",
|
|
293
|
+
permissionMode: "default",
|
|
294
|
+
permissions: {
|
|
295
|
+
allow: [
|
|
296
|
+
"Read(*)",
|
|
297
|
+
"Edit(*)",
|
|
298
|
+
"Write(*)",
|
|
299
|
+
"Glob(*)",
|
|
300
|
+
"Grep(*)",
|
|
301
|
+
"LSP(*)",
|
|
302
|
+
"Bash(npm:*)",
|
|
303
|
+
"Bash(pnpm:*)",
|
|
304
|
+
"Bash(yarn:*)",
|
|
305
|
+
"Bash(node:*)",
|
|
306
|
+
"Bash(git:*)",
|
|
307
|
+
"Bash(docker:*)",
|
|
308
|
+
"Bash(make:*)",
|
|
309
|
+
"Bash(cargo:*)",
|
|
310
|
+
"Bash(go:*)",
|
|
311
|
+
"Bash(python:*)",
|
|
312
|
+
"Bash(pip:*)",
|
|
313
|
+
"Bash(pytest:*)",
|
|
314
|
+
"Bash(jest:*)",
|
|
315
|
+
"Bash(vitest:*)"
|
|
316
|
+
],
|
|
317
|
+
deny: [
|
|
318
|
+
"Read(.env.local)",
|
|
319
|
+
"Read(**/secrets/**)",
|
|
320
|
+
"Bash(sudo:*)",
|
|
321
|
+
"Bash(ssh:*)",
|
|
322
|
+
"Bash(scp:*)",
|
|
323
|
+
"WebFetch(*)",
|
|
324
|
+
"WebSearch"
|
|
325
|
+
]
|
|
326
|
+
}
|
|
327
|
+
},
|
|
328
|
+
"audit": {
|
|
329
|
+
name: "\u5BA1\u8BA1\u6A21\u5F0F",
|
|
330
|
+
description: "\u4EC5\u8BFB\u53D6\u548C\u641C\u7D22\uFF0C\u7528\u4E8E\u5B89\u5168\u5BA1\u8BA1",
|
|
331
|
+
permissionMode: "plan",
|
|
332
|
+
permissions: {
|
|
333
|
+
allow: [
|
|
334
|
+
"Read(*)",
|
|
335
|
+
"Glob(*)",
|
|
336
|
+
"Grep(*)",
|
|
337
|
+
"LSP(*)",
|
|
338
|
+
"Bash(git log:*)",
|
|
339
|
+
"Bash(git blame:*)",
|
|
340
|
+
"Bash(git show:*)",
|
|
341
|
+
"Bash(git diff:*)",
|
|
342
|
+
"Bash(ls:*)",
|
|
343
|
+
"Bash(find:*)",
|
|
344
|
+
"Bash(wc:*)",
|
|
345
|
+
"Bash(file:*)",
|
|
346
|
+
"Bash(stat:*)"
|
|
347
|
+
],
|
|
348
|
+
deny: [
|
|
349
|
+
"Edit(*)",
|
|
350
|
+
"Write(*)",
|
|
351
|
+
"NotebookEdit(*)",
|
|
352
|
+
"Bash(rm:*)",
|
|
353
|
+
"Bash(mv:*)",
|
|
354
|
+
"Bash(cp:*)",
|
|
355
|
+
"Bash(curl:*)",
|
|
356
|
+
"Bash(wget:*)",
|
|
357
|
+
"Bash(ssh:*)",
|
|
358
|
+
"WebFetch(*)"
|
|
359
|
+
]
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
var getPermissionModeNames = () => {
|
|
364
|
+
return Object.keys(PERMISSION_PRESETS);
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
// ../../packages/core/dist/index.js
|
|
368
|
+
import crypto from "crypto";
|
|
369
|
+
import fs from "fs";
|
|
370
|
+
import path from "path";
|
|
371
|
+
var ALGORITHM = "aes-256-cbc";
|
|
372
|
+
var SECRET_KEY = crypto.scryptSync("claude-code-env-manager-secret", "salt", 32);
|
|
373
|
+
var encrypt = (text) => {
|
|
374
|
+
if (!text) return text;
|
|
375
|
+
const iv = crypto.randomBytes(16);
|
|
376
|
+
const cipher = crypto.createCipheriv(ALGORITHM, SECRET_KEY, iv);
|
|
377
|
+
let encrypted = cipher.update(text, "utf8", "hex");
|
|
378
|
+
encrypted += cipher.final("hex");
|
|
379
|
+
return `enc:${iv.toString("hex")}:${encrypted}`;
|
|
380
|
+
};
|
|
381
|
+
var decrypt = (text) => {
|
|
382
|
+
if (!text || !text.startsWith("enc:")) return text;
|
|
383
|
+
try {
|
|
384
|
+
const parts = text.split(":");
|
|
385
|
+
if (parts.length !== 3) return text;
|
|
386
|
+
const iv = Buffer.from(parts[1], "hex");
|
|
387
|
+
const encryptedText = parts[2];
|
|
388
|
+
const decipher = crypto.createDecipheriv(ALGORITHM, SECRET_KEY, iv);
|
|
389
|
+
let decrypted = decipher.update(encryptedText, "hex", "utf8");
|
|
390
|
+
decrypted += decipher.final("utf8");
|
|
391
|
+
return decrypted;
|
|
392
|
+
} catch {
|
|
393
|
+
return text;
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
var getHomeDir = () => {
|
|
397
|
+
return process.env.HOME || process.env.USERPROFILE || "";
|
|
398
|
+
};
|
|
399
|
+
var getCcemConfigDir = () => {
|
|
400
|
+
return path.join(getHomeDir(), ".ccem");
|
|
401
|
+
};
|
|
402
|
+
var getCcemConfigPath = () => {
|
|
403
|
+
return path.join(getCcemConfigDir(), "config.json");
|
|
404
|
+
};
|
|
405
|
+
var ensureCcemDir = () => {
|
|
406
|
+
const ccemDir = getCcemConfigDir();
|
|
407
|
+
if (!fs.existsSync(ccemDir)) {
|
|
408
|
+
fs.mkdirSync(ccemDir, { recursive: true });
|
|
409
|
+
}
|
|
410
|
+
return ccemDir;
|
|
411
|
+
};
|
|
412
|
+
var getLegacyConfigPath = () => {
|
|
413
|
+
const home = getHomeDir();
|
|
414
|
+
if (process.platform === "darwin") {
|
|
415
|
+
return path.join(home, "Library", "Preferences", "claude-code-env-manager-nodejs", "config.json");
|
|
416
|
+
}
|
|
417
|
+
return path.join(home, ".config", "claude-code-env-manager-nodejs", "config.json");
|
|
418
|
+
};
|
|
14
419
|
|
|
15
420
|
// src/ui.ts
|
|
16
421
|
import chalk from "chalk";
|
|
17
422
|
import Table from "cli-table3";
|
|
18
|
-
import { PERMISSION_PRESETS } from "@ccem/core";
|
|
19
423
|
|
|
20
424
|
// src/usage.ts
|
|
21
|
-
import * as
|
|
425
|
+
import * as fs2 from "fs";
|
|
22
426
|
import * as fsPromises from "fs/promises";
|
|
23
|
-
import * as
|
|
427
|
+
import * as path2 from "path";
|
|
24
428
|
import * as os from "os";
|
|
25
429
|
import * as readline from "readline";
|
|
26
|
-
|
|
27
|
-
var
|
|
430
|
+
import { fileURLToPath } from "url";
|
|
431
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
432
|
+
var __dirname = path2.dirname(__filename);
|
|
433
|
+
var CLAUDE_PROJECTS_DIR = path2.join(os.homedir(), ".claude", "projects");
|
|
434
|
+
var CCEM_DIR = path2.join(os.homedir(), ".ccem");
|
|
28
435
|
var CACHE_VERSION = 1;
|
|
29
|
-
var getCachePath = () =>
|
|
30
|
-
var getPricesPath = () =>
|
|
31
|
-
async function
|
|
436
|
+
var getCachePath = () => path2.join(CCEM_DIR, "usage-cache.json");
|
|
437
|
+
var getPricesPath = () => path2.join(CCEM_DIR, "model-prices.json");
|
|
438
|
+
async function ensureCcemDir2() {
|
|
32
439
|
try {
|
|
33
440
|
await fsPromises.access(CCEM_DIR);
|
|
34
441
|
} catch {
|
|
@@ -37,7 +444,7 @@ async function ensureCcemDir() {
|
|
|
37
444
|
}
|
|
38
445
|
var LITELLM_PRICES_URL = "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json";
|
|
39
446
|
var getBundledPricesPath = () => {
|
|
40
|
-
return
|
|
447
|
+
return path2.join(__dirname, "..", "model-prices.json");
|
|
41
448
|
};
|
|
42
449
|
var DEFAULT_PRICES = {
|
|
43
450
|
"claude-opus-4-5": {
|
|
@@ -66,7 +473,7 @@ function normalizeModelName(model) {
|
|
|
66
473
|
var pricesCache = null;
|
|
67
474
|
async function loadPrices() {
|
|
68
475
|
if (pricesCache) return pricesCache;
|
|
69
|
-
await
|
|
476
|
+
await ensureCcemDir2();
|
|
70
477
|
const pricesPath = getPricesPath();
|
|
71
478
|
try {
|
|
72
479
|
const response = await fetch(LITELLM_PRICES_URL, { signal: AbortSignal.timeout(1e3) });
|
|
@@ -156,8 +563,8 @@ async function getFileMetaAsync(filePath) {
|
|
|
156
563
|
function loadCacheSync() {
|
|
157
564
|
try {
|
|
158
565
|
const cachePath = getCachePath();
|
|
159
|
-
if (!
|
|
160
|
-
const data = JSON.parse(
|
|
566
|
+
if (!fs2.existsSync(cachePath)) return null;
|
|
567
|
+
const data = JSON.parse(fs2.readFileSync(cachePath, "utf-8"));
|
|
161
568
|
if (data.version !== CACHE_VERSION) return null;
|
|
162
569
|
return data;
|
|
163
570
|
} catch {
|
|
@@ -216,7 +623,7 @@ function getUsageStatsFromCache() {
|
|
|
216
623
|
}
|
|
217
624
|
async function saveCacheAsync(cache) {
|
|
218
625
|
try {
|
|
219
|
-
await
|
|
626
|
+
await ensureCcemDir2();
|
|
220
627
|
await fsPromises.writeFile(getCachePath(), JSON.stringify(cache, null, 2));
|
|
221
628
|
} catch {
|
|
222
629
|
}
|
|
@@ -224,7 +631,7 @@ async function saveCacheAsync(cache) {
|
|
|
224
631
|
async function parseJSONLFileAsync(filePath, prices, signal) {
|
|
225
632
|
const entries = [];
|
|
226
633
|
try {
|
|
227
|
-
const fileStream =
|
|
634
|
+
const fileStream = fs2.createReadStream(filePath, { encoding: "utf-8" });
|
|
228
635
|
const rl = readline.createInterface({
|
|
229
636
|
input: fileStream,
|
|
230
637
|
crlfDelay: Infinity
|
|
@@ -278,14 +685,14 @@ async function getAllJSONLFilesAsync() {
|
|
|
278
685
|
if (!projectsDirExists) return files;
|
|
279
686
|
const projects = await fsPromises.readdir(CLAUDE_PROJECTS_DIR);
|
|
280
687
|
for (const project of projects) {
|
|
281
|
-
const projectPath =
|
|
688
|
+
const projectPath = path2.join(CLAUDE_PROJECTS_DIR, project);
|
|
282
689
|
try {
|
|
283
690
|
const stat2 = await fsPromises.stat(projectPath);
|
|
284
691
|
if (stat2.isDirectory()) {
|
|
285
692
|
const projectFiles = await fsPromises.readdir(projectPath);
|
|
286
693
|
for (const file of projectFiles) {
|
|
287
694
|
if (file.endsWith(".jsonl")) {
|
|
288
|
-
files.push(
|
|
695
|
+
files.push(path2.join(projectPath, file));
|
|
289
696
|
}
|
|
290
697
|
}
|
|
291
698
|
}
|
|
@@ -496,9 +903,10 @@ var renderLogoWithEnvPanel = (envName, env, defaultMode) => {
|
|
|
496
903
|
const titleShort = theme.primary("CCEM");
|
|
497
904
|
const envLabel = theme.muted("Env: ") + theme.primary(envName);
|
|
498
905
|
const baseUrl = env.ANTHROPIC_BASE_URL || "-";
|
|
499
|
-
const
|
|
500
|
-
const
|
|
501
|
-
const
|
|
906
|
+
const runtimeModel = env.ANTHROPIC_MODEL || "-";
|
|
907
|
+
const opusModel = env.ANTHROPIC_DEFAULT_OPUS_MODEL || "-";
|
|
908
|
+
const haikuModel = env.ANTHROPIC_DEFAULT_HAIKU_MODEL || "-";
|
|
909
|
+
const authToken = env.ANTHROPIC_AUTH_TOKEN ? env.ANTHROPIC_AUTH_TOKEN.slice(0, 2) + "\u2022\u2022\u2022\u2022" + env.ANTHROPIC_AUTH_TOKEN.slice(-4) : "-";
|
|
502
910
|
const truncate = (s, max) => s.length > max ? s.slice(0, max - 3) + "..." : s;
|
|
503
911
|
const maskUrl = (url, max) => {
|
|
504
912
|
if (url.length <= max) return url;
|
|
@@ -506,10 +914,10 @@ var renderLogoWithEnvPanel = (envName, env, defaultMode) => {
|
|
|
506
914
|
const parsed = new URL(url);
|
|
507
915
|
const protocol = parsed.protocol + "//";
|
|
508
916
|
const host = parsed.host;
|
|
509
|
-
const
|
|
917
|
+
const path7 = parsed.pathname + parsed.search;
|
|
510
918
|
const hostStart = host.slice(0, 8);
|
|
511
919
|
const hostEnd = host.slice(-4);
|
|
512
|
-
const pathPart =
|
|
920
|
+
const pathPart = path7.length > 10 ? path7.slice(0, 7) + "..." : path7;
|
|
513
921
|
return `${protocol}${hostStart}...${hostEnd}${pathPart}`;
|
|
514
922
|
} catch {
|
|
515
923
|
return truncate(url, max);
|
|
@@ -520,15 +928,15 @@ var renderLogoWithEnvPanel = (envName, env, defaultMode) => {
|
|
|
520
928
|
if (isNarrow) {
|
|
521
929
|
envLines = [
|
|
522
930
|
envLabel,
|
|
523
|
-
theme.muted("
|
|
524
|
-
theme.muted("
|
|
931
|
+
theme.muted("Opus:".padEnd(labelWidth)) + theme.dim(truncate(opusModel, 25)),
|
|
932
|
+
theme.muted("Token:".padEnd(labelWidth)) + theme.dim(authToken)
|
|
525
933
|
];
|
|
526
934
|
} else {
|
|
527
935
|
envLines = [
|
|
528
936
|
envLabel + (defaultMode && PERMISSION_PRESETS[defaultMode] ? " " + theme.accent(`[${PERMISSION_PRESETS[defaultMode].name}]`) : ""),
|
|
529
937
|
theme.muted("URL:".padEnd(labelWidth)) + theme.dim(maskUrl(baseUrl, 40)),
|
|
530
|
-
theme.muted("
|
|
531
|
-
theme.muted("
|
|
938
|
+
theme.muted("Run:".padEnd(labelWidth)) + theme.dim(truncate(runtimeModel, 12)) + " " + theme.muted("Opus:".padEnd(labelWidth)) + theme.dim(truncate(opusModel, 15)),
|
|
939
|
+
theme.muted("Haiku:".padEnd(labelWidth)) + theme.dim(truncate(haikuModel, 15)) + " " + theme.muted("Token:".padEnd(labelWidth)) + theme.dim(authToken)
|
|
532
940
|
];
|
|
533
941
|
}
|
|
534
942
|
const lines = [];
|
|
@@ -947,74 +1355,86 @@ var selectEnvWithKeys = (registries, current) => {
|
|
|
947
1355
|
};
|
|
948
1356
|
|
|
949
1357
|
// src/permissions.ts
|
|
950
|
-
import
|
|
1358
|
+
import fs5 from "fs";
|
|
951
1359
|
import chalk3 from "chalk";
|
|
952
1360
|
import Table2 from "cli-table3";
|
|
953
|
-
import { PERMISSION_PRESETS as PERMISSION_PRESETS3, getPermissionModeNames } from "@ccem/core";
|
|
954
1361
|
|
|
955
1362
|
// src/utils.ts
|
|
956
|
-
import
|
|
957
|
-
import
|
|
958
|
-
import
|
|
959
|
-
var
|
|
1363
|
+
import crypto2 from "crypto";
|
|
1364
|
+
import fs3 from "fs";
|
|
1365
|
+
import path3 from "path";
|
|
1366
|
+
var SECRET_KEY2 = crypto2.scryptSync("claude-code-env-manager-secret", "salt", 32);
|
|
960
1367
|
var findProjectRoot = () => {
|
|
961
1368
|
let currentDir = process.cwd();
|
|
962
|
-
const root =
|
|
1369
|
+
const root = path3.parse(currentDir).root;
|
|
963
1370
|
while (currentDir !== root) {
|
|
964
|
-
if (
|
|
1371
|
+
if (fs3.existsSync(path3.join(currentDir, ".git")) || fs3.existsSync(path3.join(currentDir, "package.json"))) {
|
|
965
1372
|
return currentDir;
|
|
966
1373
|
}
|
|
967
|
-
currentDir =
|
|
1374
|
+
currentDir = path3.dirname(currentDir);
|
|
968
1375
|
}
|
|
969
1376
|
return process.cwd();
|
|
970
1377
|
};
|
|
971
1378
|
var getSettingsPath = (useLocal = true) => {
|
|
972
1379
|
const projectRoot = findProjectRoot();
|
|
973
|
-
const claudeDir =
|
|
1380
|
+
const claudeDir = path3.join(projectRoot, ".claude");
|
|
974
1381
|
const filename = useLocal ? "settings.local.json" : "settings.json";
|
|
975
|
-
return
|
|
1382
|
+
return path3.join(claudeDir, filename);
|
|
976
1383
|
};
|
|
977
1384
|
var ensureClaudeDir = () => {
|
|
978
1385
|
const projectRoot = findProjectRoot();
|
|
979
|
-
const claudeDir =
|
|
980
|
-
if (!
|
|
981
|
-
|
|
1386
|
+
const claudeDir = path3.join(projectRoot, ".claude");
|
|
1387
|
+
if (!fs3.existsSync(claudeDir)) {
|
|
1388
|
+
fs3.mkdirSync(claudeDir, { recursive: true });
|
|
982
1389
|
}
|
|
983
1390
|
return claudeDir;
|
|
984
1391
|
};
|
|
985
|
-
var
|
|
1392
|
+
var getHomeDir2 = () => {
|
|
986
1393
|
return process.env.HOME || process.env.USERPROFILE || "";
|
|
987
1394
|
};
|
|
988
1395
|
var getGlobalClaudeConfigPath = () => {
|
|
989
|
-
return
|
|
1396
|
+
return path3.join(getHomeDir2(), ".claude.json");
|
|
990
1397
|
};
|
|
991
1398
|
var getGlobalClaudeSettingsPath = () => {
|
|
992
|
-
return
|
|
1399
|
+
return path3.join(getHomeDir2(), ".claude", "settings.json");
|
|
993
1400
|
};
|
|
994
1401
|
var ensureGlobalClaudeDir = () => {
|
|
995
|
-
const claudeDir =
|
|
996
|
-
if (!
|
|
997
|
-
|
|
1402
|
+
const claudeDir = path3.join(getHomeDir2(), ".claude");
|
|
1403
|
+
if (!fs3.existsSync(claudeDir)) {
|
|
1404
|
+
fs3.mkdirSync(claudeDir, { recursive: true });
|
|
998
1405
|
}
|
|
999
1406
|
return claudeDir;
|
|
1000
1407
|
};
|
|
1001
1408
|
|
|
1002
1409
|
// src/launcher.ts
|
|
1003
1410
|
import { spawn } from "child_process";
|
|
1004
|
-
import * as
|
|
1005
|
-
import * as
|
|
1411
|
+
import * as fs4 from "fs";
|
|
1412
|
+
import * as path4 from "path";
|
|
1006
1413
|
import chalk2 from "chalk";
|
|
1007
|
-
|
|
1414
|
+
var MANAGED_CLAUDE_ENV_KEYS = [
|
|
1415
|
+
"ANTHROPIC_BASE_URL",
|
|
1416
|
+
"ANTHROPIC_AUTH_TOKEN",
|
|
1417
|
+
"ANTHROPIC_DEFAULT_OPUS_MODEL",
|
|
1418
|
+
"ANTHROPIC_DEFAULT_SONNET_MODEL",
|
|
1419
|
+
"ANTHROPIC_DEFAULT_HAIKU_MODEL",
|
|
1420
|
+
"ANTHROPIC_MODEL",
|
|
1421
|
+
"CLAUDE_CODE_SUBAGENT_MODEL",
|
|
1422
|
+
"ANTHROPIC_API_KEY",
|
|
1423
|
+
"ANTHROPIC_SMALL_FAST_MODEL"
|
|
1424
|
+
];
|
|
1008
1425
|
function buildEnvVars(envConfig) {
|
|
1009
1426
|
const vars = {};
|
|
1010
1427
|
if (envConfig.ANTHROPIC_BASE_URL) vars.ANTHROPIC_BASE_URL = envConfig.ANTHROPIC_BASE_URL;
|
|
1011
|
-
if (envConfig.
|
|
1428
|
+
if (envConfig.ANTHROPIC_AUTH_TOKEN) vars.ANTHROPIC_AUTH_TOKEN = decrypt(envConfig.ANTHROPIC_AUTH_TOKEN);
|
|
1429
|
+
if (envConfig.ANTHROPIC_DEFAULT_OPUS_MODEL) vars.ANTHROPIC_DEFAULT_OPUS_MODEL = envConfig.ANTHROPIC_DEFAULT_OPUS_MODEL;
|
|
1430
|
+
if (envConfig.ANTHROPIC_DEFAULT_SONNET_MODEL) vars.ANTHROPIC_DEFAULT_SONNET_MODEL = envConfig.ANTHROPIC_DEFAULT_SONNET_MODEL;
|
|
1431
|
+
if (envConfig.ANTHROPIC_DEFAULT_HAIKU_MODEL) vars.ANTHROPIC_DEFAULT_HAIKU_MODEL = envConfig.ANTHROPIC_DEFAULT_HAIKU_MODEL;
|
|
1012
1432
|
if (envConfig.ANTHROPIC_MODEL) vars.ANTHROPIC_MODEL = envConfig.ANTHROPIC_MODEL;
|
|
1013
|
-
if (envConfig.
|
|
1433
|
+
if (envConfig.CLAUDE_CODE_SUBAGENT_MODEL) vars.CLAUDE_CODE_SUBAGENT_MODEL = envConfig.CLAUDE_CODE_SUBAGENT_MODEL;
|
|
1014
1434
|
return vars;
|
|
1015
1435
|
}
|
|
1016
1436
|
function buildPermArgs(modeName) {
|
|
1017
|
-
const preset =
|
|
1437
|
+
const preset = PERMISSION_PRESETS[modeName];
|
|
1018
1438
|
if (!preset) return [];
|
|
1019
1439
|
const args = ["--permission-mode", preset.permissionMode];
|
|
1020
1440
|
if (preset.permissions.allow.length > 0) {
|
|
@@ -1028,22 +1448,25 @@ function buildPermArgs(modeName) {
|
|
|
1028
1448
|
return args;
|
|
1029
1449
|
}
|
|
1030
1450
|
function ensureSessionsDir() {
|
|
1031
|
-
const dir =
|
|
1032
|
-
if (!
|
|
1033
|
-
|
|
1451
|
+
const dir = path4.join(ensureCcemDir(), "sessions");
|
|
1452
|
+
if (!fs4.existsSync(dir)) {
|
|
1453
|
+
fs4.mkdirSync(dir, { recursive: true });
|
|
1034
1454
|
}
|
|
1035
1455
|
return dir;
|
|
1036
1456
|
}
|
|
1037
1457
|
async function launchClaude(options) {
|
|
1038
1458
|
const { envConfig, permMode, workingDir, sessionId, resumeSessionId, silent } = options;
|
|
1039
1459
|
const env = { ...process.env };
|
|
1460
|
+
for (const key of MANAGED_CLAUDE_ENV_KEYS) {
|
|
1461
|
+
delete env[key];
|
|
1462
|
+
}
|
|
1040
1463
|
if (envConfig) {
|
|
1041
1464
|
Object.assign(env, buildEnvVars(envConfig));
|
|
1042
1465
|
}
|
|
1043
1466
|
delete env.CLAUDECODE;
|
|
1044
1467
|
const args = [];
|
|
1045
1468
|
if (permMode) {
|
|
1046
|
-
const preset =
|
|
1469
|
+
const preset = PERMISSION_PRESETS[permMode];
|
|
1047
1470
|
if (preset) {
|
|
1048
1471
|
if (!silent) {
|
|
1049
1472
|
console.log(chalk2.green(`\u5DF2\u5E94\u7528 ${preset.name}\uFF08\u4E34\u65F6\uFF09`));
|
|
@@ -1066,14 +1489,15 @@ async function launchClaude(options) {
|
|
|
1066
1489
|
return new Promise((resolve2) => {
|
|
1067
1490
|
const child = spawn("claude", args, {
|
|
1068
1491
|
stdio: "inherit",
|
|
1069
|
-
shell:
|
|
1492
|
+
shell: false,
|
|
1493
|
+
// 直接执行二进制,避免 shell 注入风险
|
|
1070
1494
|
env
|
|
1071
1495
|
});
|
|
1072
1496
|
child.on("exit", (code) => {
|
|
1073
1497
|
if (sessionId) {
|
|
1074
1498
|
try {
|
|
1075
|
-
|
|
1076
|
-
|
|
1499
|
+
fs4.writeFileSync(
|
|
1500
|
+
path4.join(sessionsDir, `${sessionId}.exit`),
|
|
1077
1501
|
String(code ?? 0)
|
|
1078
1502
|
);
|
|
1079
1503
|
} catch {
|
|
@@ -1090,14 +1514,14 @@ async function launchClaude(options) {
|
|
|
1090
1514
|
|
|
1091
1515
|
// src/permissions.ts
|
|
1092
1516
|
var readSettings = (settingsPath) => {
|
|
1093
|
-
if (
|
|
1517
|
+
if (fs5.existsSync(settingsPath)) {
|
|
1094
1518
|
try {
|
|
1095
|
-
const content =
|
|
1519
|
+
const content = fs5.readFileSync(settingsPath, "utf-8");
|
|
1096
1520
|
return JSON.parse(content);
|
|
1097
1521
|
} catch {
|
|
1098
1522
|
console.warn(chalk3.yellow(`\u8B66\u544A: \u65E0\u6CD5\u89E3\u6790 ${settingsPath}\uFF0C\u5C06\u521B\u5EFA\u5907\u4EFD`));
|
|
1099
1523
|
const backupPath = settingsPath + ".error." + Date.now();
|
|
1100
|
-
|
|
1524
|
+
fs5.copyFileSync(settingsPath, backupPath);
|
|
1101
1525
|
console.log(chalk3.gray(`\u5907\u4EFD\u5DF2\u4FDD\u5B58\u5230: ${backupPath}`));
|
|
1102
1526
|
return {};
|
|
1103
1527
|
}
|
|
@@ -1106,7 +1530,7 @@ var readSettings = (settingsPath) => {
|
|
|
1106
1530
|
};
|
|
1107
1531
|
var writeSettings = (settingsPath, config3) => {
|
|
1108
1532
|
ensureClaudeDir();
|
|
1109
|
-
|
|
1533
|
+
fs5.writeFileSync(settingsPath, JSON.stringify(config3, null, 2) + "\n");
|
|
1110
1534
|
};
|
|
1111
1535
|
var mergePermissions = (existing, preset) => {
|
|
1112
1536
|
const existingAllow = existing.permissions?.allow || [];
|
|
@@ -1122,7 +1546,7 @@ var mergePermissions = (existing, preset) => {
|
|
|
1122
1546
|
};
|
|
1123
1547
|
};
|
|
1124
1548
|
var applyPermissionMode = (modeName) => {
|
|
1125
|
-
const preset =
|
|
1549
|
+
const preset = PERMISSION_PRESETS[modeName];
|
|
1126
1550
|
if (!preset) {
|
|
1127
1551
|
console.error(chalk3.red(`\u672A\u77E5\u7684\u6743\u9650\u6A21\u5F0F: ${modeName}`));
|
|
1128
1552
|
console.log(chalk3.yellow("\u53EF\u7528\u6A21\u5F0F: " + getPermissionModeNames().join(", ")));
|
|
@@ -1138,14 +1562,14 @@ var applyPermissionMode = (modeName) => {
|
|
|
1138
1562
|
};
|
|
1139
1563
|
var resetPermissions = () => {
|
|
1140
1564
|
const settingsPath = getSettingsPath(true);
|
|
1141
|
-
if (!
|
|
1565
|
+
if (!fs5.existsSync(settingsPath)) {
|
|
1142
1566
|
console.log(chalk3.yellow("\u6CA1\u6709\u81EA\u5B9A\u4E49\u6743\u9650\u914D\u7F6E\u9700\u8981\u91CD\u7F6E"));
|
|
1143
1567
|
return;
|
|
1144
1568
|
}
|
|
1145
1569
|
const config3 = readSettings(settingsPath);
|
|
1146
1570
|
delete config3.permissions;
|
|
1147
1571
|
if (Object.keys(config3).length === 0) {
|
|
1148
|
-
|
|
1572
|
+
fs5.unlinkSync(settingsPath);
|
|
1149
1573
|
console.log(chalk3.green("\u5DF2\u5220\u9664\u914D\u7F6E\u6587\u4EF6\uFF08\u6587\u4EF6\u4E3A\u7A7A\uFF09"));
|
|
1150
1574
|
} else {
|
|
1151
1575
|
writeSettings(settingsPath, config3);
|
|
@@ -1155,7 +1579,7 @@ var resetPermissions = () => {
|
|
|
1155
1579
|
};
|
|
1156
1580
|
var showCurrentMode = () => {
|
|
1157
1581
|
const settingsPath = getSettingsPath(true);
|
|
1158
|
-
if (!
|
|
1582
|
+
if (!fs5.existsSync(settingsPath)) {
|
|
1159
1583
|
console.log(chalk3.yellow("\u672A\u914D\u7F6E\u81EA\u5B9A\u4E49\u6743\u9650"));
|
|
1160
1584
|
console.log(chalk3.gray(`\u6587\u4EF6\u4E0D\u5B58\u5728: ${settingsPath}`));
|
|
1161
1585
|
return;
|
|
@@ -1165,7 +1589,7 @@ var showCurrentMode = () => {
|
|
|
1165
1589
|
console.log(chalk3.yellow("\u672A\u914D\u7F6E\u81EA\u5B9A\u4E49\u6743\u9650"));
|
|
1166
1590
|
return;
|
|
1167
1591
|
}
|
|
1168
|
-
const matchedPreset = Object.entries(
|
|
1592
|
+
const matchedPreset = Object.entries(PERMISSION_PRESETS).find(([_, preset]) => {
|
|
1169
1593
|
const configAllow = new Set(config3.permissions?.allow || []);
|
|
1170
1594
|
const configDeny = new Set(config3.permissions?.deny || []);
|
|
1171
1595
|
const presetAllow = new Set(preset.permissions.allow);
|
|
@@ -1197,7 +1621,7 @@ var listAvailableModes = () => {
|
|
|
1197
1621
|
style: { head: ["cyan"] },
|
|
1198
1622
|
colWidths: [15, 15, 50]
|
|
1199
1623
|
});
|
|
1200
|
-
Object.entries(
|
|
1624
|
+
Object.entries(PERMISSION_PRESETS).forEach(([key, preset]) => {
|
|
1201
1625
|
table.push([preset.name, `--${key}`, preset.description]);
|
|
1202
1626
|
});
|
|
1203
1627
|
console.log(chalk3.bold("\u53EF\u7528\u6743\u9650\u6A21\u5F0F:\n"));
|
|
@@ -1206,7 +1630,7 @@ var listAvailableModes = () => {
|
|
|
1206
1630
|
console.log(chalk3.gray("\u6C38\u4E45\u6A21\u5F0F: ccem setup perms --<mode>"));
|
|
1207
1631
|
};
|
|
1208
1632
|
var runWithTempPermissions = async (modeName, envConfig) => {
|
|
1209
|
-
const preset =
|
|
1633
|
+
const preset = PERMISSION_PRESETS[modeName];
|
|
1210
1634
|
if (!preset) {
|
|
1211
1635
|
console.error(chalk3.red(`\u672A\u77E5\u7684\u6743\u9650\u6A21\u5F0F: ${modeName}`));
|
|
1212
1636
|
console.log(chalk3.yellow("\u53EF\u7528\u6A21\u5F0F: " + getPermissionModeNames().join(", ")));
|
|
@@ -1216,13 +1640,13 @@ var runWithTempPermissions = async (modeName, envConfig) => {
|
|
|
1216
1640
|
};
|
|
1217
1641
|
|
|
1218
1642
|
// src/setup.ts
|
|
1219
|
-
import
|
|
1643
|
+
import fs6 from "fs";
|
|
1220
1644
|
import chalk4 from "chalk";
|
|
1221
1645
|
import { spawn as spawn2 } from "child_process";
|
|
1222
1646
|
var readJsonFile = (filePath) => {
|
|
1223
|
-
if (
|
|
1647
|
+
if (fs6.existsSync(filePath)) {
|
|
1224
1648
|
try {
|
|
1225
|
-
const content =
|
|
1649
|
+
const content = fs6.readFileSync(filePath, "utf-8");
|
|
1226
1650
|
return JSON.parse(content);
|
|
1227
1651
|
} catch {
|
|
1228
1652
|
console.warn(chalk4.yellow(`\u8B66\u544A: \u65E0\u6CD5\u89E3\u6790 ${filePath}`));
|
|
@@ -1232,7 +1656,7 @@ var readJsonFile = (filePath) => {
|
|
|
1232
1656
|
return {};
|
|
1233
1657
|
};
|
|
1234
1658
|
var writeJsonFile = (filePath, data) => {
|
|
1235
|
-
|
|
1659
|
+
fs6.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
|
|
1236
1660
|
};
|
|
1237
1661
|
var setupOnboarding = () => {
|
|
1238
1662
|
const configPath = getGlobalClaudeConfigPath();
|
|
@@ -1332,14 +1756,19 @@ var setupMcpTool = () => {
|
|
|
1332
1756
|
});
|
|
1333
1757
|
});
|
|
1334
1758
|
};
|
|
1335
|
-
var runSetupInit = async () => {
|
|
1759
|
+
var runSetupInit = async (options = {}) => {
|
|
1336
1760
|
console.log(chalk4.bold("\n\u{1F527} Claude Code \u521D\u59CB\u5316\u8BBE\u7F6E\n"));
|
|
1337
1761
|
console.log(chalk4.cyan("1. \u8BBE\u7F6E onboarding \u72B6\u6001"));
|
|
1338
1762
|
const step1 = setupOnboarding();
|
|
1339
1763
|
console.log(chalk4.cyan("\n2. \u914D\u7F6E\u9690\u79C1\u8BBE\u7F6E"));
|
|
1340
1764
|
const step2 = setupEnvSettings();
|
|
1341
|
-
|
|
1342
|
-
|
|
1765
|
+
let step3 = true;
|
|
1766
|
+
if (options.chrome) {
|
|
1767
|
+
console.log(chalk4.cyan("\n3. \u5B89\u88C5 MCP \u5DE5\u5177"));
|
|
1768
|
+
step3 = await setupMcpTool();
|
|
1769
|
+
} else {
|
|
1770
|
+
console.log(chalk4.gray("\n3. \u5B89\u88C5 MCP \u5DE5\u5177\uFF08\u5DF2\u8DF3\u8FC7\uFF0C\u4F7F\u7528 --chrome \u542F\u7528\uFF09"));
|
|
1771
|
+
}
|
|
1343
1772
|
console.log("");
|
|
1344
1773
|
if (step1 && step2 && step3) {
|
|
1345
1774
|
console.log(chalk4.green.bold("\u2705 \u521D\u59CB\u5316\u5B8C\u6210\uFF01"));
|
|
@@ -1354,8 +1783,8 @@ var runSetupInit = async () => {
|
|
|
1354
1783
|
|
|
1355
1784
|
// src/skills.ts
|
|
1356
1785
|
import { execSync } from "child_process";
|
|
1357
|
-
import * as
|
|
1358
|
-
import * as
|
|
1786
|
+
import * as fs7 from "fs";
|
|
1787
|
+
import * as path5 from "path";
|
|
1359
1788
|
import chalk5 from "chalk";
|
|
1360
1789
|
var SKILL_GROUPS = {
|
|
1361
1790
|
official: { label: "\u5B98\u65B9", icon: "\u{1F3E2}" },
|
|
@@ -1524,12 +1953,12 @@ function parseGitHubUrl(url) {
|
|
|
1524
1953
|
};
|
|
1525
1954
|
}
|
|
1526
1955
|
function getSkillsDir() {
|
|
1527
|
-
return
|
|
1956
|
+
return path5.join(process.cwd(), ".claude", "skills");
|
|
1528
1957
|
}
|
|
1529
1958
|
function ensureSkillsDir() {
|
|
1530
1959
|
const skillsDir = getSkillsDir();
|
|
1531
|
-
if (!
|
|
1532
|
-
|
|
1960
|
+
if (!fs7.existsSync(skillsDir)) {
|
|
1961
|
+
fs7.mkdirSync(skillsDir, { recursive: true });
|
|
1533
1962
|
} else {
|
|
1534
1963
|
cleanupTempDirs(skillsDir);
|
|
1535
1964
|
}
|
|
@@ -1537,11 +1966,11 @@ function ensureSkillsDir() {
|
|
|
1537
1966
|
}
|
|
1538
1967
|
function cleanupTempDirs(skillsDir) {
|
|
1539
1968
|
try {
|
|
1540
|
-
const entries =
|
|
1969
|
+
const entries = fs7.readdirSync(skillsDir, { withFileTypes: true });
|
|
1541
1970
|
for (const entry of entries) {
|
|
1542
1971
|
if (entry.isDirectory() && entry.name.startsWith(".tmp-")) {
|
|
1543
|
-
const tmpPath =
|
|
1544
|
-
|
|
1972
|
+
const tmpPath = path5.join(skillsDir, entry.name);
|
|
1973
|
+
fs7.rmSync(tmpPath, { recursive: true });
|
|
1545
1974
|
}
|
|
1546
1975
|
}
|
|
1547
1976
|
} catch {
|
|
@@ -1549,24 +1978,24 @@ function cleanupTempDirs(skillsDir) {
|
|
|
1549
1978
|
}
|
|
1550
1979
|
function downloadSkillWithGit(owner, repo, branch, repoPath, targetName) {
|
|
1551
1980
|
const skillsDir = ensureSkillsDir();
|
|
1552
|
-
const targetDir =
|
|
1553
|
-
if (
|
|
1981
|
+
const targetDir = path5.join(skillsDir, targetName);
|
|
1982
|
+
if (fs7.existsSync(targetDir)) {
|
|
1554
1983
|
console.log(chalk5.yellow(`Skill "${targetName}" already exists. Updating...`));
|
|
1555
|
-
|
|
1984
|
+
fs7.rmSync(targetDir, { recursive: true });
|
|
1556
1985
|
}
|
|
1557
1986
|
const repoUrl = `https://github.com/${owner}/${repo}.git`;
|
|
1558
|
-
const tempDir =
|
|
1987
|
+
const tempDir = path5.join(skillsDir, `.tmp-${Date.now()}`);
|
|
1559
1988
|
try {
|
|
1560
|
-
|
|
1989
|
+
fs7.mkdirSync(tempDir, { recursive: true });
|
|
1561
1990
|
execSync(`git init`, { cwd: tempDir, stdio: "pipe" });
|
|
1562
1991
|
execSync(`git remote add origin ${repoUrl}`, { cwd: tempDir, stdio: "pipe" });
|
|
1563
1992
|
execSync(`git config core.sparseCheckout true`, { cwd: tempDir, stdio: "pipe" });
|
|
1564
|
-
const sparseFile =
|
|
1565
|
-
|
|
1993
|
+
const sparseFile = path5.join(tempDir, ".git", "info", "sparse-checkout");
|
|
1994
|
+
fs7.writeFileSync(sparseFile, repoPath ? `${repoPath}/
|
|
1566
1995
|
` : "*\n");
|
|
1567
1996
|
execSync(`git pull --depth=1 origin ${branch}`, { cwd: tempDir, stdio: "pipe" });
|
|
1568
|
-
const sourceDir = repoPath ?
|
|
1569
|
-
if (!
|
|
1997
|
+
const sourceDir = repoPath ? path5.join(tempDir, repoPath) : tempDir;
|
|
1998
|
+
if (!fs7.existsSync(sourceDir)) {
|
|
1570
1999
|
throw new Error(`Path "${repoPath}" not found in repository`);
|
|
1571
2000
|
}
|
|
1572
2001
|
copyDir(sourceDir, targetDir);
|
|
@@ -1577,22 +2006,22 @@ function downloadSkillWithGit(owner, repo, branch, repoPath, targetName) {
|
|
|
1577
2006
|
console.error(chalk5.red(`Failed to download skill: ${errMsg}`));
|
|
1578
2007
|
return false;
|
|
1579
2008
|
} finally {
|
|
1580
|
-
if (
|
|
1581
|
-
|
|
2009
|
+
if (fs7.existsSync(tempDir)) {
|
|
2010
|
+
fs7.rmSync(tempDir, { recursive: true });
|
|
1582
2011
|
}
|
|
1583
2012
|
}
|
|
1584
2013
|
}
|
|
1585
2014
|
function copyDir(src, dest) {
|
|
1586
|
-
|
|
1587
|
-
const entries =
|
|
2015
|
+
fs7.mkdirSync(dest, { recursive: true });
|
|
2016
|
+
const entries = fs7.readdirSync(src, { withFileTypes: true });
|
|
1588
2017
|
for (const entry of entries) {
|
|
1589
2018
|
if (entry.name === ".git") continue;
|
|
1590
|
-
const srcPath =
|
|
1591
|
-
const destPath =
|
|
2019
|
+
const srcPath = path5.join(src, entry.name);
|
|
2020
|
+
const destPath = path5.join(dest, entry.name);
|
|
1592
2021
|
if (entry.isDirectory()) {
|
|
1593
2022
|
copyDir(srcPath, destPath);
|
|
1594
2023
|
} else {
|
|
1595
|
-
|
|
2024
|
+
fs7.copyFileSync(srcPath, destPath);
|
|
1596
2025
|
}
|
|
1597
2026
|
}
|
|
1598
2027
|
}
|
|
@@ -1638,7 +2067,7 @@ function addSkillFromGitHub(urlOrPreset) {
|
|
|
1638
2067
|
}
|
|
1639
2068
|
let skillName;
|
|
1640
2069
|
if (parsed.path) {
|
|
1641
|
-
skillName =
|
|
2070
|
+
skillName = path5.basename(parsed.path);
|
|
1642
2071
|
} else {
|
|
1643
2072
|
skillName = parsed.repo;
|
|
1644
2073
|
}
|
|
@@ -1652,23 +2081,23 @@ function addSkillFromGitHub(urlOrPreset) {
|
|
|
1652
2081
|
}
|
|
1653
2082
|
function listInstalledSkills() {
|
|
1654
2083
|
const skillsDir = getSkillsDir();
|
|
1655
|
-
if (!
|
|
2084
|
+
if (!fs7.existsSync(skillsDir)) {
|
|
1656
2085
|
return [];
|
|
1657
2086
|
}
|
|
1658
|
-
const entries =
|
|
2087
|
+
const entries = fs7.readdirSync(skillsDir, { withFileTypes: true });
|
|
1659
2088
|
return entries.filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => ({
|
|
1660
2089
|
name: entry.name,
|
|
1661
|
-
path:
|
|
2090
|
+
path: path5.join(skillsDir, entry.name)
|
|
1662
2091
|
}));
|
|
1663
2092
|
}
|
|
1664
2093
|
function removeSkill(name) {
|
|
1665
2094
|
const skillsDir = getSkillsDir();
|
|
1666
|
-
const targetDir =
|
|
1667
|
-
if (!
|
|
2095
|
+
const targetDir = path5.join(skillsDir, name);
|
|
2096
|
+
if (!fs7.existsSync(targetDir)) {
|
|
1668
2097
|
console.error(chalk5.red(`Skill "${name}" not found`));
|
|
1669
2098
|
return false;
|
|
1670
2099
|
}
|
|
1671
|
-
|
|
2100
|
+
fs7.rmSync(targetDir, { recursive: true });
|
|
1672
2101
|
console.log(chalk5.green(`Removed skill "${name}"`));
|
|
1673
2102
|
return true;
|
|
1674
2103
|
}
|
|
@@ -1826,19 +2255,20 @@ async function runSkillSelector() {
|
|
|
1826
2255
|
}
|
|
1827
2256
|
|
|
1828
2257
|
// src/remote.ts
|
|
1829
|
-
import
|
|
2258
|
+
import crypto3 from "crypto";
|
|
1830
2259
|
import chalk6 from "chalk";
|
|
1831
2260
|
import Conf from "conf";
|
|
1832
|
-
import { encrypt } from "@ccem/core";
|
|
1833
2261
|
var config = new Conf({
|
|
1834
|
-
projectName: "claude-code-env-manager"
|
|
2262
|
+
projectName: "claude-code-env-manager",
|
|
2263
|
+
cwd: getCcemConfigDir()
|
|
2264
|
+
// 使用统一的配置目录
|
|
1835
2265
|
});
|
|
1836
2266
|
var decryptWithSecret = (encryptedBase64, secret) => {
|
|
1837
|
-
const key =
|
|
2267
|
+
const key = crypto3.scryptSync(secret, "ccem-salt", 32);
|
|
1838
2268
|
const combined = Buffer.from(encryptedBase64, "base64");
|
|
1839
2269
|
const iv = combined.subarray(0, 16);
|
|
1840
2270
|
const encryptedHex = combined.subarray(16).toString("hex");
|
|
1841
|
-
const decipher =
|
|
2271
|
+
const decipher = crypto3.createDecipheriv("aes-256-cbc", key, iv);
|
|
1842
2272
|
let decrypted = decipher.update(encryptedHex, "hex", "utf8");
|
|
1843
2273
|
decrypted += decipher.final("utf8");
|
|
1844
2274
|
return decrypted;
|
|
@@ -1859,7 +2289,11 @@ var loadFromRemote = async (url, secret) => {
|
|
|
1859
2289
|
console.log(chalk6.gray("Fetching from remote..."));
|
|
1860
2290
|
let response;
|
|
1861
2291
|
try {
|
|
1862
|
-
response = await fetch(url
|
|
2292
|
+
response = await fetch(url, {
|
|
2293
|
+
headers: {
|
|
2294
|
+
"X-CCEM-Key": secret
|
|
2295
|
+
}
|
|
2296
|
+
});
|
|
1863
2297
|
} catch (err) {
|
|
1864
2298
|
console.error(chalk6.red("Error: Failed to connect to server"));
|
|
1865
2299
|
console.error(chalk6.gray(err.message));
|
|
@@ -1906,9 +2340,10 @@ var loadFromRemote = async (url, secret) => {
|
|
|
1906
2340
|
for (const [name, envConfig] of Object.entries(decrypted.environments)) {
|
|
1907
2341
|
const uniqueName = getUniqueName(name, existingNames);
|
|
1908
2342
|
const renamed = uniqueName !== name;
|
|
1909
|
-
const
|
|
1910
|
-
|
|
1911
|
-
|
|
2343
|
+
const normalizedConfig = normalizeEnvConfig(envConfig);
|
|
2344
|
+
const configToSave = { ...normalizedConfig };
|
|
2345
|
+
if (configToSave.ANTHROPIC_AUTH_TOKEN) {
|
|
2346
|
+
configToSave.ANTHROPIC_AUTH_TOKEN = encrypt(configToSave.ANTHROPIC_AUTH_TOKEN);
|
|
1912
2347
|
}
|
|
1913
2348
|
registries[uniqueName] = configToSave;
|
|
1914
2349
|
existingNames.add(uniqueName);
|
|
@@ -1929,6 +2364,7 @@ Loaded ${results.length} environment(s) from remote:`));
|
|
|
1929
2364
|
}
|
|
1930
2365
|
}
|
|
1931
2366
|
console.log(chalk6.gray("\nRun 'ccem ls' to see all environments."));
|
|
2367
|
+
return results;
|
|
1932
2368
|
};
|
|
1933
2369
|
|
|
1934
2370
|
// src/cron-skill.ts
|
|
@@ -2074,28 +2510,182 @@ Replace \\\`TARGET_ID\\\` or \\\`TARGET_NAME\\\` with the user's selection.
|
|
|
2074
2510
|
`;
|
|
2075
2511
|
|
|
2076
2512
|
// src/index.ts
|
|
2077
|
-
var
|
|
2078
|
-
var __dirname2 =
|
|
2079
|
-
var pkgPath =
|
|
2080
|
-
var pkg = JSON.parse(
|
|
2513
|
+
var __filename2 = fileURLToPath2(import.meta.url);
|
|
2514
|
+
var __dirname2 = path6.dirname(__filename2);
|
|
2515
|
+
var pkgPath = path6.resolve(__dirname2, "..", "package.json");
|
|
2516
|
+
var pkg = JSON.parse(fs8.readFileSync(pkgPath, "utf-8"));
|
|
2081
2517
|
var program = new Command();
|
|
2082
|
-
|
|
2518
|
+
var DEFAULT_OFFICIAL_ENV = {
|
|
2519
|
+
ANTHROPIC_BASE_URL: "https://api.anthropic.com",
|
|
2520
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: "claude-opus-4-1-20250805",
|
|
2521
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: "claude-opus-4-1-20250805",
|
|
2522
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: "claude-3-5-haiku-20241022",
|
|
2523
|
+
ANTHROPIC_MODEL: "opus"
|
|
2524
|
+
};
|
|
2525
|
+
var MANAGED_CLAUDE_ENV_KEYS2 = [
|
|
2526
|
+
"ANTHROPIC_BASE_URL",
|
|
2527
|
+
"ANTHROPIC_AUTH_TOKEN",
|
|
2528
|
+
"ANTHROPIC_DEFAULT_OPUS_MODEL",
|
|
2529
|
+
"ANTHROPIC_DEFAULT_SONNET_MODEL",
|
|
2530
|
+
"ANTHROPIC_DEFAULT_HAIKU_MODEL",
|
|
2531
|
+
"ANTHROPIC_MODEL",
|
|
2532
|
+
"CLAUDE_CODE_SUBAGENT_MODEL",
|
|
2533
|
+
"ANTHROPIC_API_KEY",
|
|
2534
|
+
"ANTHROPIC_SMALL_FAST_MODEL"
|
|
2535
|
+
];
|
|
2536
|
+
var shellQuote = (value) => `'${value.replace(/'/g, `'\\''`)}'`;
|
|
2537
|
+
var clearManagedClaudeEnv = (env) => {
|
|
2538
|
+
for (const key of MANAGED_CLAUDE_ENV_KEYS2) {
|
|
2539
|
+
delete env[key];
|
|
2540
|
+
}
|
|
2541
|
+
};
|
|
2542
|
+
var buildResolvedEnvVars = (env) => {
|
|
2543
|
+
const resolved = {};
|
|
2544
|
+
if (env.ANTHROPIC_BASE_URL) resolved.ANTHROPIC_BASE_URL = env.ANTHROPIC_BASE_URL;
|
|
2545
|
+
if (env.ANTHROPIC_AUTH_TOKEN) resolved.ANTHROPIC_AUTH_TOKEN = decrypt(env.ANTHROPIC_AUTH_TOKEN);
|
|
2546
|
+
if (env.ANTHROPIC_DEFAULT_OPUS_MODEL) resolved.ANTHROPIC_DEFAULT_OPUS_MODEL = env.ANTHROPIC_DEFAULT_OPUS_MODEL;
|
|
2547
|
+
if (env.ANTHROPIC_DEFAULT_SONNET_MODEL) resolved.ANTHROPIC_DEFAULT_SONNET_MODEL = env.ANTHROPIC_DEFAULT_SONNET_MODEL;
|
|
2548
|
+
if (env.ANTHROPIC_DEFAULT_HAIKU_MODEL) resolved.ANTHROPIC_DEFAULT_HAIKU_MODEL = env.ANTHROPIC_DEFAULT_HAIKU_MODEL;
|
|
2549
|
+
if (env.ANTHROPIC_MODEL) resolved.ANTHROPIC_MODEL = env.ANTHROPIC_MODEL;
|
|
2550
|
+
if (env.CLAUDE_CODE_SUBAGENT_MODEL) resolved.CLAUDE_CODE_SUBAGENT_MODEL = env.CLAUDE_CODE_SUBAGENT_MODEL;
|
|
2551
|
+
return resolved;
|
|
2552
|
+
};
|
|
2553
|
+
var buildShellEnvCommands = (env) => {
|
|
2554
|
+
const resolved = buildResolvedEnvVars(env);
|
|
2555
|
+
return MANAGED_CLAUDE_ENV_KEYS2.map(
|
|
2556
|
+
(key) => resolved[key] ? `export ${key}=${shellQuote(resolved[key])}` : `unset ${key}`
|
|
2557
|
+
);
|
|
2558
|
+
};
|
|
2559
|
+
ensureCcemDir();
|
|
2083
2560
|
var config2 = new Conf2({
|
|
2084
2561
|
projectName: "claude-code-env-manager",
|
|
2085
2562
|
cwd: getCcemConfigDir(),
|
|
2086
2563
|
// 使用新路径
|
|
2087
2564
|
defaults: {
|
|
2088
2565
|
registries: {
|
|
2089
|
-
|
|
2090
|
-
ANTHROPIC_BASE_URL: "https://api.anthropic.com",
|
|
2091
|
-
ANTHROPIC_MODEL: "claude-sonnet-4-5-20250929",
|
|
2092
|
-
ANTHROPIC_SMALL_FAST_MODEL: "claude-haiku-4-5-20251001"
|
|
2093
|
-
}
|
|
2566
|
+
official: DEFAULT_OFFICIAL_ENV
|
|
2094
2567
|
},
|
|
2095
2568
|
current: "official",
|
|
2096
2569
|
defaultMode: null
|
|
2097
2570
|
}
|
|
2098
2571
|
});
|
|
2572
|
+
var recoverRegistriesFromLegacy = (registries) => {
|
|
2573
|
+
const currentAuthCount = Object.values(registries).filter(
|
|
2574
|
+
(env) => Boolean(env.ANTHROPIC_AUTH_TOKEN)
|
|
2575
|
+
).length;
|
|
2576
|
+
if (currentAuthCount > 0) {
|
|
2577
|
+
return registries;
|
|
2578
|
+
}
|
|
2579
|
+
const legacyConfigPath = getLegacyConfigPath();
|
|
2580
|
+
if (!fs8.existsSync(legacyConfigPath)) {
|
|
2581
|
+
return registries;
|
|
2582
|
+
}
|
|
2583
|
+
try {
|
|
2584
|
+
const legacyRaw = JSON.parse(fs8.readFileSync(legacyConfigPath, "utf-8"));
|
|
2585
|
+
const legacyRegistries = legacyRaw.registries ?? {};
|
|
2586
|
+
let changed = false;
|
|
2587
|
+
const recovered = { ...registries };
|
|
2588
|
+
for (const [name, envConfig] of Object.entries(registries)) {
|
|
2589
|
+
const legacyEnvConfig = legacyRegistries[name];
|
|
2590
|
+
if (!legacyEnvConfig) {
|
|
2591
|
+
continue;
|
|
2592
|
+
}
|
|
2593
|
+
const recoveredEnv = recoverEnvConfigFromLegacy(envConfig, legacyEnvConfig);
|
|
2594
|
+
if (JSON.stringify(recoveredEnv) !== JSON.stringify(envConfig)) {
|
|
2595
|
+
recovered[name] = recoveredEnv;
|
|
2596
|
+
changed = true;
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
2599
|
+
return changed ? recovered : registries;
|
|
2600
|
+
} catch {
|
|
2601
|
+
return registries;
|
|
2602
|
+
}
|
|
2603
|
+
};
|
|
2604
|
+
var getRegistries = () => {
|
|
2605
|
+
const rawRegistries = config2.get("registries") ?? {};
|
|
2606
|
+
const normalizedEntries = Object.entries(rawRegistries).map(([name, envConfig]) => [
|
|
2607
|
+
name,
|
|
2608
|
+
normalizeEnvConfig(envConfig ?? {})
|
|
2609
|
+
]);
|
|
2610
|
+
const normalizedRegistries = Object.fromEntries(normalizedEntries);
|
|
2611
|
+
const repairedRegistries = recoverRegistriesFromLegacy(normalizedRegistries);
|
|
2612
|
+
if (!repairedRegistries.official) {
|
|
2613
|
+
repairedRegistries.official = { ...DEFAULT_OFFICIAL_ENV };
|
|
2614
|
+
}
|
|
2615
|
+
const changed = Object.keys(rawRegistries).length !== Object.keys(repairedRegistries).length || JSON.stringify(rawRegistries) !== JSON.stringify(repairedRegistries);
|
|
2616
|
+
if (changed) {
|
|
2617
|
+
config2.set("registries", repairedRegistries);
|
|
2618
|
+
}
|
|
2619
|
+
return repairedRegistries;
|
|
2620
|
+
};
|
|
2621
|
+
var setRegistries = (registries) => {
|
|
2622
|
+
config2.set("registries", registries);
|
|
2623
|
+
};
|
|
2624
|
+
var getDecryptedAuthToken = (envConfig) => {
|
|
2625
|
+
return envConfig.ANTHROPIC_AUTH_TOKEN ? decrypt(envConfig.ANTHROPIC_AUTH_TOKEN) : void 0;
|
|
2626
|
+
};
|
|
2627
|
+
var applyPromptAnswers = (current, answers, keepCurrentSecret) => {
|
|
2628
|
+
const next = {
|
|
2629
|
+
...current,
|
|
2630
|
+
ANTHROPIC_BASE_URL: answers.ANTHROPIC_BASE_URL?.trim() || current.ANTHROPIC_BASE_URL,
|
|
2631
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: answers.ANTHROPIC_DEFAULT_OPUS_MODEL?.trim() || current.ANTHROPIC_DEFAULT_OPUS_MODEL,
|
|
2632
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: answers.ANTHROPIC_DEFAULT_HAIKU_MODEL?.trim() || current.ANTHROPIC_DEFAULT_HAIKU_MODEL,
|
|
2633
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: answers.ANTHROPIC_DEFAULT_SONNET_MODEL?.trim() || answers.ANTHROPIC_DEFAULT_OPUS_MODEL?.trim() || current.ANTHROPIC_DEFAULT_SONNET_MODEL || current.ANTHROPIC_DEFAULT_OPUS_MODEL,
|
|
2634
|
+
ANTHROPIC_MODEL: answers.ANTHROPIC_MODEL?.trim() || current.ANTHROPIC_MODEL || "opus",
|
|
2635
|
+
CLAUDE_CODE_SUBAGENT_MODEL: answers.CLAUDE_CODE_SUBAGENT_MODEL?.trim() || current.CLAUDE_CODE_SUBAGENT_MODEL
|
|
2636
|
+
};
|
|
2637
|
+
if (!keepCurrentSecret) {
|
|
2638
|
+
next.ANTHROPIC_AUTH_TOKEN = answers.ANTHROPIC_AUTH_TOKEN ? encrypt(answers.ANTHROPIC_AUTH_TOKEN) : void 0;
|
|
2639
|
+
} else if (answers.ANTHROPIC_AUTH_TOKEN) {
|
|
2640
|
+
next.ANTHROPIC_AUTH_TOKEN = encrypt(answers.ANTHROPIC_AUTH_TOKEN);
|
|
2641
|
+
}
|
|
2642
|
+
return normalizeEnvConfig(next);
|
|
2643
|
+
};
|
|
2644
|
+
var promptForEnvironmentConfig = async (current = {}, keepCurrentSecret = false) => {
|
|
2645
|
+
return inquirer.prompt([
|
|
2646
|
+
{
|
|
2647
|
+
type: "input",
|
|
2648
|
+
name: "ANTHROPIC_BASE_URL",
|
|
2649
|
+
message: "ANTHROPIC_BASE_URL:",
|
|
2650
|
+
default: current.ANTHROPIC_BASE_URL || DEFAULT_OFFICIAL_ENV.ANTHROPIC_BASE_URL
|
|
2651
|
+
},
|
|
2652
|
+
{
|
|
2653
|
+
type: "password",
|
|
2654
|
+
name: "ANTHROPIC_AUTH_TOKEN",
|
|
2655
|
+
message: keepCurrentSecret ? "ANTHROPIC_AUTH_TOKEN (leave empty to keep current):" : "ANTHROPIC_AUTH_TOKEN:"
|
|
2656
|
+
},
|
|
2657
|
+
{
|
|
2658
|
+
type: "input",
|
|
2659
|
+
name: "ANTHROPIC_DEFAULT_OPUS_MODEL",
|
|
2660
|
+
message: "ANTHROPIC_DEFAULT_OPUS_MODEL:",
|
|
2661
|
+
default: current.ANTHROPIC_DEFAULT_OPUS_MODEL || DEFAULT_OFFICIAL_ENV.ANTHROPIC_DEFAULT_OPUS_MODEL
|
|
2662
|
+
},
|
|
2663
|
+
{
|
|
2664
|
+
type: "input",
|
|
2665
|
+
name: "ANTHROPIC_DEFAULT_HAIKU_MODEL",
|
|
2666
|
+
message: "ANTHROPIC_DEFAULT_HAIKU_MODEL:",
|
|
2667
|
+
default: current.ANTHROPIC_DEFAULT_HAIKU_MODEL || DEFAULT_OFFICIAL_ENV.ANTHROPIC_DEFAULT_HAIKU_MODEL
|
|
2668
|
+
},
|
|
2669
|
+
{
|
|
2670
|
+
type: "input",
|
|
2671
|
+
name: "ANTHROPIC_DEFAULT_SONNET_MODEL",
|
|
2672
|
+
message: "ANTHROPIC_DEFAULT_SONNET_MODEL (blank = same as opus):",
|
|
2673
|
+
default: current.ANTHROPIC_DEFAULT_SONNET_MODEL || current.ANTHROPIC_DEFAULT_OPUS_MODEL || ""
|
|
2674
|
+
},
|
|
2675
|
+
{
|
|
2676
|
+
type: "input",
|
|
2677
|
+
name: "ANTHROPIC_MODEL",
|
|
2678
|
+
message: "ANTHROPIC_MODEL (e.g. opus, opusplan, sonnet):",
|
|
2679
|
+
default: current.ANTHROPIC_MODEL || "opus"
|
|
2680
|
+
},
|
|
2681
|
+
{
|
|
2682
|
+
type: "input",
|
|
2683
|
+
name: "CLAUDE_CODE_SUBAGENT_MODEL",
|
|
2684
|
+
message: "CLAUDE_CODE_SUBAGENT_MODEL (optional):",
|
|
2685
|
+
default: current.CLAUDE_CODE_SUBAGENT_MODEL || ""
|
|
2686
|
+
}
|
|
2687
|
+
]);
|
|
2688
|
+
};
|
|
2099
2689
|
var PERMISSION_MODES = ["yolo", "dev", "readonly", "safe", "ci", "audit"];
|
|
2100
2690
|
var usageStats = null;
|
|
2101
2691
|
var usageLoading = true;
|
|
@@ -2132,9 +2722,9 @@ var initUsageStats = (onUpdate) => {
|
|
|
2132
2722
|
};
|
|
2133
2723
|
program.name("ccem").description("Claude Code Environment Manager - \u7BA1\u7406 Claude Code \u73AF\u5883\u53D8\u91CF\u548C\u6743\u9650").version(pkg.version).option("--mode", "\u67E5\u770B\u5F53\u524D\u6743\u9650\u6A21\u5F0F").option("--list-modes", "\u5217\u51FA\u6240\u6709\u53EF\u7528\u6743\u9650\u6A21\u5F0F");
|
|
2134
2724
|
PERMISSION_MODES.forEach((mode) => {
|
|
2135
|
-
const preset =
|
|
2725
|
+
const preset = PERMISSION_PRESETS[mode];
|
|
2136
2726
|
program.command(mode).description(`\u4E34\u65F6\u5E94\u7528 ${preset.name}\uFF0C\u9000\u51FA\u540E\u8FD8\u539F`).action(async () => {
|
|
2137
|
-
const registries =
|
|
2727
|
+
const registries = getRegistries();
|
|
2138
2728
|
const current = config2.get("current");
|
|
2139
2729
|
const envConfig = registries[current];
|
|
2140
2730
|
await runWithTempPermissions(mode, envConfig);
|
|
@@ -2143,15 +2733,18 @@ PERMISSION_MODES.forEach((mode) => {
|
|
|
2143
2733
|
var showCurrentEnv = (usageStats2, usageLoading2) => {
|
|
2144
2734
|
if (!process.stdout.isTTY) return;
|
|
2145
2735
|
const current = config2.get("current");
|
|
2146
|
-
const registries =
|
|
2736
|
+
const registries = getRegistries();
|
|
2147
2737
|
const env = registries[current];
|
|
2148
2738
|
const defaultMode = config2.get("defaultMode");
|
|
2149
2739
|
if (!env) return;
|
|
2150
2740
|
console.log(renderLogoWithEnvPanel(current, {
|
|
2151
2741
|
ANTHROPIC_BASE_URL: env.ANTHROPIC_BASE_URL,
|
|
2152
|
-
|
|
2742
|
+
ANTHROPIC_AUTH_TOKEN: getDecryptedAuthToken(env),
|
|
2743
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: env.ANTHROPIC_DEFAULT_OPUS_MODEL,
|
|
2744
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: env.ANTHROPIC_DEFAULT_SONNET_MODEL,
|
|
2745
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: env.ANTHROPIC_DEFAULT_HAIKU_MODEL,
|
|
2153
2746
|
ANTHROPIC_MODEL: env.ANTHROPIC_MODEL,
|
|
2154
|
-
|
|
2747
|
+
CLAUDE_CODE_SUBAGENT_MODEL: env.CLAUDE_CODE_SUBAGENT_MODEL
|
|
2155
2748
|
}, defaultMode));
|
|
2156
2749
|
console.log("");
|
|
2157
2750
|
console.log(renderUsageLine(usageStats2, usageLoading2));
|
|
@@ -2159,7 +2752,7 @@ var showCurrentEnv = (usageStats2, usageLoading2) => {
|
|
|
2159
2752
|
console.log("");
|
|
2160
2753
|
};
|
|
2161
2754
|
var switchEnvironment = async (name) => {
|
|
2162
|
-
const registries =
|
|
2755
|
+
const registries = getRegistries();
|
|
2163
2756
|
if (!registries[name]) {
|
|
2164
2757
|
console.error(chalk7.red(`Environment '${name}' not found.`));
|
|
2165
2758
|
return;
|
|
@@ -2172,11 +2765,7 @@ var switchEnvironment = async (name) => {
|
|
|
2172
2765
|
}
|
|
2173
2766
|
showCurrentEnv(null, false);
|
|
2174
2767
|
const env = registries[name];
|
|
2175
|
-
const exportCmds =
|
|
2176
|
-
if (env.ANTHROPIC_BASE_URL) exportCmds.push(`export ANTHROPIC_BASE_URL="${env.ANTHROPIC_BASE_URL}"`);
|
|
2177
|
-
if (env.ANTHROPIC_API_KEY) exportCmds.push(`export ANTHROPIC_API_KEY="${decrypt2(env.ANTHROPIC_API_KEY)}"`);
|
|
2178
|
-
if (env.ANTHROPIC_MODEL) exportCmds.push(`export ANTHROPIC_MODEL="${env.ANTHROPIC_MODEL}"`);
|
|
2179
|
-
if (env.ANTHROPIC_SMALL_FAST_MODEL) exportCmds.push(`export ANTHROPIC_SMALL_FAST_MODEL="${env.ANTHROPIC_SMALL_FAST_MODEL}"`);
|
|
2768
|
+
const exportCmds = buildShellEnvCommands(env);
|
|
2180
2769
|
if (process.stdout.isTTY) {
|
|
2181
2770
|
console.log(chalk7.yellow("\nTo apply to current shell immediately, run:"));
|
|
2182
2771
|
console.log(chalk7.cyan("eval $(ccem env)"));
|
|
@@ -2186,11 +2775,92 @@ var switchEnvironment = async (name) => {
|
|
|
2186
2775
|
exportCmds.forEach((cmd) => console.log(cmd));
|
|
2187
2776
|
}
|
|
2188
2777
|
};
|
|
2778
|
+
var getSessionsFilePath = () => path6.join(getCcemConfigDir(), "sessions.json");
|
|
2779
|
+
var getRuntimeStateFilePath = () => path6.join(getCcemConfigDir(), "runtime-state.json");
|
|
2780
|
+
var parseJsonFile = (filePath) => {
|
|
2781
|
+
if (!fs8.existsSync(filePath)) {
|
|
2782
|
+
return null;
|
|
2783
|
+
}
|
|
2784
|
+
try {
|
|
2785
|
+
return JSON.parse(fs8.readFileSync(filePath, "utf-8"));
|
|
2786
|
+
} catch {
|
|
2787
|
+
return null;
|
|
2788
|
+
}
|
|
2789
|
+
};
|
|
2790
|
+
var readInteractiveAttachSessions = () => {
|
|
2791
|
+
const sessionsById = /* @__PURE__ */ new Map();
|
|
2792
|
+
const runtimeState = parseJsonFile(getRuntimeStateFilePath());
|
|
2793
|
+
const persistedSessions = parseJsonFile(getSessionsFilePath()) ?? [];
|
|
2794
|
+
for (const entry of runtimeState?.sessions ?? []) {
|
|
2795
|
+
if (entry.runtime_kind && entry.runtime_kind !== "interactive") {
|
|
2796
|
+
continue;
|
|
2797
|
+
}
|
|
2798
|
+
const fallback = persistedSessions.find((session) => session.id === entry.runtime_id);
|
|
2799
|
+
const tmuxTarget = entry.tmux_session && entry.tmux_window ? `${entry.tmux_session}:${entry.tmux_window}` : fallback?.window_id ?? null;
|
|
2800
|
+
if (!tmuxTarget) {
|
|
2801
|
+
continue;
|
|
2802
|
+
}
|
|
2803
|
+
sessionsById.set(entry.runtime_id, {
|
|
2804
|
+
id: entry.runtime_id,
|
|
2805
|
+
projectDir: entry.project_dir,
|
|
2806
|
+
envName: entry.env_name,
|
|
2807
|
+
permMode: entry.perm_mode,
|
|
2808
|
+
status: fallback?.status ?? "running",
|
|
2809
|
+
tmuxTarget,
|
|
2810
|
+
sortTime: entry.saved_at
|
|
2811
|
+
});
|
|
2812
|
+
}
|
|
2813
|
+
for (const session of persistedSessions) {
|
|
2814
|
+
if (session.status !== "running" || session.terminal_type !== "embedded" || !session.window_id) {
|
|
2815
|
+
continue;
|
|
2816
|
+
}
|
|
2817
|
+
if (sessionsById.has(session.id)) {
|
|
2818
|
+
continue;
|
|
2819
|
+
}
|
|
2820
|
+
sessionsById.set(session.id, {
|
|
2821
|
+
id: session.id,
|
|
2822
|
+
projectDir: session.working_dir,
|
|
2823
|
+
envName: session.env_name,
|
|
2824
|
+
permMode: session.perm_mode,
|
|
2825
|
+
status: session.status,
|
|
2826
|
+
tmuxTarget: session.window_id,
|
|
2827
|
+
sortTime: session.start_time
|
|
2828
|
+
});
|
|
2829
|
+
}
|
|
2830
|
+
return [...sessionsById.values()].sort(
|
|
2831
|
+
(left, right) => right.sortTime.localeCompare(left.sortTime)
|
|
2832
|
+
);
|
|
2833
|
+
};
|
|
2834
|
+
var findAttachSession = (id) => {
|
|
2835
|
+
const sessions = readInteractiveAttachSessions();
|
|
2836
|
+
if (!id) {
|
|
2837
|
+
return sessions[0];
|
|
2838
|
+
}
|
|
2839
|
+
const exact = sessions.find((session) => session.id === id);
|
|
2840
|
+
if (exact) {
|
|
2841
|
+
return exact;
|
|
2842
|
+
}
|
|
2843
|
+
return sessions.find((session) => session.id.startsWith(id));
|
|
2844
|
+
};
|
|
2845
|
+
var attachTmuxTarget = (target) => {
|
|
2846
|
+
const args = process.env.TMUX ? ["switch-client", "-t", target] : ["attach-session", "-t", target];
|
|
2847
|
+
return new Promise((resolve2, reject) => {
|
|
2848
|
+
const child = spawn3("tmux", args, { stdio: "inherit" });
|
|
2849
|
+
child.on("error", reject);
|
|
2850
|
+
child.on("exit", (code) => {
|
|
2851
|
+
if (code === 0 || code === null) {
|
|
2852
|
+
resolve2();
|
|
2853
|
+
} else {
|
|
2854
|
+
reject(new Error(`tmux exited with code ${code}`));
|
|
2855
|
+
}
|
|
2856
|
+
});
|
|
2857
|
+
});
|
|
2858
|
+
};
|
|
2189
2859
|
program.command("ls").description("List all configured environments").action(() => {
|
|
2190
|
-
const registries =
|
|
2860
|
+
const registries = getRegistries();
|
|
2191
2861
|
const current = config2.get("current");
|
|
2192
2862
|
const table = new Table3({
|
|
2193
|
-
head: ["Name", "Base URL", "
|
|
2863
|
+
head: ["Name", "Base URL", "Opus"],
|
|
2194
2864
|
style: { head: ["cyan"] }
|
|
2195
2865
|
});
|
|
2196
2866
|
Object.keys(registries).forEach((name) => {
|
|
@@ -2199,7 +2869,7 @@ program.command("ls").description("List all configured environments").action(()
|
|
|
2199
2869
|
table.push([
|
|
2200
2870
|
prefix + name,
|
|
2201
2871
|
reg.ANTHROPIC_BASE_URL || "-",
|
|
2202
|
-
reg.
|
|
2872
|
+
reg.ANTHROPIC_DEFAULT_OPUS_MODEL || "-"
|
|
2203
2873
|
]);
|
|
2204
2874
|
});
|
|
2205
2875
|
console.log(table.toString());
|
|
@@ -2207,8 +2877,42 @@ program.command("ls").description("List all configured environments").action(()
|
|
|
2207
2877
|
program.command("use <name>").description("Switch to a specific environment").action(async (name) => {
|
|
2208
2878
|
await switchEnvironment(name);
|
|
2209
2879
|
});
|
|
2880
|
+
program.command("sessions").description("List tmux-backed interactive sessions").action(() => {
|
|
2881
|
+
const sessions = readInteractiveAttachSessions();
|
|
2882
|
+
if (sessions.length === 0) {
|
|
2883
|
+
console.log(chalk7.yellow("No tmux-backed interactive sessions found."));
|
|
2884
|
+
return;
|
|
2885
|
+
}
|
|
2886
|
+
const table = new Table3({
|
|
2887
|
+
head: ["ID", "Project", "Env", "Status", "Tmux"],
|
|
2888
|
+
style: { head: ["cyan"] }
|
|
2889
|
+
});
|
|
2890
|
+
sessions.forEach((session) => {
|
|
2891
|
+
table.push([
|
|
2892
|
+
session.id,
|
|
2893
|
+
session.projectDir,
|
|
2894
|
+
session.envName,
|
|
2895
|
+
session.status,
|
|
2896
|
+
session.tmuxTarget
|
|
2897
|
+
]);
|
|
2898
|
+
});
|
|
2899
|
+
console.log(table.toString());
|
|
2900
|
+
});
|
|
2901
|
+
program.command("attach [id]").description("Attach to a tmux-backed interactive session").action(async (id) => {
|
|
2902
|
+
const session = findAttachSession(id);
|
|
2903
|
+
if (!session) {
|
|
2904
|
+
console.error(chalk7.red(id ? `Interactive session '${id}' not found.` : "No interactive session available to attach."));
|
|
2905
|
+
process.exit(1);
|
|
2906
|
+
}
|
|
2907
|
+
try {
|
|
2908
|
+
await attachTmuxTarget(session.tmuxTarget);
|
|
2909
|
+
} catch (error) {
|
|
2910
|
+
console.error(chalk7.red(`Failed to attach ${session.tmuxTarget}: ${String(error)}`));
|
|
2911
|
+
process.exit(1);
|
|
2912
|
+
}
|
|
2913
|
+
});
|
|
2210
2914
|
program.command("add <name>").description("Add a new environment configuration").action(async (name) => {
|
|
2211
|
-
const registries =
|
|
2915
|
+
const registries = getRegistries();
|
|
2212
2916
|
if (registries[name]) {
|
|
2213
2917
|
console.log(chalk7.red(`Environment '${name}' already exists.`));
|
|
2214
2918
|
return;
|
|
@@ -2233,40 +2937,13 @@ program.command("add <name>").description("Add a new environment configuration")
|
|
|
2233
2937
|
]);
|
|
2234
2938
|
presetConfig = ENV_PRESETS[presetName];
|
|
2235
2939
|
}
|
|
2236
|
-
const answers = await
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
name: "ANTHROPIC_BASE_URL",
|
|
2240
|
-
message: "Enter ANTHROPIC_BASE_URL:",
|
|
2241
|
-
default: presetConfig.ANTHROPIC_BASE_URL || "https://api.anthropic.com"
|
|
2242
|
-
},
|
|
2243
|
-
{
|
|
2244
|
-
type: "password",
|
|
2245
|
-
name: "ANTHROPIC_API_KEY",
|
|
2246
|
-
message: "Enter ANTHROPIC_API_KEY:"
|
|
2247
|
-
},
|
|
2248
|
-
{
|
|
2249
|
-
type: "input",
|
|
2250
|
-
name: "ANTHROPIC_MODEL",
|
|
2251
|
-
message: "Enter ANTHROPIC_MODEL:",
|
|
2252
|
-
default: presetConfig.ANTHROPIC_MODEL || "claude-sonnet-4-5-20250929"
|
|
2253
|
-
},
|
|
2254
|
-
{
|
|
2255
|
-
type: "input",
|
|
2256
|
-
name: "ANTHROPIC_SMALL_FAST_MODEL",
|
|
2257
|
-
message: "Enter ANTHROPIC_SMALL_FAST_MODEL:",
|
|
2258
|
-
default: presetConfig.ANTHROPIC_SMALL_FAST_MODEL || "claude-haiku-4-5-20251001"
|
|
2259
|
-
}
|
|
2260
|
-
]);
|
|
2261
|
-
if (answers.ANTHROPIC_API_KEY) {
|
|
2262
|
-
answers.ANTHROPIC_API_KEY = encrypt2(answers.ANTHROPIC_API_KEY);
|
|
2263
|
-
}
|
|
2264
|
-
registries[name] = answers;
|
|
2265
|
-
config2.set("registries", registries);
|
|
2940
|
+
const answers = await promptForEnvironmentConfig(presetConfig);
|
|
2941
|
+
registries[name] = applyPromptAnswers(normalizeEnvConfig(presetConfig), answers, false);
|
|
2942
|
+
setRegistries(registries);
|
|
2266
2943
|
console.log(chalk7.green(`Environment '${name}' added successfully.`));
|
|
2267
2944
|
});
|
|
2268
2945
|
program.command("del <name>").description("Delete an environment configuration").action((name) => {
|
|
2269
|
-
const registries =
|
|
2946
|
+
const registries = getRegistries();
|
|
2270
2947
|
if (!registries[name]) {
|
|
2271
2948
|
console.log(chalk7.red(`Environment '${name}' not found.`));
|
|
2272
2949
|
return;
|
|
@@ -2276,7 +2953,7 @@ program.command("del <name>").description("Delete an environment configuration")
|
|
|
2276
2953
|
return;
|
|
2277
2954
|
}
|
|
2278
2955
|
delete registries[name];
|
|
2279
|
-
|
|
2956
|
+
setRegistries(registries);
|
|
2280
2957
|
const current = config2.get("current");
|
|
2281
2958
|
if (current === name) {
|
|
2282
2959
|
config2.set("current", "official");
|
|
@@ -2285,7 +2962,7 @@ program.command("del <name>").description("Delete an environment configuration")
|
|
|
2285
2962
|
console.log(chalk7.green(`Environment '${name}' deleted.`));
|
|
2286
2963
|
});
|
|
2287
2964
|
program.command("rename <old> <new>").description("Rename an environment configuration").action((oldName, newName) => {
|
|
2288
|
-
const registries =
|
|
2965
|
+
const registries = getRegistries();
|
|
2289
2966
|
if (!registries[oldName]) {
|
|
2290
2967
|
console.log(chalk7.red(`Environment '${oldName}' not found.`));
|
|
2291
2968
|
return;
|
|
@@ -2300,7 +2977,7 @@ program.command("rename <old> <new>").description("Rename an environment configu
|
|
|
2300
2977
|
}
|
|
2301
2978
|
registries[newName] = registries[oldName];
|
|
2302
2979
|
delete registries[oldName];
|
|
2303
|
-
|
|
2980
|
+
setRegistries(registries);
|
|
2304
2981
|
const current = config2.get("current");
|
|
2305
2982
|
if (current === oldName) {
|
|
2306
2983
|
config2.set("current", newName);
|
|
@@ -2308,7 +2985,7 @@ program.command("rename <old> <new>").description("Rename an environment configu
|
|
|
2308
2985
|
console.log(chalk7.green(`Environment '${oldName}' renamed to '${newName}'.`));
|
|
2309
2986
|
});
|
|
2310
2987
|
program.command("cp <source> <target>").description("Copy an environment configuration").action(async (source, target) => {
|
|
2311
|
-
const registries =
|
|
2988
|
+
const registries = getRegistries();
|
|
2312
2989
|
if (!registries[source]) {
|
|
2313
2990
|
console.log(chalk7.red(`Environment '${source}' not found.`));
|
|
2314
2991
|
return;
|
|
@@ -2318,7 +2995,7 @@ program.command("cp <source> <target>").description("Copy an environment configu
|
|
|
2318
2995
|
return;
|
|
2319
2996
|
}
|
|
2320
2997
|
registries[target] = { ...registries[source] };
|
|
2321
|
-
|
|
2998
|
+
setRegistries(registries);
|
|
2322
2999
|
console.log(chalk7.green(`Environment '${source}' copied to '${target}'.`));
|
|
2323
3000
|
const { modify } = await inquirer.prompt([
|
|
2324
3001
|
{
|
|
@@ -2330,37 +3007,9 @@ program.command("cp <source> <target>").description("Copy an environment configu
|
|
|
2330
3007
|
]);
|
|
2331
3008
|
if (modify) {
|
|
2332
3009
|
const current = registries[target];
|
|
2333
|
-
const answers = await
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
name: "ANTHROPIC_BASE_URL",
|
|
2337
|
-
message: "ANTHROPIC_BASE_URL:",
|
|
2338
|
-
default: current.ANTHROPIC_BASE_URL
|
|
2339
|
-
},
|
|
2340
|
-
{
|
|
2341
|
-
type: "password",
|
|
2342
|
-
name: "ANTHROPIC_API_KEY",
|
|
2343
|
-
message: "ANTHROPIC_API_KEY (leave empty to keep current):"
|
|
2344
|
-
},
|
|
2345
|
-
{
|
|
2346
|
-
type: "input",
|
|
2347
|
-
name: "ANTHROPIC_MODEL",
|
|
2348
|
-
message: "ANTHROPIC_MODEL:",
|
|
2349
|
-
default: current.ANTHROPIC_MODEL
|
|
2350
|
-
},
|
|
2351
|
-
{
|
|
2352
|
-
type: "input",
|
|
2353
|
-
name: "ANTHROPIC_SMALL_FAST_MODEL",
|
|
2354
|
-
message: "ANTHROPIC_SMALL_FAST_MODEL:",
|
|
2355
|
-
default: current.ANTHROPIC_SMALL_FAST_MODEL
|
|
2356
|
-
}
|
|
2357
|
-
]);
|
|
2358
|
-
if (answers.ANTHROPIC_BASE_URL) current.ANTHROPIC_BASE_URL = answers.ANTHROPIC_BASE_URL;
|
|
2359
|
-
if (answers.ANTHROPIC_API_KEY) current.ANTHROPIC_API_KEY = encrypt2(answers.ANTHROPIC_API_KEY);
|
|
2360
|
-
if (answers.ANTHROPIC_MODEL) current.ANTHROPIC_MODEL = answers.ANTHROPIC_MODEL;
|
|
2361
|
-
if (answers.ANTHROPIC_SMALL_FAST_MODEL) current.ANTHROPIC_SMALL_FAST_MODEL = answers.ANTHROPIC_SMALL_FAST_MODEL;
|
|
2362
|
-
registries[target] = current;
|
|
2363
|
-
config2.set("registries", registries);
|
|
3010
|
+
const answers = await promptForEnvironmentConfig(current, true);
|
|
3011
|
+
registries[target] = applyPromptAnswers(current, answers, true);
|
|
3012
|
+
setRegistries(registries);
|
|
2364
3013
|
console.log(chalk7.green(`Environment '${target}' updated.`));
|
|
2365
3014
|
}
|
|
2366
3015
|
});
|
|
@@ -2369,25 +3018,22 @@ program.command("current").description("Show current environment name").action((
|
|
|
2369
3018
|
console.log(chalk7.green(current));
|
|
2370
3019
|
});
|
|
2371
3020
|
program.command("env").description("Output environment variables for shell eval").option("--json", "Output as JSON").action((options) => {
|
|
2372
|
-
const registries =
|
|
3021
|
+
const registries = getRegistries();
|
|
2373
3022
|
const current = config2.get("current");
|
|
2374
3023
|
const env = registries[current];
|
|
2375
3024
|
if (!env) return;
|
|
2376
3025
|
const outputEnv = { ...env };
|
|
2377
|
-
if (outputEnv.
|
|
2378
|
-
outputEnv.
|
|
3026
|
+
if (outputEnv.ANTHROPIC_AUTH_TOKEN) {
|
|
3027
|
+
outputEnv.ANTHROPIC_AUTH_TOKEN = decrypt(outputEnv.ANTHROPIC_AUTH_TOKEN);
|
|
2379
3028
|
}
|
|
2380
3029
|
if (options.json) {
|
|
2381
3030
|
console.log(JSON.stringify(outputEnv, null, 2));
|
|
2382
3031
|
} else {
|
|
2383
|
-
|
|
2384
|
-
if (outputEnv.ANTHROPIC_API_KEY) console.log(`export ANTHROPIC_API_KEY="${outputEnv.ANTHROPIC_API_KEY}"`);
|
|
2385
|
-
if (outputEnv.ANTHROPIC_MODEL) console.log(`export ANTHROPIC_MODEL="${outputEnv.ANTHROPIC_MODEL}"`);
|
|
2386
|
-
if (outputEnv.ANTHROPIC_SMALL_FAST_MODEL) console.log(`export ANTHROPIC_SMALL_FAST_MODEL="${outputEnv.ANTHROPIC_SMALL_FAST_MODEL}"`);
|
|
3032
|
+
buildShellEnvCommands(env).forEach((cmd) => console.log(cmd));
|
|
2387
3033
|
}
|
|
2388
3034
|
});
|
|
2389
3035
|
program.command("run <command...>").description("Run a command with the current environment variables").action((command) => {
|
|
2390
|
-
const registries =
|
|
3036
|
+
const registries = getRegistries();
|
|
2391
3037
|
const current = config2.get("current");
|
|
2392
3038
|
const envConfig = registries[current];
|
|
2393
3039
|
if (!envConfig) {
|
|
@@ -2395,10 +3041,8 @@ program.command("run <command...>").description("Run a command with the current
|
|
|
2395
3041
|
process.exit(1);
|
|
2396
3042
|
}
|
|
2397
3043
|
const env = { ...process.env };
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
if (envConfig.ANTHROPIC_MODEL) env.ANTHROPIC_MODEL = envConfig.ANTHROPIC_MODEL;
|
|
2401
|
-
if (envConfig.ANTHROPIC_SMALL_FAST_MODEL) env.ANTHROPIC_SMALL_FAST_MODEL = envConfig.ANTHROPIC_SMALL_FAST_MODEL;
|
|
3044
|
+
clearManagedClaudeEnv(env);
|
|
3045
|
+
Object.assign(env, buildResolvedEnvVars(envConfig));
|
|
2402
3046
|
const [cmd, ...args] = command;
|
|
2403
3047
|
const child = spawn3(cmd, args, {
|
|
2404
3048
|
env,
|
|
@@ -2436,14 +3080,14 @@ setupCmd.command("default-mode").description("\u8BBE\u7F6E\u9ED8\u8BA4\u6743\u96
|
|
|
2436
3080
|
for (const mode of PERMISSION_MODES) {
|
|
2437
3081
|
if (options[mode]) {
|
|
2438
3082
|
config2.set("defaultMode", mode);
|
|
2439
|
-
console.log(chalk7.green(`\u5DF2\u8BBE\u7F6E\u9ED8\u8BA4\u6743\u9650\u6A21\u5F0F: ${
|
|
3083
|
+
console.log(chalk7.green(`\u5DF2\u8BBE\u7F6E\u9ED8\u8BA4\u6743\u9650\u6A21\u5F0F: ${PERMISSION_PRESETS[mode].name}`));
|
|
2440
3084
|
console.log(chalk7.gray(`\u4E0B\u6B21\u542F\u52A8 ccem \u65F6\u5C06\u9ED8\u8BA4\u4F7F\u7528\u6B64\u6A21\u5F0F`));
|
|
2441
3085
|
return;
|
|
2442
3086
|
}
|
|
2443
3087
|
}
|
|
2444
3088
|
const currentDefault = config2.get("defaultMode");
|
|
2445
|
-
if (currentDefault &&
|
|
2446
|
-
console.log(chalk7.green(`\u5F53\u524D\u9ED8\u8BA4\u6A21\u5F0F: ${
|
|
3089
|
+
if (currentDefault && PERMISSION_PRESETS[currentDefault]) {
|
|
3090
|
+
console.log(chalk7.green(`\u5F53\u524D\u9ED8\u8BA4\u6A21\u5F0F: ${PERMISSION_PRESETS[currentDefault].name}`));
|
|
2447
3091
|
} else {
|
|
2448
3092
|
console.log(chalk7.yellow("\u672A\u8BBE\u7F6E\u9ED8\u8BA4\u6743\u9650\u6A21\u5F0F"));
|
|
2449
3093
|
}
|
|
@@ -2451,36 +3095,37 @@ setupCmd.command("default-mode").description("\u8BBE\u7F6E\u9ED8\u8BA4\u6743\u96
|
|
|
2451
3095
|
console.log(chalk7.gray("\u6E05\u9664\u9ED8\u8BA4\u6A21\u5F0F: ccem setup default-mode --reset"));
|
|
2452
3096
|
console.log(chalk7.gray("\u53EF\u7528\u6A21\u5F0F: " + PERMISSION_MODES.join(", ")));
|
|
2453
3097
|
});
|
|
2454
|
-
setupCmd.command("init").description("\u521D\u59CB\u5316 Claude Code \u5168\u5C40\u914D\u7F6E\uFF08\u8DF3\u8FC7 onboarding\u3001\u7981\u7528\u9065\u6D4B\
|
|
2455
|
-
|
|
3098
|
+
setupCmd.command("init").description("\u521D\u59CB\u5316 Claude Code \u5168\u5C40\u914D\u7F6E\uFF08\u8DF3\u8FC7 onboarding\u3001\u7981\u7528\u9065\u6D4B\uFF09").option("--chrome", "\u540C\u65F6\u5B89\u88C5 chrome-devtools MCP \u5DE5\u5177").action(async function() {
|
|
3099
|
+
const options = this.opts();
|
|
3100
|
+
await runSetupInit({ chrome: !!options.chrome });
|
|
2456
3101
|
});
|
|
2457
3102
|
setupCmd.command("migrate").description("\u8FC1\u79FB\u65E7\u7248\u914D\u7F6E\u5230 ~/.ccem/").option("--clean", "\u8FC1\u79FB\u540E\u5220\u9664\u65E7\u914D\u7F6E\u6587\u4EF6").option("--force", "\u5F3A\u5236\u91CD\u65B0\u8FC1\u79FB\uFF08\u8986\u76D6\u73B0\u6709\u914D\u7F6E\uFF09").action(async function() {
|
|
2458
3103
|
const options = this.opts();
|
|
2459
3104
|
const newConfigPath = getCcemConfigPath();
|
|
2460
3105
|
const legacyConfigPath = getLegacyConfigPath();
|
|
2461
3106
|
console.log(chalk7.cyan("\n\u{1F504} \u914D\u7F6E\u8FC1\u79FB\n"));
|
|
2462
|
-
if (!
|
|
3107
|
+
if (!fs8.existsSync(legacyConfigPath)) {
|
|
2463
3108
|
console.log(chalk7.yellow("\u672A\u627E\u5230\u65E7\u7248\u914D\u7F6E\u6587\u4EF6"));
|
|
2464
3109
|
console.log(chalk7.gray(` \u65E7\u8DEF\u5F84: ${legacyConfigPath}`));
|
|
2465
3110
|
return;
|
|
2466
3111
|
}
|
|
2467
|
-
if (
|
|
3112
|
+
if (fs8.existsSync(newConfigPath) && !options.force) {
|
|
2468
3113
|
console.log(chalk7.green("\u2713 \u914D\u7F6E\u5DF2\u5728\u65B0\u8DEF\u5F84"));
|
|
2469
3114
|
console.log(chalk7.gray(` \u8DEF\u5F84: ${newConfigPath}`));
|
|
2470
3115
|
console.log(chalk7.gray("\n\u4F7F\u7528 --force \u5F3A\u5236\u91CD\u65B0\u8FC1\u79FB"));
|
|
2471
3116
|
return;
|
|
2472
3117
|
}
|
|
2473
3118
|
try {
|
|
2474
|
-
|
|
2475
|
-
|
|
3119
|
+
ensureCcemDir();
|
|
3120
|
+
fs8.copyFileSync(legacyConfigPath, newConfigPath);
|
|
2476
3121
|
console.log(chalk7.green("\u2713 \u914D\u7F6E\u5DF2\u8FC1\u79FB"));
|
|
2477
3122
|
console.log(chalk7.gray(` \u4ECE: ${legacyConfigPath}`));
|
|
2478
3123
|
console.log(chalk7.gray(` \u5230: ${newConfigPath}`));
|
|
2479
3124
|
if (options.clean) {
|
|
2480
|
-
|
|
2481
|
-
const legacyDir =
|
|
3125
|
+
fs8.unlinkSync(legacyConfigPath);
|
|
3126
|
+
const legacyDir = path6.dirname(legacyConfigPath);
|
|
2482
3127
|
try {
|
|
2483
|
-
|
|
3128
|
+
fs8.rmdirSync(legacyDir);
|
|
2484
3129
|
} catch {
|
|
2485
3130
|
}
|
|
2486
3131
|
console.log(chalk7.green("\u2713 \u5DF2\u5220\u9664\u65E7\u914D\u7F6E\u6587\u4EF6"));
|
|
@@ -2491,13 +3136,13 @@ setupCmd.command("migrate").description("\u8FC1\u79FB\u65E7\u7248\u914D\u7F6E\u5
|
|
|
2491
3136
|
});
|
|
2492
3137
|
setupCmd.command("cron").description("\u5B89\u88C5 ccem-cron skill \u5230 Claude Code\uFF08~/.claude/skills/\uFF09").option("--force", "\u5F3A\u5236\u8986\u76D6\u5DF2\u6709\u6587\u4EF6").action(async function() {
|
|
2493
3138
|
const options = this.opts();
|
|
2494
|
-
const skillDir =
|
|
2495
|
-
const targetPath =
|
|
2496
|
-
if (!
|
|
2497
|
-
|
|
3139
|
+
const skillDir = path6.join(process.env.HOME || "~", ".claude", "skills");
|
|
3140
|
+
const targetPath = path6.join(skillDir, "ccem-cron.md");
|
|
3141
|
+
if (!fs8.existsSync(skillDir)) {
|
|
3142
|
+
fs8.mkdirSync(skillDir, { recursive: true });
|
|
2498
3143
|
console.log(chalk7.gray(`\u521B\u5EFA\u76EE\u5F55: ${skillDir}`));
|
|
2499
3144
|
}
|
|
2500
|
-
if (
|
|
3145
|
+
if (fs8.existsSync(targetPath) && !options.force) {
|
|
2501
3146
|
const { overwrite } = await inquirer.prompt([
|
|
2502
3147
|
{
|
|
2503
3148
|
type: "confirm",
|
|
@@ -2511,7 +3156,7 @@ setupCmd.command("cron").description("\u5B89\u88C5 ccem-cron skill \u5230 Claude
|
|
|
2511
3156
|
return;
|
|
2512
3157
|
}
|
|
2513
3158
|
}
|
|
2514
|
-
|
|
3159
|
+
fs8.writeFileSync(targetPath, CCEM_CRON_SKILL_CONTENT, "utf-8");
|
|
2515
3160
|
console.log(chalk7.green(`\u2713 \u5DF2\u5B89\u88C5 ccem-cron skill`));
|
|
2516
3161
|
console.log(chalk7.gray(` \u8DEF\u5F84: ${targetPath}`));
|
|
2517
3162
|
console.log(chalk7.cyan(`
|
|
@@ -2567,20 +3212,33 @@ skillCmd.command("ls").description("\u5217\u51FA\u5DF2\u5B89\u88C5\u7684 skills"
|
|
|
2567
3212
|
skillCmd.command("rm <name>").description("\u5220\u9664\u5DF2\u5B89\u88C5\u7684 skill").action((name) => {
|
|
2568
3213
|
removeSkill(name);
|
|
2569
3214
|
});
|
|
2570
|
-
program.command("load <url>").description("\u4ECE\u8FDC\u7A0B\u670D\u52A1\u5668\u52A0\u8F7D\u73AF\u5883\u914D\u7F6E").requiredOption("--secret <secret>", "\u89E3\u5BC6\u5BC6\u94A5").action(async (url, options) => {
|
|
2571
|
-
await loadFromRemote(url, options.secret);
|
|
3215
|
+
program.command("load <url>").description("\u4ECE\u8FDC\u7A0B\u670D\u52A1\u5668\u52A0\u8F7D\u73AF\u5883\u914D\u7F6E").requiredOption("--secret <secret>", "\u89E3\u5BC6\u5BC6\u94A5").option("--json", "\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA\u7ED3\u679C\uFF08\u4F9B\u7A0B\u5E8F\u8C03\u7528\uFF09").action(async (url, options) => {
|
|
3216
|
+
const results = await loadFromRemote(url, options.secret);
|
|
3217
|
+
if (options.json) {
|
|
3218
|
+
console.log(JSON.stringify({
|
|
3219
|
+
count: results.length,
|
|
3220
|
+
environments: results.map((r) => ({
|
|
3221
|
+
name: r.name,
|
|
3222
|
+
original_name: r.originalName,
|
|
3223
|
+
// 使用 snake_case 匹配 Rust 结构体
|
|
3224
|
+
renamed: r.renamed
|
|
3225
|
+
}))
|
|
3226
|
+
}));
|
|
3227
|
+
}
|
|
2572
3228
|
});
|
|
2573
|
-
program.command("launch").description(false).option("--env <name>", "\u73AF\u5883\u540D\u79F0").option("--perm <mode>", "\u6743\u9650\u6A21\u5F0F").option("--session-id <id>", "\u4F1A\u8BDD ID").option("--resume-session <id>", "\u6062\u590D\u4F1A\u8BDD ID").option("--working-dir <path>", "\u5DE5\u4F5C\u76EE\u5F55").action(async function() {
|
|
3229
|
+
program.command("launch").description(false).option("--env <name>", "\u73AF\u5883\u540D\u79F0").option("--perm <mode>", "\u6743\u9650\u6A21\u5F0F").option("--session-id <id>", "\u4F1A\u8BDD ID").option("--resume-session <id>", "\u6062\u590D\u4F1A\u8BDD ID").option("--working-dir <path>", "\u5DE5\u4F5C\u76EE\u5F55").option("--proxy-base-url <url>", "Desktop internal override for ANTHROPIC_BASE_URL").option("--anthropic-base-url <url>", "Deprecated alias for --proxy-base-url").action(async function() {
|
|
2574
3230
|
const opts = this.opts();
|
|
2575
3231
|
const envName = opts.env || config2.get("current");
|
|
2576
|
-
const registries =
|
|
3232
|
+
const registries = getRegistries();
|
|
2577
3233
|
const envConfig = registries[envName];
|
|
2578
3234
|
if (!envConfig) {
|
|
2579
3235
|
console.error(chalk7.red(`Environment '${envName}' not found.`));
|
|
2580
3236
|
process.exit(1);
|
|
2581
3237
|
}
|
|
3238
|
+
const proxyBaseUrl = opts.proxyBaseUrl || opts.anthropicBaseUrl;
|
|
3239
|
+
const launchEnvConfig = proxyBaseUrl ? { ...envConfig, ANTHROPIC_BASE_URL: proxyBaseUrl } : envConfig;
|
|
2582
3240
|
await launchClaude({
|
|
2583
|
-
envConfig,
|
|
3241
|
+
envConfig: launchEnvConfig,
|
|
2584
3242
|
permMode: opts.perm,
|
|
2585
3243
|
workingDir: opts.workingDir,
|
|
2586
3244
|
sessionId: opts.sessionId,
|
|
@@ -2616,7 +3274,7 @@ program.action(async (options) => {
|
|
|
2616
3274
|
showCurrentEnv(usageStats, usageLoading);
|
|
2617
3275
|
console.log("");
|
|
2618
3276
|
const defaultMode = config2.get("defaultMode");
|
|
2619
|
-
const registries =
|
|
3277
|
+
const registries = getRegistries();
|
|
2620
3278
|
const current = config2.get("current");
|
|
2621
3279
|
const envConfig = registries[current];
|
|
2622
3280
|
const { action } = await inquirer.prompt([
|
|
@@ -2663,37 +3321,9 @@ program.action(async (options) => {
|
|
|
2663
3321
|
const envToEdit = registries[result.name];
|
|
2664
3322
|
console.log(chalk7.yellow(`
|
|
2665
3323
|
Editing environment '${result.name}'`));
|
|
2666
|
-
const answers = await
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
name: "ANTHROPIC_BASE_URL",
|
|
2670
|
-
message: "ANTHROPIC_BASE_URL:",
|
|
2671
|
-
default: envToEdit.ANTHROPIC_BASE_URL
|
|
2672
|
-
},
|
|
2673
|
-
{
|
|
2674
|
-
type: "password",
|
|
2675
|
-
name: "ANTHROPIC_API_KEY",
|
|
2676
|
-
message: "ANTHROPIC_API_KEY (leave empty to keep current):"
|
|
2677
|
-
},
|
|
2678
|
-
{
|
|
2679
|
-
type: "input",
|
|
2680
|
-
name: "ANTHROPIC_MODEL",
|
|
2681
|
-
message: "ANTHROPIC_MODEL:",
|
|
2682
|
-
default: envToEdit.ANTHROPIC_MODEL
|
|
2683
|
-
},
|
|
2684
|
-
{
|
|
2685
|
-
type: "input",
|
|
2686
|
-
name: "ANTHROPIC_SMALL_FAST_MODEL",
|
|
2687
|
-
message: "ANTHROPIC_SMALL_FAST_MODEL:",
|
|
2688
|
-
default: envToEdit.ANTHROPIC_SMALL_FAST_MODEL
|
|
2689
|
-
}
|
|
2690
|
-
]);
|
|
2691
|
-
if (answers.ANTHROPIC_BASE_URL) envToEdit.ANTHROPIC_BASE_URL = answers.ANTHROPIC_BASE_URL;
|
|
2692
|
-
if (answers.ANTHROPIC_API_KEY) envToEdit.ANTHROPIC_API_KEY = encrypt2(answers.ANTHROPIC_API_KEY);
|
|
2693
|
-
if (answers.ANTHROPIC_MODEL) envToEdit.ANTHROPIC_MODEL = answers.ANTHROPIC_MODEL;
|
|
2694
|
-
if (answers.ANTHROPIC_SMALL_FAST_MODEL) envToEdit.ANTHROPIC_SMALL_FAST_MODEL = answers.ANTHROPIC_SMALL_FAST_MODEL;
|
|
2695
|
-
registries[result.name] = envToEdit;
|
|
2696
|
-
config2.set("registries", registries);
|
|
3324
|
+
const answers = await promptForEnvironmentConfig(envToEdit, true);
|
|
3325
|
+
registries[result.name] = applyPromptAnswers(envToEdit, answers, true);
|
|
3326
|
+
setRegistries(registries);
|
|
2697
3327
|
msg.success(`Environment '${result.name}' updated.`);
|
|
2698
3328
|
await new Promise((resolve2) => setTimeout(resolve2, 800));
|
|
2699
3329
|
} else if (result.action === "rename") {
|
|
@@ -2715,7 +3345,7 @@ Editing environment '${result.name}'`));
|
|
|
2715
3345
|
]);
|
|
2716
3346
|
registries[newName] = registries[result.name];
|
|
2717
3347
|
delete registries[result.name];
|
|
2718
|
-
|
|
3348
|
+
setRegistries(registries);
|
|
2719
3349
|
if (current === result.name) {
|
|
2720
3350
|
config2.set("current", newName);
|
|
2721
3351
|
}
|
|
@@ -2736,7 +3366,7 @@ Editing environment '${result.name}'`));
|
|
|
2736
3366
|
}
|
|
2737
3367
|
]);
|
|
2738
3368
|
registries[targetName] = { ...registries[result.name] };
|
|
2739
|
-
|
|
3369
|
+
setRegistries(registries);
|
|
2740
3370
|
msg.success(`Environment '${result.name}' copied to '${targetName}'.`);
|
|
2741
3371
|
const { modify } = await inquirer.prompt([
|
|
2742
3372
|
{
|
|
@@ -2748,37 +3378,9 @@ Editing environment '${result.name}'`));
|
|
|
2748
3378
|
]);
|
|
2749
3379
|
if (modify) {
|
|
2750
3380
|
const envToEdit = registries[targetName];
|
|
2751
|
-
const editAnswers = await
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
name: "ANTHROPIC_BASE_URL",
|
|
2755
|
-
message: "ANTHROPIC_BASE_URL:",
|
|
2756
|
-
default: envToEdit.ANTHROPIC_BASE_URL
|
|
2757
|
-
},
|
|
2758
|
-
{
|
|
2759
|
-
type: "password",
|
|
2760
|
-
name: "ANTHROPIC_API_KEY",
|
|
2761
|
-
message: "ANTHROPIC_API_KEY (leave empty to keep current):"
|
|
2762
|
-
},
|
|
2763
|
-
{
|
|
2764
|
-
type: "input",
|
|
2765
|
-
name: "ANTHROPIC_MODEL",
|
|
2766
|
-
message: "ANTHROPIC_MODEL:",
|
|
2767
|
-
default: envToEdit.ANTHROPIC_MODEL
|
|
2768
|
-
},
|
|
2769
|
-
{
|
|
2770
|
-
type: "input",
|
|
2771
|
-
name: "ANTHROPIC_SMALL_FAST_MODEL",
|
|
2772
|
-
message: "ANTHROPIC_SMALL_FAST_MODEL:",
|
|
2773
|
-
default: envToEdit.ANTHROPIC_SMALL_FAST_MODEL
|
|
2774
|
-
}
|
|
2775
|
-
]);
|
|
2776
|
-
if (editAnswers.ANTHROPIC_BASE_URL) envToEdit.ANTHROPIC_BASE_URL = editAnswers.ANTHROPIC_BASE_URL;
|
|
2777
|
-
if (editAnswers.ANTHROPIC_API_KEY) envToEdit.ANTHROPIC_API_KEY = encrypt2(editAnswers.ANTHROPIC_API_KEY);
|
|
2778
|
-
if (editAnswers.ANTHROPIC_MODEL) envToEdit.ANTHROPIC_MODEL = editAnswers.ANTHROPIC_MODEL;
|
|
2779
|
-
if (editAnswers.ANTHROPIC_SMALL_FAST_MODEL) envToEdit.ANTHROPIC_SMALL_FAST_MODEL = editAnswers.ANTHROPIC_SMALL_FAST_MODEL;
|
|
2780
|
-
registries[targetName] = envToEdit;
|
|
2781
|
-
config2.set("registries", registries);
|
|
3381
|
+
const editAnswers = await promptForEnvironmentConfig(envToEdit, true);
|
|
3382
|
+
registries[targetName] = applyPromptAnswers(envToEdit, editAnswers, true);
|
|
3383
|
+
setRegistries(registries);
|
|
2782
3384
|
msg.success(`Environment '${targetName}' updated.`);
|
|
2783
3385
|
}
|
|
2784
3386
|
await new Promise((resolve2) => setTimeout(resolve2, 800));
|
|
@@ -2797,7 +3399,7 @@ Editing environment '${result.name}'`));
|
|
|
2797
3399
|
]);
|
|
2798
3400
|
if (confirm) {
|
|
2799
3401
|
delete registries[result.name];
|
|
2800
|
-
|
|
3402
|
+
setRegistries(registries);
|
|
2801
3403
|
if (current === result.name) {
|
|
2802
3404
|
config2.set("current", "official");
|
|
2803
3405
|
msg.warning(`Deleted current environment. Switched back to 'official'.`);
|
|
@@ -2839,7 +3441,7 @@ Editing environment '${result.name}'`));
|
|
|
2839
3441
|
await new Promise((resolve2) => setTimeout(resolve2, 800));
|
|
2840
3442
|
} else if (selectedMode !== "back") {
|
|
2841
3443
|
config2.set("defaultMode", selectedMode);
|
|
2842
|
-
msg.success(`Default mode set: ${
|
|
3444
|
+
msg.success(`Default mode set: ${PERMISSION_PRESETS[selectedMode].name}`);
|
|
2843
3445
|
await new Promise((resolve2) => setTimeout(resolve2, 800));
|
|
2844
3446
|
}
|
|
2845
3447
|
} else {
|