ccem 2.0.0-beta → 2.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +443 -131
- package/package.json +14 -14
package/dist/index.js
CHANGED
|
@@ -7,28 +7,343 @@ 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
|
|
10
|
+
import * as fs8 from "fs";
|
|
11
|
+
import * as path6 from "path";
|
|
12
12
|
import { fileURLToPath } from "url";
|
|
13
|
-
|
|
13
|
+
|
|
14
|
+
// ../../packages/core/dist/chunk-YO7HO5AS.js
|
|
15
|
+
var ENV_PRESETS = {
|
|
16
|
+
"GLM": {
|
|
17
|
+
ANTHROPIC_BASE_URL: "https://open.bigmodel.cn/api/anthropic",
|
|
18
|
+
ANTHROPIC_MODEL: "glm-4.6",
|
|
19
|
+
ANTHROPIC_SMALL_FAST_MODEL: "glm-4.5-air"
|
|
20
|
+
},
|
|
21
|
+
"KIMI": {
|
|
22
|
+
ANTHROPIC_BASE_URL: "https://api.moonshot.cn/anthropic",
|
|
23
|
+
ANTHROPIC_MODEL: "kimi-k2-thinking-turbo",
|
|
24
|
+
ANTHROPIC_SMALL_FAST_MODEL: "kimi-k2-turbo-preview"
|
|
25
|
+
},
|
|
26
|
+
"MiniMax": {
|
|
27
|
+
ANTHROPIC_BASE_URL: "https://api.minimaxi.com/anthropic",
|
|
28
|
+
ANTHROPIC_MODEL: "MiniMax-M2",
|
|
29
|
+
ANTHROPIC_SMALL_FAST_MODEL: "MiniMax-M2"
|
|
30
|
+
},
|
|
31
|
+
"DeepSeek": {
|
|
32
|
+
ANTHROPIC_BASE_URL: "https://api.deepseek.com/anthropic",
|
|
33
|
+
ANTHROPIC_MODEL: "deepseek-chat",
|
|
34
|
+
ANTHROPIC_SMALL_FAST_MODEL: "deepseek-chat"
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
var PERMISSION_PRESETS = {
|
|
38
|
+
"yolo": {
|
|
39
|
+
name: "YOLO \u6A21\u5F0F",
|
|
40
|
+
description: "\u5168\u90E8\u653E\u5F00\uFF0C\u65E0\u4EFB\u4F55\u9650\u5236",
|
|
41
|
+
permissionMode: "bypassPermissions",
|
|
42
|
+
permissions: {
|
|
43
|
+
allow: [
|
|
44
|
+
"Bash(*)",
|
|
45
|
+
"Read(*)",
|
|
46
|
+
"Edit(*)",
|
|
47
|
+
"Write(*)",
|
|
48
|
+
"WebFetch(*)",
|
|
49
|
+
"WebSearch(*)",
|
|
50
|
+
"Glob(*)",
|
|
51
|
+
"Grep(*)",
|
|
52
|
+
"LSP(*)",
|
|
53
|
+
"NotebookEdit(*)"
|
|
54
|
+
],
|
|
55
|
+
deny: []
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"dev": {
|
|
59
|
+
name: "\u5F00\u53D1\u6A21\u5F0F",
|
|
60
|
+
description: "\u65E5\u5E38\u5F00\u53D1\u6743\u9650\uFF0C\u4FDD\u62A4\u654F\u611F\u6587\u4EF6",
|
|
61
|
+
permissionMode: "acceptEdits",
|
|
62
|
+
permissions: {
|
|
63
|
+
allow: [
|
|
64
|
+
"Read(*)",
|
|
65
|
+
"Edit(*)",
|
|
66
|
+
"Write(*)",
|
|
67
|
+
"Glob(*)",
|
|
68
|
+
"Grep(*)",
|
|
69
|
+
"LSP(*)",
|
|
70
|
+
"NotebookEdit(*)",
|
|
71
|
+
"Bash(npm:*)",
|
|
72
|
+
"Bash(pnpm:*)",
|
|
73
|
+
"Bash(yarn:*)",
|
|
74
|
+
"Bash(bun:*)",
|
|
75
|
+
"Bash(node:*)",
|
|
76
|
+
"Bash(npx:*)",
|
|
77
|
+
"Bash(git:*)",
|
|
78
|
+
"Bash(tsc:*)",
|
|
79
|
+
"Bash(tsx:*)",
|
|
80
|
+
"Bash(eslint:*)",
|
|
81
|
+
"Bash(prettier:*)",
|
|
82
|
+
"Bash(jest:*)",
|
|
83
|
+
"Bash(vitest:*)",
|
|
84
|
+
"Bash(cargo:*)",
|
|
85
|
+
"Bash(python:*)",
|
|
86
|
+
"Bash(pip:*)",
|
|
87
|
+
"Bash(go:*)",
|
|
88
|
+
"Bash(make:*)",
|
|
89
|
+
"Bash(cmake:*)",
|
|
90
|
+
"Bash(ls:*)",
|
|
91
|
+
"Bash(cat:*)",
|
|
92
|
+
"Bash(head:*)",
|
|
93
|
+
"Bash(tail:*)",
|
|
94
|
+
"Bash(find:*)",
|
|
95
|
+
"Bash(wc:*)",
|
|
96
|
+
"Bash(mkdir:*)",
|
|
97
|
+
"Bash(cp:*)",
|
|
98
|
+
"Bash(mv:*)",
|
|
99
|
+
"Bash(touch:*)",
|
|
100
|
+
"WebSearch"
|
|
101
|
+
],
|
|
102
|
+
deny: [
|
|
103
|
+
"Read(.env)",
|
|
104
|
+
"Read(.env.*)",
|
|
105
|
+
"Read(**/secrets/**)",
|
|
106
|
+
"Read(**/*.pem)",
|
|
107
|
+
"Read(**/*.key)",
|
|
108
|
+
"Read(**/*credential*)",
|
|
109
|
+
"Bash(rm -rf:*)",
|
|
110
|
+
"Bash(sudo:*)",
|
|
111
|
+
"Bash(chmod:*)",
|
|
112
|
+
"Bash(chown:*)"
|
|
113
|
+
]
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
"readonly": {
|
|
117
|
+
name: "\u53EA\u8BFB\u6A21\u5F0F",
|
|
118
|
+
description: "\u4EC5\u5141\u8BB8\u8BFB\u53D6\u548C\u641C\u7D22\uFF0C\u7981\u6B62\u4EFB\u4F55\u4FEE\u6539",
|
|
119
|
+
permissionMode: "plan",
|
|
120
|
+
permissions: {
|
|
121
|
+
allow: [
|
|
122
|
+
"Read(*)",
|
|
123
|
+
"Glob(*)",
|
|
124
|
+
"Grep(*)",
|
|
125
|
+
"LSP(*)",
|
|
126
|
+
"Bash(git status:*)",
|
|
127
|
+
"Bash(git log:*)",
|
|
128
|
+
"Bash(git diff:*)",
|
|
129
|
+
"Bash(git branch:*)",
|
|
130
|
+
"Bash(git show:*)",
|
|
131
|
+
"Bash(ls:*)",
|
|
132
|
+
"Bash(cat:*)",
|
|
133
|
+
"Bash(head:*)",
|
|
134
|
+
"Bash(tail:*)",
|
|
135
|
+
"Bash(find:*)",
|
|
136
|
+
"Bash(wc:*)",
|
|
137
|
+
"Bash(file:*)",
|
|
138
|
+
"WebSearch"
|
|
139
|
+
],
|
|
140
|
+
deny: [
|
|
141
|
+
"Edit(*)",
|
|
142
|
+
"Write(*)",
|
|
143
|
+
"NotebookEdit(*)",
|
|
144
|
+
"Bash(rm:*)",
|
|
145
|
+
"Bash(mv:*)",
|
|
146
|
+
"Bash(cp:*)",
|
|
147
|
+
"Bash(mkdir:*)",
|
|
148
|
+
"Bash(touch:*)",
|
|
149
|
+
"Bash(git add:*)",
|
|
150
|
+
"Bash(git commit:*)",
|
|
151
|
+
"Bash(git push:*)",
|
|
152
|
+
"Bash(git checkout:*)",
|
|
153
|
+
"Bash(git reset:*)",
|
|
154
|
+
"Bash(npm install:*)",
|
|
155
|
+
"Bash(pnpm install:*)",
|
|
156
|
+
"Bash(yarn add:*)"
|
|
157
|
+
]
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
"safe": {
|
|
161
|
+
name: "\u5B89\u5168\u6A21\u5F0F",
|
|
162
|
+
description: "\u4FDD\u5B88\u6743\u9650\uFF0C\u9002\u5408\u4E0D\u719F\u6089\u7684\u4EE3\u7801\u5E93",
|
|
163
|
+
permissionMode: "default",
|
|
164
|
+
permissions: {
|
|
165
|
+
allow: [
|
|
166
|
+
"Read(*)",
|
|
167
|
+
"Glob(*)",
|
|
168
|
+
"Grep(*)",
|
|
169
|
+
"LSP(*)",
|
|
170
|
+
"Bash(git status:*)",
|
|
171
|
+
"Bash(git log:*)",
|
|
172
|
+
"Bash(git diff:*)",
|
|
173
|
+
"Bash(ls:*)",
|
|
174
|
+
"Bash(cat:*)",
|
|
175
|
+
"Bash(head:*)",
|
|
176
|
+
"Bash(tail:*)",
|
|
177
|
+
"Bash(find:*)",
|
|
178
|
+
"Bash(wc:*)"
|
|
179
|
+
],
|
|
180
|
+
deny: [
|
|
181
|
+
"Read(.env)",
|
|
182
|
+
"Read(.env.*)",
|
|
183
|
+
"Read(**/secrets/**)",
|
|
184
|
+
"Read(**/*.pem)",
|
|
185
|
+
"Read(**/*.key)",
|
|
186
|
+
"Read(**/*credential*)",
|
|
187
|
+
"Read(**/*password*)",
|
|
188
|
+
"Edit(*)",
|
|
189
|
+
"Write(*)",
|
|
190
|
+
"NotebookEdit(*)",
|
|
191
|
+
"Bash(curl:*)",
|
|
192
|
+
"Bash(wget:*)",
|
|
193
|
+
"Bash(ssh:*)",
|
|
194
|
+
"Bash(scp:*)",
|
|
195
|
+
"Bash(rm:*)",
|
|
196
|
+
"Bash(mv:*)",
|
|
197
|
+
"WebFetch(*)"
|
|
198
|
+
]
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
"ci": {
|
|
202
|
+
name: "CI/CD \u6A21\u5F0F",
|
|
203
|
+
description: "\u9002\u5408\u81EA\u52A8\u5316\u6D41\u6C34\u7EBF\u7684\u6743\u9650",
|
|
204
|
+
permissionMode: "default",
|
|
205
|
+
permissions: {
|
|
206
|
+
allow: [
|
|
207
|
+
"Read(*)",
|
|
208
|
+
"Edit(*)",
|
|
209
|
+
"Write(*)",
|
|
210
|
+
"Glob(*)",
|
|
211
|
+
"Grep(*)",
|
|
212
|
+
"LSP(*)",
|
|
213
|
+
"Bash(npm:*)",
|
|
214
|
+
"Bash(pnpm:*)",
|
|
215
|
+
"Bash(yarn:*)",
|
|
216
|
+
"Bash(node:*)",
|
|
217
|
+
"Bash(git:*)",
|
|
218
|
+
"Bash(docker:*)",
|
|
219
|
+
"Bash(make:*)",
|
|
220
|
+
"Bash(cargo:*)",
|
|
221
|
+
"Bash(go:*)",
|
|
222
|
+
"Bash(python:*)",
|
|
223
|
+
"Bash(pip:*)",
|
|
224
|
+
"Bash(pytest:*)",
|
|
225
|
+
"Bash(jest:*)",
|
|
226
|
+
"Bash(vitest:*)"
|
|
227
|
+
],
|
|
228
|
+
deny: [
|
|
229
|
+
"Read(.env.local)",
|
|
230
|
+
"Read(**/secrets/**)",
|
|
231
|
+
"Bash(sudo:*)",
|
|
232
|
+
"Bash(ssh:*)",
|
|
233
|
+
"Bash(scp:*)",
|
|
234
|
+
"WebFetch(*)",
|
|
235
|
+
"WebSearch"
|
|
236
|
+
]
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
"audit": {
|
|
240
|
+
name: "\u5BA1\u8BA1\u6A21\u5F0F",
|
|
241
|
+
description: "\u4EC5\u8BFB\u53D6\u548C\u641C\u7D22\uFF0C\u7528\u4E8E\u5B89\u5168\u5BA1\u8BA1",
|
|
242
|
+
permissionMode: "plan",
|
|
243
|
+
permissions: {
|
|
244
|
+
allow: [
|
|
245
|
+
"Read(*)",
|
|
246
|
+
"Glob(*)",
|
|
247
|
+
"Grep(*)",
|
|
248
|
+
"LSP(*)",
|
|
249
|
+
"Bash(git log:*)",
|
|
250
|
+
"Bash(git blame:*)",
|
|
251
|
+
"Bash(git show:*)",
|
|
252
|
+
"Bash(git diff:*)",
|
|
253
|
+
"Bash(ls:*)",
|
|
254
|
+
"Bash(find:*)",
|
|
255
|
+
"Bash(wc:*)",
|
|
256
|
+
"Bash(file:*)",
|
|
257
|
+
"Bash(stat:*)"
|
|
258
|
+
],
|
|
259
|
+
deny: [
|
|
260
|
+
"Edit(*)",
|
|
261
|
+
"Write(*)",
|
|
262
|
+
"NotebookEdit(*)",
|
|
263
|
+
"Bash(rm:*)",
|
|
264
|
+
"Bash(mv:*)",
|
|
265
|
+
"Bash(cp:*)",
|
|
266
|
+
"Bash(curl:*)",
|
|
267
|
+
"Bash(wget:*)",
|
|
268
|
+
"Bash(ssh:*)",
|
|
269
|
+
"WebFetch(*)"
|
|
270
|
+
]
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
var getPermissionModeNames = () => {
|
|
275
|
+
return Object.keys(PERMISSION_PRESETS);
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
// ../../packages/core/dist/index.js
|
|
279
|
+
import crypto from "crypto";
|
|
280
|
+
import fs from "fs";
|
|
281
|
+
import path from "path";
|
|
282
|
+
var ALGORITHM = "aes-256-cbc";
|
|
283
|
+
var SECRET_KEY = crypto.scryptSync("claude-code-env-manager-secret", "salt", 32);
|
|
284
|
+
var encrypt = (text) => {
|
|
285
|
+
if (!text) return text;
|
|
286
|
+
const iv = crypto.randomBytes(16);
|
|
287
|
+
const cipher = crypto.createCipheriv(ALGORITHM, SECRET_KEY, iv);
|
|
288
|
+
let encrypted = cipher.update(text, "utf8", "hex");
|
|
289
|
+
encrypted += cipher.final("hex");
|
|
290
|
+
return `enc:${iv.toString("hex")}:${encrypted}`;
|
|
291
|
+
};
|
|
292
|
+
var decrypt = (text) => {
|
|
293
|
+
if (!text || !text.startsWith("enc:")) return text;
|
|
294
|
+
try {
|
|
295
|
+
const parts = text.split(":");
|
|
296
|
+
if (parts.length !== 3) return text;
|
|
297
|
+
const iv = Buffer.from(parts[1], "hex");
|
|
298
|
+
const encryptedText = parts[2];
|
|
299
|
+
const decipher = crypto.createDecipheriv(ALGORITHM, SECRET_KEY, iv);
|
|
300
|
+
let decrypted = decipher.update(encryptedText, "hex", "utf8");
|
|
301
|
+
decrypted += decipher.final("utf8");
|
|
302
|
+
return decrypted;
|
|
303
|
+
} catch {
|
|
304
|
+
return text;
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
var getHomeDir = () => {
|
|
308
|
+
return process.env.HOME || process.env.USERPROFILE || "";
|
|
309
|
+
};
|
|
310
|
+
var getCcemConfigDir = () => {
|
|
311
|
+
return path.join(getHomeDir(), ".ccem");
|
|
312
|
+
};
|
|
313
|
+
var getCcemConfigPath = () => {
|
|
314
|
+
return path.join(getCcemConfigDir(), "config.json");
|
|
315
|
+
};
|
|
316
|
+
var ensureCcemDir = () => {
|
|
317
|
+
const ccemDir = getCcemConfigDir();
|
|
318
|
+
if (!fs.existsSync(ccemDir)) {
|
|
319
|
+
fs.mkdirSync(ccemDir, { recursive: true });
|
|
320
|
+
}
|
|
321
|
+
return ccemDir;
|
|
322
|
+
};
|
|
323
|
+
var getLegacyConfigPath = () => {
|
|
324
|
+
const home = getHomeDir();
|
|
325
|
+
if (process.platform === "darwin") {
|
|
326
|
+
return path.join(home, "Library", "Preferences", "claude-code-env-manager-nodejs", "config.json");
|
|
327
|
+
}
|
|
328
|
+
return path.join(home, ".config", "claude-code-env-manager-nodejs", "config.json");
|
|
329
|
+
};
|
|
14
330
|
|
|
15
331
|
// src/ui.ts
|
|
16
332
|
import chalk from "chalk";
|
|
17
333
|
import Table from "cli-table3";
|
|
18
|
-
import { PERMISSION_PRESETS } from "@ccem/core";
|
|
19
334
|
|
|
20
335
|
// src/usage.ts
|
|
21
|
-
import * as
|
|
336
|
+
import * as fs2 from "fs";
|
|
22
337
|
import * as fsPromises from "fs/promises";
|
|
23
|
-
import * as
|
|
338
|
+
import * as path2 from "path";
|
|
24
339
|
import * as os from "os";
|
|
25
340
|
import * as readline from "readline";
|
|
26
|
-
var CLAUDE_PROJECTS_DIR =
|
|
27
|
-
var CCEM_DIR =
|
|
341
|
+
var CLAUDE_PROJECTS_DIR = path2.join(os.homedir(), ".claude", "projects");
|
|
342
|
+
var CCEM_DIR = path2.join(os.homedir(), ".ccem");
|
|
28
343
|
var CACHE_VERSION = 1;
|
|
29
|
-
var getCachePath = () =>
|
|
30
|
-
var getPricesPath = () =>
|
|
31
|
-
async function
|
|
344
|
+
var getCachePath = () => path2.join(CCEM_DIR, "usage-cache.json");
|
|
345
|
+
var getPricesPath = () => path2.join(CCEM_DIR, "model-prices.json");
|
|
346
|
+
async function ensureCcemDir2() {
|
|
32
347
|
try {
|
|
33
348
|
await fsPromises.access(CCEM_DIR);
|
|
34
349
|
} catch {
|
|
@@ -37,7 +352,7 @@ async function ensureCcemDir() {
|
|
|
37
352
|
}
|
|
38
353
|
var LITELLM_PRICES_URL = "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json";
|
|
39
354
|
var getBundledPricesPath = () => {
|
|
40
|
-
return
|
|
355
|
+
return path2.join(__dirname, "..", "model-prices.json");
|
|
41
356
|
};
|
|
42
357
|
var DEFAULT_PRICES = {
|
|
43
358
|
"claude-opus-4-5": {
|
|
@@ -66,7 +381,7 @@ function normalizeModelName(model) {
|
|
|
66
381
|
var pricesCache = null;
|
|
67
382
|
async function loadPrices() {
|
|
68
383
|
if (pricesCache) return pricesCache;
|
|
69
|
-
await
|
|
384
|
+
await ensureCcemDir2();
|
|
70
385
|
const pricesPath = getPricesPath();
|
|
71
386
|
try {
|
|
72
387
|
const response = await fetch(LITELLM_PRICES_URL, { signal: AbortSignal.timeout(1e3) });
|
|
@@ -156,8 +471,8 @@ async function getFileMetaAsync(filePath) {
|
|
|
156
471
|
function loadCacheSync() {
|
|
157
472
|
try {
|
|
158
473
|
const cachePath = getCachePath();
|
|
159
|
-
if (!
|
|
160
|
-
const data = JSON.parse(
|
|
474
|
+
if (!fs2.existsSync(cachePath)) return null;
|
|
475
|
+
const data = JSON.parse(fs2.readFileSync(cachePath, "utf-8"));
|
|
161
476
|
if (data.version !== CACHE_VERSION) return null;
|
|
162
477
|
return data;
|
|
163
478
|
} catch {
|
|
@@ -216,7 +531,7 @@ function getUsageStatsFromCache() {
|
|
|
216
531
|
}
|
|
217
532
|
async function saveCacheAsync(cache) {
|
|
218
533
|
try {
|
|
219
|
-
await
|
|
534
|
+
await ensureCcemDir2();
|
|
220
535
|
await fsPromises.writeFile(getCachePath(), JSON.stringify(cache, null, 2));
|
|
221
536
|
} catch {
|
|
222
537
|
}
|
|
@@ -224,7 +539,7 @@ async function saveCacheAsync(cache) {
|
|
|
224
539
|
async function parseJSONLFileAsync(filePath, prices, signal) {
|
|
225
540
|
const entries = [];
|
|
226
541
|
try {
|
|
227
|
-
const fileStream =
|
|
542
|
+
const fileStream = fs2.createReadStream(filePath, { encoding: "utf-8" });
|
|
228
543
|
const rl = readline.createInterface({
|
|
229
544
|
input: fileStream,
|
|
230
545
|
crlfDelay: Infinity
|
|
@@ -278,14 +593,14 @@ async function getAllJSONLFilesAsync() {
|
|
|
278
593
|
if (!projectsDirExists) return files;
|
|
279
594
|
const projects = await fsPromises.readdir(CLAUDE_PROJECTS_DIR);
|
|
280
595
|
for (const project of projects) {
|
|
281
|
-
const projectPath =
|
|
596
|
+
const projectPath = path2.join(CLAUDE_PROJECTS_DIR, project);
|
|
282
597
|
try {
|
|
283
598
|
const stat2 = await fsPromises.stat(projectPath);
|
|
284
599
|
if (stat2.isDirectory()) {
|
|
285
600
|
const projectFiles = await fsPromises.readdir(projectPath);
|
|
286
601
|
for (const file of projectFiles) {
|
|
287
602
|
if (file.endsWith(".jsonl")) {
|
|
288
|
-
files.push(
|
|
603
|
+
files.push(path2.join(projectPath, file));
|
|
289
604
|
}
|
|
290
605
|
}
|
|
291
606
|
}
|
|
@@ -506,10 +821,10 @@ var renderLogoWithEnvPanel = (envName, env, defaultMode) => {
|
|
|
506
821
|
const parsed = new URL(url);
|
|
507
822
|
const protocol = parsed.protocol + "//";
|
|
508
823
|
const host = parsed.host;
|
|
509
|
-
const
|
|
824
|
+
const path7 = parsed.pathname + parsed.search;
|
|
510
825
|
const hostStart = host.slice(0, 8);
|
|
511
826
|
const hostEnd = host.slice(-4);
|
|
512
|
-
const pathPart =
|
|
827
|
+
const pathPart = path7.length > 10 ? path7.slice(0, 7) + "..." : path7;
|
|
513
828
|
return `${protocol}${hostStart}...${hostEnd}${pathPart}`;
|
|
514
829
|
} catch {
|
|
515
830
|
return truncate(url, max);
|
|
@@ -947,64 +1262,62 @@ var selectEnvWithKeys = (registries, current) => {
|
|
|
947
1262
|
};
|
|
948
1263
|
|
|
949
1264
|
// src/permissions.ts
|
|
950
|
-
import
|
|
1265
|
+
import fs5 from "fs";
|
|
951
1266
|
import chalk3 from "chalk";
|
|
952
1267
|
import Table2 from "cli-table3";
|
|
953
|
-
import { PERMISSION_PRESETS as PERMISSION_PRESETS3, getPermissionModeNames } from "@ccem/core";
|
|
954
1268
|
|
|
955
1269
|
// src/utils.ts
|
|
956
|
-
import
|
|
957
|
-
import
|
|
958
|
-
import
|
|
959
|
-
var
|
|
1270
|
+
import crypto2 from "crypto";
|
|
1271
|
+
import fs3 from "fs";
|
|
1272
|
+
import path3 from "path";
|
|
1273
|
+
var SECRET_KEY2 = crypto2.scryptSync("claude-code-env-manager-secret", "salt", 32);
|
|
960
1274
|
var findProjectRoot = () => {
|
|
961
1275
|
let currentDir = process.cwd();
|
|
962
|
-
const root =
|
|
1276
|
+
const root = path3.parse(currentDir).root;
|
|
963
1277
|
while (currentDir !== root) {
|
|
964
|
-
if (
|
|
1278
|
+
if (fs3.existsSync(path3.join(currentDir, ".git")) || fs3.existsSync(path3.join(currentDir, "package.json"))) {
|
|
965
1279
|
return currentDir;
|
|
966
1280
|
}
|
|
967
|
-
currentDir =
|
|
1281
|
+
currentDir = path3.dirname(currentDir);
|
|
968
1282
|
}
|
|
969
1283
|
return process.cwd();
|
|
970
1284
|
};
|
|
971
1285
|
var getSettingsPath = (useLocal = true) => {
|
|
972
1286
|
const projectRoot = findProjectRoot();
|
|
973
|
-
const claudeDir =
|
|
1287
|
+
const claudeDir = path3.join(projectRoot, ".claude");
|
|
974
1288
|
const filename = useLocal ? "settings.local.json" : "settings.json";
|
|
975
|
-
return
|
|
1289
|
+
return path3.join(claudeDir, filename);
|
|
976
1290
|
};
|
|
977
1291
|
var ensureClaudeDir = () => {
|
|
978
1292
|
const projectRoot = findProjectRoot();
|
|
979
|
-
const claudeDir =
|
|
980
|
-
if (!
|
|
981
|
-
|
|
1293
|
+
const claudeDir = path3.join(projectRoot, ".claude");
|
|
1294
|
+
if (!fs3.existsSync(claudeDir)) {
|
|
1295
|
+
fs3.mkdirSync(claudeDir, { recursive: true });
|
|
982
1296
|
}
|
|
983
1297
|
return claudeDir;
|
|
984
1298
|
};
|
|
985
|
-
var
|
|
1299
|
+
var getHomeDir2 = () => {
|
|
986
1300
|
return process.env.HOME || process.env.USERPROFILE || "";
|
|
987
1301
|
};
|
|
988
1302
|
var getGlobalClaudeConfigPath = () => {
|
|
989
|
-
return
|
|
1303
|
+
return path3.join(getHomeDir2(), ".claude.json");
|
|
990
1304
|
};
|
|
991
1305
|
var getGlobalClaudeSettingsPath = () => {
|
|
992
|
-
return
|
|
1306
|
+
return path3.join(getHomeDir2(), ".claude", "settings.json");
|
|
993
1307
|
};
|
|
994
1308
|
var ensureGlobalClaudeDir = () => {
|
|
995
|
-
const claudeDir =
|
|
996
|
-
if (!
|
|
997
|
-
|
|
1309
|
+
const claudeDir = path3.join(getHomeDir2(), ".claude");
|
|
1310
|
+
if (!fs3.existsSync(claudeDir)) {
|
|
1311
|
+
fs3.mkdirSync(claudeDir, { recursive: true });
|
|
998
1312
|
}
|
|
999
1313
|
return claudeDir;
|
|
1000
1314
|
};
|
|
1001
1315
|
|
|
1002
1316
|
// src/launcher.ts
|
|
1003
1317
|
import { spawn } from "child_process";
|
|
1004
|
-
import * as
|
|
1005
|
-
import * as
|
|
1318
|
+
import * as fs4 from "fs";
|
|
1319
|
+
import * as path4 from "path";
|
|
1006
1320
|
import chalk2 from "chalk";
|
|
1007
|
-
import { decrypt, PERMISSION_PRESETS as PERMISSION_PRESETS2, ensureCcemDir as ensureCcemDir2 } from "@ccem/core";
|
|
1008
1321
|
function buildEnvVars(envConfig) {
|
|
1009
1322
|
const vars = {};
|
|
1010
1323
|
if (envConfig.ANTHROPIC_BASE_URL) vars.ANTHROPIC_BASE_URL = envConfig.ANTHROPIC_BASE_URL;
|
|
@@ -1014,7 +1327,7 @@ function buildEnvVars(envConfig) {
|
|
|
1014
1327
|
return vars;
|
|
1015
1328
|
}
|
|
1016
1329
|
function buildPermArgs(modeName) {
|
|
1017
|
-
const preset =
|
|
1330
|
+
const preset = PERMISSION_PRESETS[modeName];
|
|
1018
1331
|
if (!preset) return [];
|
|
1019
1332
|
const args = ["--permission-mode", preset.permissionMode];
|
|
1020
1333
|
if (preset.permissions.allow.length > 0) {
|
|
@@ -1028,9 +1341,9 @@ function buildPermArgs(modeName) {
|
|
|
1028
1341
|
return args;
|
|
1029
1342
|
}
|
|
1030
1343
|
function ensureSessionsDir() {
|
|
1031
|
-
const dir =
|
|
1032
|
-
if (!
|
|
1033
|
-
|
|
1344
|
+
const dir = path4.join(ensureCcemDir(), "sessions");
|
|
1345
|
+
if (!fs4.existsSync(dir)) {
|
|
1346
|
+
fs4.mkdirSync(dir, { recursive: true });
|
|
1034
1347
|
}
|
|
1035
1348
|
return dir;
|
|
1036
1349
|
}
|
|
@@ -1043,7 +1356,7 @@ async function launchClaude(options) {
|
|
|
1043
1356
|
delete env.CLAUDECODE;
|
|
1044
1357
|
const args = [];
|
|
1045
1358
|
if (permMode) {
|
|
1046
|
-
const preset =
|
|
1359
|
+
const preset = PERMISSION_PRESETS[permMode];
|
|
1047
1360
|
if (preset) {
|
|
1048
1361
|
if (!silent) {
|
|
1049
1362
|
console.log(chalk2.green(`\u5DF2\u5E94\u7528 ${preset.name}\uFF08\u4E34\u65F6\uFF09`));
|
|
@@ -1072,8 +1385,8 @@ async function launchClaude(options) {
|
|
|
1072
1385
|
child.on("exit", (code) => {
|
|
1073
1386
|
if (sessionId) {
|
|
1074
1387
|
try {
|
|
1075
|
-
|
|
1076
|
-
|
|
1388
|
+
fs4.writeFileSync(
|
|
1389
|
+
path4.join(sessionsDir, `${sessionId}.exit`),
|
|
1077
1390
|
String(code ?? 0)
|
|
1078
1391
|
);
|
|
1079
1392
|
} catch {
|
|
@@ -1090,14 +1403,14 @@ async function launchClaude(options) {
|
|
|
1090
1403
|
|
|
1091
1404
|
// src/permissions.ts
|
|
1092
1405
|
var readSettings = (settingsPath) => {
|
|
1093
|
-
if (
|
|
1406
|
+
if (fs5.existsSync(settingsPath)) {
|
|
1094
1407
|
try {
|
|
1095
|
-
const content =
|
|
1408
|
+
const content = fs5.readFileSync(settingsPath, "utf-8");
|
|
1096
1409
|
return JSON.parse(content);
|
|
1097
1410
|
} catch {
|
|
1098
1411
|
console.warn(chalk3.yellow(`\u8B66\u544A: \u65E0\u6CD5\u89E3\u6790 ${settingsPath}\uFF0C\u5C06\u521B\u5EFA\u5907\u4EFD`));
|
|
1099
1412
|
const backupPath = settingsPath + ".error." + Date.now();
|
|
1100
|
-
|
|
1413
|
+
fs5.copyFileSync(settingsPath, backupPath);
|
|
1101
1414
|
console.log(chalk3.gray(`\u5907\u4EFD\u5DF2\u4FDD\u5B58\u5230: ${backupPath}`));
|
|
1102
1415
|
return {};
|
|
1103
1416
|
}
|
|
@@ -1106,7 +1419,7 @@ var readSettings = (settingsPath) => {
|
|
|
1106
1419
|
};
|
|
1107
1420
|
var writeSettings = (settingsPath, config3) => {
|
|
1108
1421
|
ensureClaudeDir();
|
|
1109
|
-
|
|
1422
|
+
fs5.writeFileSync(settingsPath, JSON.stringify(config3, null, 2) + "\n");
|
|
1110
1423
|
};
|
|
1111
1424
|
var mergePermissions = (existing, preset) => {
|
|
1112
1425
|
const existingAllow = existing.permissions?.allow || [];
|
|
@@ -1122,7 +1435,7 @@ var mergePermissions = (existing, preset) => {
|
|
|
1122
1435
|
};
|
|
1123
1436
|
};
|
|
1124
1437
|
var applyPermissionMode = (modeName) => {
|
|
1125
|
-
const preset =
|
|
1438
|
+
const preset = PERMISSION_PRESETS[modeName];
|
|
1126
1439
|
if (!preset) {
|
|
1127
1440
|
console.error(chalk3.red(`\u672A\u77E5\u7684\u6743\u9650\u6A21\u5F0F: ${modeName}`));
|
|
1128
1441
|
console.log(chalk3.yellow("\u53EF\u7528\u6A21\u5F0F: " + getPermissionModeNames().join(", ")));
|
|
@@ -1138,14 +1451,14 @@ var applyPermissionMode = (modeName) => {
|
|
|
1138
1451
|
};
|
|
1139
1452
|
var resetPermissions = () => {
|
|
1140
1453
|
const settingsPath = getSettingsPath(true);
|
|
1141
|
-
if (!
|
|
1454
|
+
if (!fs5.existsSync(settingsPath)) {
|
|
1142
1455
|
console.log(chalk3.yellow("\u6CA1\u6709\u81EA\u5B9A\u4E49\u6743\u9650\u914D\u7F6E\u9700\u8981\u91CD\u7F6E"));
|
|
1143
1456
|
return;
|
|
1144
1457
|
}
|
|
1145
1458
|
const config3 = readSettings(settingsPath);
|
|
1146
1459
|
delete config3.permissions;
|
|
1147
1460
|
if (Object.keys(config3).length === 0) {
|
|
1148
|
-
|
|
1461
|
+
fs5.unlinkSync(settingsPath);
|
|
1149
1462
|
console.log(chalk3.green("\u5DF2\u5220\u9664\u914D\u7F6E\u6587\u4EF6\uFF08\u6587\u4EF6\u4E3A\u7A7A\uFF09"));
|
|
1150
1463
|
} else {
|
|
1151
1464
|
writeSettings(settingsPath, config3);
|
|
@@ -1155,7 +1468,7 @@ var resetPermissions = () => {
|
|
|
1155
1468
|
};
|
|
1156
1469
|
var showCurrentMode = () => {
|
|
1157
1470
|
const settingsPath = getSettingsPath(true);
|
|
1158
|
-
if (!
|
|
1471
|
+
if (!fs5.existsSync(settingsPath)) {
|
|
1159
1472
|
console.log(chalk3.yellow("\u672A\u914D\u7F6E\u81EA\u5B9A\u4E49\u6743\u9650"));
|
|
1160
1473
|
console.log(chalk3.gray(`\u6587\u4EF6\u4E0D\u5B58\u5728: ${settingsPath}`));
|
|
1161
1474
|
return;
|
|
@@ -1165,7 +1478,7 @@ var showCurrentMode = () => {
|
|
|
1165
1478
|
console.log(chalk3.yellow("\u672A\u914D\u7F6E\u81EA\u5B9A\u4E49\u6743\u9650"));
|
|
1166
1479
|
return;
|
|
1167
1480
|
}
|
|
1168
|
-
const matchedPreset = Object.entries(
|
|
1481
|
+
const matchedPreset = Object.entries(PERMISSION_PRESETS).find(([_, preset]) => {
|
|
1169
1482
|
const configAllow = new Set(config3.permissions?.allow || []);
|
|
1170
1483
|
const configDeny = new Set(config3.permissions?.deny || []);
|
|
1171
1484
|
const presetAllow = new Set(preset.permissions.allow);
|
|
@@ -1197,7 +1510,7 @@ var listAvailableModes = () => {
|
|
|
1197
1510
|
style: { head: ["cyan"] },
|
|
1198
1511
|
colWidths: [15, 15, 50]
|
|
1199
1512
|
});
|
|
1200
|
-
Object.entries(
|
|
1513
|
+
Object.entries(PERMISSION_PRESETS).forEach(([key, preset]) => {
|
|
1201
1514
|
table.push([preset.name, `--${key}`, preset.description]);
|
|
1202
1515
|
});
|
|
1203
1516
|
console.log(chalk3.bold("\u53EF\u7528\u6743\u9650\u6A21\u5F0F:\n"));
|
|
@@ -1206,7 +1519,7 @@ var listAvailableModes = () => {
|
|
|
1206
1519
|
console.log(chalk3.gray("\u6C38\u4E45\u6A21\u5F0F: ccem setup perms --<mode>"));
|
|
1207
1520
|
};
|
|
1208
1521
|
var runWithTempPermissions = async (modeName, envConfig) => {
|
|
1209
|
-
const preset =
|
|
1522
|
+
const preset = PERMISSION_PRESETS[modeName];
|
|
1210
1523
|
if (!preset) {
|
|
1211
1524
|
console.error(chalk3.red(`\u672A\u77E5\u7684\u6743\u9650\u6A21\u5F0F: ${modeName}`));
|
|
1212
1525
|
console.log(chalk3.yellow("\u53EF\u7528\u6A21\u5F0F: " + getPermissionModeNames().join(", ")));
|
|
@@ -1216,13 +1529,13 @@ var runWithTempPermissions = async (modeName, envConfig) => {
|
|
|
1216
1529
|
};
|
|
1217
1530
|
|
|
1218
1531
|
// src/setup.ts
|
|
1219
|
-
import
|
|
1532
|
+
import fs6 from "fs";
|
|
1220
1533
|
import chalk4 from "chalk";
|
|
1221
1534
|
import { spawn as spawn2 } from "child_process";
|
|
1222
1535
|
var readJsonFile = (filePath) => {
|
|
1223
|
-
if (
|
|
1536
|
+
if (fs6.existsSync(filePath)) {
|
|
1224
1537
|
try {
|
|
1225
|
-
const content =
|
|
1538
|
+
const content = fs6.readFileSync(filePath, "utf-8");
|
|
1226
1539
|
return JSON.parse(content);
|
|
1227
1540
|
} catch {
|
|
1228
1541
|
console.warn(chalk4.yellow(`\u8B66\u544A: \u65E0\u6CD5\u89E3\u6790 ${filePath}`));
|
|
@@ -1232,7 +1545,7 @@ var readJsonFile = (filePath) => {
|
|
|
1232
1545
|
return {};
|
|
1233
1546
|
};
|
|
1234
1547
|
var writeJsonFile = (filePath, data) => {
|
|
1235
|
-
|
|
1548
|
+
fs6.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
|
|
1236
1549
|
};
|
|
1237
1550
|
var setupOnboarding = () => {
|
|
1238
1551
|
const configPath = getGlobalClaudeConfigPath();
|
|
@@ -1354,8 +1667,8 @@ var runSetupInit = async () => {
|
|
|
1354
1667
|
|
|
1355
1668
|
// src/skills.ts
|
|
1356
1669
|
import { execSync } from "child_process";
|
|
1357
|
-
import * as
|
|
1358
|
-
import * as
|
|
1670
|
+
import * as fs7 from "fs";
|
|
1671
|
+
import * as path5 from "path";
|
|
1359
1672
|
import chalk5 from "chalk";
|
|
1360
1673
|
var SKILL_GROUPS = {
|
|
1361
1674
|
official: { label: "\u5B98\u65B9", icon: "\u{1F3E2}" },
|
|
@@ -1524,12 +1837,12 @@ function parseGitHubUrl(url) {
|
|
|
1524
1837
|
};
|
|
1525
1838
|
}
|
|
1526
1839
|
function getSkillsDir() {
|
|
1527
|
-
return
|
|
1840
|
+
return path5.join(process.cwd(), ".claude", "skills");
|
|
1528
1841
|
}
|
|
1529
1842
|
function ensureSkillsDir() {
|
|
1530
1843
|
const skillsDir = getSkillsDir();
|
|
1531
|
-
if (!
|
|
1532
|
-
|
|
1844
|
+
if (!fs7.existsSync(skillsDir)) {
|
|
1845
|
+
fs7.mkdirSync(skillsDir, { recursive: true });
|
|
1533
1846
|
} else {
|
|
1534
1847
|
cleanupTempDirs(skillsDir);
|
|
1535
1848
|
}
|
|
@@ -1537,11 +1850,11 @@ function ensureSkillsDir() {
|
|
|
1537
1850
|
}
|
|
1538
1851
|
function cleanupTempDirs(skillsDir) {
|
|
1539
1852
|
try {
|
|
1540
|
-
const entries =
|
|
1853
|
+
const entries = fs7.readdirSync(skillsDir, { withFileTypes: true });
|
|
1541
1854
|
for (const entry of entries) {
|
|
1542
1855
|
if (entry.isDirectory() && entry.name.startsWith(".tmp-")) {
|
|
1543
|
-
const tmpPath =
|
|
1544
|
-
|
|
1856
|
+
const tmpPath = path5.join(skillsDir, entry.name);
|
|
1857
|
+
fs7.rmSync(tmpPath, { recursive: true });
|
|
1545
1858
|
}
|
|
1546
1859
|
}
|
|
1547
1860
|
} catch {
|
|
@@ -1549,24 +1862,24 @@ function cleanupTempDirs(skillsDir) {
|
|
|
1549
1862
|
}
|
|
1550
1863
|
function downloadSkillWithGit(owner, repo, branch, repoPath, targetName) {
|
|
1551
1864
|
const skillsDir = ensureSkillsDir();
|
|
1552
|
-
const targetDir =
|
|
1553
|
-
if (
|
|
1865
|
+
const targetDir = path5.join(skillsDir, targetName);
|
|
1866
|
+
if (fs7.existsSync(targetDir)) {
|
|
1554
1867
|
console.log(chalk5.yellow(`Skill "${targetName}" already exists. Updating...`));
|
|
1555
|
-
|
|
1868
|
+
fs7.rmSync(targetDir, { recursive: true });
|
|
1556
1869
|
}
|
|
1557
1870
|
const repoUrl = `https://github.com/${owner}/${repo}.git`;
|
|
1558
|
-
const tempDir =
|
|
1871
|
+
const tempDir = path5.join(skillsDir, `.tmp-${Date.now()}`);
|
|
1559
1872
|
try {
|
|
1560
|
-
|
|
1873
|
+
fs7.mkdirSync(tempDir, { recursive: true });
|
|
1561
1874
|
execSync(`git init`, { cwd: tempDir, stdio: "pipe" });
|
|
1562
1875
|
execSync(`git remote add origin ${repoUrl}`, { cwd: tempDir, stdio: "pipe" });
|
|
1563
1876
|
execSync(`git config core.sparseCheckout true`, { cwd: tempDir, stdio: "pipe" });
|
|
1564
|
-
const sparseFile =
|
|
1565
|
-
|
|
1877
|
+
const sparseFile = path5.join(tempDir, ".git", "info", "sparse-checkout");
|
|
1878
|
+
fs7.writeFileSync(sparseFile, repoPath ? `${repoPath}/
|
|
1566
1879
|
` : "*\n");
|
|
1567
1880
|
execSync(`git pull --depth=1 origin ${branch}`, { cwd: tempDir, stdio: "pipe" });
|
|
1568
|
-
const sourceDir = repoPath ?
|
|
1569
|
-
if (!
|
|
1881
|
+
const sourceDir = repoPath ? path5.join(tempDir, repoPath) : tempDir;
|
|
1882
|
+
if (!fs7.existsSync(sourceDir)) {
|
|
1570
1883
|
throw new Error(`Path "${repoPath}" not found in repository`);
|
|
1571
1884
|
}
|
|
1572
1885
|
copyDir(sourceDir, targetDir);
|
|
@@ -1577,22 +1890,22 @@ function downloadSkillWithGit(owner, repo, branch, repoPath, targetName) {
|
|
|
1577
1890
|
console.error(chalk5.red(`Failed to download skill: ${errMsg}`));
|
|
1578
1891
|
return false;
|
|
1579
1892
|
} finally {
|
|
1580
|
-
if (
|
|
1581
|
-
|
|
1893
|
+
if (fs7.existsSync(tempDir)) {
|
|
1894
|
+
fs7.rmSync(tempDir, { recursive: true });
|
|
1582
1895
|
}
|
|
1583
1896
|
}
|
|
1584
1897
|
}
|
|
1585
1898
|
function copyDir(src, dest) {
|
|
1586
|
-
|
|
1587
|
-
const entries =
|
|
1899
|
+
fs7.mkdirSync(dest, { recursive: true });
|
|
1900
|
+
const entries = fs7.readdirSync(src, { withFileTypes: true });
|
|
1588
1901
|
for (const entry of entries) {
|
|
1589
1902
|
if (entry.name === ".git") continue;
|
|
1590
|
-
const srcPath =
|
|
1591
|
-
const destPath =
|
|
1903
|
+
const srcPath = path5.join(src, entry.name);
|
|
1904
|
+
const destPath = path5.join(dest, entry.name);
|
|
1592
1905
|
if (entry.isDirectory()) {
|
|
1593
1906
|
copyDir(srcPath, destPath);
|
|
1594
1907
|
} else {
|
|
1595
|
-
|
|
1908
|
+
fs7.copyFileSync(srcPath, destPath);
|
|
1596
1909
|
}
|
|
1597
1910
|
}
|
|
1598
1911
|
}
|
|
@@ -1638,7 +1951,7 @@ function addSkillFromGitHub(urlOrPreset) {
|
|
|
1638
1951
|
}
|
|
1639
1952
|
let skillName;
|
|
1640
1953
|
if (parsed.path) {
|
|
1641
|
-
skillName =
|
|
1954
|
+
skillName = path5.basename(parsed.path);
|
|
1642
1955
|
} else {
|
|
1643
1956
|
skillName = parsed.repo;
|
|
1644
1957
|
}
|
|
@@ -1652,23 +1965,23 @@ function addSkillFromGitHub(urlOrPreset) {
|
|
|
1652
1965
|
}
|
|
1653
1966
|
function listInstalledSkills() {
|
|
1654
1967
|
const skillsDir = getSkillsDir();
|
|
1655
|
-
if (!
|
|
1968
|
+
if (!fs7.existsSync(skillsDir)) {
|
|
1656
1969
|
return [];
|
|
1657
1970
|
}
|
|
1658
|
-
const entries =
|
|
1971
|
+
const entries = fs7.readdirSync(skillsDir, { withFileTypes: true });
|
|
1659
1972
|
return entries.filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => ({
|
|
1660
1973
|
name: entry.name,
|
|
1661
|
-
path:
|
|
1974
|
+
path: path5.join(skillsDir, entry.name)
|
|
1662
1975
|
}));
|
|
1663
1976
|
}
|
|
1664
1977
|
function removeSkill(name) {
|
|
1665
1978
|
const skillsDir = getSkillsDir();
|
|
1666
|
-
const targetDir =
|
|
1667
|
-
if (!
|
|
1979
|
+
const targetDir = path5.join(skillsDir, name);
|
|
1980
|
+
if (!fs7.existsSync(targetDir)) {
|
|
1668
1981
|
console.error(chalk5.red(`Skill "${name}" not found`));
|
|
1669
1982
|
return false;
|
|
1670
1983
|
}
|
|
1671
|
-
|
|
1984
|
+
fs7.rmSync(targetDir, { recursive: true });
|
|
1672
1985
|
console.log(chalk5.green(`Removed skill "${name}"`));
|
|
1673
1986
|
return true;
|
|
1674
1987
|
}
|
|
@@ -1826,19 +2139,18 @@ async function runSkillSelector() {
|
|
|
1826
2139
|
}
|
|
1827
2140
|
|
|
1828
2141
|
// src/remote.ts
|
|
1829
|
-
import
|
|
2142
|
+
import crypto3 from "crypto";
|
|
1830
2143
|
import chalk6 from "chalk";
|
|
1831
2144
|
import Conf from "conf";
|
|
1832
|
-
import { encrypt } from "@ccem/core";
|
|
1833
2145
|
var config = new Conf({
|
|
1834
2146
|
projectName: "claude-code-env-manager"
|
|
1835
2147
|
});
|
|
1836
2148
|
var decryptWithSecret = (encryptedBase64, secret) => {
|
|
1837
|
-
const key =
|
|
2149
|
+
const key = crypto3.scryptSync(secret, "ccem-salt", 32);
|
|
1838
2150
|
const combined = Buffer.from(encryptedBase64, "base64");
|
|
1839
2151
|
const iv = combined.subarray(0, 16);
|
|
1840
2152
|
const encryptedHex = combined.subarray(16).toString("hex");
|
|
1841
|
-
const decipher =
|
|
2153
|
+
const decipher = crypto3.createDecipheriv("aes-256-cbc", key, iv);
|
|
1842
2154
|
let decrypted = decipher.update(encryptedHex, "hex", "utf8");
|
|
1843
2155
|
decrypted += decipher.final("utf8");
|
|
1844
2156
|
return decrypted;
|
|
@@ -2075,11 +2387,11 @@ Replace \\\`TARGET_ID\\\` or \\\`TARGET_NAME\\\` with the user's selection.
|
|
|
2075
2387
|
|
|
2076
2388
|
// src/index.ts
|
|
2077
2389
|
var __filename = fileURLToPath(import.meta.url);
|
|
2078
|
-
var __dirname2 =
|
|
2079
|
-
var pkgPath =
|
|
2080
|
-
var pkg = JSON.parse(
|
|
2390
|
+
var __dirname2 = path6.dirname(__filename);
|
|
2391
|
+
var pkgPath = path6.resolve(__dirname2, "..", "package.json");
|
|
2392
|
+
var pkg = JSON.parse(fs8.readFileSync(pkgPath, "utf-8"));
|
|
2081
2393
|
var program = new Command();
|
|
2082
|
-
|
|
2394
|
+
ensureCcemDir();
|
|
2083
2395
|
var config2 = new Conf2({
|
|
2084
2396
|
projectName: "claude-code-env-manager",
|
|
2085
2397
|
cwd: getCcemConfigDir(),
|
|
@@ -2132,7 +2444,7 @@ var initUsageStats = (onUpdate) => {
|
|
|
2132
2444
|
};
|
|
2133
2445
|
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
2446
|
PERMISSION_MODES.forEach((mode) => {
|
|
2135
|
-
const preset =
|
|
2447
|
+
const preset = PERMISSION_PRESETS[mode];
|
|
2136
2448
|
program.command(mode).description(`\u4E34\u65F6\u5E94\u7528 ${preset.name}\uFF0C\u9000\u51FA\u540E\u8FD8\u539F`).action(async () => {
|
|
2137
2449
|
const registries = config2.get("registries");
|
|
2138
2450
|
const current = config2.get("current");
|
|
@@ -2149,7 +2461,7 @@ var showCurrentEnv = (usageStats2, usageLoading2) => {
|
|
|
2149
2461
|
if (!env) return;
|
|
2150
2462
|
console.log(renderLogoWithEnvPanel(current, {
|
|
2151
2463
|
ANTHROPIC_BASE_URL: env.ANTHROPIC_BASE_URL,
|
|
2152
|
-
ANTHROPIC_API_KEY: env.ANTHROPIC_API_KEY ?
|
|
2464
|
+
ANTHROPIC_API_KEY: env.ANTHROPIC_API_KEY ? decrypt(env.ANTHROPIC_API_KEY) : void 0,
|
|
2153
2465
|
ANTHROPIC_MODEL: env.ANTHROPIC_MODEL,
|
|
2154
2466
|
ANTHROPIC_SMALL_FAST_MODEL: env.ANTHROPIC_SMALL_FAST_MODEL
|
|
2155
2467
|
}, defaultMode));
|
|
@@ -2174,7 +2486,7 @@ var switchEnvironment = async (name) => {
|
|
|
2174
2486
|
const env = registries[name];
|
|
2175
2487
|
const exportCmds = [];
|
|
2176
2488
|
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="${
|
|
2489
|
+
if (env.ANTHROPIC_API_KEY) exportCmds.push(`export ANTHROPIC_API_KEY="${decrypt(env.ANTHROPIC_API_KEY)}"`);
|
|
2178
2490
|
if (env.ANTHROPIC_MODEL) exportCmds.push(`export ANTHROPIC_MODEL="${env.ANTHROPIC_MODEL}"`);
|
|
2179
2491
|
if (env.ANTHROPIC_SMALL_FAST_MODEL) exportCmds.push(`export ANTHROPIC_SMALL_FAST_MODEL="${env.ANTHROPIC_SMALL_FAST_MODEL}"`);
|
|
2180
2492
|
if (process.stdout.isTTY) {
|
|
@@ -2259,7 +2571,7 @@ program.command("add <name>").description("Add a new environment configuration")
|
|
|
2259
2571
|
}
|
|
2260
2572
|
]);
|
|
2261
2573
|
if (answers.ANTHROPIC_API_KEY) {
|
|
2262
|
-
answers.ANTHROPIC_API_KEY =
|
|
2574
|
+
answers.ANTHROPIC_API_KEY = encrypt(answers.ANTHROPIC_API_KEY);
|
|
2263
2575
|
}
|
|
2264
2576
|
registries[name] = answers;
|
|
2265
2577
|
config2.set("registries", registries);
|
|
@@ -2356,7 +2668,7 @@ program.command("cp <source> <target>").description("Copy an environment configu
|
|
|
2356
2668
|
}
|
|
2357
2669
|
]);
|
|
2358
2670
|
if (answers.ANTHROPIC_BASE_URL) current.ANTHROPIC_BASE_URL = answers.ANTHROPIC_BASE_URL;
|
|
2359
|
-
if (answers.ANTHROPIC_API_KEY) current.ANTHROPIC_API_KEY =
|
|
2671
|
+
if (answers.ANTHROPIC_API_KEY) current.ANTHROPIC_API_KEY = encrypt(answers.ANTHROPIC_API_KEY);
|
|
2360
2672
|
if (answers.ANTHROPIC_MODEL) current.ANTHROPIC_MODEL = answers.ANTHROPIC_MODEL;
|
|
2361
2673
|
if (answers.ANTHROPIC_SMALL_FAST_MODEL) current.ANTHROPIC_SMALL_FAST_MODEL = answers.ANTHROPIC_SMALL_FAST_MODEL;
|
|
2362
2674
|
registries[target] = current;
|
|
@@ -2375,7 +2687,7 @@ program.command("env").description("Output environment variables for shell eval"
|
|
|
2375
2687
|
if (!env) return;
|
|
2376
2688
|
const outputEnv = { ...env };
|
|
2377
2689
|
if (outputEnv.ANTHROPIC_API_KEY) {
|
|
2378
|
-
outputEnv.ANTHROPIC_API_KEY =
|
|
2690
|
+
outputEnv.ANTHROPIC_API_KEY = decrypt(outputEnv.ANTHROPIC_API_KEY);
|
|
2379
2691
|
}
|
|
2380
2692
|
if (options.json) {
|
|
2381
2693
|
console.log(JSON.stringify(outputEnv, null, 2));
|
|
@@ -2396,7 +2708,7 @@ program.command("run <command...>").description("Run a command with the current
|
|
|
2396
2708
|
}
|
|
2397
2709
|
const env = { ...process.env };
|
|
2398
2710
|
if (envConfig.ANTHROPIC_BASE_URL) env.ANTHROPIC_BASE_URL = envConfig.ANTHROPIC_BASE_URL;
|
|
2399
|
-
if (envConfig.ANTHROPIC_API_KEY) env.ANTHROPIC_API_KEY =
|
|
2711
|
+
if (envConfig.ANTHROPIC_API_KEY) env.ANTHROPIC_API_KEY = decrypt(envConfig.ANTHROPIC_API_KEY || "");
|
|
2400
2712
|
if (envConfig.ANTHROPIC_MODEL) env.ANTHROPIC_MODEL = envConfig.ANTHROPIC_MODEL;
|
|
2401
2713
|
if (envConfig.ANTHROPIC_SMALL_FAST_MODEL) env.ANTHROPIC_SMALL_FAST_MODEL = envConfig.ANTHROPIC_SMALL_FAST_MODEL;
|
|
2402
2714
|
const [cmd, ...args] = command;
|
|
@@ -2436,14 +2748,14 @@ setupCmd.command("default-mode").description("\u8BBE\u7F6E\u9ED8\u8BA4\u6743\u96
|
|
|
2436
2748
|
for (const mode of PERMISSION_MODES) {
|
|
2437
2749
|
if (options[mode]) {
|
|
2438
2750
|
config2.set("defaultMode", mode);
|
|
2439
|
-
console.log(chalk7.green(`\u5DF2\u8BBE\u7F6E\u9ED8\u8BA4\u6743\u9650\u6A21\u5F0F: ${
|
|
2751
|
+
console.log(chalk7.green(`\u5DF2\u8BBE\u7F6E\u9ED8\u8BA4\u6743\u9650\u6A21\u5F0F: ${PERMISSION_PRESETS[mode].name}`));
|
|
2440
2752
|
console.log(chalk7.gray(`\u4E0B\u6B21\u542F\u52A8 ccem \u65F6\u5C06\u9ED8\u8BA4\u4F7F\u7528\u6B64\u6A21\u5F0F`));
|
|
2441
2753
|
return;
|
|
2442
2754
|
}
|
|
2443
2755
|
}
|
|
2444
2756
|
const currentDefault = config2.get("defaultMode");
|
|
2445
|
-
if (currentDefault &&
|
|
2446
|
-
console.log(chalk7.green(`\u5F53\u524D\u9ED8\u8BA4\u6A21\u5F0F: ${
|
|
2757
|
+
if (currentDefault && PERMISSION_PRESETS[currentDefault]) {
|
|
2758
|
+
console.log(chalk7.green(`\u5F53\u524D\u9ED8\u8BA4\u6A21\u5F0F: ${PERMISSION_PRESETS[currentDefault].name}`));
|
|
2447
2759
|
} else {
|
|
2448
2760
|
console.log(chalk7.yellow("\u672A\u8BBE\u7F6E\u9ED8\u8BA4\u6743\u9650\u6A21\u5F0F"));
|
|
2449
2761
|
}
|
|
@@ -2459,28 +2771,28 @@ setupCmd.command("migrate").description("\u8FC1\u79FB\u65E7\u7248\u914D\u7F6E\u5
|
|
|
2459
2771
|
const newConfigPath = getCcemConfigPath();
|
|
2460
2772
|
const legacyConfigPath = getLegacyConfigPath();
|
|
2461
2773
|
console.log(chalk7.cyan("\n\u{1F504} \u914D\u7F6E\u8FC1\u79FB\n"));
|
|
2462
|
-
if (!
|
|
2774
|
+
if (!fs8.existsSync(legacyConfigPath)) {
|
|
2463
2775
|
console.log(chalk7.yellow("\u672A\u627E\u5230\u65E7\u7248\u914D\u7F6E\u6587\u4EF6"));
|
|
2464
2776
|
console.log(chalk7.gray(` \u65E7\u8DEF\u5F84: ${legacyConfigPath}`));
|
|
2465
2777
|
return;
|
|
2466
2778
|
}
|
|
2467
|
-
if (
|
|
2779
|
+
if (fs8.existsSync(newConfigPath) && !options.force) {
|
|
2468
2780
|
console.log(chalk7.green("\u2713 \u914D\u7F6E\u5DF2\u5728\u65B0\u8DEF\u5F84"));
|
|
2469
2781
|
console.log(chalk7.gray(` \u8DEF\u5F84: ${newConfigPath}`));
|
|
2470
2782
|
console.log(chalk7.gray("\n\u4F7F\u7528 --force \u5F3A\u5236\u91CD\u65B0\u8FC1\u79FB"));
|
|
2471
2783
|
return;
|
|
2472
2784
|
}
|
|
2473
2785
|
try {
|
|
2474
|
-
|
|
2475
|
-
|
|
2786
|
+
ensureCcemDir();
|
|
2787
|
+
fs8.copyFileSync(legacyConfigPath, newConfigPath);
|
|
2476
2788
|
console.log(chalk7.green("\u2713 \u914D\u7F6E\u5DF2\u8FC1\u79FB"));
|
|
2477
2789
|
console.log(chalk7.gray(` \u4ECE: ${legacyConfigPath}`));
|
|
2478
2790
|
console.log(chalk7.gray(` \u5230: ${newConfigPath}`));
|
|
2479
2791
|
if (options.clean) {
|
|
2480
|
-
|
|
2481
|
-
const legacyDir =
|
|
2792
|
+
fs8.unlinkSync(legacyConfigPath);
|
|
2793
|
+
const legacyDir = path6.dirname(legacyConfigPath);
|
|
2482
2794
|
try {
|
|
2483
|
-
|
|
2795
|
+
fs8.rmdirSync(legacyDir);
|
|
2484
2796
|
} catch {
|
|
2485
2797
|
}
|
|
2486
2798
|
console.log(chalk7.green("\u2713 \u5DF2\u5220\u9664\u65E7\u914D\u7F6E\u6587\u4EF6"));
|
|
@@ -2491,13 +2803,13 @@ setupCmd.command("migrate").description("\u8FC1\u79FB\u65E7\u7248\u914D\u7F6E\u5
|
|
|
2491
2803
|
});
|
|
2492
2804
|
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
2805
|
const options = this.opts();
|
|
2494
|
-
const skillDir =
|
|
2495
|
-
const targetPath =
|
|
2496
|
-
if (!
|
|
2497
|
-
|
|
2806
|
+
const skillDir = path6.join(process.env.HOME || "~", ".claude", "skills");
|
|
2807
|
+
const targetPath = path6.join(skillDir, "ccem-cron.md");
|
|
2808
|
+
if (!fs8.existsSync(skillDir)) {
|
|
2809
|
+
fs8.mkdirSync(skillDir, { recursive: true });
|
|
2498
2810
|
console.log(chalk7.gray(`\u521B\u5EFA\u76EE\u5F55: ${skillDir}`));
|
|
2499
2811
|
}
|
|
2500
|
-
if (
|
|
2812
|
+
if (fs8.existsSync(targetPath) && !options.force) {
|
|
2501
2813
|
const { overwrite } = await inquirer.prompt([
|
|
2502
2814
|
{
|
|
2503
2815
|
type: "confirm",
|
|
@@ -2511,7 +2823,7 @@ setupCmd.command("cron").description("\u5B89\u88C5 ccem-cron skill \u5230 Claude
|
|
|
2511
2823
|
return;
|
|
2512
2824
|
}
|
|
2513
2825
|
}
|
|
2514
|
-
|
|
2826
|
+
fs8.writeFileSync(targetPath, CCEM_CRON_SKILL_CONTENT, "utf-8");
|
|
2515
2827
|
console.log(chalk7.green(`\u2713 \u5DF2\u5B89\u88C5 ccem-cron skill`));
|
|
2516
2828
|
console.log(chalk7.gray(` \u8DEF\u5F84: ${targetPath}`));
|
|
2517
2829
|
console.log(chalk7.cyan(`
|
|
@@ -2689,7 +3001,7 @@ Editing environment '${result.name}'`));
|
|
|
2689
3001
|
}
|
|
2690
3002
|
]);
|
|
2691
3003
|
if (answers.ANTHROPIC_BASE_URL) envToEdit.ANTHROPIC_BASE_URL = answers.ANTHROPIC_BASE_URL;
|
|
2692
|
-
if (answers.ANTHROPIC_API_KEY) envToEdit.ANTHROPIC_API_KEY =
|
|
3004
|
+
if (answers.ANTHROPIC_API_KEY) envToEdit.ANTHROPIC_API_KEY = encrypt(answers.ANTHROPIC_API_KEY);
|
|
2693
3005
|
if (answers.ANTHROPIC_MODEL) envToEdit.ANTHROPIC_MODEL = answers.ANTHROPIC_MODEL;
|
|
2694
3006
|
if (answers.ANTHROPIC_SMALL_FAST_MODEL) envToEdit.ANTHROPIC_SMALL_FAST_MODEL = answers.ANTHROPIC_SMALL_FAST_MODEL;
|
|
2695
3007
|
registries[result.name] = envToEdit;
|
|
@@ -2774,7 +3086,7 @@ Editing environment '${result.name}'`));
|
|
|
2774
3086
|
}
|
|
2775
3087
|
]);
|
|
2776
3088
|
if (editAnswers.ANTHROPIC_BASE_URL) envToEdit.ANTHROPIC_BASE_URL = editAnswers.ANTHROPIC_BASE_URL;
|
|
2777
|
-
if (editAnswers.ANTHROPIC_API_KEY) envToEdit.ANTHROPIC_API_KEY =
|
|
3089
|
+
if (editAnswers.ANTHROPIC_API_KEY) envToEdit.ANTHROPIC_API_KEY = encrypt(editAnswers.ANTHROPIC_API_KEY);
|
|
2778
3090
|
if (editAnswers.ANTHROPIC_MODEL) envToEdit.ANTHROPIC_MODEL = editAnswers.ANTHROPIC_MODEL;
|
|
2779
3091
|
if (editAnswers.ANTHROPIC_SMALL_FAST_MODEL) envToEdit.ANTHROPIC_SMALL_FAST_MODEL = editAnswers.ANTHROPIC_SMALL_FAST_MODEL;
|
|
2780
3092
|
registries[targetName] = envToEdit;
|
|
@@ -2839,7 +3151,7 @@ Editing environment '${result.name}'`));
|
|
|
2839
3151
|
await new Promise((resolve2) => setTimeout(resolve2, 800));
|
|
2840
3152
|
} else if (selectedMode !== "back") {
|
|
2841
3153
|
config2.set("defaultMode", selectedMode);
|
|
2842
|
-
msg.success(`Default mode set: ${
|
|
3154
|
+
msg.success(`Default mode set: ${PERMISSION_PRESETS[selectedMode].name}`);
|
|
2843
3155
|
await new Promise((resolve2) => setTimeout(resolve2, 800));
|
|
2844
3156
|
}
|
|
2845
3157
|
} else {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ccem",
|
|
3
|
-
"version": "2.0.0-beta",
|
|
3
|
+
"version": "2.0.0-beta.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Claude Code Environment Manager",
|
|
6
6
|
"author": {
|
|
@@ -16,18 +16,7 @@
|
|
|
16
16
|
"bin": {
|
|
17
17
|
"ccem": "./dist/index.js"
|
|
18
18
|
},
|
|
19
|
-
"scripts": {
|
|
20
|
-
"generate-logo": "bash scripts/generate-logo.sh",
|
|
21
|
-
"build": "tsup",
|
|
22
|
-
"dev": "tsup --watch",
|
|
23
|
-
"start": "node dist/index.js",
|
|
24
|
-
"postinstall": "node ./scripts/migrate.js",
|
|
25
|
-
"test": "vitest",
|
|
26
|
-
"test:run": "vitest run",
|
|
27
|
-
"test:coverage": "vitest run --coverage"
|
|
28
|
-
},
|
|
29
19
|
"dependencies": {
|
|
30
|
-
"@ccem/core": "workspace:*",
|
|
31
20
|
"chalk": "^4.1.2",
|
|
32
21
|
"cli-table3": "^0.6.3",
|
|
33
22
|
"commander": "^12.0.0",
|
|
@@ -46,6 +35,17 @@
|
|
|
46
35
|
"asciify-image": "^0.1.10",
|
|
47
36
|
"tsup": "^8.0.2",
|
|
48
37
|
"typescript": "^5.3.3",
|
|
49
|
-
"vitest": "^4.0.18"
|
|
38
|
+
"vitest": "^4.0.18",
|
|
39
|
+
"@ccem/core": "2.0.0-beta.2"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"generate-logo": "bash scripts/generate-logo.sh",
|
|
43
|
+
"build": "tsup",
|
|
44
|
+
"dev": "tsup --watch",
|
|
45
|
+
"start": "node dist/index.js",
|
|
46
|
+
"postinstall": "node ./scripts/migrate.js",
|
|
47
|
+
"test": "vitest",
|
|
48
|
+
"test:run": "vitest run",
|
|
49
|
+
"test:coverage": "vitest run --coverage"
|
|
50
50
|
}
|
|
51
|
-
}
|
|
51
|
+
}
|