ccem 2.0.0-beta.2 → 2.0.0-beta.21
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 +947 -276
- package/model-prices.json +12 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -7,31 +7,127 @@ import inquirer from "inquirer";
|
|
|
7
7
|
import chalk7 from "chalk";
|
|
8
8
|
import Table3 from "cli-table3";
|
|
9
9
|
import { spawn as spawn3 } from "child_process";
|
|
10
|
-
import * as
|
|
11
|
-
import * as
|
|
12
|
-
import { fileURLToPath } from "url";
|
|
10
|
+
import * as fs9 from "fs";
|
|
11
|
+
import * as path7 from "path";
|
|
12
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
13
13
|
|
|
14
|
-
// ../../packages/core/dist/chunk-
|
|
14
|
+
// ../../packages/core/dist/chunk-KHQOO3XJ.js
|
|
15
|
+
var TIER_MODEL_ALIASES = /* @__PURE__ */ new Set(["opus", "sonnet", "haiku"]);
|
|
16
|
+
function normalizeEnvConfig(envConfig, defaultRuntimeModel = "opus") {
|
|
17
|
+
const hasTierDefaults = Boolean(envConfig.ANTHROPIC_DEFAULT_OPUS_MODEL) || Boolean(envConfig.ANTHROPIC_DEFAULT_SONNET_MODEL) || Boolean(envConfig.ANTHROPIC_DEFAULT_HAIKU_MODEL);
|
|
18
|
+
const defaultOpusModel = envConfig.ANTHROPIC_DEFAULT_OPUS_MODEL ?? (hasTierDefaults ? void 0 : envConfig.ANTHROPIC_MODEL);
|
|
19
|
+
const defaultSonnetModel = envConfig.ANTHROPIC_DEFAULT_SONNET_MODEL ?? defaultOpusModel ?? (hasTierDefaults ? void 0 : envConfig.ANTHROPIC_MODEL);
|
|
20
|
+
const defaultHaikuModel = envConfig.ANTHROPIC_DEFAULT_HAIKU_MODEL ?? envConfig.ANTHROPIC_SMALL_FAST_MODEL;
|
|
21
|
+
return {
|
|
22
|
+
...envConfig.ANTHROPIC_BASE_URL && {
|
|
23
|
+
ANTHROPIC_BASE_URL: envConfig.ANTHROPIC_BASE_URL
|
|
24
|
+
},
|
|
25
|
+
...(envConfig.ANTHROPIC_AUTH_TOKEN ?? envConfig.ANTHROPIC_API_KEY) && {
|
|
26
|
+
ANTHROPIC_AUTH_TOKEN: envConfig.ANTHROPIC_AUTH_TOKEN ?? envConfig.ANTHROPIC_API_KEY
|
|
27
|
+
},
|
|
28
|
+
...defaultOpusModel && {
|
|
29
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: defaultOpusModel
|
|
30
|
+
},
|
|
31
|
+
...defaultSonnetModel && {
|
|
32
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: defaultSonnetModel
|
|
33
|
+
},
|
|
34
|
+
...defaultHaikuModel && {
|
|
35
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: defaultHaikuModel
|
|
36
|
+
},
|
|
37
|
+
ANTHROPIC_MODEL: hasTierDefaults ? envConfig.ANTHROPIC_MODEL ?? defaultRuntimeModel : defaultRuntimeModel,
|
|
38
|
+
...envConfig.CLAUDE_CODE_SUBAGENT_MODEL && {
|
|
39
|
+
CLAUDE_CODE_SUBAGENT_MODEL: envConfig.CLAUDE_CODE_SUBAGENT_MODEL
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function shouldRecoverTierModel(model) {
|
|
44
|
+
return !model || TIER_MODEL_ALIASES.has(model);
|
|
45
|
+
}
|
|
46
|
+
function recoverEnvConfigFromLegacy(currentEnvConfig, legacyEnvConfig) {
|
|
47
|
+
const current = normalizeEnvConfig(currentEnvConfig);
|
|
48
|
+
const legacy = normalizeEnvConfig(legacyEnvConfig);
|
|
49
|
+
return {
|
|
50
|
+
...current,
|
|
51
|
+
...!current.ANTHROPIC_AUTH_TOKEN && legacy.ANTHROPIC_AUTH_TOKEN && {
|
|
52
|
+
ANTHROPIC_AUTH_TOKEN: legacy.ANTHROPIC_AUTH_TOKEN
|
|
53
|
+
},
|
|
54
|
+
...shouldRecoverTierModel(current.ANTHROPIC_DEFAULT_OPUS_MODEL) && legacy.ANTHROPIC_DEFAULT_OPUS_MODEL && {
|
|
55
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: legacy.ANTHROPIC_DEFAULT_OPUS_MODEL
|
|
56
|
+
},
|
|
57
|
+
...shouldRecoverTierModel(current.ANTHROPIC_DEFAULT_SONNET_MODEL) && legacy.ANTHROPIC_DEFAULT_SONNET_MODEL && {
|
|
58
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: legacy.ANTHROPIC_DEFAULT_SONNET_MODEL
|
|
59
|
+
},
|
|
60
|
+
...shouldRecoverTierModel(current.ANTHROPIC_DEFAULT_HAIKU_MODEL) && legacy.ANTHROPIC_DEFAULT_HAIKU_MODEL && {
|
|
61
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: legacy.ANTHROPIC_DEFAULT_HAIKU_MODEL
|
|
62
|
+
},
|
|
63
|
+
...!current.CLAUDE_CODE_SUBAGENT_MODEL && legacy.CLAUDE_CODE_SUBAGENT_MODEL && {
|
|
64
|
+
CLAUDE_CODE_SUBAGENT_MODEL: legacy.CLAUDE_CODE_SUBAGENT_MODEL
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
15
68
|
var ENV_PRESETS = {
|
|
16
69
|
"GLM": {
|
|
17
70
|
ANTHROPIC_BASE_URL: "https://open.bigmodel.cn/api/anthropic",
|
|
18
|
-
|
|
19
|
-
|
|
71
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: "glm-5.1",
|
|
72
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: "glm-5.1",
|
|
73
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: "glm-4.5-air",
|
|
74
|
+
ANTHROPIC_MODEL: "opus"
|
|
20
75
|
},
|
|
21
76
|
"KIMI": {
|
|
22
77
|
ANTHROPIC_BASE_URL: "https://api.moonshot.cn/anthropic",
|
|
23
|
-
|
|
24
|
-
|
|
78
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: "kimi-k2.5",
|
|
79
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: "kimi-k2.5",
|
|
80
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: "kimi-k2.5",
|
|
81
|
+
ANTHROPIC_MODEL: "opus"
|
|
82
|
+
},
|
|
83
|
+
"KimiCodePlan": {
|
|
84
|
+
ANTHROPIC_BASE_URL: "https://api.kimi.com/coding/",
|
|
85
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: "kimi-for-coding",
|
|
86
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: "kimi-for-coding",
|
|
87
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: "kimi-for-coding",
|
|
88
|
+
ANTHROPIC_MODEL: "opus"
|
|
25
89
|
},
|
|
26
90
|
"MiniMax": {
|
|
27
91
|
ANTHROPIC_BASE_URL: "https://api.minimaxi.com/anthropic",
|
|
28
|
-
|
|
29
|
-
|
|
92
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: "MiniMax-M2.7",
|
|
93
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: "MiniMax-M2.7",
|
|
94
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: "MiniMax-M2.7-highspeed",
|
|
95
|
+
ANTHROPIC_MODEL: "opus"
|
|
30
96
|
},
|
|
31
97
|
"DeepSeek": {
|
|
32
98
|
ANTHROPIC_BASE_URL: "https://api.deepseek.com/anthropic",
|
|
33
|
-
|
|
34
|
-
|
|
99
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: "deepseek-v4-pro",
|
|
100
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: "deepseek-v4-pro",
|
|
101
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: "deepseek-v4-flash",
|
|
102
|
+
ANTHROPIC_MODEL: "opus"
|
|
103
|
+
},
|
|
104
|
+
"Bailian": {
|
|
105
|
+
ANTHROPIC_BASE_URL: "https://dashscope.aliyuncs.com/api/v2/apps/claude-code-proxy",
|
|
106
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: "qwen3-coder-next",
|
|
107
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: "qwen3-coder-plus",
|
|
108
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: "qwen3-coder-flash",
|
|
109
|
+
ANTHROPIC_MODEL: "opus"
|
|
110
|
+
},
|
|
111
|
+
"BailianCodePlan": {
|
|
112
|
+
ANTHROPIC_BASE_URL: "https://coding.dashscope.aliyuncs.com/api/v2/apps/claude-code-proxy",
|
|
113
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: "qwen3-coder-next",
|
|
114
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: "qwen3-coder-plus",
|
|
115
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: "qwen3-coder-flash",
|
|
116
|
+
ANTHROPIC_MODEL: "opus"
|
|
117
|
+
},
|
|
118
|
+
"OpenRouter": {
|
|
119
|
+
ANTHROPIC_BASE_URL: "https://openrouter.ai/api/v1",
|
|
120
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: "anthropic/claude-opus-4.6",
|
|
121
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: "anthropic/claude-sonnet-4.6",
|
|
122
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: "anthropic/claude-haiku-4.5",
|
|
123
|
+
ANTHROPIC_MODEL: "opus"
|
|
124
|
+
},
|
|
125
|
+
"Ollama": {
|
|
126
|
+
ANTHROPIC_BASE_URL: "http://localhost:11434",
|
|
127
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: "gemma4:31b",
|
|
128
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: "gemma4:26b",
|
|
129
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: "gemma4:e4b",
|
|
130
|
+
ANTHROPIC_MODEL: "opus"
|
|
35
131
|
}
|
|
36
132
|
};
|
|
37
133
|
var PERMISSION_PRESETS = {
|
|
@@ -338,6 +434,9 @@ import * as fsPromises from "fs/promises";
|
|
|
338
434
|
import * as path2 from "path";
|
|
339
435
|
import * as os from "os";
|
|
340
436
|
import * as readline from "readline";
|
|
437
|
+
import { fileURLToPath } from "url";
|
|
438
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
439
|
+
var __dirname = path2.dirname(__filename);
|
|
341
440
|
var CLAUDE_PROJECTS_DIR = path2.join(os.homedir(), ".claude", "projects");
|
|
342
441
|
var CCEM_DIR = path2.join(os.homedir(), ".ccem");
|
|
343
442
|
var CACHE_VERSION = 1;
|
|
@@ -811,9 +910,10 @@ var renderLogoWithEnvPanel = (envName, env, defaultMode) => {
|
|
|
811
910
|
const titleShort = theme.primary("CCEM");
|
|
812
911
|
const envLabel = theme.muted("Env: ") + theme.primary(envName);
|
|
813
912
|
const baseUrl = env.ANTHROPIC_BASE_URL || "-";
|
|
814
|
-
const
|
|
815
|
-
const
|
|
816
|
-
const
|
|
913
|
+
const runtimeModel = env.ANTHROPIC_MODEL || "-";
|
|
914
|
+
const opusModel = env.ANTHROPIC_DEFAULT_OPUS_MODEL || "-";
|
|
915
|
+
const haikuModel = env.ANTHROPIC_DEFAULT_HAIKU_MODEL || "-";
|
|
916
|
+
const authToken = env.ANTHROPIC_AUTH_TOKEN ? env.ANTHROPIC_AUTH_TOKEN.slice(0, 2) + "\u2022\u2022\u2022\u2022" + env.ANTHROPIC_AUTH_TOKEN.slice(-4) : "-";
|
|
817
917
|
const truncate = (s, max) => s.length > max ? s.slice(0, max - 3) + "..." : s;
|
|
818
918
|
const maskUrl = (url, max) => {
|
|
819
919
|
if (url.length <= max) return url;
|
|
@@ -821,10 +921,10 @@ var renderLogoWithEnvPanel = (envName, env, defaultMode) => {
|
|
|
821
921
|
const parsed = new URL(url);
|
|
822
922
|
const protocol = parsed.protocol + "//";
|
|
823
923
|
const host = parsed.host;
|
|
824
|
-
const
|
|
924
|
+
const path8 = parsed.pathname + parsed.search;
|
|
825
925
|
const hostStart = host.slice(0, 8);
|
|
826
926
|
const hostEnd = host.slice(-4);
|
|
827
|
-
const pathPart =
|
|
927
|
+
const pathPart = path8.length > 10 ? path8.slice(0, 7) + "..." : path8;
|
|
828
928
|
return `${protocol}${hostStart}...${hostEnd}${pathPart}`;
|
|
829
929
|
} catch {
|
|
830
930
|
return truncate(url, max);
|
|
@@ -835,15 +935,15 @@ var renderLogoWithEnvPanel = (envName, env, defaultMode) => {
|
|
|
835
935
|
if (isNarrow) {
|
|
836
936
|
envLines = [
|
|
837
937
|
envLabel,
|
|
838
|
-
theme.muted("
|
|
839
|
-
theme.muted("
|
|
938
|
+
theme.muted("Opus:".padEnd(labelWidth)) + theme.dim(truncate(opusModel, 25)),
|
|
939
|
+
theme.muted("Token:".padEnd(labelWidth)) + theme.dim(authToken)
|
|
840
940
|
];
|
|
841
941
|
} else {
|
|
842
942
|
envLines = [
|
|
843
943
|
envLabel + (defaultMode && PERMISSION_PRESETS[defaultMode] ? " " + theme.accent(`[${PERMISSION_PRESETS[defaultMode].name}]`) : ""),
|
|
844
944
|
theme.muted("URL:".padEnd(labelWidth)) + theme.dim(maskUrl(baseUrl, 40)),
|
|
845
|
-
theme.muted("
|
|
846
|
-
theme.muted("
|
|
945
|
+
theme.muted("Run:".padEnd(labelWidth)) + theme.dim(truncate(runtimeModel, 12)) + " " + theme.muted("Opus:".padEnd(labelWidth)) + theme.dim(truncate(opusModel, 15)),
|
|
946
|
+
theme.muted("Haiku:".padEnd(labelWidth)) + theme.dim(truncate(haikuModel, 15)) + " " + theme.muted("Token:".padEnd(labelWidth)) + theme.dim(authToken)
|
|
847
947
|
];
|
|
848
948
|
}
|
|
849
949
|
const lines = [];
|
|
@@ -1262,7 +1362,7 @@ var selectEnvWithKeys = (registries, current) => {
|
|
|
1262
1362
|
};
|
|
1263
1363
|
|
|
1264
1364
|
// src/permissions.ts
|
|
1265
|
-
import
|
|
1365
|
+
import fs6 from "fs";
|
|
1266
1366
|
import chalk3 from "chalk";
|
|
1267
1367
|
import Table2 from "cli-table3";
|
|
1268
1368
|
|
|
@@ -1315,15 +1415,367 @@ var ensureGlobalClaudeDir = () => {
|
|
|
1315
1415
|
|
|
1316
1416
|
// src/launcher.ts
|
|
1317
1417
|
import { spawn } from "child_process";
|
|
1318
|
-
import * as
|
|
1319
|
-
import * as
|
|
1418
|
+
import * as fs5 from "fs";
|
|
1419
|
+
import * as path5 from "path";
|
|
1320
1420
|
import chalk2 from "chalk";
|
|
1421
|
+
|
|
1422
|
+
// src/sessionProvenance.ts
|
|
1423
|
+
import { execFileSync } from "child_process";
|
|
1424
|
+
import fs4 from "fs";
|
|
1425
|
+
import { createRequire } from "module";
|
|
1426
|
+
import os2 from "os";
|
|
1427
|
+
import path4 from "path";
|
|
1428
|
+
var DEFAULT_CONFIG_SOURCE = "ccem";
|
|
1429
|
+
var STATE_DB_FILE_NAME = "state.sqlite";
|
|
1430
|
+
var BIND_POLL_INTERVAL_MS = 500;
|
|
1431
|
+
var BIND_POLL_TIMEOUT_MS = 2e4;
|
|
1432
|
+
var SQLITE_EXPERIMENTAL_WARNING = "SQLite is an experimental feature";
|
|
1433
|
+
var require2 = createRequire(import.meta.url);
|
|
1434
|
+
var databaseSyncCtor = null;
|
|
1435
|
+
var databaseSyncResolved = false;
|
|
1436
|
+
var sqlite3CliAvailable = null;
|
|
1437
|
+
function startCliClaudeProvenanceTracking(options) {
|
|
1438
|
+
const envName = normalizeText(options.envName) ?? "unknown";
|
|
1439
|
+
const workingDir = normalizeText(options.workingDir);
|
|
1440
|
+
if (!workingDir) {
|
|
1441
|
+
return null;
|
|
1442
|
+
}
|
|
1443
|
+
const ccemSessionId = `cli-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
1444
|
+
try {
|
|
1445
|
+
registerLaunch({
|
|
1446
|
+
ccemSessionId,
|
|
1447
|
+
client: "claude",
|
|
1448
|
+
envName,
|
|
1449
|
+
configSource: DEFAULT_CONFIG_SOURCE,
|
|
1450
|
+
workingDir,
|
|
1451
|
+
permMode: normalizeText(options.permMode),
|
|
1452
|
+
launchMode: "cli_external",
|
|
1453
|
+
startedVia: "cli",
|
|
1454
|
+
sourceSessionId: normalizeText(options.resumeSessionId)
|
|
1455
|
+
});
|
|
1456
|
+
} catch (error) {
|
|
1457
|
+
reportTrackingError("register launch", error);
|
|
1458
|
+
return null;
|
|
1459
|
+
}
|
|
1460
|
+
let timer = null;
|
|
1461
|
+
const stop = () => {
|
|
1462
|
+
if (timer) {
|
|
1463
|
+
clearInterval(timer);
|
|
1464
|
+
timer = null;
|
|
1465
|
+
}
|
|
1466
|
+
};
|
|
1467
|
+
const resumeSessionId = normalizeText(options.resumeSessionId);
|
|
1468
|
+
if (resumeSessionId) {
|
|
1469
|
+
return { ccemSessionId, stop };
|
|
1470
|
+
}
|
|
1471
|
+
const startedAtMs = Date.now();
|
|
1472
|
+
timer = setInterval(() => {
|
|
1473
|
+
if (Date.now() - startedAtMs > BIND_POLL_TIMEOUT_MS) {
|
|
1474
|
+
stop();
|
|
1475
|
+
return;
|
|
1476
|
+
}
|
|
1477
|
+
try {
|
|
1478
|
+
const jsonlPath = discoverClaudeJsonlPath(workingDir, startedAtMs);
|
|
1479
|
+
if (!jsonlPath) {
|
|
1480
|
+
return;
|
|
1481
|
+
}
|
|
1482
|
+
const sourceSessionId = readClaudeSessionId(jsonlPath) ?? normalizeText(path4.parse(jsonlPath).name);
|
|
1483
|
+
if (!sourceSessionId) {
|
|
1484
|
+
return;
|
|
1485
|
+
}
|
|
1486
|
+
bindSourceSessionId("claude", ccemSessionId, sourceSessionId);
|
|
1487
|
+
stop();
|
|
1488
|
+
} catch (error) {
|
|
1489
|
+
reportTrackingError("bind source session id", error);
|
|
1490
|
+
stop();
|
|
1491
|
+
}
|
|
1492
|
+
}, BIND_POLL_INTERVAL_MS);
|
|
1493
|
+
return { ccemSessionId, stop };
|
|
1494
|
+
}
|
|
1495
|
+
function registerLaunch(options) {
|
|
1496
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1497
|
+
const DatabaseSync = getDatabaseSyncCtor();
|
|
1498
|
+
if (DatabaseSync) {
|
|
1499
|
+
const db = openNodeSqliteDb(DatabaseSync);
|
|
1500
|
+
try {
|
|
1501
|
+
const statement = db.prepare(`
|
|
1502
|
+
INSERT INTO session_provenance (
|
|
1503
|
+
ccem_session_id,
|
|
1504
|
+
client,
|
|
1505
|
+
env_name,
|
|
1506
|
+
config_source,
|
|
1507
|
+
working_dir,
|
|
1508
|
+
perm_mode,
|
|
1509
|
+
launch_mode,
|
|
1510
|
+
started_via,
|
|
1511
|
+
source_session_id,
|
|
1512
|
+
created_at,
|
|
1513
|
+
updated_at
|
|
1514
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1515
|
+
ON CONFLICT(ccem_session_id) DO UPDATE SET
|
|
1516
|
+
client = excluded.client,
|
|
1517
|
+
env_name = excluded.env_name,
|
|
1518
|
+
config_source = COALESCE(excluded.config_source, session_provenance.config_source),
|
|
1519
|
+
working_dir = excluded.working_dir,
|
|
1520
|
+
perm_mode = COALESCE(excluded.perm_mode, session_provenance.perm_mode),
|
|
1521
|
+
launch_mode = excluded.launch_mode,
|
|
1522
|
+
started_via = excluded.started_via,
|
|
1523
|
+
source_session_id = COALESCE(excluded.source_session_id, session_provenance.source_session_id),
|
|
1524
|
+
updated_at = excluded.updated_at
|
|
1525
|
+
`);
|
|
1526
|
+
statement.run(
|
|
1527
|
+
options.ccemSessionId,
|
|
1528
|
+
options.client,
|
|
1529
|
+
options.envName,
|
|
1530
|
+
normalizeText(options.configSource),
|
|
1531
|
+
options.workingDir,
|
|
1532
|
+
normalizeText(options.permMode),
|
|
1533
|
+
options.launchMode,
|
|
1534
|
+
options.startedVia,
|
|
1535
|
+
normalizeText(options.sourceSessionId),
|
|
1536
|
+
now,
|
|
1537
|
+
now
|
|
1538
|
+
);
|
|
1539
|
+
return;
|
|
1540
|
+
} finally {
|
|
1541
|
+
db.close();
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
execSqlite3Cli(`
|
|
1545
|
+
INSERT INTO session_provenance (
|
|
1546
|
+
ccem_session_id,
|
|
1547
|
+
client,
|
|
1548
|
+
env_name,
|
|
1549
|
+
config_source,
|
|
1550
|
+
working_dir,
|
|
1551
|
+
perm_mode,
|
|
1552
|
+
launch_mode,
|
|
1553
|
+
started_via,
|
|
1554
|
+
source_session_id,
|
|
1555
|
+
created_at,
|
|
1556
|
+
updated_at
|
|
1557
|
+
) VALUES (
|
|
1558
|
+
${sqliteLiteral(options.ccemSessionId)},
|
|
1559
|
+
${sqliteLiteral(options.client)},
|
|
1560
|
+
${sqliteLiteral(options.envName)},
|
|
1561
|
+
${sqliteLiteral(normalizeText(options.configSource))},
|
|
1562
|
+
${sqliteLiteral(options.workingDir)},
|
|
1563
|
+
${sqliteLiteral(normalizeText(options.permMode))},
|
|
1564
|
+
${sqliteLiteral(options.launchMode)},
|
|
1565
|
+
${sqliteLiteral(options.startedVia)},
|
|
1566
|
+
${sqliteLiteral(normalizeText(options.sourceSessionId))},
|
|
1567
|
+
${sqliteLiteral(now)},
|
|
1568
|
+
${sqliteLiteral(now)}
|
|
1569
|
+
)
|
|
1570
|
+
ON CONFLICT(ccem_session_id) DO UPDATE SET
|
|
1571
|
+
client = excluded.client,
|
|
1572
|
+
env_name = excluded.env_name,
|
|
1573
|
+
config_source = COALESCE(excluded.config_source, session_provenance.config_source),
|
|
1574
|
+
working_dir = excluded.working_dir,
|
|
1575
|
+
perm_mode = COALESCE(excluded.perm_mode, session_provenance.perm_mode),
|
|
1576
|
+
launch_mode = excluded.launch_mode,
|
|
1577
|
+
started_via = excluded.started_via,
|
|
1578
|
+
source_session_id = COALESCE(excluded.source_session_id, session_provenance.source_session_id),
|
|
1579
|
+
updated_at = excluded.updated_at;
|
|
1580
|
+
`);
|
|
1581
|
+
}
|
|
1582
|
+
function bindSourceSessionId(client, ccemSessionId, sourceSessionId) {
|
|
1583
|
+
const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1584
|
+
const DatabaseSync = getDatabaseSyncCtor();
|
|
1585
|
+
if (DatabaseSync) {
|
|
1586
|
+
const db = openNodeSqliteDb(DatabaseSync);
|
|
1587
|
+
try {
|
|
1588
|
+
const statement = db.prepare(`
|
|
1589
|
+
UPDATE session_provenance
|
|
1590
|
+
SET source_session_id = ?, updated_at = ?
|
|
1591
|
+
WHERE ccem_session_id = ? AND client = ?
|
|
1592
|
+
`);
|
|
1593
|
+
statement.run(sourceSessionId, updatedAt, ccemSessionId, client);
|
|
1594
|
+
return;
|
|
1595
|
+
} finally {
|
|
1596
|
+
db.close();
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
execSqlite3Cli(`
|
|
1600
|
+
UPDATE session_provenance
|
|
1601
|
+
SET source_session_id = ${sqliteLiteral(sourceSessionId)},
|
|
1602
|
+
updated_at = ${sqliteLiteral(updatedAt)}
|
|
1603
|
+
WHERE ccem_session_id = ${sqliteLiteral(ccemSessionId)}
|
|
1604
|
+
AND client = ${sqliteLiteral(client)};
|
|
1605
|
+
`);
|
|
1606
|
+
}
|
|
1607
|
+
function openNodeSqliteDb(DatabaseSync) {
|
|
1608
|
+
const dbPath = getStateDbPath();
|
|
1609
|
+
fs4.mkdirSync(path4.dirname(dbPath), { recursive: true });
|
|
1610
|
+
const db = new DatabaseSync(dbPath);
|
|
1611
|
+
db.exec(schemaSql());
|
|
1612
|
+
return db;
|
|
1613
|
+
}
|
|
1614
|
+
function getDatabaseSyncCtor() {
|
|
1615
|
+
if (databaseSyncResolved) {
|
|
1616
|
+
return databaseSyncCtor;
|
|
1617
|
+
}
|
|
1618
|
+
const originalEmitWarning = process.emitWarning;
|
|
1619
|
+
process.emitWarning = ((warning, ...args) => {
|
|
1620
|
+
const message = typeof warning === "string" ? warning : warning instanceof Error ? warning.message : String(warning);
|
|
1621
|
+
if (message.includes(SQLITE_EXPERIMENTAL_WARNING)) {
|
|
1622
|
+
return;
|
|
1623
|
+
}
|
|
1624
|
+
return originalEmitWarning(warning, ...args);
|
|
1625
|
+
});
|
|
1626
|
+
try {
|
|
1627
|
+
const loaded = require2("node:sqlite");
|
|
1628
|
+
databaseSyncCtor = loaded.DatabaseSync;
|
|
1629
|
+
} catch {
|
|
1630
|
+
databaseSyncCtor = null;
|
|
1631
|
+
} finally {
|
|
1632
|
+
process.emitWarning = originalEmitWarning;
|
|
1633
|
+
databaseSyncResolved = true;
|
|
1634
|
+
}
|
|
1635
|
+
return databaseSyncCtor;
|
|
1636
|
+
}
|
|
1637
|
+
function execSqlite3Cli(statementSql) {
|
|
1638
|
+
if (sqlite3CliAvailable == null) {
|
|
1639
|
+
try {
|
|
1640
|
+
execFileSync("sqlite3", ["-version"], { stdio: "ignore" });
|
|
1641
|
+
sqlite3CliAvailable = true;
|
|
1642
|
+
} catch {
|
|
1643
|
+
sqlite3CliAvailable = false;
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
if (!sqlite3CliAvailable) {
|
|
1647
|
+
throw new Error("sqlite3 CLI is unavailable and node:sqlite is not supported by this Node runtime");
|
|
1648
|
+
}
|
|
1649
|
+
const dbPath = getStateDbPath();
|
|
1650
|
+
fs4.mkdirSync(path4.dirname(dbPath), { recursive: true });
|
|
1651
|
+
execFileSync("sqlite3", [dbPath], {
|
|
1652
|
+
input: `${schemaSql()}
|
|
1653
|
+
${statementSql}
|
|
1654
|
+
`,
|
|
1655
|
+
stdio: ["pipe", "ignore", "pipe"]
|
|
1656
|
+
});
|
|
1657
|
+
}
|
|
1658
|
+
function getStateDbPath() {
|
|
1659
|
+
const override = normalizeText(process.env.CCEM_STATE_DB_PATH);
|
|
1660
|
+
if (override) {
|
|
1661
|
+
return override;
|
|
1662
|
+
}
|
|
1663
|
+
return path4.join(os2.homedir(), ".ccem", STATE_DB_FILE_NAME);
|
|
1664
|
+
}
|
|
1665
|
+
function schemaSql() {
|
|
1666
|
+
return `
|
|
1667
|
+
PRAGMA journal_mode = WAL;
|
|
1668
|
+
PRAGMA synchronous = NORMAL;
|
|
1669
|
+
CREATE TABLE IF NOT EXISTS session_provenance (
|
|
1670
|
+
ccem_session_id TEXT PRIMARY KEY,
|
|
1671
|
+
client TEXT NOT NULL,
|
|
1672
|
+
env_name TEXT NOT NULL,
|
|
1673
|
+
config_source TEXT,
|
|
1674
|
+
working_dir TEXT NOT NULL,
|
|
1675
|
+
perm_mode TEXT,
|
|
1676
|
+
launch_mode TEXT NOT NULL,
|
|
1677
|
+
started_via TEXT NOT NULL,
|
|
1678
|
+
source_session_id TEXT,
|
|
1679
|
+
created_at TEXT NOT NULL,
|
|
1680
|
+
updated_at TEXT NOT NULL
|
|
1681
|
+
);
|
|
1682
|
+
CREATE INDEX IF NOT EXISTS idx_session_provenance_client_source
|
|
1683
|
+
ON session_provenance (client, source_session_id);
|
|
1684
|
+
CREATE INDEX IF NOT EXISTS idx_session_provenance_client_updated
|
|
1685
|
+
ON session_provenance (client, updated_at DESC);
|
|
1686
|
+
`;
|
|
1687
|
+
}
|
|
1688
|
+
function discoverClaudeJsonlPath(projectDir, startedAtMs) {
|
|
1689
|
+
const projectsDir = path4.join(os2.homedir(), ".claude", "projects");
|
|
1690
|
+
const earliestModifiedAt = startedAtMs - 15e3;
|
|
1691
|
+
for (const key of projectDirKeys(projectDir)) {
|
|
1692
|
+
const baseDir = path4.join(projectsDir, key);
|
|
1693
|
+
if (!fs4.existsSync(baseDir)) {
|
|
1694
|
+
continue;
|
|
1695
|
+
}
|
|
1696
|
+
const candidates = fs4.readdirSync(baseDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl")).map((entry) => {
|
|
1697
|
+
const fullPath = path4.join(baseDir, entry.name);
|
|
1698
|
+
return { path: fullPath, modifiedAt: fs4.statSync(fullPath).mtimeMs };
|
|
1699
|
+
}).filter((candidate) => candidate.modifiedAt >= earliestModifiedAt).sort((left, right) => right.modifiedAt - left.modifiedAt);
|
|
1700
|
+
if (candidates.length > 0) {
|
|
1701
|
+
return candidates[0].path;
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
return null;
|
|
1705
|
+
}
|
|
1706
|
+
function projectDirKeys(projectDir) {
|
|
1707
|
+
const candidates = [projectDir];
|
|
1708
|
+
try {
|
|
1709
|
+
candidates.push(fs4.realpathSync(projectDir));
|
|
1710
|
+
} catch {
|
|
1711
|
+
}
|
|
1712
|
+
if (process.platform === "darwin") {
|
|
1713
|
+
if (projectDir.startsWith("/private/")) {
|
|
1714
|
+
candidates.push(projectDir.slice("/private".length));
|
|
1715
|
+
} else if (projectDir.startsWith("/tmp")) {
|
|
1716
|
+
candidates.push(path4.posix.join("/private", projectDir));
|
|
1717
|
+
} else if (projectDir.startsWith("/var/")) {
|
|
1718
|
+
candidates.push(path4.posix.join("/private", projectDir));
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1721
|
+
return [...new Set(candidates.map(projectDirKey))];
|
|
1722
|
+
}
|
|
1723
|
+
function projectDirKey(projectDir) {
|
|
1724
|
+
return projectDir.replace(/[\/\\: ]/g, "-");
|
|
1725
|
+
}
|
|
1726
|
+
function readClaudeSessionId(jsonlPath) {
|
|
1727
|
+
const lines = fs4.readFileSync(jsonlPath, "utf8").split("\n");
|
|
1728
|
+
for (const line of lines) {
|
|
1729
|
+
if (!line.trim()) {
|
|
1730
|
+
continue;
|
|
1731
|
+
}
|
|
1732
|
+
try {
|
|
1733
|
+
const value = JSON.parse(line);
|
|
1734
|
+
const sessionId = normalizeText(value.sessionId);
|
|
1735
|
+
if (sessionId) {
|
|
1736
|
+
return sessionId;
|
|
1737
|
+
}
|
|
1738
|
+
} catch {
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
return null;
|
|
1742
|
+
}
|
|
1743
|
+
function normalizeText(value) {
|
|
1744
|
+
const trimmed = value?.trim();
|
|
1745
|
+
return trimmed ? trimmed : void 0;
|
|
1746
|
+
}
|
|
1747
|
+
function sqliteLiteral(value) {
|
|
1748
|
+
if (!value) {
|
|
1749
|
+
return "NULL";
|
|
1750
|
+
}
|
|
1751
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
1752
|
+
}
|
|
1753
|
+
function reportTrackingError(stage, error) {
|
|
1754
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1755
|
+
console.error(`[ccem] Failed to ${stage}: ${message}`);
|
|
1756
|
+
}
|
|
1757
|
+
|
|
1758
|
+
// src/launcher.ts
|
|
1759
|
+
var MANAGED_CLAUDE_ENV_KEYS = [
|
|
1760
|
+
"ANTHROPIC_BASE_URL",
|
|
1761
|
+
"ANTHROPIC_AUTH_TOKEN",
|
|
1762
|
+
"ANTHROPIC_DEFAULT_OPUS_MODEL",
|
|
1763
|
+
"ANTHROPIC_DEFAULT_SONNET_MODEL",
|
|
1764
|
+
"ANTHROPIC_DEFAULT_HAIKU_MODEL",
|
|
1765
|
+
"ANTHROPIC_MODEL",
|
|
1766
|
+
"CLAUDE_CODE_SUBAGENT_MODEL",
|
|
1767
|
+
"ANTHROPIC_API_KEY",
|
|
1768
|
+
"ANTHROPIC_SMALL_FAST_MODEL"
|
|
1769
|
+
];
|
|
1321
1770
|
function buildEnvVars(envConfig) {
|
|
1322
1771
|
const vars = {};
|
|
1323
1772
|
if (envConfig.ANTHROPIC_BASE_URL) vars.ANTHROPIC_BASE_URL = envConfig.ANTHROPIC_BASE_URL;
|
|
1324
|
-
if (envConfig.
|
|
1773
|
+
if (envConfig.ANTHROPIC_AUTH_TOKEN) vars.ANTHROPIC_AUTH_TOKEN = decrypt(envConfig.ANTHROPIC_AUTH_TOKEN);
|
|
1774
|
+
if (envConfig.ANTHROPIC_DEFAULT_OPUS_MODEL) vars.ANTHROPIC_DEFAULT_OPUS_MODEL = envConfig.ANTHROPIC_DEFAULT_OPUS_MODEL;
|
|
1775
|
+
if (envConfig.ANTHROPIC_DEFAULT_SONNET_MODEL) vars.ANTHROPIC_DEFAULT_SONNET_MODEL = envConfig.ANTHROPIC_DEFAULT_SONNET_MODEL;
|
|
1776
|
+
if (envConfig.ANTHROPIC_DEFAULT_HAIKU_MODEL) vars.ANTHROPIC_DEFAULT_HAIKU_MODEL = envConfig.ANTHROPIC_DEFAULT_HAIKU_MODEL;
|
|
1325
1777
|
if (envConfig.ANTHROPIC_MODEL) vars.ANTHROPIC_MODEL = envConfig.ANTHROPIC_MODEL;
|
|
1326
|
-
if (envConfig.
|
|
1778
|
+
if (envConfig.CLAUDE_CODE_SUBAGENT_MODEL) vars.CLAUDE_CODE_SUBAGENT_MODEL = envConfig.CLAUDE_CODE_SUBAGENT_MODEL;
|
|
1327
1779
|
return vars;
|
|
1328
1780
|
}
|
|
1329
1781
|
function buildPermArgs(modeName) {
|
|
@@ -1341,15 +1793,18 @@ function buildPermArgs(modeName) {
|
|
|
1341
1793
|
return args;
|
|
1342
1794
|
}
|
|
1343
1795
|
function ensureSessionsDir() {
|
|
1344
|
-
const dir =
|
|
1345
|
-
if (!
|
|
1346
|
-
|
|
1796
|
+
const dir = path5.join(ensureCcemDir(), "sessions");
|
|
1797
|
+
if (!fs5.existsSync(dir)) {
|
|
1798
|
+
fs5.mkdirSync(dir, { recursive: true });
|
|
1347
1799
|
}
|
|
1348
1800
|
return dir;
|
|
1349
1801
|
}
|
|
1350
1802
|
async function launchClaude(options) {
|
|
1351
|
-
const { envConfig, permMode, workingDir, sessionId, resumeSessionId, silent } = options;
|
|
1803
|
+
const { envName, envConfig, permMode, workingDir, sessionId, resumeSessionId, silent } = options;
|
|
1352
1804
|
const env = { ...process.env };
|
|
1805
|
+
for (const key of MANAGED_CLAUDE_ENV_KEYS) {
|
|
1806
|
+
delete env[key];
|
|
1807
|
+
}
|
|
1353
1808
|
if (envConfig) {
|
|
1354
1809
|
Object.assign(env, buildEnvVars(envConfig));
|
|
1355
1810
|
}
|
|
@@ -1372,21 +1827,36 @@ async function launchClaude(options) {
|
|
|
1372
1827
|
if (workingDir) {
|
|
1373
1828
|
process.chdir(workingDir);
|
|
1374
1829
|
}
|
|
1830
|
+
const effectiveWorkingDir = process.cwd();
|
|
1375
1831
|
if (!silent && !permMode) {
|
|
1376
1832
|
console.log(renderStarting());
|
|
1377
1833
|
}
|
|
1378
1834
|
const sessionsDir = ensureSessionsDir();
|
|
1379
1835
|
return new Promise((resolve2) => {
|
|
1836
|
+
let provenanceTracking = null;
|
|
1380
1837
|
const child = spawn("claude", args, {
|
|
1381
1838
|
stdio: "inherit",
|
|
1382
|
-
shell:
|
|
1839
|
+
shell: false,
|
|
1840
|
+
// 直接执行二进制,避免 shell 注入风险
|
|
1383
1841
|
env
|
|
1384
1842
|
});
|
|
1843
|
+
child.once("spawn", () => {
|
|
1844
|
+
if (sessionId) {
|
|
1845
|
+
return;
|
|
1846
|
+
}
|
|
1847
|
+
provenanceTracking = startCliClaudeProvenanceTracking({
|
|
1848
|
+
envName: envName ?? "unknown",
|
|
1849
|
+
workingDir: effectiveWorkingDir,
|
|
1850
|
+
permMode,
|
|
1851
|
+
resumeSessionId
|
|
1852
|
+
});
|
|
1853
|
+
});
|
|
1385
1854
|
child.on("exit", (code) => {
|
|
1855
|
+
provenanceTracking?.stop();
|
|
1386
1856
|
if (sessionId) {
|
|
1387
1857
|
try {
|
|
1388
|
-
|
|
1389
|
-
|
|
1858
|
+
fs5.writeFileSync(
|
|
1859
|
+
path5.join(sessionsDir, `${sessionId}.exit`),
|
|
1390
1860
|
String(code ?? 0)
|
|
1391
1861
|
);
|
|
1392
1862
|
} catch {
|
|
@@ -1395,7 +1865,24 @@ async function launchClaude(options) {
|
|
|
1395
1865
|
process.exit(code ?? 0);
|
|
1396
1866
|
});
|
|
1397
1867
|
child.on("error", (err) => {
|
|
1398
|
-
|
|
1868
|
+
provenanceTracking?.stop();
|
|
1869
|
+
if (err.code === "ENOENT") {
|
|
1870
|
+
console.error("");
|
|
1871
|
+
console.error(chalk2.red.bold("\u2718 \u672A\u627E\u5230 Claude Code"));
|
|
1872
|
+
console.error("");
|
|
1873
|
+
console.error(chalk2.white(" CCEM \u9700\u8981 Claude Code CLI \u624D\u80FD\u542F\u52A8\u4F1A\u8BDD\uFF0C\u4F46\u5728\u7CFB\u7EDF\u4E2D\u672A\u68C0\u6D4B\u5230 ") + chalk2.cyan("claude") + chalk2.white(" \u547D\u4EE4\u3002"));
|
|
1874
|
+
console.error("");
|
|
1875
|
+
console.error(chalk2.white(" \u8BF7\u5148\u5B89\u88C5 Claude Code:"));
|
|
1876
|
+
console.error(chalk2.cyan(" npm install -g @anthropic-ai/claude-code"));
|
|
1877
|
+
console.error("");
|
|
1878
|
+
console.error(chalk2.gray(" \u5982\u679C\u5DF2\u5B89\u88C5\u4F46\u4ECD\u62A5\u9519\uFF0C\u8BF7\u68C0\u67E5:"));
|
|
1879
|
+
console.error(chalk2.gray(" 1. \u8FD0\u884C claude --version \u786E\u8BA4\u5B89\u88C5\u6210\u529F"));
|
|
1880
|
+
console.error(chalk2.gray(" 2. \u786E\u4FDD npm \u5168\u5C40\u76EE\u5F55\u5728\u7CFB\u7EDF PATH \u4E2D\uFF08npm config get prefix\uFF09"));
|
|
1881
|
+
console.error(chalk2.gray(" 3. \u5B89\u88C5\u540E\u8BF7\u91CD\u542F\u7EC8\u7AEF"));
|
|
1882
|
+
console.error("");
|
|
1883
|
+
} else {
|
|
1884
|
+
console.error(chalk2.red(`\u542F\u52A8 Claude Code \u5931\u8D25: ${err.message}`));
|
|
1885
|
+
}
|
|
1399
1886
|
process.exit(1);
|
|
1400
1887
|
});
|
|
1401
1888
|
});
|
|
@@ -1403,14 +1890,14 @@ async function launchClaude(options) {
|
|
|
1403
1890
|
|
|
1404
1891
|
// src/permissions.ts
|
|
1405
1892
|
var readSettings = (settingsPath) => {
|
|
1406
|
-
if (
|
|
1893
|
+
if (fs6.existsSync(settingsPath)) {
|
|
1407
1894
|
try {
|
|
1408
|
-
const content =
|
|
1895
|
+
const content = fs6.readFileSync(settingsPath, "utf-8");
|
|
1409
1896
|
return JSON.parse(content);
|
|
1410
1897
|
} catch {
|
|
1411
1898
|
console.warn(chalk3.yellow(`\u8B66\u544A: \u65E0\u6CD5\u89E3\u6790 ${settingsPath}\uFF0C\u5C06\u521B\u5EFA\u5907\u4EFD`));
|
|
1412
1899
|
const backupPath = settingsPath + ".error." + Date.now();
|
|
1413
|
-
|
|
1900
|
+
fs6.copyFileSync(settingsPath, backupPath);
|
|
1414
1901
|
console.log(chalk3.gray(`\u5907\u4EFD\u5DF2\u4FDD\u5B58\u5230: ${backupPath}`));
|
|
1415
1902
|
return {};
|
|
1416
1903
|
}
|
|
@@ -1419,7 +1906,7 @@ var readSettings = (settingsPath) => {
|
|
|
1419
1906
|
};
|
|
1420
1907
|
var writeSettings = (settingsPath, config3) => {
|
|
1421
1908
|
ensureClaudeDir();
|
|
1422
|
-
|
|
1909
|
+
fs6.writeFileSync(settingsPath, JSON.stringify(config3, null, 2) + "\n");
|
|
1423
1910
|
};
|
|
1424
1911
|
var mergePermissions = (existing, preset) => {
|
|
1425
1912
|
const existingAllow = existing.permissions?.allow || [];
|
|
@@ -1451,14 +1938,14 @@ var applyPermissionMode = (modeName) => {
|
|
|
1451
1938
|
};
|
|
1452
1939
|
var resetPermissions = () => {
|
|
1453
1940
|
const settingsPath = getSettingsPath(true);
|
|
1454
|
-
if (!
|
|
1941
|
+
if (!fs6.existsSync(settingsPath)) {
|
|
1455
1942
|
console.log(chalk3.yellow("\u6CA1\u6709\u81EA\u5B9A\u4E49\u6743\u9650\u914D\u7F6E\u9700\u8981\u91CD\u7F6E"));
|
|
1456
1943
|
return;
|
|
1457
1944
|
}
|
|
1458
1945
|
const config3 = readSettings(settingsPath);
|
|
1459
1946
|
delete config3.permissions;
|
|
1460
1947
|
if (Object.keys(config3).length === 0) {
|
|
1461
|
-
|
|
1948
|
+
fs6.unlinkSync(settingsPath);
|
|
1462
1949
|
console.log(chalk3.green("\u5DF2\u5220\u9664\u914D\u7F6E\u6587\u4EF6\uFF08\u6587\u4EF6\u4E3A\u7A7A\uFF09"));
|
|
1463
1950
|
} else {
|
|
1464
1951
|
writeSettings(settingsPath, config3);
|
|
@@ -1468,7 +1955,7 @@ var resetPermissions = () => {
|
|
|
1468
1955
|
};
|
|
1469
1956
|
var showCurrentMode = () => {
|
|
1470
1957
|
const settingsPath = getSettingsPath(true);
|
|
1471
|
-
if (!
|
|
1958
|
+
if (!fs6.existsSync(settingsPath)) {
|
|
1472
1959
|
console.log(chalk3.yellow("\u672A\u914D\u7F6E\u81EA\u5B9A\u4E49\u6743\u9650"));
|
|
1473
1960
|
console.log(chalk3.gray(`\u6587\u4EF6\u4E0D\u5B58\u5728: ${settingsPath}`));
|
|
1474
1961
|
return;
|
|
@@ -1518,24 +2005,24 @@ var listAvailableModes = () => {
|
|
|
1518
2005
|
console.log(chalk3.gray("\n\u4E34\u65F6\u6A21\u5F0F: ccem <mode>"));
|
|
1519
2006
|
console.log(chalk3.gray("\u6C38\u4E45\u6A21\u5F0F: ccem setup perms --<mode>"));
|
|
1520
2007
|
};
|
|
1521
|
-
var runWithTempPermissions = async (modeName, envConfig) => {
|
|
2008
|
+
var runWithTempPermissions = async (modeName, envConfig, envName) => {
|
|
1522
2009
|
const preset = PERMISSION_PRESETS[modeName];
|
|
1523
2010
|
if (!preset) {
|
|
1524
2011
|
console.error(chalk3.red(`\u672A\u77E5\u7684\u6743\u9650\u6A21\u5F0F: ${modeName}`));
|
|
1525
2012
|
console.log(chalk3.yellow("\u53EF\u7528\u6A21\u5F0F: " + getPermissionModeNames().join(", ")));
|
|
1526
2013
|
process.exit(1);
|
|
1527
2014
|
}
|
|
1528
|
-
await launchClaude({ envConfig, permMode: modeName });
|
|
2015
|
+
await launchClaude({ envConfig, envName, permMode: modeName });
|
|
1529
2016
|
};
|
|
1530
2017
|
|
|
1531
2018
|
// src/setup.ts
|
|
1532
|
-
import
|
|
2019
|
+
import fs7 from "fs";
|
|
1533
2020
|
import chalk4 from "chalk";
|
|
1534
2021
|
import { spawn as spawn2 } from "child_process";
|
|
1535
2022
|
var readJsonFile = (filePath) => {
|
|
1536
|
-
if (
|
|
2023
|
+
if (fs7.existsSync(filePath)) {
|
|
1537
2024
|
try {
|
|
1538
|
-
const content =
|
|
2025
|
+
const content = fs7.readFileSync(filePath, "utf-8");
|
|
1539
2026
|
return JSON.parse(content);
|
|
1540
2027
|
} catch {
|
|
1541
2028
|
console.warn(chalk4.yellow(`\u8B66\u544A: \u65E0\u6CD5\u89E3\u6790 ${filePath}`));
|
|
@@ -1545,7 +2032,7 @@ var readJsonFile = (filePath) => {
|
|
|
1545
2032
|
return {};
|
|
1546
2033
|
};
|
|
1547
2034
|
var writeJsonFile = (filePath, data) => {
|
|
1548
|
-
|
|
2035
|
+
fs7.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
|
|
1549
2036
|
};
|
|
1550
2037
|
var setupOnboarding = () => {
|
|
1551
2038
|
const configPath = getGlobalClaudeConfigPath();
|
|
@@ -1645,14 +2132,19 @@ var setupMcpTool = () => {
|
|
|
1645
2132
|
});
|
|
1646
2133
|
});
|
|
1647
2134
|
};
|
|
1648
|
-
var runSetupInit = async () => {
|
|
2135
|
+
var runSetupInit = async (options = {}) => {
|
|
1649
2136
|
console.log(chalk4.bold("\n\u{1F527} Claude Code \u521D\u59CB\u5316\u8BBE\u7F6E\n"));
|
|
1650
2137
|
console.log(chalk4.cyan("1. \u8BBE\u7F6E onboarding \u72B6\u6001"));
|
|
1651
2138
|
const step1 = setupOnboarding();
|
|
1652
2139
|
console.log(chalk4.cyan("\n2. \u914D\u7F6E\u9690\u79C1\u8BBE\u7F6E"));
|
|
1653
2140
|
const step2 = setupEnvSettings();
|
|
1654
|
-
|
|
1655
|
-
|
|
2141
|
+
let step3 = true;
|
|
2142
|
+
if (options.chrome) {
|
|
2143
|
+
console.log(chalk4.cyan("\n3. \u5B89\u88C5 MCP \u5DE5\u5177"));
|
|
2144
|
+
step3 = await setupMcpTool();
|
|
2145
|
+
} else {
|
|
2146
|
+
console.log(chalk4.gray("\n3. \u5B89\u88C5 MCP \u5DE5\u5177\uFF08\u5DF2\u8DF3\u8FC7\uFF0C\u4F7F\u7528 --chrome \u542F\u7528\uFF09"));
|
|
2147
|
+
}
|
|
1656
2148
|
console.log("");
|
|
1657
2149
|
if (step1 && step2 && step3) {
|
|
1658
2150
|
console.log(chalk4.green.bold("\u2705 \u521D\u59CB\u5316\u5B8C\u6210\uFF01"));
|
|
@@ -1667,8 +2159,8 @@ var runSetupInit = async () => {
|
|
|
1667
2159
|
|
|
1668
2160
|
// src/skills.ts
|
|
1669
2161
|
import { execSync } from "child_process";
|
|
1670
|
-
import * as
|
|
1671
|
-
import * as
|
|
2162
|
+
import * as fs8 from "fs";
|
|
2163
|
+
import * as path6 from "path";
|
|
1672
2164
|
import chalk5 from "chalk";
|
|
1673
2165
|
var SKILL_GROUPS = {
|
|
1674
2166
|
official: { label: "\u5B98\u65B9", icon: "\u{1F3E2}" },
|
|
@@ -1837,12 +2329,12 @@ function parseGitHubUrl(url) {
|
|
|
1837
2329
|
};
|
|
1838
2330
|
}
|
|
1839
2331
|
function getSkillsDir() {
|
|
1840
|
-
return
|
|
2332
|
+
return path6.join(process.cwd(), ".claude", "skills");
|
|
1841
2333
|
}
|
|
1842
2334
|
function ensureSkillsDir() {
|
|
1843
2335
|
const skillsDir = getSkillsDir();
|
|
1844
|
-
if (!
|
|
1845
|
-
|
|
2336
|
+
if (!fs8.existsSync(skillsDir)) {
|
|
2337
|
+
fs8.mkdirSync(skillsDir, { recursive: true });
|
|
1846
2338
|
} else {
|
|
1847
2339
|
cleanupTempDirs(skillsDir);
|
|
1848
2340
|
}
|
|
@@ -1850,11 +2342,11 @@ function ensureSkillsDir() {
|
|
|
1850
2342
|
}
|
|
1851
2343
|
function cleanupTempDirs(skillsDir) {
|
|
1852
2344
|
try {
|
|
1853
|
-
const entries =
|
|
2345
|
+
const entries = fs8.readdirSync(skillsDir, { withFileTypes: true });
|
|
1854
2346
|
for (const entry of entries) {
|
|
1855
2347
|
if (entry.isDirectory() && entry.name.startsWith(".tmp-")) {
|
|
1856
|
-
const tmpPath =
|
|
1857
|
-
|
|
2348
|
+
const tmpPath = path6.join(skillsDir, entry.name);
|
|
2349
|
+
fs8.rmSync(tmpPath, { recursive: true });
|
|
1858
2350
|
}
|
|
1859
2351
|
}
|
|
1860
2352
|
} catch {
|
|
@@ -1862,24 +2354,24 @@ function cleanupTempDirs(skillsDir) {
|
|
|
1862
2354
|
}
|
|
1863
2355
|
function downloadSkillWithGit(owner, repo, branch, repoPath, targetName) {
|
|
1864
2356
|
const skillsDir = ensureSkillsDir();
|
|
1865
|
-
const targetDir =
|
|
1866
|
-
if (
|
|
2357
|
+
const targetDir = path6.join(skillsDir, targetName);
|
|
2358
|
+
if (fs8.existsSync(targetDir)) {
|
|
1867
2359
|
console.log(chalk5.yellow(`Skill "${targetName}" already exists. Updating...`));
|
|
1868
|
-
|
|
2360
|
+
fs8.rmSync(targetDir, { recursive: true });
|
|
1869
2361
|
}
|
|
1870
2362
|
const repoUrl = `https://github.com/${owner}/${repo}.git`;
|
|
1871
|
-
const tempDir =
|
|
2363
|
+
const tempDir = path6.join(skillsDir, `.tmp-${Date.now()}`);
|
|
1872
2364
|
try {
|
|
1873
|
-
|
|
2365
|
+
fs8.mkdirSync(tempDir, { recursive: true });
|
|
1874
2366
|
execSync(`git init`, { cwd: tempDir, stdio: "pipe" });
|
|
1875
2367
|
execSync(`git remote add origin ${repoUrl}`, { cwd: tempDir, stdio: "pipe" });
|
|
1876
2368
|
execSync(`git config core.sparseCheckout true`, { cwd: tempDir, stdio: "pipe" });
|
|
1877
|
-
const sparseFile =
|
|
1878
|
-
|
|
2369
|
+
const sparseFile = path6.join(tempDir, ".git", "info", "sparse-checkout");
|
|
2370
|
+
fs8.writeFileSync(sparseFile, repoPath ? `${repoPath}/
|
|
1879
2371
|
` : "*\n");
|
|
1880
2372
|
execSync(`git pull --depth=1 origin ${branch}`, { cwd: tempDir, stdio: "pipe" });
|
|
1881
|
-
const sourceDir = repoPath ?
|
|
1882
|
-
if (!
|
|
2373
|
+
const sourceDir = repoPath ? path6.join(tempDir, repoPath) : tempDir;
|
|
2374
|
+
if (!fs8.existsSync(sourceDir)) {
|
|
1883
2375
|
throw new Error(`Path "${repoPath}" not found in repository`);
|
|
1884
2376
|
}
|
|
1885
2377
|
copyDir(sourceDir, targetDir);
|
|
@@ -1890,22 +2382,22 @@ function downloadSkillWithGit(owner, repo, branch, repoPath, targetName) {
|
|
|
1890
2382
|
console.error(chalk5.red(`Failed to download skill: ${errMsg}`));
|
|
1891
2383
|
return false;
|
|
1892
2384
|
} finally {
|
|
1893
|
-
if (
|
|
1894
|
-
|
|
2385
|
+
if (fs8.existsSync(tempDir)) {
|
|
2386
|
+
fs8.rmSync(tempDir, { recursive: true });
|
|
1895
2387
|
}
|
|
1896
2388
|
}
|
|
1897
2389
|
}
|
|
1898
2390
|
function copyDir(src, dest) {
|
|
1899
|
-
|
|
1900
|
-
const entries =
|
|
2391
|
+
fs8.mkdirSync(dest, { recursive: true });
|
|
2392
|
+
const entries = fs8.readdirSync(src, { withFileTypes: true });
|
|
1901
2393
|
for (const entry of entries) {
|
|
1902
2394
|
if (entry.name === ".git") continue;
|
|
1903
|
-
const srcPath =
|
|
1904
|
-
const destPath =
|
|
2395
|
+
const srcPath = path6.join(src, entry.name);
|
|
2396
|
+
const destPath = path6.join(dest, entry.name);
|
|
1905
2397
|
if (entry.isDirectory()) {
|
|
1906
2398
|
copyDir(srcPath, destPath);
|
|
1907
2399
|
} else {
|
|
1908
|
-
|
|
2400
|
+
fs8.copyFileSync(srcPath, destPath);
|
|
1909
2401
|
}
|
|
1910
2402
|
}
|
|
1911
2403
|
}
|
|
@@ -1951,7 +2443,7 @@ function addSkillFromGitHub(urlOrPreset) {
|
|
|
1951
2443
|
}
|
|
1952
2444
|
let skillName;
|
|
1953
2445
|
if (parsed.path) {
|
|
1954
|
-
skillName =
|
|
2446
|
+
skillName = path6.basename(parsed.path);
|
|
1955
2447
|
} else {
|
|
1956
2448
|
skillName = parsed.repo;
|
|
1957
2449
|
}
|
|
@@ -1965,23 +2457,23 @@ function addSkillFromGitHub(urlOrPreset) {
|
|
|
1965
2457
|
}
|
|
1966
2458
|
function listInstalledSkills() {
|
|
1967
2459
|
const skillsDir = getSkillsDir();
|
|
1968
|
-
if (!
|
|
2460
|
+
if (!fs8.existsSync(skillsDir)) {
|
|
1969
2461
|
return [];
|
|
1970
2462
|
}
|
|
1971
|
-
const entries =
|
|
2463
|
+
const entries = fs8.readdirSync(skillsDir, { withFileTypes: true });
|
|
1972
2464
|
return entries.filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => ({
|
|
1973
2465
|
name: entry.name,
|
|
1974
|
-
path:
|
|
2466
|
+
path: path6.join(skillsDir, entry.name)
|
|
1975
2467
|
}));
|
|
1976
2468
|
}
|
|
1977
2469
|
function removeSkill(name) {
|
|
1978
2470
|
const skillsDir = getSkillsDir();
|
|
1979
|
-
const targetDir =
|
|
1980
|
-
if (!
|
|
2471
|
+
const targetDir = path6.join(skillsDir, name);
|
|
2472
|
+
if (!fs8.existsSync(targetDir)) {
|
|
1981
2473
|
console.error(chalk5.red(`Skill "${name}" not found`));
|
|
1982
2474
|
return false;
|
|
1983
2475
|
}
|
|
1984
|
-
|
|
2476
|
+
fs8.rmSync(targetDir, { recursive: true });
|
|
1985
2477
|
console.log(chalk5.green(`Removed skill "${name}"`));
|
|
1986
2478
|
return true;
|
|
1987
2479
|
}
|
|
@@ -2143,7 +2635,9 @@ import crypto3 from "crypto";
|
|
|
2143
2635
|
import chalk6 from "chalk";
|
|
2144
2636
|
import Conf from "conf";
|
|
2145
2637
|
var config = new Conf({
|
|
2146
|
-
projectName: "claude-code-env-manager"
|
|
2638
|
+
projectName: "claude-code-env-manager",
|
|
2639
|
+
cwd: getCcemConfigDir()
|
|
2640
|
+
// 使用统一的配置目录
|
|
2147
2641
|
});
|
|
2148
2642
|
var decryptWithSecret = (encryptedBase64, secret) => {
|
|
2149
2643
|
const key = crypto3.scryptSync(secret, "ccem-salt", 32);
|
|
@@ -2171,7 +2665,11 @@ var loadFromRemote = async (url, secret) => {
|
|
|
2171
2665
|
console.log(chalk6.gray("Fetching from remote..."));
|
|
2172
2666
|
let response;
|
|
2173
2667
|
try {
|
|
2174
|
-
response = await fetch(url
|
|
2668
|
+
response = await fetch(url, {
|
|
2669
|
+
headers: {
|
|
2670
|
+
"X-CCEM-Key": secret
|
|
2671
|
+
}
|
|
2672
|
+
});
|
|
2175
2673
|
} catch (err) {
|
|
2176
2674
|
console.error(chalk6.red("Error: Failed to connect to server"));
|
|
2177
2675
|
console.error(chalk6.gray(err.message));
|
|
@@ -2218,9 +2716,10 @@ var loadFromRemote = async (url, secret) => {
|
|
|
2218
2716
|
for (const [name, envConfig] of Object.entries(decrypted.environments)) {
|
|
2219
2717
|
const uniqueName = getUniqueName(name, existingNames);
|
|
2220
2718
|
const renamed = uniqueName !== name;
|
|
2221
|
-
const
|
|
2222
|
-
|
|
2223
|
-
|
|
2719
|
+
const normalizedConfig = normalizeEnvConfig(envConfig);
|
|
2720
|
+
const configToSave = { ...normalizedConfig };
|
|
2721
|
+
if (configToSave.ANTHROPIC_AUTH_TOKEN) {
|
|
2722
|
+
configToSave.ANTHROPIC_AUTH_TOKEN = encrypt(configToSave.ANTHROPIC_AUTH_TOKEN);
|
|
2224
2723
|
}
|
|
2225
2724
|
registries[uniqueName] = configToSave;
|
|
2226
2725
|
existingNames.add(uniqueName);
|
|
@@ -2241,6 +2740,7 @@ Loaded ${results.length} environment(s) from remote:`));
|
|
|
2241
2740
|
}
|
|
2242
2741
|
}
|
|
2243
2742
|
console.log(chalk6.gray("\nRun 'ccem ls' to see all environments."));
|
|
2743
|
+
return results;
|
|
2244
2744
|
};
|
|
2245
2745
|
|
|
2246
2746
|
// src/cron-skill.ts
|
|
@@ -2386,11 +2886,52 @@ Replace \\\`TARGET_ID\\\` or \\\`TARGET_NAME\\\` with the user's selection.
|
|
|
2386
2886
|
`;
|
|
2387
2887
|
|
|
2388
2888
|
// src/index.ts
|
|
2389
|
-
var
|
|
2390
|
-
var __dirname2 =
|
|
2391
|
-
var pkgPath =
|
|
2392
|
-
var pkg = JSON.parse(
|
|
2889
|
+
var __filename2 = fileURLToPath2(import.meta.url);
|
|
2890
|
+
var __dirname2 = path7.dirname(__filename2);
|
|
2891
|
+
var pkgPath = path7.resolve(__dirname2, "..", "package.json");
|
|
2892
|
+
var pkg = JSON.parse(fs9.readFileSync(pkgPath, "utf-8"));
|
|
2393
2893
|
var program = new Command();
|
|
2894
|
+
var DEFAULT_OFFICIAL_ENV = {
|
|
2895
|
+
ANTHROPIC_BASE_URL: "https://api.anthropic.com",
|
|
2896
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: "claude-opus-4-1-20250805",
|
|
2897
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: "claude-opus-4-1-20250805",
|
|
2898
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: "claude-3-5-haiku-20241022",
|
|
2899
|
+
ANTHROPIC_MODEL: "opus"
|
|
2900
|
+
};
|
|
2901
|
+
var MANAGED_CLAUDE_ENV_KEYS2 = [
|
|
2902
|
+
"ANTHROPIC_BASE_URL",
|
|
2903
|
+
"ANTHROPIC_AUTH_TOKEN",
|
|
2904
|
+
"ANTHROPIC_DEFAULT_OPUS_MODEL",
|
|
2905
|
+
"ANTHROPIC_DEFAULT_SONNET_MODEL",
|
|
2906
|
+
"ANTHROPIC_DEFAULT_HAIKU_MODEL",
|
|
2907
|
+
"ANTHROPIC_MODEL",
|
|
2908
|
+
"CLAUDE_CODE_SUBAGENT_MODEL",
|
|
2909
|
+
"ANTHROPIC_API_KEY",
|
|
2910
|
+
"ANTHROPIC_SMALL_FAST_MODEL"
|
|
2911
|
+
];
|
|
2912
|
+
var shellQuote = (value) => `'${value.replace(/'/g, `'\\''`)}'`;
|
|
2913
|
+
var clearManagedClaudeEnv = (env) => {
|
|
2914
|
+
for (const key of MANAGED_CLAUDE_ENV_KEYS2) {
|
|
2915
|
+
delete env[key];
|
|
2916
|
+
}
|
|
2917
|
+
};
|
|
2918
|
+
var buildResolvedEnvVars = (env) => {
|
|
2919
|
+
const resolved = {};
|
|
2920
|
+
if (env.ANTHROPIC_BASE_URL) resolved.ANTHROPIC_BASE_URL = env.ANTHROPIC_BASE_URL;
|
|
2921
|
+
if (env.ANTHROPIC_AUTH_TOKEN) resolved.ANTHROPIC_AUTH_TOKEN = decrypt(env.ANTHROPIC_AUTH_TOKEN);
|
|
2922
|
+
if (env.ANTHROPIC_DEFAULT_OPUS_MODEL) resolved.ANTHROPIC_DEFAULT_OPUS_MODEL = env.ANTHROPIC_DEFAULT_OPUS_MODEL;
|
|
2923
|
+
if (env.ANTHROPIC_DEFAULT_SONNET_MODEL) resolved.ANTHROPIC_DEFAULT_SONNET_MODEL = env.ANTHROPIC_DEFAULT_SONNET_MODEL;
|
|
2924
|
+
if (env.ANTHROPIC_DEFAULT_HAIKU_MODEL) resolved.ANTHROPIC_DEFAULT_HAIKU_MODEL = env.ANTHROPIC_DEFAULT_HAIKU_MODEL;
|
|
2925
|
+
if (env.ANTHROPIC_MODEL) resolved.ANTHROPIC_MODEL = env.ANTHROPIC_MODEL;
|
|
2926
|
+
if (env.CLAUDE_CODE_SUBAGENT_MODEL) resolved.CLAUDE_CODE_SUBAGENT_MODEL = env.CLAUDE_CODE_SUBAGENT_MODEL;
|
|
2927
|
+
return resolved;
|
|
2928
|
+
};
|
|
2929
|
+
var buildShellEnvCommands = (env) => {
|
|
2930
|
+
const resolved = buildResolvedEnvVars(env);
|
|
2931
|
+
return MANAGED_CLAUDE_ENV_KEYS2.map(
|
|
2932
|
+
(key) => resolved[key] ? `export ${key}=${shellQuote(resolved[key])}` : `unset ${key}`
|
|
2933
|
+
);
|
|
2934
|
+
};
|
|
2394
2935
|
ensureCcemDir();
|
|
2395
2936
|
var config2 = new Conf2({
|
|
2396
2937
|
projectName: "claude-code-env-manager",
|
|
@@ -2398,16 +2939,129 @@ var config2 = new Conf2({
|
|
|
2398
2939
|
// 使用新路径
|
|
2399
2940
|
defaults: {
|
|
2400
2941
|
registries: {
|
|
2401
|
-
|
|
2402
|
-
ANTHROPIC_BASE_URL: "https://api.anthropic.com",
|
|
2403
|
-
ANTHROPIC_MODEL: "claude-sonnet-4-5-20250929",
|
|
2404
|
-
ANTHROPIC_SMALL_FAST_MODEL: "claude-haiku-4-5-20251001"
|
|
2405
|
-
}
|
|
2942
|
+
official: DEFAULT_OFFICIAL_ENV
|
|
2406
2943
|
},
|
|
2407
2944
|
current: "official",
|
|
2408
2945
|
defaultMode: null
|
|
2409
2946
|
}
|
|
2410
2947
|
});
|
|
2948
|
+
var recoverRegistriesFromLegacy = (registries) => {
|
|
2949
|
+
const currentAuthCount = Object.values(registries).filter(
|
|
2950
|
+
(env) => Boolean(env.ANTHROPIC_AUTH_TOKEN)
|
|
2951
|
+
).length;
|
|
2952
|
+
if (currentAuthCount > 0) {
|
|
2953
|
+
return registries;
|
|
2954
|
+
}
|
|
2955
|
+
const legacyConfigPath = getLegacyConfigPath();
|
|
2956
|
+
if (!fs9.existsSync(legacyConfigPath)) {
|
|
2957
|
+
return registries;
|
|
2958
|
+
}
|
|
2959
|
+
try {
|
|
2960
|
+
const legacyRaw = JSON.parse(fs9.readFileSync(legacyConfigPath, "utf-8"));
|
|
2961
|
+
const legacyRegistries = legacyRaw.registries ?? {};
|
|
2962
|
+
let changed = false;
|
|
2963
|
+
const recovered = { ...registries };
|
|
2964
|
+
for (const [name, envConfig] of Object.entries(registries)) {
|
|
2965
|
+
const legacyEnvConfig = legacyRegistries[name];
|
|
2966
|
+
if (!legacyEnvConfig) {
|
|
2967
|
+
continue;
|
|
2968
|
+
}
|
|
2969
|
+
const recoveredEnv = recoverEnvConfigFromLegacy(envConfig, legacyEnvConfig);
|
|
2970
|
+
if (JSON.stringify(recoveredEnv) !== JSON.stringify(envConfig)) {
|
|
2971
|
+
recovered[name] = recoveredEnv;
|
|
2972
|
+
changed = true;
|
|
2973
|
+
}
|
|
2974
|
+
}
|
|
2975
|
+
return changed ? recovered : registries;
|
|
2976
|
+
} catch {
|
|
2977
|
+
return registries;
|
|
2978
|
+
}
|
|
2979
|
+
};
|
|
2980
|
+
var getRegistries = () => {
|
|
2981
|
+
const rawRegistries = config2.get("registries") ?? {};
|
|
2982
|
+
const normalizedEntries = Object.entries(rawRegistries).map(([name, envConfig]) => [
|
|
2983
|
+
name,
|
|
2984
|
+
normalizeEnvConfig(envConfig ?? {})
|
|
2985
|
+
]);
|
|
2986
|
+
const normalizedRegistries = Object.fromEntries(normalizedEntries);
|
|
2987
|
+
const repairedRegistries = recoverRegistriesFromLegacy(normalizedRegistries);
|
|
2988
|
+
if (!repairedRegistries.official) {
|
|
2989
|
+
repairedRegistries.official = { ...DEFAULT_OFFICIAL_ENV };
|
|
2990
|
+
}
|
|
2991
|
+
const changed = Object.keys(rawRegistries).length !== Object.keys(repairedRegistries).length || JSON.stringify(rawRegistries) !== JSON.stringify(repairedRegistries);
|
|
2992
|
+
if (changed) {
|
|
2993
|
+
config2.set("registries", repairedRegistries);
|
|
2994
|
+
}
|
|
2995
|
+
return repairedRegistries;
|
|
2996
|
+
};
|
|
2997
|
+
var setRegistries = (registries) => {
|
|
2998
|
+
config2.set("registries", registries);
|
|
2999
|
+
};
|
|
3000
|
+
var getDecryptedAuthToken = (envConfig) => {
|
|
3001
|
+
return envConfig.ANTHROPIC_AUTH_TOKEN ? decrypt(envConfig.ANTHROPIC_AUTH_TOKEN) : void 0;
|
|
3002
|
+
};
|
|
3003
|
+
var applyPromptAnswers = (current, answers, keepCurrentSecret) => {
|
|
3004
|
+
const next = {
|
|
3005
|
+
...current,
|
|
3006
|
+
ANTHROPIC_BASE_URL: answers.ANTHROPIC_BASE_URL?.trim() || current.ANTHROPIC_BASE_URL,
|
|
3007
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: answers.ANTHROPIC_DEFAULT_OPUS_MODEL?.trim() || current.ANTHROPIC_DEFAULT_OPUS_MODEL,
|
|
3008
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: answers.ANTHROPIC_DEFAULT_HAIKU_MODEL?.trim() || current.ANTHROPIC_DEFAULT_HAIKU_MODEL,
|
|
3009
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: answers.ANTHROPIC_DEFAULT_SONNET_MODEL?.trim() || answers.ANTHROPIC_DEFAULT_OPUS_MODEL?.trim() || current.ANTHROPIC_DEFAULT_SONNET_MODEL || current.ANTHROPIC_DEFAULT_OPUS_MODEL,
|
|
3010
|
+
ANTHROPIC_MODEL: answers.ANTHROPIC_MODEL?.trim() || current.ANTHROPIC_MODEL || "opus",
|
|
3011
|
+
CLAUDE_CODE_SUBAGENT_MODEL: answers.CLAUDE_CODE_SUBAGENT_MODEL?.trim() || current.CLAUDE_CODE_SUBAGENT_MODEL
|
|
3012
|
+
};
|
|
3013
|
+
if (!keepCurrentSecret) {
|
|
3014
|
+
next.ANTHROPIC_AUTH_TOKEN = answers.ANTHROPIC_AUTH_TOKEN ? encrypt(answers.ANTHROPIC_AUTH_TOKEN) : void 0;
|
|
3015
|
+
} else if (answers.ANTHROPIC_AUTH_TOKEN) {
|
|
3016
|
+
next.ANTHROPIC_AUTH_TOKEN = encrypt(answers.ANTHROPIC_AUTH_TOKEN);
|
|
3017
|
+
}
|
|
3018
|
+
return normalizeEnvConfig(next);
|
|
3019
|
+
};
|
|
3020
|
+
var promptForEnvironmentConfig = async (current = {}, keepCurrentSecret = false) => {
|
|
3021
|
+
return inquirer.prompt([
|
|
3022
|
+
{
|
|
3023
|
+
type: "input",
|
|
3024
|
+
name: "ANTHROPIC_BASE_URL",
|
|
3025
|
+
message: "ANTHROPIC_BASE_URL:",
|
|
3026
|
+
default: current.ANTHROPIC_BASE_URL || DEFAULT_OFFICIAL_ENV.ANTHROPIC_BASE_URL
|
|
3027
|
+
},
|
|
3028
|
+
{
|
|
3029
|
+
type: "password",
|
|
3030
|
+
name: "ANTHROPIC_AUTH_TOKEN",
|
|
3031
|
+
message: keepCurrentSecret ? "ANTHROPIC_AUTH_TOKEN (leave empty to keep current):" : "ANTHROPIC_AUTH_TOKEN:"
|
|
3032
|
+
},
|
|
3033
|
+
{
|
|
3034
|
+
type: "input",
|
|
3035
|
+
name: "ANTHROPIC_DEFAULT_OPUS_MODEL",
|
|
3036
|
+
message: "ANTHROPIC_DEFAULT_OPUS_MODEL:",
|
|
3037
|
+
default: current.ANTHROPIC_DEFAULT_OPUS_MODEL || DEFAULT_OFFICIAL_ENV.ANTHROPIC_DEFAULT_OPUS_MODEL
|
|
3038
|
+
},
|
|
3039
|
+
{
|
|
3040
|
+
type: "input",
|
|
3041
|
+
name: "ANTHROPIC_DEFAULT_HAIKU_MODEL",
|
|
3042
|
+
message: "ANTHROPIC_DEFAULT_HAIKU_MODEL:",
|
|
3043
|
+
default: current.ANTHROPIC_DEFAULT_HAIKU_MODEL || DEFAULT_OFFICIAL_ENV.ANTHROPIC_DEFAULT_HAIKU_MODEL
|
|
3044
|
+
},
|
|
3045
|
+
{
|
|
3046
|
+
type: "input",
|
|
3047
|
+
name: "ANTHROPIC_DEFAULT_SONNET_MODEL",
|
|
3048
|
+
message: "ANTHROPIC_DEFAULT_SONNET_MODEL (blank = same as opus):",
|
|
3049
|
+
default: current.ANTHROPIC_DEFAULT_SONNET_MODEL || current.ANTHROPIC_DEFAULT_OPUS_MODEL || ""
|
|
3050
|
+
},
|
|
3051
|
+
{
|
|
3052
|
+
type: "input",
|
|
3053
|
+
name: "ANTHROPIC_MODEL",
|
|
3054
|
+
message: "ANTHROPIC_MODEL (e.g. opus, opusplan, sonnet):",
|
|
3055
|
+
default: current.ANTHROPIC_MODEL || "opus"
|
|
3056
|
+
},
|
|
3057
|
+
{
|
|
3058
|
+
type: "input",
|
|
3059
|
+
name: "CLAUDE_CODE_SUBAGENT_MODEL",
|
|
3060
|
+
message: "CLAUDE_CODE_SUBAGENT_MODEL (optional):",
|
|
3061
|
+
default: current.CLAUDE_CODE_SUBAGENT_MODEL || ""
|
|
3062
|
+
}
|
|
3063
|
+
]);
|
|
3064
|
+
};
|
|
2411
3065
|
var PERMISSION_MODES = ["yolo", "dev", "readonly", "safe", "ci", "audit"];
|
|
2412
3066
|
var usageStats = null;
|
|
2413
3067
|
var usageLoading = true;
|
|
@@ -2446,24 +3100,27 @@ program.name("ccem").description("Claude Code Environment Manager - \u7BA1\u7406
|
|
|
2446
3100
|
PERMISSION_MODES.forEach((mode) => {
|
|
2447
3101
|
const preset = PERMISSION_PRESETS[mode];
|
|
2448
3102
|
program.command(mode).description(`\u4E34\u65F6\u5E94\u7528 ${preset.name}\uFF0C\u9000\u51FA\u540E\u8FD8\u539F`).action(async () => {
|
|
2449
|
-
const registries =
|
|
3103
|
+
const registries = getRegistries();
|
|
2450
3104
|
const current = config2.get("current");
|
|
2451
3105
|
const envConfig = registries[current];
|
|
2452
|
-
await runWithTempPermissions(mode, envConfig);
|
|
3106
|
+
await runWithTempPermissions(mode, envConfig, current);
|
|
2453
3107
|
});
|
|
2454
3108
|
});
|
|
2455
3109
|
var showCurrentEnv = (usageStats2, usageLoading2) => {
|
|
2456
3110
|
if (!process.stdout.isTTY) return;
|
|
2457
3111
|
const current = config2.get("current");
|
|
2458
|
-
const registries =
|
|
3112
|
+
const registries = getRegistries();
|
|
2459
3113
|
const env = registries[current];
|
|
2460
3114
|
const defaultMode = config2.get("defaultMode");
|
|
2461
3115
|
if (!env) return;
|
|
2462
3116
|
console.log(renderLogoWithEnvPanel(current, {
|
|
2463
3117
|
ANTHROPIC_BASE_URL: env.ANTHROPIC_BASE_URL,
|
|
2464
|
-
|
|
3118
|
+
ANTHROPIC_AUTH_TOKEN: getDecryptedAuthToken(env),
|
|
3119
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: env.ANTHROPIC_DEFAULT_OPUS_MODEL,
|
|
3120
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: env.ANTHROPIC_DEFAULT_SONNET_MODEL,
|
|
3121
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: env.ANTHROPIC_DEFAULT_HAIKU_MODEL,
|
|
2465
3122
|
ANTHROPIC_MODEL: env.ANTHROPIC_MODEL,
|
|
2466
|
-
|
|
3123
|
+
CLAUDE_CODE_SUBAGENT_MODEL: env.CLAUDE_CODE_SUBAGENT_MODEL
|
|
2467
3124
|
}, defaultMode));
|
|
2468
3125
|
console.log("");
|
|
2469
3126
|
console.log(renderUsageLine(usageStats2, usageLoading2));
|
|
@@ -2471,7 +3128,7 @@ var showCurrentEnv = (usageStats2, usageLoading2) => {
|
|
|
2471
3128
|
console.log("");
|
|
2472
3129
|
};
|
|
2473
3130
|
var switchEnvironment = async (name) => {
|
|
2474
|
-
const registries =
|
|
3131
|
+
const registries = getRegistries();
|
|
2475
3132
|
if (!registries[name]) {
|
|
2476
3133
|
console.error(chalk7.red(`Environment '${name}' not found.`));
|
|
2477
3134
|
return;
|
|
@@ -2484,11 +3141,7 @@ var switchEnvironment = async (name) => {
|
|
|
2484
3141
|
}
|
|
2485
3142
|
showCurrentEnv(null, false);
|
|
2486
3143
|
const env = registries[name];
|
|
2487
|
-
const exportCmds =
|
|
2488
|
-
if (env.ANTHROPIC_BASE_URL) exportCmds.push(`export ANTHROPIC_BASE_URL="${env.ANTHROPIC_BASE_URL}"`);
|
|
2489
|
-
if (env.ANTHROPIC_API_KEY) exportCmds.push(`export ANTHROPIC_API_KEY="${decrypt(env.ANTHROPIC_API_KEY)}"`);
|
|
2490
|
-
if (env.ANTHROPIC_MODEL) exportCmds.push(`export ANTHROPIC_MODEL="${env.ANTHROPIC_MODEL}"`);
|
|
2491
|
-
if (env.ANTHROPIC_SMALL_FAST_MODEL) exportCmds.push(`export ANTHROPIC_SMALL_FAST_MODEL="${env.ANTHROPIC_SMALL_FAST_MODEL}"`);
|
|
3144
|
+
const exportCmds = buildShellEnvCommands(env);
|
|
2492
3145
|
if (process.stdout.isTTY) {
|
|
2493
3146
|
console.log(chalk7.yellow("\nTo apply to current shell immediately, run:"));
|
|
2494
3147
|
console.log(chalk7.cyan("eval $(ccem env)"));
|
|
@@ -2498,11 +3151,92 @@ var switchEnvironment = async (name) => {
|
|
|
2498
3151
|
exportCmds.forEach((cmd) => console.log(cmd));
|
|
2499
3152
|
}
|
|
2500
3153
|
};
|
|
3154
|
+
var getSessionsFilePath = () => path7.join(getCcemConfigDir(), "sessions.json");
|
|
3155
|
+
var getRuntimeStateFilePath = () => path7.join(getCcemConfigDir(), "runtime-state.json");
|
|
3156
|
+
var parseJsonFile = (filePath) => {
|
|
3157
|
+
if (!fs9.existsSync(filePath)) {
|
|
3158
|
+
return null;
|
|
3159
|
+
}
|
|
3160
|
+
try {
|
|
3161
|
+
return JSON.parse(fs9.readFileSync(filePath, "utf-8"));
|
|
3162
|
+
} catch {
|
|
3163
|
+
return null;
|
|
3164
|
+
}
|
|
3165
|
+
};
|
|
3166
|
+
var readInteractiveAttachSessions = () => {
|
|
3167
|
+
const sessionsById = /* @__PURE__ */ new Map();
|
|
3168
|
+
const runtimeState = parseJsonFile(getRuntimeStateFilePath());
|
|
3169
|
+
const persistedSessions = parseJsonFile(getSessionsFilePath()) ?? [];
|
|
3170
|
+
for (const entry of runtimeState?.sessions ?? []) {
|
|
3171
|
+
if (entry.runtime_kind && entry.runtime_kind !== "interactive") {
|
|
3172
|
+
continue;
|
|
3173
|
+
}
|
|
3174
|
+
const fallback = persistedSessions.find((session) => session.id === entry.runtime_id);
|
|
3175
|
+
const tmuxTarget = entry.tmux_session && entry.tmux_window ? `${entry.tmux_session}:${entry.tmux_window}` : fallback?.window_id ?? null;
|
|
3176
|
+
if (!tmuxTarget) {
|
|
3177
|
+
continue;
|
|
3178
|
+
}
|
|
3179
|
+
sessionsById.set(entry.runtime_id, {
|
|
3180
|
+
id: entry.runtime_id,
|
|
3181
|
+
projectDir: entry.project_dir,
|
|
3182
|
+
envName: entry.env_name,
|
|
3183
|
+
permMode: entry.perm_mode,
|
|
3184
|
+
status: fallback?.status ?? "running",
|
|
3185
|
+
tmuxTarget,
|
|
3186
|
+
sortTime: entry.saved_at
|
|
3187
|
+
});
|
|
3188
|
+
}
|
|
3189
|
+
for (const session of persistedSessions) {
|
|
3190
|
+
if (session.status !== "running" || session.terminal_type !== "embedded" || !session.window_id) {
|
|
3191
|
+
continue;
|
|
3192
|
+
}
|
|
3193
|
+
if (sessionsById.has(session.id)) {
|
|
3194
|
+
continue;
|
|
3195
|
+
}
|
|
3196
|
+
sessionsById.set(session.id, {
|
|
3197
|
+
id: session.id,
|
|
3198
|
+
projectDir: session.working_dir,
|
|
3199
|
+
envName: session.env_name,
|
|
3200
|
+
permMode: session.perm_mode,
|
|
3201
|
+
status: session.status,
|
|
3202
|
+
tmuxTarget: session.window_id,
|
|
3203
|
+
sortTime: session.start_time
|
|
3204
|
+
});
|
|
3205
|
+
}
|
|
3206
|
+
return [...sessionsById.values()].sort(
|
|
3207
|
+
(left, right) => right.sortTime.localeCompare(left.sortTime)
|
|
3208
|
+
);
|
|
3209
|
+
};
|
|
3210
|
+
var findAttachSession = (id) => {
|
|
3211
|
+
const sessions = readInteractiveAttachSessions();
|
|
3212
|
+
if (!id) {
|
|
3213
|
+
return sessions[0];
|
|
3214
|
+
}
|
|
3215
|
+
const exact = sessions.find((session) => session.id === id);
|
|
3216
|
+
if (exact) {
|
|
3217
|
+
return exact;
|
|
3218
|
+
}
|
|
3219
|
+
return sessions.find((session) => session.id.startsWith(id));
|
|
3220
|
+
};
|
|
3221
|
+
var attachTmuxTarget = (target) => {
|
|
3222
|
+
const args = process.env.TMUX ? ["switch-client", "-t", target] : ["attach-session", "-t", target];
|
|
3223
|
+
return new Promise((resolve2, reject) => {
|
|
3224
|
+
const child = spawn3("tmux", args, { stdio: "inherit" });
|
|
3225
|
+
child.on("error", reject);
|
|
3226
|
+
child.on("exit", (code) => {
|
|
3227
|
+
if (code === 0 || code === null) {
|
|
3228
|
+
resolve2();
|
|
3229
|
+
} else {
|
|
3230
|
+
reject(new Error(`tmux exited with code ${code}`));
|
|
3231
|
+
}
|
|
3232
|
+
});
|
|
3233
|
+
});
|
|
3234
|
+
};
|
|
2501
3235
|
program.command("ls").description("List all configured environments").action(() => {
|
|
2502
|
-
const registries =
|
|
3236
|
+
const registries = getRegistries();
|
|
2503
3237
|
const current = config2.get("current");
|
|
2504
3238
|
const table = new Table3({
|
|
2505
|
-
head: ["Name", "Base URL", "
|
|
3239
|
+
head: ["Name", "Base URL", "Opus"],
|
|
2506
3240
|
style: { head: ["cyan"] }
|
|
2507
3241
|
});
|
|
2508
3242
|
Object.keys(registries).forEach((name) => {
|
|
@@ -2511,7 +3245,7 @@ program.command("ls").description("List all configured environments").action(()
|
|
|
2511
3245
|
table.push([
|
|
2512
3246
|
prefix + name,
|
|
2513
3247
|
reg.ANTHROPIC_BASE_URL || "-",
|
|
2514
|
-
reg.
|
|
3248
|
+
reg.ANTHROPIC_DEFAULT_OPUS_MODEL || "-"
|
|
2515
3249
|
]);
|
|
2516
3250
|
});
|
|
2517
3251
|
console.log(table.toString());
|
|
@@ -2519,8 +3253,42 @@ program.command("ls").description("List all configured environments").action(()
|
|
|
2519
3253
|
program.command("use <name>").description("Switch to a specific environment").action(async (name) => {
|
|
2520
3254
|
await switchEnvironment(name);
|
|
2521
3255
|
});
|
|
3256
|
+
program.command("sessions").description("List tmux-backed interactive sessions").action(() => {
|
|
3257
|
+
const sessions = readInteractiveAttachSessions();
|
|
3258
|
+
if (sessions.length === 0) {
|
|
3259
|
+
console.log(chalk7.yellow("No tmux-backed interactive sessions found."));
|
|
3260
|
+
return;
|
|
3261
|
+
}
|
|
3262
|
+
const table = new Table3({
|
|
3263
|
+
head: ["ID", "Project", "Env", "Status", "Tmux"],
|
|
3264
|
+
style: { head: ["cyan"] }
|
|
3265
|
+
});
|
|
3266
|
+
sessions.forEach((session) => {
|
|
3267
|
+
table.push([
|
|
3268
|
+
session.id,
|
|
3269
|
+
session.projectDir,
|
|
3270
|
+
session.envName,
|
|
3271
|
+
session.status,
|
|
3272
|
+
session.tmuxTarget
|
|
3273
|
+
]);
|
|
3274
|
+
});
|
|
3275
|
+
console.log(table.toString());
|
|
3276
|
+
});
|
|
3277
|
+
program.command("attach [id]").description("Attach to a tmux-backed interactive session").action(async (id) => {
|
|
3278
|
+
const session = findAttachSession(id);
|
|
3279
|
+
if (!session) {
|
|
3280
|
+
console.error(chalk7.red(id ? `Interactive session '${id}' not found.` : "No interactive session available to attach."));
|
|
3281
|
+
process.exit(1);
|
|
3282
|
+
}
|
|
3283
|
+
try {
|
|
3284
|
+
await attachTmuxTarget(session.tmuxTarget);
|
|
3285
|
+
} catch (error) {
|
|
3286
|
+
console.error(chalk7.red(`Failed to attach ${session.tmuxTarget}: ${String(error)}`));
|
|
3287
|
+
process.exit(1);
|
|
3288
|
+
}
|
|
3289
|
+
});
|
|
2522
3290
|
program.command("add <name>").description("Add a new environment configuration").action(async (name) => {
|
|
2523
|
-
const registries =
|
|
3291
|
+
const registries = getRegistries();
|
|
2524
3292
|
if (registries[name]) {
|
|
2525
3293
|
console.log(chalk7.red(`Environment '${name}' already exists.`));
|
|
2526
3294
|
return;
|
|
@@ -2545,40 +3313,13 @@ program.command("add <name>").description("Add a new environment configuration")
|
|
|
2545
3313
|
]);
|
|
2546
3314
|
presetConfig = ENV_PRESETS[presetName];
|
|
2547
3315
|
}
|
|
2548
|
-
const answers = await
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
name: "ANTHROPIC_BASE_URL",
|
|
2552
|
-
message: "Enter ANTHROPIC_BASE_URL:",
|
|
2553
|
-
default: presetConfig.ANTHROPIC_BASE_URL || "https://api.anthropic.com"
|
|
2554
|
-
},
|
|
2555
|
-
{
|
|
2556
|
-
type: "password",
|
|
2557
|
-
name: "ANTHROPIC_API_KEY",
|
|
2558
|
-
message: "Enter ANTHROPIC_API_KEY:"
|
|
2559
|
-
},
|
|
2560
|
-
{
|
|
2561
|
-
type: "input",
|
|
2562
|
-
name: "ANTHROPIC_MODEL",
|
|
2563
|
-
message: "Enter ANTHROPIC_MODEL:",
|
|
2564
|
-
default: presetConfig.ANTHROPIC_MODEL || "claude-sonnet-4-5-20250929"
|
|
2565
|
-
},
|
|
2566
|
-
{
|
|
2567
|
-
type: "input",
|
|
2568
|
-
name: "ANTHROPIC_SMALL_FAST_MODEL",
|
|
2569
|
-
message: "Enter ANTHROPIC_SMALL_FAST_MODEL:",
|
|
2570
|
-
default: presetConfig.ANTHROPIC_SMALL_FAST_MODEL || "claude-haiku-4-5-20251001"
|
|
2571
|
-
}
|
|
2572
|
-
]);
|
|
2573
|
-
if (answers.ANTHROPIC_API_KEY) {
|
|
2574
|
-
answers.ANTHROPIC_API_KEY = encrypt(answers.ANTHROPIC_API_KEY);
|
|
2575
|
-
}
|
|
2576
|
-
registries[name] = answers;
|
|
2577
|
-
config2.set("registries", registries);
|
|
3316
|
+
const answers = await promptForEnvironmentConfig(presetConfig);
|
|
3317
|
+
registries[name] = applyPromptAnswers(normalizeEnvConfig(presetConfig), answers, false);
|
|
3318
|
+
setRegistries(registries);
|
|
2578
3319
|
console.log(chalk7.green(`Environment '${name}' added successfully.`));
|
|
2579
3320
|
});
|
|
2580
3321
|
program.command("del <name>").description("Delete an environment configuration").action((name) => {
|
|
2581
|
-
const registries =
|
|
3322
|
+
const registries = getRegistries();
|
|
2582
3323
|
if (!registries[name]) {
|
|
2583
3324
|
console.log(chalk7.red(`Environment '${name}' not found.`));
|
|
2584
3325
|
return;
|
|
@@ -2588,7 +3329,7 @@ program.command("del <name>").description("Delete an environment configuration")
|
|
|
2588
3329
|
return;
|
|
2589
3330
|
}
|
|
2590
3331
|
delete registries[name];
|
|
2591
|
-
|
|
3332
|
+
setRegistries(registries);
|
|
2592
3333
|
const current = config2.get("current");
|
|
2593
3334
|
if (current === name) {
|
|
2594
3335
|
config2.set("current", "official");
|
|
@@ -2597,7 +3338,7 @@ program.command("del <name>").description("Delete an environment configuration")
|
|
|
2597
3338
|
console.log(chalk7.green(`Environment '${name}' deleted.`));
|
|
2598
3339
|
});
|
|
2599
3340
|
program.command("rename <old> <new>").description("Rename an environment configuration").action((oldName, newName) => {
|
|
2600
|
-
const registries =
|
|
3341
|
+
const registries = getRegistries();
|
|
2601
3342
|
if (!registries[oldName]) {
|
|
2602
3343
|
console.log(chalk7.red(`Environment '${oldName}' not found.`));
|
|
2603
3344
|
return;
|
|
@@ -2612,7 +3353,7 @@ program.command("rename <old> <new>").description("Rename an environment configu
|
|
|
2612
3353
|
}
|
|
2613
3354
|
registries[newName] = registries[oldName];
|
|
2614
3355
|
delete registries[oldName];
|
|
2615
|
-
|
|
3356
|
+
setRegistries(registries);
|
|
2616
3357
|
const current = config2.get("current");
|
|
2617
3358
|
if (current === oldName) {
|
|
2618
3359
|
config2.set("current", newName);
|
|
@@ -2620,7 +3361,7 @@ program.command("rename <old> <new>").description("Rename an environment configu
|
|
|
2620
3361
|
console.log(chalk7.green(`Environment '${oldName}' renamed to '${newName}'.`));
|
|
2621
3362
|
});
|
|
2622
3363
|
program.command("cp <source> <target>").description("Copy an environment configuration").action(async (source, target) => {
|
|
2623
|
-
const registries =
|
|
3364
|
+
const registries = getRegistries();
|
|
2624
3365
|
if (!registries[source]) {
|
|
2625
3366
|
console.log(chalk7.red(`Environment '${source}' not found.`));
|
|
2626
3367
|
return;
|
|
@@ -2630,7 +3371,7 @@ program.command("cp <source> <target>").description("Copy an environment configu
|
|
|
2630
3371
|
return;
|
|
2631
3372
|
}
|
|
2632
3373
|
registries[target] = { ...registries[source] };
|
|
2633
|
-
|
|
3374
|
+
setRegistries(registries);
|
|
2634
3375
|
console.log(chalk7.green(`Environment '${source}' copied to '${target}'.`));
|
|
2635
3376
|
const { modify } = await inquirer.prompt([
|
|
2636
3377
|
{
|
|
@@ -2642,37 +3383,9 @@ program.command("cp <source> <target>").description("Copy an environment configu
|
|
|
2642
3383
|
]);
|
|
2643
3384
|
if (modify) {
|
|
2644
3385
|
const current = registries[target];
|
|
2645
|
-
const answers = await
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
name: "ANTHROPIC_BASE_URL",
|
|
2649
|
-
message: "ANTHROPIC_BASE_URL:",
|
|
2650
|
-
default: current.ANTHROPIC_BASE_URL
|
|
2651
|
-
},
|
|
2652
|
-
{
|
|
2653
|
-
type: "password",
|
|
2654
|
-
name: "ANTHROPIC_API_KEY",
|
|
2655
|
-
message: "ANTHROPIC_API_KEY (leave empty to keep current):"
|
|
2656
|
-
},
|
|
2657
|
-
{
|
|
2658
|
-
type: "input",
|
|
2659
|
-
name: "ANTHROPIC_MODEL",
|
|
2660
|
-
message: "ANTHROPIC_MODEL:",
|
|
2661
|
-
default: current.ANTHROPIC_MODEL
|
|
2662
|
-
},
|
|
2663
|
-
{
|
|
2664
|
-
type: "input",
|
|
2665
|
-
name: "ANTHROPIC_SMALL_FAST_MODEL",
|
|
2666
|
-
message: "ANTHROPIC_SMALL_FAST_MODEL:",
|
|
2667
|
-
default: current.ANTHROPIC_SMALL_FAST_MODEL
|
|
2668
|
-
}
|
|
2669
|
-
]);
|
|
2670
|
-
if (answers.ANTHROPIC_BASE_URL) current.ANTHROPIC_BASE_URL = answers.ANTHROPIC_BASE_URL;
|
|
2671
|
-
if (answers.ANTHROPIC_API_KEY) current.ANTHROPIC_API_KEY = encrypt(answers.ANTHROPIC_API_KEY);
|
|
2672
|
-
if (answers.ANTHROPIC_MODEL) current.ANTHROPIC_MODEL = answers.ANTHROPIC_MODEL;
|
|
2673
|
-
if (answers.ANTHROPIC_SMALL_FAST_MODEL) current.ANTHROPIC_SMALL_FAST_MODEL = answers.ANTHROPIC_SMALL_FAST_MODEL;
|
|
2674
|
-
registries[target] = current;
|
|
2675
|
-
config2.set("registries", registries);
|
|
3386
|
+
const answers = await promptForEnvironmentConfig(current, true);
|
|
3387
|
+
registries[target] = applyPromptAnswers(current, answers, true);
|
|
3388
|
+
setRegistries(registries);
|
|
2676
3389
|
console.log(chalk7.green(`Environment '${target}' updated.`));
|
|
2677
3390
|
}
|
|
2678
3391
|
});
|
|
@@ -2681,25 +3394,22 @@ program.command("current").description("Show current environment name").action((
|
|
|
2681
3394
|
console.log(chalk7.green(current));
|
|
2682
3395
|
});
|
|
2683
3396
|
program.command("env").description("Output environment variables for shell eval").option("--json", "Output as JSON").action((options) => {
|
|
2684
|
-
const registries =
|
|
3397
|
+
const registries = getRegistries();
|
|
2685
3398
|
const current = config2.get("current");
|
|
2686
3399
|
const env = registries[current];
|
|
2687
3400
|
if (!env) return;
|
|
2688
3401
|
const outputEnv = { ...env };
|
|
2689
|
-
if (outputEnv.
|
|
2690
|
-
outputEnv.
|
|
3402
|
+
if (outputEnv.ANTHROPIC_AUTH_TOKEN) {
|
|
3403
|
+
outputEnv.ANTHROPIC_AUTH_TOKEN = decrypt(outputEnv.ANTHROPIC_AUTH_TOKEN);
|
|
2691
3404
|
}
|
|
2692
3405
|
if (options.json) {
|
|
2693
3406
|
console.log(JSON.stringify(outputEnv, null, 2));
|
|
2694
3407
|
} else {
|
|
2695
|
-
|
|
2696
|
-
if (outputEnv.ANTHROPIC_API_KEY) console.log(`export ANTHROPIC_API_KEY="${outputEnv.ANTHROPIC_API_KEY}"`);
|
|
2697
|
-
if (outputEnv.ANTHROPIC_MODEL) console.log(`export ANTHROPIC_MODEL="${outputEnv.ANTHROPIC_MODEL}"`);
|
|
2698
|
-
if (outputEnv.ANTHROPIC_SMALL_FAST_MODEL) console.log(`export ANTHROPIC_SMALL_FAST_MODEL="${outputEnv.ANTHROPIC_SMALL_FAST_MODEL}"`);
|
|
3408
|
+
buildShellEnvCommands(env).forEach((cmd) => console.log(cmd));
|
|
2699
3409
|
}
|
|
2700
3410
|
});
|
|
2701
3411
|
program.command("run <command...>").description("Run a command with the current environment variables").action((command) => {
|
|
2702
|
-
const registries =
|
|
3412
|
+
const registries = getRegistries();
|
|
2703
3413
|
const current = config2.get("current");
|
|
2704
3414
|
const envConfig = registries[current];
|
|
2705
3415
|
if (!envConfig) {
|
|
@@ -2707,10 +3417,8 @@ program.command("run <command...>").description("Run a command with the current
|
|
|
2707
3417
|
process.exit(1);
|
|
2708
3418
|
}
|
|
2709
3419
|
const env = { ...process.env };
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
if (envConfig.ANTHROPIC_MODEL) env.ANTHROPIC_MODEL = envConfig.ANTHROPIC_MODEL;
|
|
2713
|
-
if (envConfig.ANTHROPIC_SMALL_FAST_MODEL) env.ANTHROPIC_SMALL_FAST_MODEL = envConfig.ANTHROPIC_SMALL_FAST_MODEL;
|
|
3420
|
+
clearManagedClaudeEnv(env);
|
|
3421
|
+
Object.assign(env, buildResolvedEnvVars(envConfig));
|
|
2714
3422
|
const [cmd, ...args] = command;
|
|
2715
3423
|
const child = spawn3(cmd, args, {
|
|
2716
3424
|
env,
|
|
@@ -2763,20 +3471,21 @@ setupCmd.command("default-mode").description("\u8BBE\u7F6E\u9ED8\u8BA4\u6743\u96
|
|
|
2763
3471
|
console.log(chalk7.gray("\u6E05\u9664\u9ED8\u8BA4\u6A21\u5F0F: ccem setup default-mode --reset"));
|
|
2764
3472
|
console.log(chalk7.gray("\u53EF\u7528\u6A21\u5F0F: " + PERMISSION_MODES.join(", ")));
|
|
2765
3473
|
});
|
|
2766
|
-
setupCmd.command("init").description("\u521D\u59CB\u5316 Claude Code \u5168\u5C40\u914D\u7F6E\uFF08\u8DF3\u8FC7 onboarding\u3001\u7981\u7528\u9065\u6D4B\
|
|
2767
|
-
|
|
3474
|
+
setupCmd.command("init").description("\u521D\u59CB\u5316 Claude Code \u5168\u5C40\u914D\u7F6E\uFF08\u8DF3\u8FC7 onboarding\u3001\u7981\u7528\u9065\u6D4B\uFF09").option("--chrome", "\u540C\u65F6\u5B89\u88C5 chrome-devtools MCP \u5DE5\u5177").action(async function() {
|
|
3475
|
+
const options = this.opts();
|
|
3476
|
+
await runSetupInit({ chrome: !!options.chrome });
|
|
2768
3477
|
});
|
|
2769
3478
|
setupCmd.command("migrate").description("\u8FC1\u79FB\u65E7\u7248\u914D\u7F6E\u5230 ~/.ccem/").option("--clean", "\u8FC1\u79FB\u540E\u5220\u9664\u65E7\u914D\u7F6E\u6587\u4EF6").option("--force", "\u5F3A\u5236\u91CD\u65B0\u8FC1\u79FB\uFF08\u8986\u76D6\u73B0\u6709\u914D\u7F6E\uFF09").action(async function() {
|
|
2770
3479
|
const options = this.opts();
|
|
2771
3480
|
const newConfigPath = getCcemConfigPath();
|
|
2772
3481
|
const legacyConfigPath = getLegacyConfigPath();
|
|
2773
3482
|
console.log(chalk7.cyan("\n\u{1F504} \u914D\u7F6E\u8FC1\u79FB\n"));
|
|
2774
|
-
if (!
|
|
3483
|
+
if (!fs9.existsSync(legacyConfigPath)) {
|
|
2775
3484
|
console.log(chalk7.yellow("\u672A\u627E\u5230\u65E7\u7248\u914D\u7F6E\u6587\u4EF6"));
|
|
2776
3485
|
console.log(chalk7.gray(` \u65E7\u8DEF\u5F84: ${legacyConfigPath}`));
|
|
2777
3486
|
return;
|
|
2778
3487
|
}
|
|
2779
|
-
if (
|
|
3488
|
+
if (fs9.existsSync(newConfigPath) && !options.force) {
|
|
2780
3489
|
console.log(chalk7.green("\u2713 \u914D\u7F6E\u5DF2\u5728\u65B0\u8DEF\u5F84"));
|
|
2781
3490
|
console.log(chalk7.gray(` \u8DEF\u5F84: ${newConfigPath}`));
|
|
2782
3491
|
console.log(chalk7.gray("\n\u4F7F\u7528 --force \u5F3A\u5236\u91CD\u65B0\u8FC1\u79FB"));
|
|
@@ -2784,15 +3493,15 @@ setupCmd.command("migrate").description("\u8FC1\u79FB\u65E7\u7248\u914D\u7F6E\u5
|
|
|
2784
3493
|
}
|
|
2785
3494
|
try {
|
|
2786
3495
|
ensureCcemDir();
|
|
2787
|
-
|
|
3496
|
+
fs9.copyFileSync(legacyConfigPath, newConfigPath);
|
|
2788
3497
|
console.log(chalk7.green("\u2713 \u914D\u7F6E\u5DF2\u8FC1\u79FB"));
|
|
2789
3498
|
console.log(chalk7.gray(` \u4ECE: ${legacyConfigPath}`));
|
|
2790
3499
|
console.log(chalk7.gray(` \u5230: ${newConfigPath}`));
|
|
2791
3500
|
if (options.clean) {
|
|
2792
|
-
|
|
2793
|
-
const legacyDir =
|
|
3501
|
+
fs9.unlinkSync(legacyConfigPath);
|
|
3502
|
+
const legacyDir = path7.dirname(legacyConfigPath);
|
|
2794
3503
|
try {
|
|
2795
|
-
|
|
3504
|
+
fs9.rmdirSync(legacyDir);
|
|
2796
3505
|
} catch {
|
|
2797
3506
|
}
|
|
2798
3507
|
console.log(chalk7.green("\u2713 \u5DF2\u5220\u9664\u65E7\u914D\u7F6E\u6587\u4EF6"));
|
|
@@ -2803,13 +3512,13 @@ setupCmd.command("migrate").description("\u8FC1\u79FB\u65E7\u7248\u914D\u7F6E\u5
|
|
|
2803
3512
|
});
|
|
2804
3513
|
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() {
|
|
2805
3514
|
const options = this.opts();
|
|
2806
|
-
const skillDir =
|
|
2807
|
-
const targetPath =
|
|
2808
|
-
if (!
|
|
2809
|
-
|
|
3515
|
+
const skillDir = path7.join(process.env.HOME || "~", ".claude", "skills");
|
|
3516
|
+
const targetPath = path7.join(skillDir, "ccem-cron.md");
|
|
3517
|
+
if (!fs9.existsSync(skillDir)) {
|
|
3518
|
+
fs9.mkdirSync(skillDir, { recursive: true });
|
|
2810
3519
|
console.log(chalk7.gray(`\u521B\u5EFA\u76EE\u5F55: ${skillDir}`));
|
|
2811
3520
|
}
|
|
2812
|
-
if (
|
|
3521
|
+
if (fs9.existsSync(targetPath) && !options.force) {
|
|
2813
3522
|
const { overwrite } = await inquirer.prompt([
|
|
2814
3523
|
{
|
|
2815
3524
|
type: "confirm",
|
|
@@ -2823,7 +3532,7 @@ setupCmd.command("cron").description("\u5B89\u88C5 ccem-cron skill \u5230 Claude
|
|
|
2823
3532
|
return;
|
|
2824
3533
|
}
|
|
2825
3534
|
}
|
|
2826
|
-
|
|
3535
|
+
fs9.writeFileSync(targetPath, CCEM_CRON_SKILL_CONTENT, "utf-8");
|
|
2827
3536
|
console.log(chalk7.green(`\u2713 \u5DF2\u5B89\u88C5 ccem-cron skill`));
|
|
2828
3537
|
console.log(chalk7.gray(` \u8DEF\u5F84: ${targetPath}`));
|
|
2829
3538
|
console.log(chalk7.cyan(`
|
|
@@ -2879,20 +3588,34 @@ skillCmd.command("ls").description("\u5217\u51FA\u5DF2\u5B89\u88C5\u7684 skills"
|
|
|
2879
3588
|
skillCmd.command("rm <name>").description("\u5220\u9664\u5DF2\u5B89\u88C5\u7684 skill").action((name) => {
|
|
2880
3589
|
removeSkill(name);
|
|
2881
3590
|
});
|
|
2882
|
-
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) => {
|
|
2883
|
-
await loadFromRemote(url, options.secret);
|
|
3591
|
+
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) => {
|
|
3592
|
+
const results = await loadFromRemote(url, options.secret);
|
|
3593
|
+
if (options.json) {
|
|
3594
|
+
console.log(JSON.stringify({
|
|
3595
|
+
count: results.length,
|
|
3596
|
+
environments: results.map((r) => ({
|
|
3597
|
+
name: r.name,
|
|
3598
|
+
original_name: r.originalName,
|
|
3599
|
+
// 使用 snake_case 匹配 Rust 结构体
|
|
3600
|
+
renamed: r.renamed
|
|
3601
|
+
}))
|
|
3602
|
+
}));
|
|
3603
|
+
}
|
|
2884
3604
|
});
|
|
2885
|
-
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() {
|
|
3605
|
+
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() {
|
|
2886
3606
|
const opts = this.opts();
|
|
2887
3607
|
const envName = opts.env || config2.get("current");
|
|
2888
|
-
const registries =
|
|
3608
|
+
const registries = getRegistries();
|
|
2889
3609
|
const envConfig = registries[envName];
|
|
2890
3610
|
if (!envConfig) {
|
|
2891
3611
|
console.error(chalk7.red(`Environment '${envName}' not found.`));
|
|
2892
3612
|
process.exit(1);
|
|
2893
3613
|
}
|
|
3614
|
+
const proxyBaseUrl = opts.proxyBaseUrl || opts.anthropicBaseUrl;
|
|
3615
|
+
const launchEnvConfig = proxyBaseUrl ? { ...envConfig, ANTHROPIC_BASE_URL: proxyBaseUrl } : envConfig;
|
|
2894
3616
|
await launchClaude({
|
|
2895
|
-
|
|
3617
|
+
envName,
|
|
3618
|
+
envConfig: launchEnvConfig,
|
|
2896
3619
|
permMode: opts.perm,
|
|
2897
3620
|
workingDir: opts.workingDir,
|
|
2898
3621
|
sessionId: opts.sessionId,
|
|
@@ -2928,7 +3651,7 @@ program.action(async (options) => {
|
|
|
2928
3651
|
showCurrentEnv(usageStats, usageLoading);
|
|
2929
3652
|
console.log("");
|
|
2930
3653
|
const defaultMode = config2.get("defaultMode");
|
|
2931
|
-
const registries =
|
|
3654
|
+
const registries = getRegistries();
|
|
2932
3655
|
const current = config2.get("current");
|
|
2933
3656
|
const envConfig = registries[current];
|
|
2934
3657
|
const { action } = await inquirer.prompt([
|
|
@@ -2950,7 +3673,11 @@ program.action(async (options) => {
|
|
|
2950
3673
|
msg.error("No environment configuration found.");
|
|
2951
3674
|
process.exit(1);
|
|
2952
3675
|
}
|
|
2953
|
-
await launchClaude({
|
|
3676
|
+
await launchClaude({
|
|
3677
|
+
envName: current,
|
|
3678
|
+
envConfig,
|
|
3679
|
+
permMode: defaultMode || void 0
|
|
3680
|
+
});
|
|
2954
3681
|
return;
|
|
2955
3682
|
} else if (action === "usage") {
|
|
2956
3683
|
console.clear();
|
|
@@ -2975,37 +3702,9 @@ program.action(async (options) => {
|
|
|
2975
3702
|
const envToEdit = registries[result.name];
|
|
2976
3703
|
console.log(chalk7.yellow(`
|
|
2977
3704
|
Editing environment '${result.name}'`));
|
|
2978
|
-
const answers = await
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
name: "ANTHROPIC_BASE_URL",
|
|
2982
|
-
message: "ANTHROPIC_BASE_URL:",
|
|
2983
|
-
default: envToEdit.ANTHROPIC_BASE_URL
|
|
2984
|
-
},
|
|
2985
|
-
{
|
|
2986
|
-
type: "password",
|
|
2987
|
-
name: "ANTHROPIC_API_KEY",
|
|
2988
|
-
message: "ANTHROPIC_API_KEY (leave empty to keep current):"
|
|
2989
|
-
},
|
|
2990
|
-
{
|
|
2991
|
-
type: "input",
|
|
2992
|
-
name: "ANTHROPIC_MODEL",
|
|
2993
|
-
message: "ANTHROPIC_MODEL:",
|
|
2994
|
-
default: envToEdit.ANTHROPIC_MODEL
|
|
2995
|
-
},
|
|
2996
|
-
{
|
|
2997
|
-
type: "input",
|
|
2998
|
-
name: "ANTHROPIC_SMALL_FAST_MODEL",
|
|
2999
|
-
message: "ANTHROPIC_SMALL_FAST_MODEL:",
|
|
3000
|
-
default: envToEdit.ANTHROPIC_SMALL_FAST_MODEL
|
|
3001
|
-
}
|
|
3002
|
-
]);
|
|
3003
|
-
if (answers.ANTHROPIC_BASE_URL) envToEdit.ANTHROPIC_BASE_URL = answers.ANTHROPIC_BASE_URL;
|
|
3004
|
-
if (answers.ANTHROPIC_API_KEY) envToEdit.ANTHROPIC_API_KEY = encrypt(answers.ANTHROPIC_API_KEY);
|
|
3005
|
-
if (answers.ANTHROPIC_MODEL) envToEdit.ANTHROPIC_MODEL = answers.ANTHROPIC_MODEL;
|
|
3006
|
-
if (answers.ANTHROPIC_SMALL_FAST_MODEL) envToEdit.ANTHROPIC_SMALL_FAST_MODEL = answers.ANTHROPIC_SMALL_FAST_MODEL;
|
|
3007
|
-
registries[result.name] = envToEdit;
|
|
3008
|
-
config2.set("registries", registries);
|
|
3705
|
+
const answers = await promptForEnvironmentConfig(envToEdit, true);
|
|
3706
|
+
registries[result.name] = applyPromptAnswers(envToEdit, answers, true);
|
|
3707
|
+
setRegistries(registries);
|
|
3009
3708
|
msg.success(`Environment '${result.name}' updated.`);
|
|
3010
3709
|
await new Promise((resolve2) => setTimeout(resolve2, 800));
|
|
3011
3710
|
} else if (result.action === "rename") {
|
|
@@ -3027,7 +3726,7 @@ Editing environment '${result.name}'`));
|
|
|
3027
3726
|
]);
|
|
3028
3727
|
registries[newName] = registries[result.name];
|
|
3029
3728
|
delete registries[result.name];
|
|
3030
|
-
|
|
3729
|
+
setRegistries(registries);
|
|
3031
3730
|
if (current === result.name) {
|
|
3032
3731
|
config2.set("current", newName);
|
|
3033
3732
|
}
|
|
@@ -3048,7 +3747,7 @@ Editing environment '${result.name}'`));
|
|
|
3048
3747
|
}
|
|
3049
3748
|
]);
|
|
3050
3749
|
registries[targetName] = { ...registries[result.name] };
|
|
3051
|
-
|
|
3750
|
+
setRegistries(registries);
|
|
3052
3751
|
msg.success(`Environment '${result.name}' copied to '${targetName}'.`);
|
|
3053
3752
|
const { modify } = await inquirer.prompt([
|
|
3054
3753
|
{
|
|
@@ -3060,37 +3759,9 @@ Editing environment '${result.name}'`));
|
|
|
3060
3759
|
]);
|
|
3061
3760
|
if (modify) {
|
|
3062
3761
|
const envToEdit = registries[targetName];
|
|
3063
|
-
const editAnswers = await
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
name: "ANTHROPIC_BASE_URL",
|
|
3067
|
-
message: "ANTHROPIC_BASE_URL:",
|
|
3068
|
-
default: envToEdit.ANTHROPIC_BASE_URL
|
|
3069
|
-
},
|
|
3070
|
-
{
|
|
3071
|
-
type: "password",
|
|
3072
|
-
name: "ANTHROPIC_API_KEY",
|
|
3073
|
-
message: "ANTHROPIC_API_KEY (leave empty to keep current):"
|
|
3074
|
-
},
|
|
3075
|
-
{
|
|
3076
|
-
type: "input",
|
|
3077
|
-
name: "ANTHROPIC_MODEL",
|
|
3078
|
-
message: "ANTHROPIC_MODEL:",
|
|
3079
|
-
default: envToEdit.ANTHROPIC_MODEL
|
|
3080
|
-
},
|
|
3081
|
-
{
|
|
3082
|
-
type: "input",
|
|
3083
|
-
name: "ANTHROPIC_SMALL_FAST_MODEL",
|
|
3084
|
-
message: "ANTHROPIC_SMALL_FAST_MODEL:",
|
|
3085
|
-
default: envToEdit.ANTHROPIC_SMALL_FAST_MODEL
|
|
3086
|
-
}
|
|
3087
|
-
]);
|
|
3088
|
-
if (editAnswers.ANTHROPIC_BASE_URL) envToEdit.ANTHROPIC_BASE_URL = editAnswers.ANTHROPIC_BASE_URL;
|
|
3089
|
-
if (editAnswers.ANTHROPIC_API_KEY) envToEdit.ANTHROPIC_API_KEY = encrypt(editAnswers.ANTHROPIC_API_KEY);
|
|
3090
|
-
if (editAnswers.ANTHROPIC_MODEL) envToEdit.ANTHROPIC_MODEL = editAnswers.ANTHROPIC_MODEL;
|
|
3091
|
-
if (editAnswers.ANTHROPIC_SMALL_FAST_MODEL) envToEdit.ANTHROPIC_SMALL_FAST_MODEL = editAnswers.ANTHROPIC_SMALL_FAST_MODEL;
|
|
3092
|
-
registries[targetName] = envToEdit;
|
|
3093
|
-
config2.set("registries", registries);
|
|
3762
|
+
const editAnswers = await promptForEnvironmentConfig(envToEdit, true);
|
|
3763
|
+
registries[targetName] = applyPromptAnswers(envToEdit, editAnswers, true);
|
|
3764
|
+
setRegistries(registries);
|
|
3094
3765
|
msg.success(`Environment '${targetName}' updated.`);
|
|
3095
3766
|
}
|
|
3096
3767
|
await new Promise((resolve2) => setTimeout(resolve2, 800));
|
|
@@ -3109,7 +3780,7 @@ Editing environment '${result.name}'`));
|
|
|
3109
3780
|
]);
|
|
3110
3781
|
if (confirm) {
|
|
3111
3782
|
delete registries[result.name];
|
|
3112
|
-
|
|
3783
|
+
setRegistries(registries);
|
|
3113
3784
|
if (current === result.name) {
|
|
3114
3785
|
config2.set("current", "official");
|
|
3115
3786
|
msg.warning(`Deleted current environment. Switched back to 'official'.`);
|
|
@@ -3131,7 +3802,7 @@ Editing environment '${result.name}'`));
|
|
|
3131
3802
|
}
|
|
3132
3803
|
]);
|
|
3133
3804
|
if (permMode !== "back") {
|
|
3134
|
-
await runWithTempPermissions(permMode, envConfig);
|
|
3805
|
+
await runWithTempPermissions(permMode, envConfig, current);
|
|
3135
3806
|
return;
|
|
3136
3807
|
}
|
|
3137
3808
|
} else if (action === "setDefault") {
|