ccem 2.0.0-beta → 2.0.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +476 -140
  2. package/package.json +14 -14
package/dist/index.js CHANGED
@@ -7,28 +7,346 @@ 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 fs7 from "fs";
11
- import * as path5 from "path";
12
- import { fileURLToPath } from "url";
13
- import { encrypt as encrypt2, decrypt as decrypt2, ENV_PRESETS, PERMISSION_PRESETS as PERMISSION_PRESETS4, getCcemConfigDir, ensureCcemDir as ensureCcemDir3, getCcemConfigPath, getLegacyConfigPath } from "@ccem/core";
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-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 fs from "fs";
336
+ import * as fs2 from "fs";
22
337
  import * as fsPromises from "fs/promises";
23
- import * as path from "path";
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 = path.join(os.homedir(), ".claude", "projects");
27
- var CCEM_DIR = path.join(os.homedir(), ".ccem");
341
+ import { fileURLToPath } from "url";
342
+ var __filename = fileURLToPath(import.meta.url);
343
+ var __dirname = path2.dirname(__filename);
344
+ var CLAUDE_PROJECTS_DIR = path2.join(os.homedir(), ".claude", "projects");
345
+ var CCEM_DIR = path2.join(os.homedir(), ".ccem");
28
346
  var CACHE_VERSION = 1;
