ccem 2.0.0-beta.9 → 2.0.0
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 +489 -94
- package/model-prices.json +12 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -7,11 +7,11 @@ import inquirer from "inquirer";
|
|
|
7
7
|
import chalk7 from "chalk";
|
|
8
8
|
import Table3 from "cli-table3";
|
|
9
9
|
import { spawn as spawn3 } from "child_process";
|
|
10
|
-
import * as
|
|
11
|
-
import * as
|
|
10
|
+
import * as fs9 from "fs";
|
|
11
|
+
import * as path7 from "path";
|
|
12
12
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
13
13
|
|
|
14
|
-
// ../../packages/core/dist/chunk-
|
|
14
|
+
// ../../packages/core/dist/chunk-25EB45SM.js
|
|
15
15
|
var TIER_MODEL_ALIASES = /* @__PURE__ */ new Set(["opus", "sonnet", "haiku"]);
|
|
16
16
|
function normalizeEnvConfig(envConfig, defaultRuntimeModel = "opus") {
|
|
17
17
|
const hasTierDefaults = Boolean(envConfig.ANTHROPIC_DEFAULT_OPUS_MODEL) || Boolean(envConfig.ANTHROPIC_DEFAULT_SONNET_MODEL) || Boolean(envConfig.ANTHROPIC_DEFAULT_HAIKU_MODEL);
|
|
@@ -68,8 +68,8 @@ function recoverEnvConfigFromLegacy(currentEnvConfig, legacyEnvConfig) {
|
|
|
68
68
|
var ENV_PRESETS = {
|
|
69
69
|
"GLM": {
|
|
70
70
|
ANTHROPIC_BASE_URL: "https://open.bigmodel.cn/api/anthropic",
|
|
71
|
-
ANTHROPIC_DEFAULT_OPUS_MODEL: "glm-5",
|
|
72
|
-
ANTHROPIC_DEFAULT_SONNET_MODEL: "glm-5",
|
|
71
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: "glm-5.1",
|
|
72
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: "glm-5.1",
|
|
73
73
|
ANTHROPIC_DEFAULT_HAIKU_MODEL: "glm-4.5-air",
|
|
74
74
|
ANTHROPIC_MODEL: "opus"
|
|
75
75
|
},
|
|
@@ -96,30 +96,51 @@ var ENV_PRESETS = {
|
|
|
96
96
|
},
|
|
97
97
|
"DeepSeek": {
|
|
98
98
|
ANTHROPIC_BASE_URL: "https://api.deepseek.com/anthropic",
|
|
99
|
-
ANTHROPIC_DEFAULT_OPUS_MODEL: "deepseek-
|
|
100
|
-
ANTHROPIC_DEFAULT_SONNET_MODEL: "deepseek-
|
|
101
|
-
ANTHROPIC_DEFAULT_HAIKU_MODEL: "deepseek-
|
|
99
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: "deepseek-v4-pro[1m]",
|
|
100
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: "deepseek-v4-pro[1m]",
|
|
101
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: "deepseek-v4-flash",
|
|
102
102
|
ANTHROPIC_MODEL: "opus"
|
|
103
103
|
},
|
|
104
104
|
"Bailian": {
|
|
105
105
|
ANTHROPIC_BASE_URL: "https://dashscope.aliyuncs.com/api/v2/apps/claude-code-proxy",
|
|
106
|
-
ANTHROPIC_DEFAULT_OPUS_MODEL: "qwen3-coder-
|
|
106
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: "qwen3-coder-next",
|
|
107
107
|
ANTHROPIC_DEFAULT_SONNET_MODEL: "qwen3-coder-plus",
|
|
108
108
|
ANTHROPIC_DEFAULT_HAIKU_MODEL: "qwen3-coder-flash",
|
|
109
109
|
ANTHROPIC_MODEL: "opus"
|
|
110
110
|
},
|
|
111
111
|
"BailianCodePlan": {
|
|
112
112
|
ANTHROPIC_BASE_URL: "https://coding.dashscope.aliyuncs.com/api/v2/apps/claude-code-proxy",
|
|
113
|
-
ANTHROPIC_DEFAULT_OPUS_MODEL: "qwen3-coder-
|
|
113
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: "qwen3-coder-next",
|
|
114
114
|
ANTHROPIC_DEFAULT_SONNET_MODEL: "qwen3-coder-plus",
|
|
115
|
-
ANTHROPIC_DEFAULT_HAIKU_MODEL: "qwen3-coder-
|
|
115
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: "qwen3-coder-flash",
|
|
116
116
|
ANTHROPIC_MODEL: "opus"
|
|
117
117
|
},
|
|
118
118
|
"OpenRouter": {
|
|
119
119
|
ANTHROPIC_BASE_URL: "https://openrouter.ai/api/v1",
|
|
120
|
-
ANTHROPIC_DEFAULT_OPUS_MODEL: "anthropic/claude-opus-4
|
|
121
|
-
ANTHROPIC_DEFAULT_SONNET_MODEL: "anthropic/claude-
|
|
122
|
-
ANTHROPIC_DEFAULT_HAIKU_MODEL: "anthropic/claude-
|
|
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"
|
|
131
|
+
},
|
|
132
|
+
"MiMo": {
|
|
133
|
+
ANTHROPIC_BASE_URL: "https://api.xiaomimimo.com/anthropic",
|
|
134
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: "mimo-v2.5-pro",
|
|
135
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: "mimo-v2.5-pro",
|
|
136
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: "mimo-v2.5",
|
|
137
|
+
ANTHROPIC_MODEL: "opus"
|
|
138
|
+
},
|
|
139
|
+
"MiMoTokenPlan": {
|
|
140
|
+
ANTHROPIC_BASE_URL: "https://token-plan-cn.xiaomimimo.com/anthropic",
|
|
141
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: "mimo-v2.5-pro",
|
|
142
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: "mimo-v2.5-pro",
|
|
143
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: "mimo-v2.5",
|
|
123
144
|
ANTHROPIC_MODEL: "opus"
|
|
124
145
|
}
|
|
125
146
|
};
|
|
@@ -914,10 +935,10 @@ var renderLogoWithEnvPanel = (envName, env, defaultMode) => {
|
|
|
914
935
|
const parsed = new URL(url);
|
|
915
936
|
const protocol = parsed.protocol + "//";
|
|
916
937
|
const host = parsed.host;
|
|
917
|
-
const
|
|
938
|
+
const path8 = parsed.pathname + parsed.search;
|
|
918
939
|
const hostStart = host.slice(0, 8);
|
|
919
940
|
const hostEnd = host.slice(-4);
|
|
920
|
-
const pathPart =
|
|
941
|
+
const pathPart = path8.length > 10 ? path8.slice(0, 7) + "..." : path8;
|
|
921
942
|
return `${protocol}${hostStart}...${hostEnd}${pathPart}`;
|
|
922
943
|
} catch {
|
|
923
944
|
return truncate(url, max);
|
|
@@ -1355,7 +1376,7 @@ var selectEnvWithKeys = (registries, current) => {
|
|
|
1355
1376
|
};
|
|
1356
1377
|
|
|
1357
1378
|
// src/permissions.ts
|
|
1358
|
-
import
|
|
1379
|
+
import fs6 from "fs";
|
|
1359
1380
|
import chalk3 from "chalk";
|
|
1360
1381
|
import Table2 from "cli-table3";
|
|
1361
1382
|
|
|
@@ -1408,9 +1429,347 @@ var ensureGlobalClaudeDir = () => {
|
|
|
1408
1429
|
|
|
1409
1430
|
// src/launcher.ts
|
|
1410
1431
|
import { spawn } from "child_process";
|
|
1411
|
-
import * as
|
|
1412
|
-
import * as
|
|
1432
|
+
import * as fs5 from "fs";
|
|
1433
|
+
import * as path5 from "path";
|
|
1413
1434
|
import chalk2 from "chalk";
|
|
1435
|
+
|
|
1436
|
+
// src/sessionProvenance.ts
|
|
1437
|
+
import { execFileSync } from "child_process";
|
|
1438
|
+
import fs4 from "fs";
|
|
1439
|
+
import { createRequire } from "module";
|
|
1440
|
+
import os2 from "os";
|
|
1441
|
+
import path4 from "path";
|
|
1442
|
+
var DEFAULT_CONFIG_SOURCE = "ccem";
|
|
1443
|
+
var STATE_DB_FILE_NAME = "state.sqlite";
|
|
1444
|
+
var BIND_POLL_INTERVAL_MS = 500;
|
|
1445
|
+
var BIND_POLL_TIMEOUT_MS = 2e4;
|
|
1446
|
+
var SQLITE_EXPERIMENTAL_WARNING = "SQLite is an experimental feature";
|
|
1447
|
+
var require2 = createRequire(import.meta.url);
|
|
1448
|
+
var databaseSyncCtor = null;
|
|
1449
|
+
var databaseSyncResolved = false;
|
|
1450
|
+
var sqlite3CliAvailable = null;
|
|
1451
|
+
function startCliClaudeProvenanceTracking(options) {
|
|
1452
|
+
const envName = normalizeText(options.envName) ?? "unknown";
|
|
1453
|
+
const workingDir = normalizeText(options.workingDir);
|
|
1454
|
+
if (!workingDir) {
|
|
1455
|
+
return null;
|
|
1456
|
+
}
|
|
1457
|
+
const ccemSessionId = `cli-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
1458
|
+
try {
|
|
1459
|
+
registerLaunch({
|
|
1460
|
+
ccemSessionId,
|
|
1461
|
+
client: "claude",
|
|
1462
|
+
envName,
|
|
1463
|
+
configSource: DEFAULT_CONFIG_SOURCE,
|
|
1464
|
+
workingDir,
|
|
1465
|
+
permMode: normalizeText(options.permMode),
|
|
1466
|
+
launchMode: "cli_external",
|
|
1467
|
+
startedVia: "cli",
|
|
1468
|
+
sourceSessionId: normalizeText(options.resumeSessionId)
|
|
1469
|
+
});
|
|
1470
|
+
} catch (error) {
|
|
1471
|
+
reportTrackingError("register launch", error);
|
|
1472
|
+
return null;
|
|
1473
|
+
}
|
|
1474
|
+
let timer = null;
|
|
1475
|
+
const stop = () => {
|
|
1476
|
+
if (timer) {
|
|
1477
|
+
clearInterval(timer);
|
|
1478
|
+
timer = null;
|
|
1479
|
+
}
|
|
1480
|
+
};
|
|
1481
|
+
const resumeSessionId = normalizeText(options.resumeSessionId);
|
|
1482
|
+
if (resumeSessionId) {
|
|
1483
|
+
return { ccemSessionId, stop };
|
|
1484
|
+
}
|
|
1485
|
+
const startedAtMs = Date.now();
|
|
1486
|
+
timer = setInterval(() => {
|
|
1487
|
+
if (Date.now() - startedAtMs > BIND_POLL_TIMEOUT_MS) {
|
|
1488
|
+
stop();
|
|
1489
|
+
return;
|
|
1490
|
+
}
|
|
1491
|
+
try {
|
|
1492
|
+
const jsonlPath = discoverClaudeJsonlPath(workingDir, startedAtMs);
|
|
1493
|
+
if (!jsonlPath) {
|
|
1494
|
+
return;
|
|
1495
|
+
}
|
|
1496
|
+
const sourceSessionId = readClaudeSessionId(jsonlPath) ?? normalizeText(path4.parse(jsonlPath).name);
|
|
1497
|
+
if (!sourceSessionId) {
|
|
1498
|
+
return;
|
|
1499
|
+
}
|
|
1500
|
+
bindSourceSessionId("claude", ccemSessionId, sourceSessionId);
|
|
1501
|
+
stop();
|
|
1502
|
+
} catch (error) {
|
|
1503
|
+
reportTrackingError("bind source session id", error);
|
|
1504
|
+
stop();
|
|
1505
|
+
}
|
|
1506
|
+
}, BIND_POLL_INTERVAL_MS);
|
|
1507
|
+
return { ccemSessionId, stop };
|
|
1508
|
+
}
|
|
1509
|
+
function registerLaunch(options) {
|
|
1510
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1511
|
+
const DatabaseSync = getDatabaseSyncCtor();
|
|
1512
|
+
if (DatabaseSync) {
|
|
1513
|
+
const db = openNodeSqliteDb(DatabaseSync);
|
|
1514
|
+
try {
|
|
1515
|
+
const statement = db.prepare(`
|
|
1516
|
+
INSERT INTO session_provenance (
|
|
1517
|
+
ccem_session_id,
|
|
1518
|
+
client,
|
|
1519
|
+
env_name,
|
|
1520
|
+
config_source,
|
|
1521
|
+
working_dir,
|
|
1522
|
+
perm_mode,
|
|
1523
|
+
launch_mode,
|
|
1524
|
+
started_via,
|
|
1525
|
+
source_session_id,
|
|
1526
|
+
created_at,
|
|
1527
|
+
updated_at
|
|
1528
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1529
|
+
ON CONFLICT(ccem_session_id) DO UPDATE SET
|
|
1530
|
+
client = excluded.client,
|
|
1531
|
+
env_name = excluded.env_name,
|
|
1532
|
+
config_source = COALESCE(excluded.config_source, session_provenance.config_source),
|
|
1533
|
+
working_dir = excluded.working_dir,
|
|
1534
|
+
perm_mode = COALESCE(excluded.perm_mode, session_provenance.perm_mode),
|
|
1535
|
+
launch_mode = excluded.launch_mode,
|
|
1536
|
+
started_via = excluded.started_via,
|
|
1537
|
+
source_session_id = COALESCE(excluded.source_session_id, session_provenance.source_session_id),
|
|
1538
|
+
updated_at = excluded.updated_at
|
|
1539
|
+
`);
|
|
1540
|
+
statement.run(
|
|
1541
|
+
options.ccemSessionId,
|
|
1542
|
+
options.client,
|
|
1543
|
+
options.envName,
|
|
1544
|
+
normalizeText(options.configSource),
|
|
1545
|
+
options.workingDir,
|
|
1546
|
+
normalizeText(options.permMode),
|
|
1547
|
+
options.launchMode,
|
|
1548
|
+
options.startedVia,
|
|
1549
|
+
normalizeText(options.sourceSessionId),
|
|
1550
|
+
now,
|
|
1551
|
+
now
|
|
1552
|
+
);
|
|
1553
|
+
return;
|
|
1554
|
+
} finally {
|
|
1555
|
+
db.close();
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
execSqlite3Cli(`
|
|
1559
|
+
INSERT INTO session_provenance (
|
|
1560
|
+
ccem_session_id,
|
|
1561
|
+
client,
|
|
1562
|
+
env_name,
|
|
1563
|
+
config_source,
|
|
1564
|
+
working_dir,
|
|
1565
|
+
perm_mode,
|
|
1566
|
+
launch_mode,
|
|
1567
|
+
started_via,
|
|
1568
|
+
source_session_id,
|
|
1569
|
+
created_at,
|
|
1570
|
+
updated_at
|
|
1571
|
+
) VALUES (
|
|
1572
|
+
${sqliteLiteral(options.ccemSessionId)},
|
|
1573
|
+
${sqliteLiteral(options.client)},
|
|
1574
|
+
${sqliteLiteral(options.envName)},
|
|
1575
|
+
${sqliteLiteral(normalizeText(options.configSource))},
|
|
1576
|
+
${sqliteLiteral(options.workingDir)},
|
|
1577
|
+
${sqliteLiteral(normalizeText(options.permMode))},
|
|
1578
|
+
${sqliteLiteral(options.launchMode)},
|
|
1579
|
+
${sqliteLiteral(options.startedVia)},
|
|
1580
|
+
${sqliteLiteral(normalizeText(options.sourceSessionId))},
|
|
1581
|
+
${sqliteLiteral(now)},
|
|
1582
|
+
${sqliteLiteral(now)}
|
|
1583
|
+
)
|
|
1584
|
+
ON CONFLICT(ccem_session_id) DO UPDATE SET
|
|
1585
|
+
client = excluded.client,
|
|
1586
|
+
env_name = excluded.env_name,
|
|
1587
|
+
config_source = COALESCE(excluded.config_source, session_provenance.config_source),
|
|
1588
|
+
working_dir = excluded.working_dir,
|
|
1589
|
+
perm_mode = COALESCE(excluded.perm_mode, session_provenance.perm_mode),
|
|
1590
|
+
launch_mode = excluded.launch_mode,
|
|
1591
|
+
started_via = excluded.started_via,
|
|
1592
|
+
source_session_id = COALESCE(excluded.source_session_id, session_provenance.source_session_id),
|
|
1593
|
+
updated_at = excluded.updated_at;
|
|
1594
|
+
`);
|
|
1595
|
+
}
|
|
1596
|
+
function bindSourceSessionId(client, ccemSessionId, sourceSessionId) {
|
|
1597
|
+
const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1598
|
+
const DatabaseSync = getDatabaseSyncCtor();
|
|
1599
|
+
if (DatabaseSync) {
|
|
1600
|
+
const db = openNodeSqliteDb(DatabaseSync);
|
|
1601
|
+
try {
|
|
1602
|
+
const statement = db.prepare(`
|
|
1603
|
+
UPDATE session_provenance
|
|
1604
|
+
SET source_session_id = ?, updated_at = ?
|
|
1605
|
+
WHERE ccem_session_id = ? AND client = ?
|
|
1606
|
+
`);
|
|
1607
|
+
statement.run(sourceSessionId, updatedAt, ccemSessionId, client);
|
|
1608
|
+
return;
|
|
1609
|
+
} finally {
|
|
1610
|
+
db.close();
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
execSqlite3Cli(`
|
|
1614
|
+
UPDATE session_provenance
|
|
1615
|
+
SET source_session_id = ${sqliteLiteral(sourceSessionId)},
|
|
1616
|
+
updated_at = ${sqliteLiteral(updatedAt)}
|
|
1617
|
+
WHERE ccem_session_id = ${sqliteLiteral(ccemSessionId)}
|
|
1618
|
+
AND client = ${sqliteLiteral(client)};
|
|
1619
|
+
`);
|
|
1620
|
+
}
|
|
1621
|
+
function openNodeSqliteDb(DatabaseSync) {
|
|
1622
|
+
const dbPath = getStateDbPath();
|
|
1623
|
+
fs4.mkdirSync(path4.dirname(dbPath), { recursive: true });
|
|
1624
|
+
const db = new DatabaseSync(dbPath);
|
|
1625
|
+
db.exec(schemaSql());
|
|
1626
|
+
return db;
|
|
1627
|
+
}
|
|
1628
|
+
function getDatabaseSyncCtor() {
|
|
1629
|
+
if (databaseSyncResolved) {
|
|
1630
|
+
return databaseSyncCtor;
|
|
1631
|
+
}
|
|
1632
|
+
const originalEmitWarning = process.emitWarning;
|
|
1633
|
+
process.emitWarning = ((warning, ...args) => {
|
|
1634
|
+
const message = typeof warning === "string" ? warning : warning instanceof Error ? warning.message : String(warning);
|
|
1635
|
+
if (message.includes(SQLITE_EXPERIMENTAL_WARNING)) {
|
|
1636
|
+
return;
|
|
1637
|
+
}
|
|
1638
|
+
return originalEmitWarning(warning, ...args);
|
|
1639
|
+
});
|
|
1640
|
+
try {
|
|
1641
|
+
const loaded = require2("node:sqlite");
|
|
1642
|
+
databaseSyncCtor = loaded.DatabaseSync;
|
|
1643
|
+
} catch {
|
|
1644
|
+
databaseSyncCtor = null;
|
|
1645
|
+
} finally {
|
|
1646
|
+
process.emitWarning = originalEmitWarning;
|
|
1647
|
+
databaseSyncResolved = true;
|
|
1648
|
+
}
|
|
1649
|
+
return databaseSyncCtor;
|
|
1650
|
+
}
|
|
1651
|
+
function execSqlite3Cli(statementSql) {
|
|
1652
|
+
if (sqlite3CliAvailable == null) {
|
|
1653
|
+
try {
|
|
1654
|
+
execFileSync("sqlite3", ["-version"], { stdio: "ignore" });
|
|
1655
|
+
sqlite3CliAvailable = true;
|
|
1656
|
+
} catch {
|
|
1657
|
+
sqlite3CliAvailable = false;
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
if (!sqlite3CliAvailable) {
|
|
1661
|
+
throw new Error("sqlite3 CLI is unavailable and node:sqlite is not supported by this Node runtime");
|
|
1662
|
+
}
|
|
1663
|
+
const dbPath = getStateDbPath();
|
|
1664
|
+
fs4.mkdirSync(path4.dirname(dbPath), { recursive: true });
|
|
1665
|
+
execFileSync("sqlite3", [dbPath], {
|
|
1666
|
+
input: `${schemaSql()}
|
|
1667
|
+
${statementSql}
|
|
1668
|
+
`,
|
|
1669
|
+
stdio: ["pipe", "ignore", "pipe"]
|
|
1670
|
+
});
|
|
1671
|
+
}
|
|
1672
|
+
function getStateDbPath() {
|
|
1673
|
+
const override = normalizeText(process.env.CCEM_STATE_DB_PATH);
|
|
1674
|
+
if (override) {
|
|
1675
|
+
return override;
|
|
1676
|
+
}
|
|
1677
|
+
return path4.join(os2.homedir(), ".ccem", STATE_DB_FILE_NAME);
|
|
1678
|
+
}
|
|
1679
|
+
function schemaSql() {
|
|
1680
|
+
return `
|
|
1681
|
+
PRAGMA journal_mode = WAL;
|
|
1682
|
+
PRAGMA synchronous = NORMAL;
|
|
1683
|
+
CREATE TABLE IF NOT EXISTS session_provenance (
|
|
1684
|
+
ccem_session_id TEXT PRIMARY KEY,
|
|
1685
|
+
client TEXT NOT NULL,
|
|
1686
|
+
env_name TEXT NOT NULL,
|
|
1687
|
+
config_source TEXT,
|
|
1688
|
+
working_dir TEXT NOT NULL,
|
|
1689
|
+
perm_mode TEXT,
|
|
1690
|
+
launch_mode TEXT NOT NULL,
|
|
1691
|
+
started_via TEXT NOT NULL,
|
|
1692
|
+
source_session_id TEXT,
|
|
1693
|
+
created_at TEXT NOT NULL,
|
|
1694
|
+
updated_at TEXT NOT NULL
|
|
1695
|
+
);
|
|
1696
|
+
CREATE INDEX IF NOT EXISTS idx_session_provenance_client_source
|
|
1697
|
+
ON session_provenance (client, source_session_id);
|
|
1698
|
+
CREATE INDEX IF NOT EXISTS idx_session_provenance_client_updated
|
|
1699
|
+
ON session_provenance (client, updated_at DESC);
|
|
1700
|
+
`;
|
|
1701
|
+
}
|
|
1702
|
+
function discoverClaudeJsonlPath(projectDir, startedAtMs) {
|
|
1703
|
+
const projectsDir = path4.join(os2.homedir(), ".claude", "projects");
|
|
1704
|
+
const earliestModifiedAt = startedAtMs - 15e3;
|
|
1705
|
+
for (const key of projectDirKeys(projectDir)) {
|
|
1706
|
+
const baseDir = path4.join(projectsDir, key);
|
|
1707
|
+
if (!fs4.existsSync(baseDir)) {
|
|
1708
|
+
continue;
|
|
1709
|
+
}
|
|
1710
|
+
const candidates = fs4.readdirSync(baseDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl")).map((entry) => {
|
|
1711
|
+
const fullPath = path4.join(baseDir, entry.name);
|
|
1712
|
+
return { path: fullPath, modifiedAt: fs4.statSync(fullPath).mtimeMs };
|
|
1713
|
+
}).filter((candidate) => candidate.modifiedAt >= earliestModifiedAt).sort((left, right) => right.modifiedAt - left.modifiedAt);
|
|
1714
|
+
if (candidates.length > 0) {
|
|
1715
|
+
return candidates[0].path;
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
return null;
|
|
1719
|
+
}
|
|
1720
|
+
function projectDirKeys(projectDir) {
|
|
1721
|
+
const candidates = [projectDir];
|
|
1722
|
+
try {
|
|
1723
|
+
candidates.push(fs4.realpathSync(projectDir));
|
|
1724
|
+
} catch {
|
|
1725
|
+
}
|
|
1726
|
+
if (process.platform === "darwin") {
|
|
1727
|
+
if (projectDir.startsWith("/private/")) {
|
|
1728
|
+
candidates.push(projectDir.slice("/private".length));
|
|
1729
|
+
} else if (projectDir.startsWith("/tmp")) {
|
|
1730
|
+
candidates.push(path4.posix.join("/private", projectDir));
|
|
1731
|
+
} else if (projectDir.startsWith("/var/")) {
|
|
1732
|
+
candidates.push(path4.posix.join("/private", projectDir));
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
return [...new Set(candidates.map(projectDirKey))];
|
|
1736
|
+
}
|
|
1737
|
+
function projectDirKey(projectDir) {
|
|
1738
|
+
return projectDir.replace(/[\/\\: ]/g, "-");
|
|
1739
|
+
}
|
|
1740
|
+
function readClaudeSessionId(jsonlPath) {
|
|
1741
|
+
const lines = fs4.readFileSync(jsonlPath, "utf8").split("\n");
|
|
1742
|
+
for (const line of lines) {
|
|
1743
|
+
if (!line.trim()) {
|
|
1744
|
+
continue;
|
|
1745
|
+
}
|
|
1746
|
+
try {
|
|
1747
|
+
const value = JSON.parse(line);
|
|
1748
|
+
const sessionId = normalizeText(value.sessionId);
|
|
1749
|
+
if (sessionId) {
|
|
1750
|
+
return sessionId;
|
|
1751
|
+
}
|
|
1752
|
+
} catch {
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
return null;
|
|
1756
|
+
}
|
|
1757
|
+
function normalizeText(value) {
|
|
1758
|
+
const trimmed = value?.trim();
|
|
1759
|
+
return trimmed ? trimmed : void 0;
|
|
1760
|
+
}
|
|
1761
|
+
function sqliteLiteral(value) {
|
|
1762
|
+
if (!value) {
|
|
1763
|
+
return "NULL";
|
|
1764
|
+
}
|
|
1765
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
1766
|
+
}
|
|
1767
|
+
function reportTrackingError(stage, error) {
|
|
1768
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1769
|
+
console.error(`[ccem] Failed to ${stage}: ${message}`);
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
// src/launcher.ts
|
|
1414
1773
|
var MANAGED_CLAUDE_ENV_KEYS = [
|
|
1415
1774
|
"ANTHROPIC_BASE_URL",
|
|
1416
1775
|
"ANTHROPIC_AUTH_TOKEN",
|
|
@@ -1448,14 +1807,14 @@ function buildPermArgs(modeName) {
|
|
|
1448
1807
|
return args;
|
|
1449
1808
|
}
|
|
1450
1809
|
function ensureSessionsDir() {
|
|
1451
|
-
const dir =
|
|
1452
|
-
if (!
|
|
1453
|
-
|
|
1810
|
+
const dir = path5.join(ensureCcemDir(), "sessions");
|
|
1811
|
+
if (!fs5.existsSync(dir)) {
|
|
1812
|
+
fs5.mkdirSync(dir, { recursive: true });
|
|
1454
1813
|
}
|
|
1455
1814
|
return dir;
|
|
1456
1815
|
}
|
|
1457
1816
|
async function launchClaude(options) {
|
|
1458
|
-
const { envConfig, permMode, workingDir, sessionId, resumeSessionId, silent } = options;
|
|
1817
|
+
const { envName, envConfig, permMode, workingDir, sessionId, resumeSessionId, silent } = options;
|
|
1459
1818
|
const env = { ...process.env };
|
|
1460
1819
|
for (const key of MANAGED_CLAUDE_ENV_KEYS) {
|
|
1461
1820
|
delete env[key];
|
|
@@ -1482,22 +1841,36 @@ async function launchClaude(options) {
|
|
|
1482
1841
|
if (workingDir) {
|
|
1483
1842
|
process.chdir(workingDir);
|
|
1484
1843
|
}
|
|
1844
|
+
const effectiveWorkingDir = process.cwd();
|
|
1485
1845
|
if (!silent && !permMode) {
|
|
1486
1846
|
console.log(renderStarting());
|
|
1487
1847
|
}
|
|
1488
1848
|
const sessionsDir = ensureSessionsDir();
|
|
1489
1849
|
return new Promise((resolve2) => {
|
|
1850
|
+
let provenanceTracking = null;
|
|
1490
1851
|
const child = spawn("claude", args, {
|
|
1491
1852
|
stdio: "inherit",
|
|
1492
1853
|
shell: false,
|
|
1493
1854
|
// 直接执行二进制,避免 shell 注入风险
|
|
1494
1855
|
env
|
|
1495
1856
|
});
|
|
1857
|
+
child.once("spawn", () => {
|
|
1858
|
+
if (sessionId) {
|
|
1859
|
+
return;
|
|
1860
|
+
}
|
|
1861
|
+
provenanceTracking = startCliClaudeProvenanceTracking({
|
|
1862
|
+
envName: envName ?? "unknown",
|
|
1863
|
+
workingDir: effectiveWorkingDir,
|
|
1864
|
+
permMode,
|
|
1865
|
+
resumeSessionId
|
|
1866
|
+
});
|
|
1867
|
+
});
|
|
1496
1868
|
child.on("exit", (code) => {
|
|
1869
|
+
provenanceTracking?.stop();
|
|
1497
1870
|
if (sessionId) {
|
|
1498
1871
|
try {
|
|
1499
|
-
|
|
1500
|
-
|
|
1872
|
+
fs5.writeFileSync(
|
|
1873
|
+
path5.join(sessionsDir, `${sessionId}.exit`),
|
|
1501
1874
|
String(code ?? 0)
|
|
1502
1875
|
);
|
|
1503
1876
|
} catch {
|
|
@@ -1506,7 +1879,24 @@ async function launchClaude(options) {
|
|
|
1506
1879
|
process.exit(code ?? 0);
|
|
1507
1880
|
});
|
|
1508
1881
|
child.on("error", (err) => {
|
|
1509
|
-
|
|
1882
|
+
provenanceTracking?.stop();
|
|
1883
|
+
if (err.code === "ENOENT") {
|
|
1884
|
+
console.error("");
|
|
1885
|
+
console.error(chalk2.red.bold("\u2718 \u672A\u627E\u5230 Claude Code"));
|
|
1886
|
+
console.error("");
|
|
1887
|
+
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"));
|
|
1888
|
+
console.error("");
|
|
1889
|
+
console.error(chalk2.white(" \u8BF7\u5148\u5B89\u88C5 Claude Code:"));
|
|
1890
|
+
console.error(chalk2.cyan(" npm install -g @anthropic-ai/claude-code"));
|
|
1891
|
+
console.error("");
|
|
1892
|
+
console.error(chalk2.gray(" \u5982\u679C\u5DF2\u5B89\u88C5\u4F46\u4ECD\u62A5\u9519\uFF0C\u8BF7\u68C0\u67E5:"));
|
|
1893
|
+
console.error(chalk2.gray(" 1. \u8FD0\u884C claude --version \u786E\u8BA4\u5B89\u88C5\u6210\u529F"));
|
|
1894
|
+
console.error(chalk2.gray(" 2. \u786E\u4FDD npm \u5168\u5C40\u76EE\u5F55\u5728\u7CFB\u7EDF PATH \u4E2D\uFF08npm config get prefix\uFF09"));
|
|
1895
|
+
console.error(chalk2.gray(" 3. \u5B89\u88C5\u540E\u8BF7\u91CD\u542F\u7EC8\u7AEF"));
|
|
1896
|
+
console.error("");
|
|
1897
|
+
} else {
|
|
1898
|
+
console.error(chalk2.red(`\u542F\u52A8 Claude Code \u5931\u8D25: ${err.message}`));
|
|
1899
|
+
}
|
|
1510
1900
|
process.exit(1);
|
|
1511
1901
|
});
|
|
1512
1902
|
});
|
|
@@ -1514,14 +1904,14 @@ async function launchClaude(options) {
|
|
|
1514
1904
|
|
|
1515
1905
|
// src/permissions.ts
|
|
1516
1906
|
var readSettings = (settingsPath) => {
|
|
1517
|
-
if (
|
|
1907
|
+
if (fs6.existsSync(settingsPath)) {
|
|
1518
1908
|
try {
|
|
1519
|
-
const content =
|
|
1909
|
+
const content = fs6.readFileSync(settingsPath, "utf-8");
|
|
1520
1910
|
return JSON.parse(content);
|
|
1521
1911
|
} catch {
|
|
1522
1912
|
console.warn(chalk3.yellow(`\u8B66\u544A: \u65E0\u6CD5\u89E3\u6790 ${settingsPath}\uFF0C\u5C06\u521B\u5EFA\u5907\u4EFD`));
|
|
1523
1913
|
const backupPath = settingsPath + ".error." + Date.now();
|
|
1524
|
-
|
|
1914
|
+
fs6.copyFileSync(settingsPath, backupPath);
|
|
1525
1915
|
console.log(chalk3.gray(`\u5907\u4EFD\u5DF2\u4FDD\u5B58\u5230: ${backupPath}`));
|
|
1526
1916
|
return {};
|
|
1527
1917
|
}
|
|
@@ -1530,7 +1920,7 @@ var readSettings = (settingsPath) => {
|
|
|
1530
1920
|
};
|
|
1531
1921
|
var writeSettings = (settingsPath, config3) => {
|
|
1532
1922
|
ensureClaudeDir();
|
|
1533
|
-
|
|
1923
|
+
fs6.writeFileSync(settingsPath, JSON.stringify(config3, null, 2) + "\n");
|
|
1534
1924
|
};
|
|
1535
1925
|
var mergePermissions = (existing, preset) => {
|
|
1536
1926
|
const existingAllow = existing.permissions?.allow || [];
|
|
@@ -1562,14 +1952,14 @@ var applyPermissionMode = (modeName) => {
|
|
|
1562
1952
|
};
|
|
1563
1953
|
var resetPermissions = () => {
|
|
1564
1954
|
const settingsPath = getSettingsPath(true);
|
|
1565
|
-
if (!
|
|
1955
|
+
if (!fs6.existsSync(settingsPath)) {
|
|
1566
1956
|
console.log(chalk3.yellow("\u6CA1\u6709\u81EA\u5B9A\u4E49\u6743\u9650\u914D\u7F6E\u9700\u8981\u91CD\u7F6E"));
|
|
1567
1957
|
return;
|
|
1568
1958
|
}
|
|
1569
1959
|
const config3 = readSettings(settingsPath);
|
|
1570
1960
|
delete config3.permissions;
|
|
1571
1961
|
if (Object.keys(config3).length === 0) {
|
|
1572
|
-
|
|
1962
|
+
fs6.unlinkSync(settingsPath);
|
|
1573
1963
|
console.log(chalk3.green("\u5DF2\u5220\u9664\u914D\u7F6E\u6587\u4EF6\uFF08\u6587\u4EF6\u4E3A\u7A7A\uFF09"));
|
|
1574
1964
|
} else {
|
|
1575
1965
|
writeSettings(settingsPath, config3);
|
|
@@ -1579,7 +1969,7 @@ var resetPermissions = () => {
|
|
|
1579
1969
|
};
|
|
1580
1970
|
var showCurrentMode = () => {
|
|
1581
1971
|
const settingsPath = getSettingsPath(true);
|
|
1582
|
-
if (!
|
|
1972
|
+
if (!fs6.existsSync(settingsPath)) {
|
|
1583
1973
|
console.log(chalk3.yellow("\u672A\u914D\u7F6E\u81EA\u5B9A\u4E49\u6743\u9650"));
|
|
1584
1974
|
console.log(chalk3.gray(`\u6587\u4EF6\u4E0D\u5B58\u5728: ${settingsPath}`));
|
|
1585
1975
|
return;
|
|
@@ -1629,24 +2019,24 @@ var listAvailableModes = () => {
|
|
|
1629
2019
|
console.log(chalk3.gray("\n\u4E34\u65F6\u6A21\u5F0F: ccem <mode>"));
|
|
1630
2020
|
console.log(chalk3.gray("\u6C38\u4E45\u6A21\u5F0F: ccem setup perms --<mode>"));
|
|
1631
2021
|
};
|
|
1632
|
-
var runWithTempPermissions = async (modeName, envConfig) => {
|
|
2022
|
+
var runWithTempPermissions = async (modeName, envConfig, envName) => {
|
|
1633
2023
|
const preset = PERMISSION_PRESETS[modeName];
|
|
1634
2024
|
if (!preset) {
|
|
1635
2025
|
console.error(chalk3.red(`\u672A\u77E5\u7684\u6743\u9650\u6A21\u5F0F: ${modeName}`));
|
|
1636
2026
|
console.log(chalk3.yellow("\u53EF\u7528\u6A21\u5F0F: " + getPermissionModeNames().join(", ")));
|
|
1637
2027
|
process.exit(1);
|
|
1638
2028
|
}
|
|
1639
|
-
await launchClaude({ envConfig, permMode: modeName });
|
|
2029
|
+
await launchClaude({ envConfig, envName, permMode: modeName });
|
|
1640
2030
|
};
|
|
1641
2031
|
|
|
1642
2032
|
// src/setup.ts
|
|
1643
|
-
import
|
|
2033
|
+
import fs7 from "fs";
|
|
1644
2034
|
import chalk4 from "chalk";
|
|
1645
2035
|
import { spawn as spawn2 } from "child_process";
|
|
1646
2036
|
var readJsonFile = (filePath) => {
|
|
1647
|
-
if (
|
|
2037
|
+
if (fs7.existsSync(filePath)) {
|
|
1648
2038
|
try {
|
|
1649
|
-
const content =
|
|
2039
|
+
const content = fs7.readFileSync(filePath, "utf-8");
|
|
1650
2040
|
return JSON.parse(content);
|
|
1651
2041
|
} catch {
|
|
1652
2042
|
console.warn(chalk4.yellow(`\u8B66\u544A: \u65E0\u6CD5\u89E3\u6790 ${filePath}`));
|
|
@@ -1656,7 +2046,7 @@ var readJsonFile = (filePath) => {
|
|
|
1656
2046
|
return {};
|
|
1657
2047
|
};
|
|
1658
2048
|
var writeJsonFile = (filePath, data) => {
|
|
1659
|
-
|
|
2049
|
+
fs7.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
|
|
1660
2050
|
};
|
|
1661
2051
|
var setupOnboarding = () => {
|
|
1662
2052
|
const configPath = getGlobalClaudeConfigPath();
|
|
@@ -1783,8 +2173,8 @@ var runSetupInit = async (options = {}) => {
|
|
|
1783
2173
|
|
|
1784
2174
|
// src/skills.ts
|
|
1785
2175
|
import { execSync } from "child_process";
|
|
1786
|
-
import * as
|
|
1787
|
-
import * as
|
|
2176
|
+
import * as fs8 from "fs";
|
|
2177
|
+
import * as path6 from "path";
|
|
1788
2178
|
import chalk5 from "chalk";
|
|
1789
2179
|
var SKILL_GROUPS = {
|
|
1790
2180
|
official: { label: "\u5B98\u65B9", icon: "\u{1F3E2}" },
|
|
@@ -1953,12 +2343,12 @@ function parseGitHubUrl(url) {
|
|
|
1953
2343
|
};
|
|
1954
2344
|
}
|
|
1955
2345
|
function getSkillsDir() {
|
|
1956
|
-
return
|
|
2346
|
+
return path6.join(process.cwd(), ".claude", "skills");
|
|
1957
2347
|
}
|
|
1958
2348
|
function ensureSkillsDir() {
|
|
1959
2349
|
const skillsDir = getSkillsDir();
|
|
1960
|
-
if (!
|
|
1961
|
-
|
|
2350
|
+
if (!fs8.existsSync(skillsDir)) {
|
|
2351
|
+
fs8.mkdirSync(skillsDir, { recursive: true });
|
|
1962
2352
|
} else {
|
|
1963
2353
|
cleanupTempDirs(skillsDir);
|
|
1964
2354
|
}
|
|
@@ -1966,11 +2356,11 @@ function ensureSkillsDir() {
|
|
|
1966
2356
|
}
|
|
1967
2357
|
function cleanupTempDirs(skillsDir) {
|
|
1968
2358
|
try {
|
|
1969
|
-
const entries =
|
|
2359
|
+
const entries = fs8.readdirSync(skillsDir, { withFileTypes: true });
|
|
1970
2360
|
for (const entry of entries) {
|
|
1971
2361
|
if (entry.isDirectory() && entry.name.startsWith(".tmp-")) {
|
|
1972
|
-
const tmpPath =
|
|
1973
|
-
|
|
2362
|
+
const tmpPath = path6.join(skillsDir, entry.name);
|
|
2363
|
+
fs8.rmSync(tmpPath, { recursive: true });
|
|
1974
2364
|
}
|
|
1975
2365
|
}
|
|
1976
2366
|
} catch {
|
|
@@ -1978,24 +2368,24 @@ function cleanupTempDirs(skillsDir) {
|
|
|
1978
2368
|
}
|
|
1979
2369
|
function downloadSkillWithGit(owner, repo, branch, repoPath, targetName) {
|
|
1980
2370
|
const skillsDir = ensureSkillsDir();
|
|
1981
|
-
const targetDir =
|
|
1982
|
-
if (
|
|
2371
|
+
const targetDir = path6.join(skillsDir, targetName);
|
|
2372
|
+
if (fs8.existsSync(targetDir)) {
|
|
1983
2373
|
console.log(chalk5.yellow(`Skill "${targetName}" already exists. Updating...`));
|
|
1984
|
-
|
|
2374
|
+
fs8.rmSync(targetDir, { recursive: true });
|
|
1985
2375
|
}
|
|
1986
2376
|
const repoUrl = `https://github.com/${owner}/${repo}.git`;
|
|
1987
|
-
const tempDir =
|
|
2377
|
+
const tempDir = path6.join(skillsDir, `.tmp-${Date.now()}`);
|
|
1988
2378
|
try {
|
|
1989
|
-
|
|
2379
|
+
fs8.mkdirSync(tempDir, { recursive: true });
|
|
1990
2380
|
execSync(`git init`, { cwd: tempDir, stdio: "pipe" });
|
|
1991
2381
|
execSync(`git remote add origin ${repoUrl}`, { cwd: tempDir, stdio: "pipe" });
|
|
1992
2382
|
execSync(`git config core.sparseCheckout true`, { cwd: tempDir, stdio: "pipe" });
|
|
1993
|
-
const sparseFile =
|
|
1994
|
-
|
|
2383
|
+
const sparseFile = path6.join(tempDir, ".git", "info", "sparse-checkout");
|
|
2384
|
+
fs8.writeFileSync(sparseFile, repoPath ? `${repoPath}/
|
|
1995
2385
|
` : "*\n");
|
|
1996
2386
|
execSync(`git pull --depth=1 origin ${branch}`, { cwd: tempDir, stdio: "pipe" });
|
|
1997
|
-
const sourceDir = repoPath ?
|
|
1998
|
-
if (!
|
|
2387
|
+
const sourceDir = repoPath ? path6.join(tempDir, repoPath) : tempDir;
|
|
2388
|
+
if (!fs8.existsSync(sourceDir)) {
|
|
1999
2389
|
throw new Error(`Path "${repoPath}" not found in repository`);
|
|
2000
2390
|
}
|
|
2001
2391
|
copyDir(sourceDir, targetDir);
|
|
@@ -2006,22 +2396,22 @@ function downloadSkillWithGit(owner, repo, branch, repoPath, targetName) {
|
|
|
2006
2396
|
console.error(chalk5.red(`Failed to download skill: ${errMsg}`));
|
|
2007
2397
|
return false;
|
|
2008
2398
|
} finally {
|
|
2009
|
-
if (
|
|
2010
|
-
|
|
2399
|
+
if (fs8.existsSync(tempDir)) {
|
|
2400
|
+
fs8.rmSync(tempDir, { recursive: true });
|
|
2011
2401
|
}
|
|
2012
2402
|
}
|
|
2013
2403
|
}
|
|
2014
2404
|
function copyDir(src, dest) {
|
|
2015
|
-
|
|
2016
|
-
const entries =
|
|
2405
|
+
fs8.mkdirSync(dest, { recursive: true });
|
|
2406
|
+
const entries = fs8.readdirSync(src, { withFileTypes: true });
|
|
2017
2407
|
for (const entry of entries) {
|
|
2018
2408
|
if (entry.name === ".git") continue;
|
|
2019
|
-
const srcPath =
|
|
2020
|
-
const destPath =
|
|
2409
|
+
const srcPath = path6.join(src, entry.name);
|
|
2410
|
+
const destPath = path6.join(dest, entry.name);
|
|
2021
2411
|
if (entry.isDirectory()) {
|
|
2022
2412
|
copyDir(srcPath, destPath);
|
|
2023
2413
|
} else {
|
|
2024
|
-
|
|
2414
|
+
fs8.copyFileSync(srcPath, destPath);
|
|
2025
2415
|
}
|
|
2026
2416
|
}
|
|
2027
2417
|
}
|
|
@@ -2067,7 +2457,7 @@ function addSkillFromGitHub(urlOrPreset) {
|
|
|
2067
2457
|
}
|
|
2068
2458
|
let skillName;
|
|
2069
2459
|
if (parsed.path) {
|
|
2070
|
-
skillName =
|
|
2460
|
+
skillName = path6.basename(parsed.path);
|
|
2071
2461
|
} else {
|
|
2072
2462
|
skillName = parsed.repo;
|
|
2073
2463
|
}
|
|
@@ -2081,23 +2471,23 @@ function addSkillFromGitHub(urlOrPreset) {
|
|
|
2081
2471
|
}
|
|
2082
2472
|
function listInstalledSkills() {
|
|
2083
2473
|
const skillsDir = getSkillsDir();
|
|
2084
|
-
if (!
|
|
2474
|
+
if (!fs8.existsSync(skillsDir)) {
|
|
2085
2475
|
return [];
|
|
2086
2476
|
}
|
|
2087
|
-
const entries =
|
|
2477
|
+
const entries = fs8.readdirSync(skillsDir, { withFileTypes: true });
|
|
2088
2478
|
return entries.filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => ({
|
|
2089
2479
|
name: entry.name,
|
|
2090
|
-
path:
|
|
2480
|
+
path: path6.join(skillsDir, entry.name)
|
|
2091
2481
|
}));
|
|
2092
2482
|
}
|
|
2093
2483
|
function removeSkill(name) {
|
|
2094
2484
|
const skillsDir = getSkillsDir();
|
|
2095
|
-
const targetDir =
|
|
2096
|
-
if (!
|
|
2485
|
+
const targetDir = path6.join(skillsDir, name);
|
|
2486
|
+
if (!fs8.existsSync(targetDir)) {
|
|
2097
2487
|
console.error(chalk5.red(`Skill "${name}" not found`));
|
|
2098
2488
|
return false;
|
|
2099
2489
|
}
|
|
2100
|
-
|
|
2490
|
+
fs8.rmSync(targetDir, { recursive: true });
|
|
2101
2491
|
console.log(chalk5.green(`Removed skill "${name}"`));
|
|
2102
2492
|
return true;
|
|
2103
2493
|
}
|
|
@@ -2511,9 +2901,9 @@ Replace \\\`TARGET_ID\\\` or \\\`TARGET_NAME\\\` with the user's selection.
|
|
|
2511
2901
|
|
|
2512
2902
|
// src/index.ts
|
|
2513
2903
|
var __filename2 = fileURLToPath2(import.meta.url);
|
|
2514
|
-
var __dirname2 =
|
|
2515
|
-
var pkgPath =
|
|
2516
|
-
var pkg = JSON.parse(
|
|
2904
|
+
var __dirname2 = path7.dirname(__filename2);
|
|
2905
|
+
var pkgPath = path7.resolve(__dirname2, "..", "package.json");
|
|
2906
|
+
var pkg = JSON.parse(fs9.readFileSync(pkgPath, "utf-8"));
|
|
2517
2907
|
var program = new Command();
|
|
2518
2908
|
var DEFAULT_OFFICIAL_ENV = {
|
|
2519
2909
|
ANTHROPIC_BASE_URL: "https://api.anthropic.com",
|
|
@@ -2577,11 +2967,11 @@ var recoverRegistriesFromLegacy = (registries) => {
|
|
|
2577
2967
|
return registries;
|
|
2578
2968
|
}
|
|
2579
2969
|
const legacyConfigPath = getLegacyConfigPath();
|
|
2580
|
-
if (!
|
|
2970
|
+
if (!fs9.existsSync(legacyConfigPath)) {
|
|
2581
2971
|
return registries;
|
|
2582
2972
|
}
|
|
2583
2973
|
try {
|
|
2584
|
-
const legacyRaw = JSON.parse(
|
|
2974
|
+
const legacyRaw = JSON.parse(fs9.readFileSync(legacyConfigPath, "utf-8"));
|
|
2585
2975
|
const legacyRegistries = legacyRaw.registries ?? {};
|
|
2586
2976
|
let changed = false;
|
|
2587
2977
|
const recovered = { ...registries };
|
|
@@ -2727,7 +3117,7 @@ PERMISSION_MODES.forEach((mode) => {
|
|
|
2727
3117
|
const registries = getRegistries();
|
|
2728
3118
|
const current = config2.get("current");
|
|
2729
3119
|
const envConfig = registries[current];
|
|
2730
|
-
await runWithTempPermissions(mode, envConfig);
|
|
3120
|
+
await runWithTempPermissions(mode, envConfig, current);
|
|
2731
3121
|
});
|
|
2732
3122
|
});
|
|
2733
3123
|
var showCurrentEnv = (usageStats2, usageLoading2) => {
|
|
@@ -2775,14 +3165,14 @@ var switchEnvironment = async (name) => {
|
|
|
2775
3165
|
exportCmds.forEach((cmd) => console.log(cmd));
|
|
2776
3166
|
}
|
|
2777
3167
|
};
|
|
2778
|
-
var getSessionsFilePath = () =>
|
|
2779
|
-
var getRuntimeStateFilePath = () =>
|
|
3168
|
+
var getSessionsFilePath = () => path7.join(getCcemConfigDir(), "sessions.json");
|
|
3169
|
+
var getRuntimeStateFilePath = () => path7.join(getCcemConfigDir(), "runtime-state.json");
|
|
2780
3170
|
var parseJsonFile = (filePath) => {
|
|
2781
|
-
if (!
|
|
3171
|
+
if (!fs9.existsSync(filePath)) {
|
|
2782
3172
|
return null;
|
|
2783
3173
|
}
|
|
2784
3174
|
try {
|
|
2785
|
-
return JSON.parse(
|
|
3175
|
+
return JSON.parse(fs9.readFileSync(filePath, "utf-8"));
|
|
2786
3176
|
} catch {
|
|
2787
3177
|
return null;
|
|
2788
3178
|
}
|
|
@@ -3104,12 +3494,12 @@ setupCmd.command("migrate").description("\u8FC1\u79FB\u65E7\u7248\u914D\u7F6E\u5
|
|
|
3104
3494
|
const newConfigPath = getCcemConfigPath();
|
|
3105
3495
|
const legacyConfigPath = getLegacyConfigPath();
|
|
3106
3496
|
console.log(chalk7.cyan("\n\u{1F504} \u914D\u7F6E\u8FC1\u79FB\n"));
|
|
3107
|
-
if (!
|
|
3497
|
+
if (!fs9.existsSync(legacyConfigPath)) {
|
|
3108
3498
|
console.log(chalk7.yellow("\u672A\u627E\u5230\u65E7\u7248\u914D\u7F6E\u6587\u4EF6"));
|
|
3109
3499
|
console.log(chalk7.gray(` \u65E7\u8DEF\u5F84: ${legacyConfigPath}`));
|
|
3110
3500
|
return;
|
|
3111
3501
|
}
|
|
3112
|
-
if (
|
|
3502
|
+
if (fs9.existsSync(newConfigPath) && !options.force) {
|
|
3113
3503
|
console.log(chalk7.green("\u2713 \u914D\u7F6E\u5DF2\u5728\u65B0\u8DEF\u5F84"));
|
|
3114
3504
|
console.log(chalk7.gray(` \u8DEF\u5F84: ${newConfigPath}`));
|
|
3115
3505
|
console.log(chalk7.gray("\n\u4F7F\u7528 --force \u5F3A\u5236\u91CD\u65B0\u8FC1\u79FB"));
|
|
@@ -3117,15 +3507,15 @@ setupCmd.command("migrate").description("\u8FC1\u79FB\u65E7\u7248\u914D\u7F6E\u5
|
|
|
3117
3507
|
}
|
|
3118
3508
|
try {
|
|
3119
3509
|
ensureCcemDir();
|
|
3120
|
-
|
|
3510
|
+
fs9.copyFileSync(legacyConfigPath, newConfigPath);
|
|
3121
3511
|
console.log(chalk7.green("\u2713 \u914D\u7F6E\u5DF2\u8FC1\u79FB"));
|
|
3122
3512
|
console.log(chalk7.gray(` \u4ECE: ${legacyConfigPath}`));
|
|
3123
3513
|
console.log(chalk7.gray(` \u5230: ${newConfigPath}`));
|
|
3124
3514
|
if (options.clean) {
|
|
3125
|
-
|
|
3126
|
-
const legacyDir =
|
|
3515
|
+
fs9.unlinkSync(legacyConfigPath);
|
|
3516
|
+
const legacyDir = path7.dirname(legacyConfigPath);
|
|
3127
3517
|
try {
|
|
3128
|
-
|
|
3518
|
+
fs9.rmdirSync(legacyDir);
|
|
3129
3519
|
} catch {
|
|
3130
3520
|
}
|
|
3131
3521
|
console.log(chalk7.green("\u2713 \u5DF2\u5220\u9664\u65E7\u914D\u7F6E\u6587\u4EF6"));
|
|
@@ -3136,13 +3526,13 @@ setupCmd.command("migrate").description("\u8FC1\u79FB\u65E7\u7248\u914D\u7F6E\u5
|
|
|
3136
3526
|
});
|
|
3137
3527
|
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() {
|
|
3138
3528
|
const options = this.opts();
|
|
3139
|
-
const skillDir =
|
|
3140
|
-
const targetPath =
|
|
3141
|
-
if (!
|
|
3142
|
-
|
|
3529
|
+
const skillDir = path7.join(process.env.HOME || "~", ".claude", "skills");
|
|
3530
|
+
const targetPath = path7.join(skillDir, "ccem-cron.md");
|
|
3531
|
+
if (!fs9.existsSync(skillDir)) {
|
|
3532
|
+
fs9.mkdirSync(skillDir, { recursive: true });
|
|
3143
3533
|
console.log(chalk7.gray(`\u521B\u5EFA\u76EE\u5F55: ${skillDir}`));
|
|
3144
3534
|
}
|
|
3145
|
-
if (
|
|
3535
|
+
if (fs9.existsSync(targetPath) && !options.force) {
|
|
3146
3536
|
const { overwrite } = await inquirer.prompt([
|
|
3147
3537
|
{
|
|
3148
3538
|
type: "confirm",
|
|
@@ -3156,7 +3546,7 @@ setupCmd.command("cron").description("\u5B89\u88C5 ccem-cron skill \u5230 Claude
|
|
|
3156
3546
|
return;
|
|
3157
3547
|
}
|
|
3158
3548
|
}
|
|
3159
|
-
|
|
3549
|
+
fs9.writeFileSync(targetPath, CCEM_CRON_SKILL_CONTENT, "utf-8");
|
|
3160
3550
|
console.log(chalk7.green(`\u2713 \u5DF2\u5B89\u88C5 ccem-cron skill`));
|
|
3161
3551
|
console.log(chalk7.gray(` \u8DEF\u5F84: ${targetPath}`));
|
|
3162
3552
|
console.log(chalk7.cyan(`
|
|
@@ -3238,6 +3628,7 @@ program.command("launch").description(false).option("--env <name>", "\u73AF\u588
|
|
|
3238
3628
|
const proxyBaseUrl = opts.proxyBaseUrl || opts.anthropicBaseUrl;
|
|
3239
3629
|
const launchEnvConfig = proxyBaseUrl ? { ...envConfig, ANTHROPIC_BASE_URL: proxyBaseUrl } : envConfig;
|
|
3240
3630
|
await launchClaude({
|
|
3631
|
+
envName,
|
|
3241
3632
|
envConfig: launchEnvConfig,
|
|
3242
3633
|
permMode: opts.perm,
|
|
3243
3634
|
workingDir: opts.workingDir,
|
|
@@ -3296,7 +3687,11 @@ program.action(async (options) => {
|
|
|
3296
3687
|
msg.error("No environment configuration found.");
|
|
3297
3688
|
process.exit(1);
|
|
3298
3689
|
}
|
|
3299
|
-
await launchClaude({
|
|
3690
|
+
await launchClaude({
|
|
3691
|
+
envName: current,
|
|
3692
|
+
envConfig,
|
|
3693
|
+
permMode: defaultMode || void 0
|
|
3694
|
+
});
|
|
3300
3695
|
return;
|
|
3301
3696
|
} else if (action === "usage") {
|
|
3302
3697
|
console.clear();
|
|
@@ -3421,7 +3816,7 @@ Editing environment '${result.name}'`));
|
|
|
3421
3816
|
}
|
|
3422
3817
|
]);
|
|
3423
3818
|
if (permMode !== "back") {
|
|
3424
|
-
await runWithTempPermissions(permMode, envConfig);
|
|
3819
|
+
await runWithTempPermissions(permMode, envConfig, current);
|
|
3425
3820
|
return;
|
|
3426
3821
|
}
|
|
3427
3822
|
} else if (action === "setDefault") {
|
package/model-prices.json
CHANGED
|
@@ -2159,6 +2159,18 @@
|
|
|
2159
2159
|
"input_cost_per_token": 2.8e-7,
|
|
2160
2160
|
"output_cost_per_token": 4e-7
|
|
2161
2161
|
},
|
|
2162
|
+
"deepseek/deepseek-v4-pro": {
|
|
2163
|
+
"input_cost_per_token": 1.66e-6,
|
|
2164
|
+
"output_cost_per_token": 3.31e-6,
|
|
2165
|
+
"cache_read_input_token_cost": 3.3e-7,
|
|
2166
|
+
"cache_creation_input_token_cost": 0
|
|
2167
|
+
},
|
|
2168
|
+
"deepseek/deepseek-v4-flash": {
|
|
2169
|
+
"input_cost_per_token": 1.38e-7,
|
|
2170
|
+
"output_cost_per_token": 2.76e-7,
|
|
2171
|
+
"cache_read_input_token_cost": 2.8e-8,
|
|
2172
|
+
"cache_creation_input_token_cost": 0
|
|
2173
|
+
},
|
|
2162
2174
|
"deepseek.v3-v1:0": {
|
|
2163
2175
|
"input_cost_per_token": 5.8e-7,
|
|
2164
2176
|
"output_cost_per_token": 0.00000168
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ccem",
|
|
3
|
-
"version": "2.0.0
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Claude Code Environment Manager",
|
|
6
6
|
"author": {
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"tsup": "^8.0.2",
|
|
37
37
|
"typescript": "^5.3.3",
|
|
38
38
|
"vitest": "^4.0.18",
|
|
39
|
-
"@ccem/core": "2.0.0
|
|
39
|
+
"@ccem/core": "2.0.0"
|
|
40
40
|
},
|
|
41
41
|
"scripts": {
|
|
42
42
|
"generate-logo": "bash scripts/generate-logo.sh",
|