29
- var getCachePath = () => path.join(CCEM_DIR, "usage-cache.json");
30
- var getPricesPath = () => path.join(CCEM_DIR, "model-prices.json");
31
- async function ensureCcemDir() {
347
+ var getCachePath = () => path2.join(CCEM_DIR, "usage-cache.json");
348
+ var getPricesPath = () => path2.join(CCEM_DIR, "model-prices.json");
349
+ async function ensureCcemDir2() {
32
350
  try {
33
351
  await fsPromises.access(CCEM_DIR);
34
352
  } catch {
@@ -37,7 +355,7 @@ async function ensureCcemDir() {
37
355
  }
38
356
  var LITELLM_PRICES_URL = "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json";
39
357
  var getBundledPricesPath = () => {
40
- return path.join(__dirname, "..", "model-prices.json");
358
+ return path2.join(__dirname, "..", "model-prices.json");
41
359
  };
42
360
  var DEFAULT_PRICES = {
43
361
  "claude-opus-4-5": {
@@ -66,7 +384,7 @@ function normalizeModelName(model) {
66
384
  var pricesCache = null;
67
385
  async function loadPrices() {
68
386
  if (pricesCache) return pricesCache;
69
- await ensureCcemDir();
387
+ await ensureCcemDir2();
70
388
  const pricesPath = getPricesPath();
71
389
  try {
72
390
  const response = await fetch(LITELLM_PRICES_URL, { signal: AbortSignal.timeout(1e3) });
@@ -156,8 +474,8 @@ async function getFileMetaAsync(filePath) {
156
474
  function loadCacheSync() {
157
475
  try {
158
476
  const cachePath = getCachePath();
159
- if (!fs.existsSync(cachePath)) return null;
160
- const data = JSON.parse(fs.readFileSync(cachePath, "utf-8"));
477
+ if (!fs2.existsSync(cachePath)) return null;
478
+ const data = JSON.parse(fs2.readFileSync(cachePath, "utf-8"));
161
479
  if (data.version !== CACHE_VERSION) return null;
162
480
  return data;
163
481
  } catch {
@@ -216,7 +534,7 @@ function getUsageStatsFromCache() {
216
534
  }
217
535
  async function saveCacheAsync(cache) {
218
536
  try {
219
- await ensureCcemDir();
537
+ await ensureCcemDir2();
220
538
  await fsPromises.writeFile(getCachePath(), JSON.stringify(cache, null, 2));
221
539
  } catch {
222
540
  }
@@ -224,7 +542,7 @@ async function saveCacheAsync(cache) {
224
542
  async function parseJSONLFileAsync(filePath, prices, signal) {
225
543
  const entries = [];
226
544
  try {
227
- const fileStream = fs.createReadStream(filePath, { encoding: "utf-8" });
545
+ const fileStream = fs2.createReadStream(filePath, { encoding: "utf-8" });
228
546
  const rl = readline.createInterface({
229
547
  input: fileStream,
230
548
  crlfDelay: Infinity
@@ -278,14 +596,14 @@ async function getAllJSONLFilesAsync() {
278
596
  if (!projectsDirExists) return files;
279
597
  const projects = await fsPromises.readdir(CLAUDE_PROJECTS_DIR);
280
598
  for (const project of projects) {
281
- const projectPath = path.join(CLAUDE_PROJECTS_DIR, project);
599
+ const projectPath = path2.join(CLAUDE_PROJECTS_DIR, project);
282
600
  try {
283
601
  const stat2 = await fsPromises.stat(projectPath);
284
602
  if (stat2.isDirectory()) {
285
603
  const projectFiles = await fsPromises.readdir(projectPath);
286
604
  for (const file of projectFiles) {
287
605
  if (file.endsWith(".jsonl")) {
288
- files.push(path.join(projectPath, file));
606
+ files.push(path2.join(projectPath, file));
289
607
  }
290
608
  }
291
609
  }
@@ -506,10 +824,10 @@ var renderLogoWithEnvPanel = (envName, env, defaultMode) => {
506
824
  const parsed = new URL(url);
507
825
  const protocol = parsed.protocol + "//";
508
826
  const host = parsed.host;
509
- const path6 = parsed.pathname + parsed.search;
827
+ const path7 = parsed.pathname + parsed.search;
510
828
  const hostStart = host.slice(0, 8);
511
829
  const hostEnd = host.slice(-4);
512
- const pathPart = path6.length > 10 ? path6.slice(0, 7) + "..." : path6;
830
+ const pathPart = path7.length > 10 ? path7.slice(0, 7) + "..." : path7;
513
831
  return `${protocol}${hostStart}...${hostEnd}${pathPart}`;
514
832
  } catch {
515
833
  return truncate(url, max);
@@ -947,64 +1265,62 @@ var selectEnvWithKeys = (registries, current) => {
947
1265
  };
948
1266
 
949
1267
  // src/permissions.ts
950
- import fs4 from "fs";
1268
+ import fs5 from "fs";
951
1269
  import chalk3 from "chalk";
952
1270
  import Table2 from "cli-table3";
953
- import { PERMISSION_PRESETS as PERMISSION_PRESETS3, getPermissionModeNames } from "@ccem/core";
954
1271
 
955
1272
  // src/utils.ts
956
- import crypto from "crypto";
957
- import fs2 from "fs";
958
- import path2 from "path";
959
- var SECRET_KEY = crypto.scryptSync("claude-code-env-manager-secret", "salt", 32);
1273
+ import crypto2 from "crypto";
1274
+ import fs3 from "fs";
1275
+ import path3 from "path";
1276
+ var SECRET_KEY2 = crypto2.scryptSync("claude-code-env-manager-secret", "salt", 32);
960
1277
  var findProjectRoot = () => {
961
1278
  let currentDir = process.cwd();
962
- const root = path2.parse(currentDir).root;
1279
+ const root = path3.parse(currentDir).root;
963
1280
  while (currentDir !== root) {
964
- if (fs2.existsSync(path2.join(currentDir, ".git")) || fs2.existsSync(path2.join(currentDir, "package.json"))) {
1281
+ if (fs3.existsSync(path3.join(currentDir, ".git")) || fs3.existsSync(path3.join(currentDir, "package.json"))) {
965
1282
  return currentDir;
966
1283
  }
967
- currentDir = path2.dirname(currentDir);
1284
+ currentDir = path3.dirname(currentDir);
968
1285
  }
969
1286
  return process.cwd();
970
1287
  };
971
1288
  var getSettingsPath = (useLocal = true) => {
972
1289
  const projectRoot = findProjectRoot();
973
- const claudeDir = path2.join(projectRoot, ".claude");
1290
+ const claudeDir = path3.join(projectRoot, ".claude");
974
1291
  const filename = useLocal ? "settings.local.json" : "settings.json";
975
- return path2.join(claudeDir, filename);
1292
+ return path3.join(claudeDir, filename);
976
1293
  };
977
1294
  var ensureClaudeDir = () => {
978
1295
  const projectRoot = findProjectRoot();
979
- const claudeDir = path2.join(projectRoot, ".claude");
980
- if (!fs2.existsSync(claudeDir)) {
981
- fs2.mkdirSync(claudeDir, { recursive: true });
1296
+ const claudeDir = path3.join(projectRoot, ".claude");
1297
+ if (!fs3.existsSync(claudeDir)) {
1298
+ fs3.mkdirSync(claudeDir, { recursive: true });
982
1299
  }
983
1300
  return claudeDir;
984
1301
  };
985
- var getHomeDir = () => {
1302
+ var getHomeDir2 = () => {
986
1303
  return process.env.HOME || process.env.USERPROFILE || "";
987
1304
  };
988
1305
  var getGlobalClaudeConfigPath = () => {
989
- return path2.join(getHomeDir(), ".claude.json");
1306
+ return path3.join(getHomeDir2(), ".claude.json");
990
1307
  };
991
1308
  var getGlobalClaudeSettingsPath = () => {
992
- return path2.join(getHomeDir(), ".claude", "settings.json");
1309
+ return path3.join(getHomeDir2(), ".claude", "settings.json");
993
1310
  };
994
1311
  var ensureGlobalClaudeDir = () => {
995
- const claudeDir = path2.join(getHomeDir(), ".claude");
996
- if (!fs2.existsSync(claudeDir)) {
997
- fs2.mkdirSync(claudeDir, { recursive: true });
1312
+ const claudeDir = path3.join(getHomeDir2(), ".claude");
1313
+ if (!fs3.existsSync(claudeDir)) {
1314
+ fs3.mkdirSync(claudeDir, { recursive: true });
998
1315
  }
999
1316
  return claudeDir;
1000
1317
  };
1001
1318
 
1002
1319
  // src/launcher.ts
1003
1320
  import { spawn } from "child_process";
1004
- import * as fs3 from "fs";
1005
- import * as path3 from "path";
1321
+ import * as fs4 from "fs";
1322
+ import * as path4 from "path";
1006
1323
  import chalk2 from "chalk";
1007
- import { decrypt, PERMISSION_PRESETS as PERMISSION_PRESETS2, ensureCcemDir as ensureCcemDir2 } from "@ccem/core";
1008
1324
  function buildEnvVars(envConfig) {
1009
1325
  const vars = {};
1010
1326
  if (envConfig.ANTHROPIC_BASE_URL) vars.ANTHROPIC_BASE_URL = envConfig.ANTHROPIC_BASE_URL;
@@ -1014,7 +1330,7 @@ function buildEnvVars(envConfig) {
1014
1330
  return vars;
1015
1331
  }
1016
1332
  function buildPermArgs(modeName) {
1017
- const preset = PERMISSION_PRESETS2[modeName];
1333
+ const preset = PERMISSION_PRESETS[modeName];
1018
1334
  if (!preset) return [];
1019
1335
  const args = ["--permission-mode", preset.permissionMode];
1020
1336
  if (preset.permissions.allow.length > 0) {
@@ -1028,9 +1344,9 @@ function buildPermArgs(modeName) {
1028
1344
  return args;
1029
1345
  }
1030
1346
  function ensureSessionsDir() {
1031
- const dir = path3.join(ensureCcemDir2(), "sessions");
1032
- if (!fs3.existsSync(dir)) {
1033
- fs3.mkdirSync(dir, { recursive: true });
1347
+ const dir = path4.join(ensureCcemDir(), "sessions");
1348
+ if (!fs4.existsSync(dir)) {
1349
+ fs4.mkdirSync(dir, { recursive: true });
1034
1350
  }
1035
1351
  return dir;
1036
1352
  }
@@ -1043,7 +1359,7 @@ async function launchClaude(options) {
1043
1359
  delete env.CLAUDECODE;
1044
1360
  const args = [];
1045
1361
  if (permMode) {
1046
- const preset = PERMISSION_PRESETS2[permMode];
1362
+ const preset = PERMISSION_PRESETS[permMode];
1047
1363
  if (preset) {
1048
1364
  if (!silent) {
1049
1365
  console.log(chalk2.green(`\u5DF2\u5E94\u7528 ${preset.name}\uFF08\u4E34\u65F6\uFF09`));
@@ -1066,14 +1382,15 @@ async function launchClaude(options) {
1066
1382
  return new Promise((resolve2) => {
1067
1383
  const child = spawn("claude", args, {
1068
1384
  stdio: "inherit",
1069
- shell: true,
1385
+ shell: false,
1386
+ // 直接执行二进制,避免 shell 注入风险
1070
1387
  env
1071
1388
  });
1072
1389
  child.on("exit", (code) => {
1073
1390
  if (sessionId) {
1074
1391
  try {
1075
- fs3.writeFileSync(
1076
- path3.join(sessionsDir, `${sessionId}.exit`),
1392
+ fs4.writeFileSync(
1393
+ path4.join(sessionsDir, `${sessionId}.exit`),
1077
1394
  String(code ?? 0)
1078
1395
  );
1079
1396
  } catch {
@@ -1090,14 +1407,14 @@ async function launchClaude(options) {
1090
1407
 
1091
1408
  // src/permissions.ts
1092
1409
  var readSettings = (settingsPath) => {
1093
- if (fs4.existsSync(settingsPath)) {
1410
+ if (fs5.existsSync(settingsPath)) {
1094
1411
  try {
1095
- const content = fs4.readFileSync(settingsPath, "utf-8");
1412
+ const content = fs5.readFileSync(settingsPath, "utf-8");
1096
1413
  return JSON.parse(content);
1097
1414
  } catch {
1098
1415
  console.warn(chalk3.yellow(`\u8B66\u544A: \u65E0\u6CD5\u89E3\u6790 ${settingsPath}\uFF0C\u5C06\u521B\u5EFA\u5907\u4EFD`));
1099
1416
  const backupPath = settingsPath + ".error." + Date.now();
1100
- fs4.copyFileSync(settingsPath, backupPath);
1417
+ fs5.copyFileSync(settingsPath, backupPath);
1101
1418
  console.log(chalk3.gray(`\u5907\u4EFD\u5DF2\u4FDD\u5B58\u5230: ${backupPath}`));
1102
1419
  return {};
1103
1420
  }
@@ -1106,7 +1423,7 @@ var readSettings = (settingsPath) => {
1106
1423
  };
1107
1424
  var writeSettings = (settingsPath, config3) => {
1108
1425
  ensureClaudeDir();
1109
- fs4.writeFileSync(settingsPath, JSON.stringify(config3, null, 2) + "\n");
1426
+ fs5.writeFileSync(settingsPath, JSON.stringify(config3, null, 2) + "\n");
1110
1427
  };
1111
1428
  var mergePermissions = (existing, preset) => {
1112
1429
  const existingAllow = existing.permissions?.allow || [];
@@ -1122,7 +1439,7 @@ var mergePermissions = (existing, preset) => {
1122
1439
  };
1123
1440
  };
1124
1441
  var applyPermissionMode = (modeName) => {
1125
- const preset = PERMISSION_PRESETS3[modeName];
1442
+ const preset = PERMISSION_PRESETS[modeName];
1126
1443
  if (!preset) {
1127
1444
  console.error(chalk3.red(`\u672A\u77E5\u7684\u6743\u9650\u6A21\u5F0F: ${modeName}`));
1128
1445
  console.log(chalk3.yellow("\u53EF\u7528\u6A21\u5F0F: " + getPermissionModeNames().join(", ")));
@@ -1138,14 +1455,14 @@ var applyPermissionMode = (modeName) => {
1138
1455
  };
1139
1456
  var resetPermissions = () => {
1140
1457
  const settingsPath = getSettingsPath(true);
1141
- if (!fs4.existsSync(settingsPath)) {
1458
+ if (!fs5.existsSync(settingsPath)) {
1142
1459
  console.log(chalk3.yellow("\u6CA1\u6709\u81EA\u5B9A\u4E49\u6743\u9650\u914D\u7F6E\u9700\u8981\u91CD\u7F6E"));
1143
1460
  return;
1144
1461
  }
1145
1462
  const config3 = readSettings(settingsPath);
1146
1463
  delete config3.permissions;
1147
1464
  if (Object.keys(config3).length === 0) {
1148
- fs4.unlinkSync(settingsPath);
1465
+ fs5.unlinkSync(settingsPath);
1149
1466
  console.log(chalk3.green("\u5DF2\u5220\u9664\u914D\u7F6E\u6587\u4EF6\uFF08\u6587\u4EF6\u4E3A\u7A7A\uFF09"));
1150
1467
  } else {
1151
1468
  writeSettings(settingsPath, config3);
@@ -1155,7 +1472,7 @@ var resetPermissions = () => {
1155
1472
  };
1156
1473
  var showCurrentMode = () => {
1157
1474
  const settingsPath = getSettingsPath(true);
1158
- if (!fs4.existsSync(settingsPath)) {
1475
+ if (!fs5.existsSync(settingsPath)) {
1159
1476
  console.log(chalk3.yellow("\u672A\u914D\u7F6E\u81EA\u5B9A\u4E49\u6743\u9650"));
1160
1477
  console.log(chalk3.gray(`\u6587\u4EF6\u4E0D\u5B58\u5728: ${settingsPath}`));
1161
1478
  return;
@@ -1165,7 +1482,7 @@ var showCurrentMode = () => {
1165
1482
  console.log(chalk3.yellow("\u672A\u914D\u7F6E\u81EA\u5B9A\u4E49\u6743\u9650"));
1166
1483
  return;
1167
1484
  }
1168
- const matchedPreset = Object.entries(PERMISSION_PRESETS3).find(([_, preset]) => {
1485
+ const matchedPreset = Object.entries(PERMISSION_PRESETS).find(([_, preset]) => {
1169
1486
  const configAllow = new Set(config3.permissions?.allow || []);
1170
1487
  const configDeny = new Set(config3.permissions?.deny || []);
1171
1488
  const presetAllow = new Set(preset.permissions.allow);
@@ -1197,7 +1514,7 @@ var listAvailableModes = () => {
1197
1514
  style: { head: ["cyan"] },
1198
1515
  colWidths: [15, 15, 50]
1199
1516
  });
1200
- Object.entries(PERMISSION_PRESETS3).forEach(([key, preset]) => {
1517
+ Object.entries(PERMISSION_PRESETS).forEach(([key, preset]) => {
1201
1518
  table.push([preset.name, `--${key}`, preset.description]);
1202
1519
  });
1203
1520
  console.log(chalk3.bold("\u53EF\u7528\u6743\u9650\u6A21\u5F0F:\n"));
@@ -1206,7 +1523,7 @@ var listAvailableModes = () => {
1206
1523
  console.log(chalk3.gray("\u6C38\u4E45\u6A21\u5F0F: ccem setup perms --<mode>"));
1207
1524
  };
1208
1525
  var runWithTempPermissions = async (modeName, envConfig) => {
1209
- const preset = PERMISSION_PRESETS3[modeName];
1526
+ const preset = PERMISSION_PRESETS[modeName];
1210
1527
  if (!preset) {
1211
1528
  console.error(chalk3.red(`\u672A\u77E5\u7684\u6743\u9650\u6A21\u5F0F: ${modeName}`));
1212
1529
  console.log(chalk3.yellow("\u53EF\u7528\u6A21\u5F0F: " + getPermissionModeNames().join(", ")));
@@ -1216,13 +1533,13 @@ var runWithTempPermissions = async (modeName, envConfig) => {
1216
1533
  };
1217
1534
 
1218
1535
  // src/setup.ts
1219
- import fs5 from "fs";
1536
+ import fs6 from "fs";
1220
1537
  import chalk4 from "chalk";
1221
1538
  import { spawn as spawn2 } from "child_process";
1222
1539
  var readJsonFile = (filePath) => {
1223
- if (fs5.existsSync(filePath)) {
1540
+ if (fs6.existsSync(filePath)) {
1224
1541
  try {
1225
- const content = fs5.readFileSync(filePath, "utf-8");
1542
+ const content = fs6.readFileSync(filePath, "utf-8");
1226
1543
  return JSON.parse(content);
1227
1544
  } catch {
1228
1545
  console.warn(chalk4.yellow(`\u8B66\u544A: \u65E0\u6CD5\u89E3\u6790 ${filePath}`));
@@ -1232,7 +1549,7 @@ var readJsonFile = (filePath) => {
1232
1549
  return {};
1233
1550
  };
1234
1551
  var writeJsonFile = (filePath, data) => {
1235
- fs5.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
1552
+ fs6.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
1236
1553
  };
1237
1554
  var setupOnboarding = () => {
1238
1555
  const configPath = getGlobalClaudeConfigPath();
@@ -1354,8 +1671,8 @@ var runSetupInit = async () => {
1354
1671
 
1355
1672
  // src/skills.ts
1356
1673
  import { execSync } from "child_process";
1357
- import * as fs6 from "fs";
1358
- import * as path4 from "path";
1674
+ import * as fs7 from "fs";
1675
+ import * as path5 from "path";
1359
1676
  import chalk5 from "chalk";
1360
1677
  var SKILL_GROUPS = {
1361
1678
  official: { label: "\u5B98\u65B9", icon: "\u{1F3E2}" },
@@ -1524,12 +1841,12 @@ function parseGitHubUrl(url) {
1524
1841
  };
1525
1842
  }
1526
1843
  function getSkillsDir() {
1527
- return path4.join(process.cwd(), ".claude", "skills");
1844
+ return path5.join(process.cwd(), ".claude", "skills");
1528
1845
  }
1529
1846
  function ensureSkillsDir() {
1530
1847
  const skillsDir = getSkillsDir();
1531
- if (!fs6.existsSync(skillsDir)) {
1532
- fs6.mkdirSync(skillsDir, { recursive: true });
1848
+ if (!fs7.existsSync(skillsDir)) {
1849
+ fs7.mkdirSync(skillsDir, { recursive: true });
1533
1850
  } else {
1534
1851
  cleanupTempDirs(skillsDir);
1535
1852
  }
@@ -1537,11 +1854,11 @@ function ensureSkillsDir() {
1537
1854
  }
1538
1855
  function cleanupTempDirs(skillsDir) {
1539
1856
  try {
1540
- const entries = fs6.readdirSync(skillsDir, { withFileTypes: true });
1857
+ const entries = fs7.readdirSync(skillsDir, { withFileTypes: true });
1541
1858
  for (const entry of entries) {
1542
1859
  if (entry.isDirectory() && entry.name.startsWith(".tmp-")) {
1543
- const tmpPath = path4.join(skillsDir, entry.name);
1544
- fs6.rmSync(tmpPath, { recursive: true });
1860
+ const tmpPath = path5.join(skillsDir, entry.name);
1861
+ fs7.rmSync(tmpPath, { recursive: true });
1545
1862
  }
1546
1863
  }
1547
1864
  } catch {
@@ -1549,24 +1866,24 @@ function cleanupTempDirs(skillsDir) {
1549
1866
  }
1550
1867
  function downloadSkillWithGit(owner, repo, branch, repoPath, targetName) {
1551
1868
  const skillsDir = ensureSkillsDir();
1552
- const targetDir = path4.join(skillsDir, targetName);
1553
- if (fs6.existsSync(targetDir)) {
1869
+ const targetDir = path5.join(skillsDir, targetName);
1870
+ if (fs7.existsSync(targetDir)) {
1554
1871
  console.log(chalk5.yellow(`Skill "${targetName}" already exists. Updating...`));
1555
- fs6.rmSync(targetDir, { recursive: true });
1872
+ fs7.rmSync(targetDir, { recursive: true });
1556
1873
  }
1557
1874
  const repoUrl = `https://github.com/${owner}/${repo}.git`;
1558
- const tempDir = path4.join(skillsDir, `.tmp-${Date.now()}`);
1875
+ const tempDir = path5.join(skillsDir, `.tmp-${Date.now()}`);
1559
1876
  try {
1560
- fs6.mkdirSync(tempDir, { recursive: true });
1877
+ fs7.mkdirSync(tempDir, { recursive: true });
1561
1878
  execSync(`git init`, { cwd: tempDir, stdio: "pipe" });
1562
1879
  execSync(`git remote add origin ${repoUrl}`, { cwd: tempDir, stdio: "pipe" });
1563
1880
  execSync(`git config core.sparseCheckout true`, { cwd: tempDir, stdio: "pipe" });
1564
- const sparseFile = path4.join(tempDir, ".git", "info", "sparse-checkout");
1565
- fs6.writeFileSync(sparseFile, repoPath ? `${repoPath}/
1881
+ const sparseFile = path5.join(tempDir, ".git", "info", "sparse-checkout");
1882
+ fs7.writeFileSync(sparseFile, repoPath ? `${repoPath}/
1566
1883
  ` : "*\n");
1567
1884
  execSync(`git pull --depth=1 origin ${branch}`, { cwd: tempDir, stdio: "pipe" });
1568
- const sourceDir = repoPath ? path4.join(tempDir, repoPath) : tempDir;
1569
- if (!fs6.existsSync(sourceDir)) {
1885
+ const sourceDir = repoPath ? path5.join(tempDir, repoPath) : tempDir;
1886
+ if (!fs7.existsSync(sourceDir)) {
1570
1887
  throw new Error(`Path "${repoPath}" not found in repository`);
1571
1888
  }
1572
1889
  copyDir(sourceDir, targetDir);
@@ -1577,22 +1894,22 @@ function downloadSkillWithGit(owner, repo, branch, repoPath, targetName) {
1577
1894
  console.error(chalk5.red(`Failed to download skill: ${errMsg}`));
1578
1895
  return false;
1579
1896
  } finally {
1580
- if (fs6.existsSync(tempDir)) {
1581
- fs6.rmSync(tempDir, { recursive: true });
1897
+ if (fs7.existsSync(tempDir)) {
1898
+ fs7.rmSync(tempDir, { recursive: true });
1582
1899
  }
1583
1900
  }
1584
1901
  }
1585
1902
  function copyDir(src, dest) {
1586
- fs6.mkdirSync(dest, { recursive: true });
1587
- const entries = fs6.readdirSync(src, { withFileTypes: true });
1903
+ fs7.mkdirSync(dest, { recursive: true });
1904
+ const entries = fs7.readdirSync(src, { withFileTypes: true });
1588
1905
  for (const entry of entries) {
1589
1906
  if (entry.name === ".git") continue;
1590
- const srcPath = path4.join(src, entry.name);
1591
- const destPath = path4.join(dest, entry.name);
1907
+ const srcPath = path5.join(src, entry.name);
1908
+ const destPath = path5.join(dest, entry.name);
1592
1909
  if (entry.isDirectory()) {
1593
1910
  copyDir(srcPath, destPath);
1594
1911
  } else {
1595
- fs6.copyFileSync(srcPath, destPath);
1912
+ fs7.copyFileSync(srcPath, destPath);
1596
1913
  }
1597
1914
  }
1598
1915
  }
@@ -1638,7 +1955,7 @@ function addSkillFromGitHub(urlOrPreset) {
1638
1955
  }
1639
1956
  let skillName;
1640
1957
  if (parsed.path) {
1641
- skillName = path4.basename(parsed.path);
1958
+ skillName = path5.basename(parsed.path);
1642
1959
  } else {
1643
1960
  skillName = parsed.repo;
1644
1961
  }
@@ -1652,23 +1969,23 @@ function addSkillFromGitHub(urlOrPreset) {
1652
1969
  }
1653
1970
  function listInstalledSkills() {
1654
1971
  const skillsDir = getSkillsDir();
1655
- if (!fs6.existsSync(skillsDir)) {
1972
+ if (!fs7.existsSync(skillsDir)) {
1656
1973
  return [];
1657
1974
  }
1658
- const entries = fs6.readdirSync(skillsDir, { withFileTypes: true });
1975
+ const entries = fs7.readdirSync(skillsDir, { withFileTypes: true });
1659
1976
  return entries.filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => ({
1660
1977
  name: entry.name,
1661
- path: path4.join(skillsDir, entry.name)
1978
+ path: path5.join(skillsDir, entry.name)
1662
1979
  }));
1663
1980
  }
1664
1981
  function removeSkill(name) {
1665
1982
  const skillsDir = getSkillsDir();
1666
- const targetDir = path4.join(skillsDir, name);
1667
- if (!fs6.existsSync(targetDir)) {
1983
+ const targetDir = path5.join(skillsDir, name);
1984
+ if (!fs7.existsSync(targetDir)) {
1668
1985
  console.error(chalk5.red(`Skill "${name}" not found`));
1669
1986
  return false;
1670
1987
  }
1671
- fs6.rmSync(targetDir, { recursive: true });
1988
+ fs7.rmSync(targetDir, { recursive: true });
1672
1989
  console.log(chalk5.green(`Removed skill "${name}"`));
1673
1990
  return true;
1674
1991
  }
@@ -1826,19 +2143,20 @@ async function runSkillSelector() {
1826
2143
  }
1827
2144
 
1828
2145
  // src/remote.ts
1829
- import crypto2 from "crypto";
2146
+ import crypto3 from "crypto";
1830
2147
  import chalk6 from "chalk";
1831
2148
  import Conf from "conf";
1832
- import { encrypt } from "@ccem/core";
1833
2149
  var config = new Conf({
1834
- projectName: "claude-code-env-manager"
2150
+ projectName: "claude-code-env-manager",
2151
+ cwd: getCcemConfigDir()
2152
+ // 使用统一的配置目录
1835
2153
  });
1836
2154
  var decryptWithSecret = (encryptedBase64, secret) => {
1837
- const key = crypto2.scryptSync(secret, "ccem-salt", 32);
2155
+ const key = crypto3.scryptSync(secret, "ccem-salt", 32);
1838
2156
  const combined = Buffer.from(encryptedBase64, "base64");
1839
2157
  const iv = combined.subarray(0, 16);
1840
2158
  const encryptedHex = combined.subarray(16).toString("hex");
1841
- const decipher = crypto2.createDecipheriv("aes-256-cbc", key, iv);
2159
+ const decipher = crypto3.createDecipheriv("aes-256-cbc", key, iv);
1842
2160
  let decrypted = decipher.update(encryptedHex, "hex", "utf8");
1843
2161
  decrypted += decipher.final("utf8");
1844
2162
  return decrypted;
@@ -1859,7 +2177,11 @@ var loadFromRemote = async (url, secret) => {
1859
2177
  console.log(chalk6.gray("Fetching from remote..."));
1860
2178
  let response;
1861
2179
  try {
1862
- response = await fetch(url);
2180
+ response = await fetch(url, {
2181
+ headers: {
2182
+ "X-CCEM-Key": secret
2183
+ }
2184
+ });
1863
2185
  } catch (err) {
1864
2186
  console.error(chalk6.red("Error: Failed to connect to server"));
1865
2187
  console.error(chalk6.gray(err.message));
@@ -1929,6 +2251,7 @@ Loaded ${results.length} environment(s) from remote:`));
1929
2251
  }
1930
2252
  }
1931
2253
  console.log(chalk6.gray("\nRun 'ccem ls' to see all environments."));
2254
+ return results;
1932
2255
  };
1933
2256
 
1934
2257
  // src/cron-skill.ts
@@ -2074,12 +2397,12 @@ Replace \\\`TARGET_ID\\\` or \\\`TARGET_NAME\\\` with the user's selection.
2074
2397
  `;
2075
2398
 
2076
2399
  // src/index.ts
2077
- var __filename = fileURLToPath(import.meta.url);
2078
- var __dirname2 = path5.dirname(__filename);
2079
- var pkgPath = path5.resolve(__dirname2, "..", "package.json");
2080
- var pkg = JSON.parse(fs7.readFileSync(pkgPath, "utf-8"));
2400
+ var __filename2 = fileURLToPath2(import.meta.url);
2401
+ var __dirname2 = path6.dirname(__filename2);
2402
+ var pkgPath = path6.resolve(__dirname2, "..", "package.json");
2403
+ var pkg = JSON.parse(fs8.readFileSync(pkgPath, "utf-8"));
2081
2404
  var program = new Command();
2082
- ensureCcemDir3();
2405
+ ensureCcemDir();
2083
2406
  var config2 = new Conf2({
2084
2407
  projectName: "claude-code-env-manager",
2085
2408
  cwd: getCcemConfigDir(),
@@ -2132,7 +2455,7 @@ var initUsageStats = (onUpdate) => {
2132
2455
  };
2133
2456
  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
2457
  PERMISSION_MODES.forEach((mode) => {
2135
- const preset = PERMISSION_PRESETS4[mode];
2458
+ const preset = PERMISSION_PRESETS[mode];
2136
2459
  program.command(mode).description(`\u4E34\u65F6\u5E94\u7528 ${preset.name}\uFF0C\u9000\u51FA\u540E\u8FD8\u539F`).action(async () => {
2137
2460
  const registries = config2.get("registries");
2138
2461
  const current = config2.get("current");
@@ -2149,7 +2472,7 @@ var showCurrentEnv = (usageStats2, usageLoading2) => {
2149
2472
  if (!env) return;
2150
2473
  console.log(renderLogoWithEnvPanel(current, {
2151
2474
  ANTHROPIC_BASE_URL: env.ANTHROPIC_BASE_URL,
2152
- ANTHROPIC_API_KEY: env.ANTHROPIC_API_KEY ? decrypt2(env.ANTHROPIC_API_KEY) : void 0,
2475
+ ANTHROPIC_API_KEY: env.ANTHROPIC_API_KEY ? decrypt(env.ANTHROPIC_API_KEY) : void 0,
2153
2476
  ANTHROPIC_MODEL: env.ANTHROPIC_MODEL,
2154
2477
  ANTHROPIC_SMALL_FAST_MODEL: env.ANTHROPIC_SMALL_FAST_MODEL
2155
2478
  }, defaultMode));
@@ -2174,7 +2497,7 @@ var switchEnvironment = async (name) => {
2174
2497
  const env = registries[name];
2175
2498
  const exportCmds = [];
2176
2499
  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)}"`);
2500
+ if (env.ANTHROPIC_API_KEY) exportCmds.push(`export ANTHROPIC_API_KEY="${decrypt(env.ANTHROPIC_API_KEY)}"`);
2178
2501
  if (env.ANTHROPIC_MODEL) exportCmds.push(`export ANTHROPIC_MODEL="${env.ANTHROPIC_MODEL}"`);
2179
2502
  if (env.ANTHROPIC_SMALL_FAST_MODEL) exportCmds.push(`export ANTHROPIC_SMALL_FAST_MODEL="${env.ANTHROPIC_SMALL_FAST_MODEL}"`);
2180
2503
  if (process.stdout.isTTY) {
@@ -2259,7 +2582,7 @@ program.command("add <name>").description("Add a new environment configuration")
2259
2582
  }
2260
2583
  ]);
2261
2584
  if (answers.ANTHROPIC_API_KEY) {
2262
- answers.ANTHROPIC_API_KEY = encrypt2(answers.ANTHROPIC_API_KEY);
2585
+ answers.ANTHROPIC_API_KEY = encrypt(answers.ANTHROPIC_API_KEY);
2263
2586
  }
2264
2587
  registries[name] = answers;
2265
2588
  config2.set("registries", registries);
@@ -2356,7 +2679,7 @@ program.command("cp <source> <target>").description("Copy an environment configu
2356
2679
  }
2357
2680
  ]);
2358
2681
  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);
2682
+ if (answers.ANTHROPIC_API_KEY) current.ANTHROPIC_API_KEY = encrypt(answers.ANTHROPIC_API_KEY);
2360
2683
  if (answers.ANTHROPIC_MODEL) current.ANTHROPIC_MODEL = answers.ANTHROPIC_MODEL;
2361
2684
  if (answers.ANTHROPIC_SMALL_FAST_MODEL) current.ANTHROPIC_SMALL_FAST_MODEL = answers.ANTHROPIC_SMALL_FAST_MODEL;
2362
2685
  registries[target] = current;
@@ -2375,7 +2698,7 @@ program.command("env").description("Output environment variables for shell eval"
2375
2698
  if (!env) return;
2376
2699
  const outputEnv = { ...env };
2377
2700
  if (outputEnv.ANTHROPIC_API_KEY) {
2378
- outputEnv.ANTHROPIC_API_KEY = decrypt2(outputEnv.ANTHROPIC_API_KEY);
2701
+ outputEnv.ANTHROPIC_API_KEY = decrypt(outputEnv.ANTHROPIC_API_KEY);
2379
2702
  }
2380
2703
  if (options.json) {
2381
2704
  console.log(JSON.stringify(outputEnv, null, 2));
@@ -2396,7 +2719,7 @@ program.command("run <command...>").description("Run a command with the current
2396
2719
  }
2397
2720
  const env = { ...process.env };
2398
2721
  if (envConfig.ANTHROPIC_BASE_URL) env.ANTHROPIC_BASE_URL = envConfig.ANTHROPIC_BASE_URL;
2399
- if (envConfig.ANTHROPIC_API_KEY) env.ANTHROPIC_API_KEY = decrypt2(envConfig.ANTHROPIC_API_KEY || "");
2722
+ if (envConfig.ANTHROPIC_API_KEY) env.ANTHROPIC_API_KEY = decrypt(envConfig.ANTHROPIC_API_KEY || "");
2400
2723
  if (envConfig.ANTHROPIC_MODEL) env.ANTHROPIC_MODEL = envConfig.ANTHROPIC_MODEL;
2401
2724
  if (envConfig.ANTHROPIC_SMALL_FAST_MODEL) env.ANTHROPIC_SMALL_FAST_MODEL = envConfig.ANTHROPIC_SMALL_FAST_MODEL;
2402
2725
  const [cmd, ...args] = command;
@@ -2436,14 +2759,14 @@ setupCmd.command("default-mode").description("\u8BBE\u7F6E\u9ED8\u8BA4\u6743\u96
2436
2759
  for (const mode of PERMISSION_MODES) {
2437
2760
  if (options[mode]) {
2438
2761
  config2.set("defaultMode", mode);
2439
- console.log(chalk7.green(`\u5DF2\u8BBE\u7F6E\u9ED8\u8BA4\u6743\u9650\u6A21\u5F0F: ${PERMISSION_PRESETS4[mode].name}`));
2762
+ console.log(chalk7.green(`\u5DF2\u8BBE\u7F6E\u9ED8\u8BA4\u6743\u9650\u6A21\u5F0F: ${PERMISSION_PRESETS[mode].name}`));
2440
2763
  console.log(chalk7.gray(`\u4E0B\u6B21\u542F\u52A8 ccem \u65F6\u5C06\u9ED8\u8BA4\u4F7F\u7528\u6B64\u6A21\u5F0F`));
2441
2764
  return;
2442
2765
  }
2443
2766
  }
2444
2767
  const currentDefault = config2.get("defaultMode");
2445
- if (currentDefault && PERMISSION_PRESETS4[currentDefault]) {
2446
- console.log(chalk7.green(`\u5F53\u524D\u9ED8\u8BA4\u6A21\u5F0F: ${PERMISSION_PRESETS4[currentDefault].name}`));
2768
+ if (currentDefault && PERMISSION_PRESETS[currentDefault]) {
2769
+ console.log(chalk7.green(`\u5F53\u524D\u9ED8\u8BA4\u6A21\u5F0F: ${PERMISSION_PRESETS[currentDefault].name}`));
2447
2770
  } else {
2448
2771
  console.log(chalk7.yellow("\u672A\u8BBE\u7F6E\u9ED8\u8BA4\u6743\u9650\u6A21\u5F0F"));
2449
2772
  }
@@ -2459,28 +2782,28 @@ setupCmd.command("migrate").description("\u8FC1\u79FB\u65E7\u7248\u914D\u7F6E\u5
2459
2782
  const newConfigPath = getCcemConfigPath();
2460
2783
  const legacyConfigPath = getLegacyConfigPath();
2461
2784
  console.log(chalk7.cyan("\n\u{1F504} \u914D\u7F6E\u8FC1\u79FB\n"));
2462
- if (!fs7.existsSync(legacyConfigPath)) {
2785
+ if (!fs8.existsSync(legacyConfigPath)) {
2463
2786
  console.log(chalk7.yellow("\u672A\u627E\u5230\u65E7\u7248\u914D\u7F6E\u6587\u4EF6"));
2464
2787
  console.log(chalk7.gray(` \u65E7\u8DEF\u5F84: ${legacyConfigPath}`));
2465
2788
  return;
2466
2789
  }
2467
- if (fs7.existsSync(newConfigPath) && !options.force) {
2790
+ if (fs8.existsSync(newConfigPath) && !options.force) {
2468
2791
  console.log(chalk7.green("\u2713 \u914D\u7F6E\u5DF2\u5728\u65B0\u8DEF\u5F84"));
2469
2792
  console.log(chalk7.gray(` \u8DEF\u5F84: ${newConfigPath}`));
2470
2793
  console.log(chalk7.gray("\n\u4F7F\u7528 --force \u5F3A\u5236\u91CD\u65B0\u8FC1\u79FB"));
2471
2794
  return;
2472
2795
  }
2473
2796
  try {
2474
- ensureCcemDir3();
2475
- fs7.copyFileSync(legacyConfigPath, newConfigPath);
2797
+ ensureCcemDir();
2798
+ fs8.copyFileSync(legacyConfigPath, newConfigPath);
2476
2799
  console.log(chalk7.green("\u2713 \u914D\u7F6E\u5DF2\u8FC1\u79FB"));
2477
2800
  console.log(chalk7.gray(` \u4ECE: ${legacyConfigPath}`));
2478
2801
  console.log(chalk7.gray(` \u5230: ${newConfigPath}`));
2479
2802
  if (options.clean) {
2480
- fs7.unlinkSync(legacyConfigPath);
2481
- const legacyDir = path5.dirname(legacyConfigPath);
2803
+ fs8.unlinkSync(legacyConfigPath);
2804
+ const legacyDir = path6.dirname(legacyConfigPath);
2482
2805
  try {
2483
- fs7.rmdirSync(legacyDir);
2806
+ fs8.rmdirSync(legacyDir);
2484
2807
  } catch {
2485
2808
  }
2486
2809
  console.log(chalk7.green("\u2713 \u5DF2\u5220\u9664\u65E7\u914D\u7F6E\u6587\u4EF6"));
@@ -2491,13 +2814,13 @@ setupCmd.command("migrate").description("\u8FC1\u79FB\u65E7\u7248\u914D\u7F6E\u5
2491
2814
  });
2492
2815
  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
2816
  const options = this.opts();
2494
- const skillDir = path5.join(process.env.HOME || "~", ".claude", "skills");
2495
- const targetPath = path5.join(skillDir, "ccem-cron.md");
2496
- if (!fs7.existsSync(skillDir)) {
2497
- fs7.mkdirSync(skillDir, { recursive: true });
2817
+ const skillDir = path6.join(process.env.HOME || "~", ".claude", "skills");
2818
+ const targetPath = path6.join(skillDir, "ccem-cron.md");
2819
+ if (!fs8.existsSync(skillDir)) {
2820
+ fs8.mkdirSync(skillDir, { recursive: true });
2498
2821
  console.log(chalk7.gray(`\u521B\u5EFA\u76EE\u5F55: ${skillDir}`));
2499
2822
  }
2500
- if (fs7.existsSync(targetPath) && !options.force) {
2823
+ if (fs8.existsSync(targetPath) && !options.force) {
2501
2824
  const { overwrite } = await inquirer.prompt([
2502
2825
  {
2503
2826
  type: "confirm",
@@ -2511,7 +2834,7 @@ setupCmd.command("cron").description("\u5B89\u88C5 ccem-cron skill \u5230 Claude
2511
2834
  return;
2512
2835
  }
2513
2836
  }
2514
- fs7.writeFileSync(targetPath, CCEM_CRON_SKILL_CONTENT, "utf-8");
2837
+ fs8.writeFileSync(targetPath, CCEM_CRON_SKILL_CONTENT, "utf-8");
2515
2838
  console.log(chalk7.green(`\u2713 \u5DF2\u5B89\u88C5 ccem-cron skill`));
2516
2839
  console.log(chalk7.gray(` \u8DEF\u5F84: ${targetPath}`));
2517
2840
  console.log(chalk7.cyan(`
@@ -2567,10 +2890,21 @@ skillCmd.command("ls").description("\u5217\u51FA\u5DF2\u5B89\u88C5\u7684 skills"
2567
2890
  skillCmd.command("rm <name>").description("\u5220\u9664\u5DF2\u5B89\u88C5\u7684 skill").action((name) => {
2568
2891
  removeSkill(name);
2569
2892
  });
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);
2893
+ 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) => {
2894
+ const results = await loadFromRemote(url, options.secret);
2895
+ if (options.json) {
2896
+ console.log(JSON.stringify({
2897
+ count: results.length,
2898
+ environments: results.map((r) => ({
2899
+ name: r.name,
2900
+ original_name: r.originalName,
2901
+ // 使用 snake_case 匹配 Rust 结构体
2902
+ renamed: r.renamed
2903
+ }))
2904
+ }));
2905
+ }
2572
2906
  });
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() {
2907
+ 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
2908
  const opts = this.opts();
2575
2909
  const envName = opts.env || config2.get("current");
2576
2910
  const registries = config2.get("registries");
@@ -2579,8 +2913,10 @@ program.command("launch").description(false).option("--env <name>", "\u73AF\u588
2579
2913
  console.error(chalk7.red(`Environment '${envName}' not found.`));
2580
2914
  process.exit(1);
2581
2915
  }
2916
+ const proxyBaseUrl = opts.proxyBaseUrl || opts.anthropicBaseUrl;
2917
+ const launchEnvConfig = proxyBaseUrl ? { ...envConfig, ANTHROPIC_BASE_URL: proxyBaseUrl } : envConfig;
2582
2918
  await launchClaude({
2583
- envConfig,
2919
+ envConfig: launchEnvConfig,
2584
2920
  permMode: opts.perm,
2585
2921
  workingDir: opts.workingDir,
2586
2922
  sessionId: opts.sessionId,
@@ -2689,7 +3025,7 @@ Editing environment '${result.name}'`));
2689
3025
  }
2690
3026
  ]);
2691
3027
  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);
3028
+ if (answers.ANTHROPIC_API_KEY) envToEdit.ANTHROPIC_API_KEY = encrypt(answers.ANTHROPIC_API_KEY);
2693
3029
  if (answers.ANTHROPIC_MODEL) envToEdit.ANTHROPIC_MODEL = answers.ANTHROPIC_MODEL;
2694
3030
  if (answers.ANTHROPIC_SMALL_FAST_MODEL) envToEdit.ANTHROPIC_SMALL_FAST_MODEL = answers.ANTHROPIC_SMALL_FAST_MODEL;
2695
3031
  registries[result.name] = envToEdit;
@@ -2774,7 +3110,7 @@ Editing environment '${result.name}'`));
2774
3110
  }
2775
3111
  ]);
2776
3112
  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);
3113
+ if (editAnswers.ANTHROPIC_API_KEY) envToEdit.ANTHROPIC_API_KEY = encrypt(editAnswers.ANTHROPIC_API_KEY);
2778
3114
  if (editAnswers.ANTHROPIC_MODEL) envToEdit.ANTHROPIC_MODEL = editAnswers.ANTHROPIC_MODEL;
2779
3115
  if (editAnswers.ANTHROPIC_SMALL_FAST_MODEL) envToEdit.ANTHROPIC_SMALL_FAST_MODEL = editAnswers.ANTHROPIC_SMALL_FAST_MODEL;
2780
3116
  registries[targetName] = envToEdit;
@@ -2839,7 +3175,7 @@ Editing environment '${result.name}'`));
2839
3175
  await new Promise((resolve2) => setTimeout(resolve2, 800));
2840
3176
  } else if (selectedMode !== "back") {
2841
3177
  config2.set("defaultMode", selectedMode);
2842
- msg.success(`Default mode set: ${PERMISSION_PRESETS4[selectedMode].name}`);
3178
+ msg.success(`Default mode set: ${PERMISSION_PRESETS[selectedMode].name}`);
2843
3179
  await new Promise((resolve2) => setTimeout(resolve2, 800));
2844
3180
  }
2845
3181
  } 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.3",
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.3"
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
+ }