ccem 2.0.0-beta.16 → 2.0.0-beta.18
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 +439 -81
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7,8 +7,8 @@ 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
14
|
// ../../packages/core/dist/chunk-DRN6L2YP.js
|
|
@@ -921,10 +921,10 @@ var renderLogoWithEnvPanel = (envName, env, defaultMode) => {
|
|
|
921
921
|
const parsed = new URL(url);
|
|
922
922
|
const protocol = parsed.protocol + "//";
|
|
923
923
|
const host = parsed.host;
|
|
924
|
-
const
|
|
924
|
+
const path8 = parsed.pathname + parsed.search;
|
|
925
925
|
const hostStart = host.slice(0, 8);
|
|
926
926
|
const hostEnd = host.slice(-4);
|
|
927
|
-
const pathPart =
|
|
927
|
+
const pathPart = path8.length > 10 ? path8.slice(0, 7) + "..." : path8;
|
|
928
928
|
return `${protocol}${hostStart}...${hostEnd}${pathPart}`;
|
|
929
929
|
} catch {
|
|
930
930
|
return truncate(url, max);
|
|
@@ -1362,7 +1362,7 @@ var selectEnvWithKeys = (registries, current) => {
|
|
|
1362
1362
|
};
|
|
1363
1363
|
|
|
1364
1364
|
// src/permissions.ts
|
|
1365
|
-
import
|
|
1365
|
+
import fs6 from "fs";
|
|
1366
1366
|
import chalk3 from "chalk";
|
|
1367
1367
|
import Table2 from "cli-table3";
|
|
1368
1368
|
|
|
@@ -1415,9 +1415,347 @@ var ensureGlobalClaudeDir = () => {
|
|
|
1415
1415
|
|
|
1416
1416
|
// src/launcher.ts
|
|
1417
1417
|
import { spawn } from "child_process";
|
|
1418
|
-
import * as
|
|
1419
|
-
import * as
|
|
1418
|
+
import * as fs5 from "fs";
|
|
1419
|
+
import * as path5 from "path";
|
|
1420
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
|
|
1421
1759
|
var MANAGED_CLAUDE_ENV_KEYS = [
|
|
1422
1760
|
"ANTHROPIC_BASE_URL",
|
|
1423
1761
|
"ANTHROPIC_AUTH_TOKEN",
|
|
@@ -1455,14 +1793,14 @@ function buildPermArgs(modeName) {
|
|
|
1455
1793
|
return args;
|
|
1456
1794
|
}
|
|
1457
1795
|
function ensureSessionsDir() {
|
|
1458
|
-
const dir =
|
|
1459
|
-
if (!
|
|
1460
|
-
|
|
1796
|
+
const dir = path5.join(ensureCcemDir(), "sessions");
|
|
1797
|
+
if (!fs5.existsSync(dir)) {
|
|
1798
|
+
fs5.mkdirSync(dir, { recursive: true });
|
|
1461
1799
|
}
|
|
1462
1800
|
return dir;
|
|
1463
1801
|
}
|
|
1464
1802
|
async function launchClaude(options) {
|
|
1465
|
-
const { envConfig, permMode, workingDir, sessionId, resumeSessionId, silent } = options;
|
|
1803
|
+
const { envName, envConfig, permMode, workingDir, sessionId, resumeSessionId, silent } = options;
|
|
1466
1804
|
const env = { ...process.env };
|
|
1467
1805
|
for (const key of MANAGED_CLAUDE_ENV_KEYS) {
|
|
1468
1806
|
delete env[key];
|
|
@@ -1489,22 +1827,36 @@ async function launchClaude(options) {
|
|
|
1489
1827
|
if (workingDir) {
|
|
1490
1828
|
process.chdir(workingDir);
|
|
1491
1829
|
}
|
|
1830
|
+
const effectiveWorkingDir = process.cwd();
|
|
1492
1831
|
if (!silent && !permMode) {
|
|
1493
1832
|
console.log(renderStarting());
|
|
1494
1833
|
}
|
|
1495
1834
|
const sessionsDir = ensureSessionsDir();
|
|
1496
1835
|
return new Promise((resolve2) => {
|
|
1836
|
+
let provenanceTracking = null;
|
|
1497
1837
|
const child = spawn("claude", args, {
|
|
1498
1838
|
stdio: "inherit",
|
|
1499
1839
|
shell: false,
|
|
1500
1840
|
// 直接执行二进制,避免 shell 注入风险
|
|
1501
1841
|
env
|
|
1502
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
|
+
});
|
|
1503
1854
|
child.on("exit", (code) => {
|
|
1855
|
+
provenanceTracking?.stop();
|
|
1504
1856
|
if (sessionId) {
|
|
1505
1857
|
try {
|
|
1506
|
-
|
|
1507
|
-
|
|
1858
|
+
fs5.writeFileSync(
|
|
1859
|
+
path5.join(sessionsDir, `${sessionId}.exit`),
|
|
1508
1860
|
String(code ?? 0)
|
|
1509
1861
|
);
|
|
1510
1862
|
} catch {
|
|
@@ -1513,6 +1865,7 @@ async function launchClaude(options) {
|
|
|
1513
1865
|
process.exit(code ?? 0);
|
|
1514
1866
|
});
|
|
1515
1867
|
child.on("error", (err) => {
|
|
1868
|
+
provenanceTracking?.stop();
|
|
1516
1869
|
if (err.code === "ENOENT") {
|
|
1517
1870
|
console.error("");
|
|
1518
1871
|
console.error(chalk2.red.bold("\u2718 \u672A\u627E\u5230 Claude Code"));
|
|
@@ -1537,14 +1890,14 @@ async function launchClaude(options) {
|
|
|
1537
1890
|
|
|
1538
1891
|
// src/permissions.ts
|
|
1539
1892
|
var readSettings = (settingsPath) => {
|
|
1540
|
-
if (
|
|
1893
|
+
if (fs6.existsSync(settingsPath)) {
|
|
1541
1894
|
try {
|
|
1542
|
-
const content =
|
|
1895
|
+
const content = fs6.readFileSync(settingsPath, "utf-8");
|
|
1543
1896
|
return JSON.parse(content);
|
|
1544
1897
|
} catch {
|
|
1545
1898
|
console.warn(chalk3.yellow(`\u8B66\u544A: \u65E0\u6CD5\u89E3\u6790 ${settingsPath}\uFF0C\u5C06\u521B\u5EFA\u5907\u4EFD`));
|
|
1546
1899
|
const backupPath = settingsPath + ".error." + Date.now();
|
|
1547
|
-
|
|
1900
|
+
fs6.copyFileSync(settingsPath, backupPath);
|
|
1548
1901
|
console.log(chalk3.gray(`\u5907\u4EFD\u5DF2\u4FDD\u5B58\u5230: ${backupPath}`));
|
|
1549
1902
|
return {};
|
|
1550
1903
|
}
|
|
@@ -1553,7 +1906,7 @@ var readSettings = (settingsPath) => {
|
|
|
1553
1906
|
};
|
|
1554
1907
|
var writeSettings = (settingsPath, config3) => {
|
|
1555
1908
|
ensureClaudeDir();
|
|
1556
|
-
|
|
1909
|
+
fs6.writeFileSync(settingsPath, JSON.stringify(config3, null, 2) + "\n");
|
|
1557
1910
|
};
|
|
1558
1911
|
var mergePermissions = (existing, preset) => {
|
|
1559
1912
|
const existingAllow = existing.permissions?.allow || [];
|
|
@@ -1585,14 +1938,14 @@ var applyPermissionMode = (modeName) => {
|
|
|
1585
1938
|
};
|
|
1586
1939
|
var resetPermissions = () => {
|
|
1587
1940
|
const settingsPath = getSettingsPath(true);
|
|
1588
|
-
if (!
|
|
1941
|
+
if (!fs6.existsSync(settingsPath)) {
|
|
1589
1942
|
console.log(chalk3.yellow("\u6CA1\u6709\u81EA\u5B9A\u4E49\u6743\u9650\u914D\u7F6E\u9700\u8981\u91CD\u7F6E"));
|
|
1590
1943
|
return;
|
|
1591
1944
|
}
|
|
1592
1945
|
const config3 = readSettings(settingsPath);
|
|
1593
1946
|
delete config3.permissions;
|
|
1594
1947
|
if (Object.keys(config3).length === 0) {
|
|
1595
|
-
|
|
1948
|
+
fs6.unlinkSync(settingsPath);
|
|
1596
1949
|
console.log(chalk3.green("\u5DF2\u5220\u9664\u914D\u7F6E\u6587\u4EF6\uFF08\u6587\u4EF6\u4E3A\u7A7A\uFF09"));
|
|
1597
1950
|
} else {
|
|
1598
1951
|
writeSettings(settingsPath, config3);
|
|
@@ -1602,7 +1955,7 @@ var resetPermissions = () => {
|
|
|
1602
1955
|
};
|
|
1603
1956
|
var showCurrentMode = () => {
|
|
1604
1957
|
const settingsPath = getSettingsPath(true);
|
|
1605
|
-
if (!
|
|
1958
|
+
if (!fs6.existsSync(settingsPath)) {
|
|
1606
1959
|
console.log(chalk3.yellow("\u672A\u914D\u7F6E\u81EA\u5B9A\u4E49\u6743\u9650"));
|
|
1607
1960
|
console.log(chalk3.gray(`\u6587\u4EF6\u4E0D\u5B58\u5728: ${settingsPath}`));
|
|
1608
1961
|
return;
|
|
@@ -1652,24 +2005,24 @@ var listAvailableModes = () => {
|
|
|
1652
2005
|
console.log(chalk3.gray("\n\u4E34\u65F6\u6A21\u5F0F: ccem <mode>"));
|
|
1653
2006
|
console.log(chalk3.gray("\u6C38\u4E45\u6A21\u5F0F: ccem setup perms --<mode>"));
|
|
1654
2007
|
};
|
|
1655
|
-
var runWithTempPermissions = async (modeName, envConfig) => {
|
|
2008
|
+
var runWithTempPermissions = async (modeName, envConfig, envName) => {
|
|
1656
2009
|
const preset = PERMISSION_PRESETS[modeName];
|
|
1657
2010
|
if (!preset) {
|
|
1658
2011
|
console.error(chalk3.red(`\u672A\u77E5\u7684\u6743\u9650\u6A21\u5F0F: ${modeName}`));
|
|
1659
2012
|
console.log(chalk3.yellow("\u53EF\u7528\u6A21\u5F0F: " + getPermissionModeNames().join(", ")));
|
|
1660
2013
|
process.exit(1);
|
|
1661
2014
|
}
|
|
1662
|
-
await launchClaude({ envConfig, permMode: modeName });
|
|
2015
|
+
await launchClaude({ envConfig, envName, permMode: modeName });
|
|
1663
2016
|
};
|
|
1664
2017
|
|
|
1665
2018
|
// src/setup.ts
|
|
1666
|
-
import
|
|
2019
|
+
import fs7 from "fs";
|
|
1667
2020
|
import chalk4 from "chalk";
|
|
1668
2021
|
import { spawn as spawn2 } from "child_process";
|
|
1669
2022
|
var readJsonFile = (filePath) => {
|
|
1670
|
-
if (
|
|
2023
|
+
if (fs7.existsSync(filePath)) {
|
|
1671
2024
|
try {
|
|
1672
|
-
const content =
|
|
2025
|
+
const content = fs7.readFileSync(filePath, "utf-8");
|
|
1673
2026
|
return JSON.parse(content);
|
|
1674
2027
|
} catch {
|
|
1675
2028
|
console.warn(chalk4.yellow(`\u8B66\u544A: \u65E0\u6CD5\u89E3\u6790 ${filePath}`));
|
|
@@ -1679,7 +2032,7 @@ var readJsonFile = (filePath) => {
|
|
|
1679
2032
|
return {};
|
|
1680
2033
|
};
|
|
1681
2034
|
var writeJsonFile = (filePath, data) => {
|
|
1682
|
-
|
|
2035
|
+
fs7.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
|
|
1683
2036
|
};
|
|
1684
2037
|
var setupOnboarding = () => {
|
|
1685
2038
|
const configPath = getGlobalClaudeConfigPath();
|
|
@@ -1806,8 +2159,8 @@ var runSetupInit = async (options = {}) => {
|
|
|
1806
2159
|
|
|
1807
2160
|
// src/skills.ts
|
|
1808
2161
|
import { execSync } from "child_process";
|
|
1809
|
-
import * as
|
|
1810
|
-
import * as
|
|
2162
|
+
import * as fs8 from "fs";
|
|
2163
|
+
import * as path6 from "path";
|
|
1811
2164
|
import chalk5 from "chalk";
|
|
1812
2165
|
var SKILL_GROUPS = {
|
|
1813
2166
|
official: { label: "\u5B98\u65B9", icon: "\u{1F3E2}" },
|
|
@@ -1976,12 +2329,12 @@ function parseGitHubUrl(url) {
|
|
|
1976
2329
|
};
|
|
1977
2330
|
}
|
|
1978
2331
|
function getSkillsDir() {
|
|
1979
|
-
return
|
|
2332
|
+
return path6.join(process.cwd(), ".claude", "skills");
|
|
1980
2333
|
}
|
|
1981
2334
|
function ensureSkillsDir() {
|
|
1982
2335
|
const skillsDir = getSkillsDir();
|
|
1983
|
-
if (!
|
|
1984
|
-
|
|
2336
|
+
if (!fs8.existsSync(skillsDir)) {
|
|
2337
|
+
fs8.mkdirSync(skillsDir, { recursive: true });
|
|
1985
2338
|
} else {
|
|
1986
2339
|
cleanupTempDirs(skillsDir);
|
|
1987
2340
|
}
|
|
@@ -1989,11 +2342,11 @@ function ensureSkillsDir() {
|
|
|
1989
2342
|
}
|
|
1990
2343
|
function cleanupTempDirs(skillsDir) {
|
|
1991
2344
|
try {
|
|
1992
|
-
const entries =
|
|
2345
|
+
const entries = fs8.readdirSync(skillsDir, { withFileTypes: true });
|
|
1993
2346
|
for (const entry of entries) {
|
|
1994
2347
|
if (entry.isDirectory() && entry.name.startsWith(".tmp-")) {
|
|
1995
|
-
const tmpPath =
|
|
1996
|
-
|
|
2348
|
+
const tmpPath = path6.join(skillsDir, entry.name);
|
|
2349
|
+
fs8.rmSync(tmpPath, { recursive: true });
|
|
1997
2350
|
}
|
|
1998
2351
|
}
|
|
1999
2352
|
} catch {
|
|
@@ -2001,24 +2354,24 @@ function cleanupTempDirs(skillsDir) {
|
|
|
2001
2354
|
}
|
|
2002
2355
|
function downloadSkillWithGit(owner, repo, branch, repoPath, targetName) {
|
|
2003
2356
|
const skillsDir = ensureSkillsDir();
|
|
2004
|
-
const targetDir =
|
|
2005
|
-
if (
|
|
2357
|
+
const targetDir = path6.join(skillsDir, targetName);
|
|
2358
|
+
if (fs8.existsSync(targetDir)) {
|
|
2006
2359
|
console.log(chalk5.yellow(`Skill "${targetName}" already exists. Updating...`));
|
|
2007
|
-
|
|
2360
|
+
fs8.rmSync(targetDir, { recursive: true });
|
|
2008
2361
|
}
|
|
2009
2362
|
const repoUrl = `https://github.com/${owner}/${repo}.git`;
|
|
2010
|
-
const tempDir =
|
|
2363
|
+
const tempDir = path6.join(skillsDir, `.tmp-${Date.now()}`);
|
|
2011
2364
|
try {
|
|
2012
|
-
|
|
2365
|
+
fs8.mkdirSync(tempDir, { recursive: true });
|
|
2013
2366
|
execSync(`git init`, { cwd: tempDir, stdio: "pipe" });
|
|
2014
2367
|
execSync(`git remote add origin ${repoUrl}`, { cwd: tempDir, stdio: "pipe" });
|
|
2015
2368
|
execSync(`git config core.sparseCheckout true`, { cwd: tempDir, stdio: "pipe" });
|
|
2016
|
-
const sparseFile =
|
|
2017
|
-
|
|
2369
|
+
const sparseFile = path6.join(tempDir, ".git", "info", "sparse-checkout");
|
|
2370
|
+
fs8.writeFileSync(sparseFile, repoPath ? `${repoPath}/
|
|
2018
2371
|
` : "*\n");
|
|
2019
2372
|
execSync(`git pull --depth=1 origin ${branch}`, { cwd: tempDir, stdio: "pipe" });
|
|
2020
|
-
const sourceDir = repoPath ?
|
|
2021
|
-
if (!
|
|
2373
|
+
const sourceDir = repoPath ? path6.join(tempDir, repoPath) : tempDir;
|
|
2374
|
+
if (!fs8.existsSync(sourceDir)) {
|
|
2022
2375
|
throw new Error(`Path "${repoPath}" not found in repository`);
|
|
2023
2376
|
}
|
|
2024
2377
|
copyDir(sourceDir, targetDir);
|
|
@@ -2029,22 +2382,22 @@ function downloadSkillWithGit(owner, repo, branch, repoPath, targetName) {
|
|
|
2029
2382
|
console.error(chalk5.red(`Failed to download skill: ${errMsg}`));
|
|
2030
2383
|
return false;
|
|
2031
2384
|
} finally {
|
|
2032
|
-
if (
|
|
2033
|
-
|
|
2385
|
+
if (fs8.existsSync(tempDir)) {
|
|
2386
|
+
fs8.rmSync(tempDir, { recursive: true });
|
|
2034
2387
|
}
|
|
2035
2388
|
}
|
|
2036
2389
|
}
|
|
2037
2390
|
function copyDir(src, dest) {
|
|
2038
|
-
|
|
2039
|
-
const entries =
|
|
2391
|
+
fs8.mkdirSync(dest, { recursive: true });
|
|
2392
|
+
const entries = fs8.readdirSync(src, { withFileTypes: true });
|
|
2040
2393
|
for (const entry of entries) {
|
|
2041
2394
|
if (entry.name === ".git") continue;
|
|
2042
|
-
const srcPath =
|
|
2043
|
-
const destPath =
|
|
2395
|
+
const srcPath = path6.join(src, entry.name);
|
|
2396
|
+
const destPath = path6.join(dest, entry.name);
|
|
2044
2397
|
if (entry.isDirectory()) {
|
|
2045
2398
|
copyDir(srcPath, destPath);
|
|
2046
2399
|
} else {
|
|
2047
|
-
|
|
2400
|
+
fs8.copyFileSync(srcPath, destPath);
|
|
2048
2401
|
}
|
|
2049
2402
|
}
|
|
2050
2403
|
}
|
|
@@ -2090,7 +2443,7 @@ function addSkillFromGitHub(urlOrPreset) {
|
|
|
2090
2443
|
}
|
|
2091
2444
|
let skillName;
|
|
2092
2445
|
if (parsed.path) {
|
|
2093
|
-
skillName =
|
|
2446
|
+
skillName = path6.basename(parsed.path);
|
|
2094
2447
|
} else {
|
|
2095
2448
|
skillName = parsed.repo;
|
|
2096
2449
|
}
|
|
@@ -2104,23 +2457,23 @@ function addSkillFromGitHub(urlOrPreset) {
|
|
|
2104
2457
|
}
|
|
2105
2458
|
function listInstalledSkills() {
|
|
2106
2459
|
const skillsDir = getSkillsDir();
|
|
2107
|
-
if (!
|
|
2460
|
+
if (!fs8.existsSync(skillsDir)) {
|
|
2108
2461
|
return [];
|
|
2109
2462
|
}
|
|
2110
|
-
const entries =
|
|
2463
|
+
const entries = fs8.readdirSync(skillsDir, { withFileTypes: true });
|
|
2111
2464
|
return entries.filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => ({
|
|
2112
2465
|
name: entry.name,
|
|
2113
|
-
path:
|
|
2466
|
+
path: path6.join(skillsDir, entry.name)
|
|
2114
2467
|
}));
|
|
2115
2468
|
}
|
|
2116
2469
|
function removeSkill(name) {
|
|
2117
2470
|
const skillsDir = getSkillsDir();
|
|
2118
|
-
const targetDir =
|
|
2119
|
-
if (!
|
|
2471
|
+
const targetDir = path6.join(skillsDir, name);
|
|
2472
|
+
if (!fs8.existsSync(targetDir)) {
|
|
2120
2473
|
console.error(chalk5.red(`Skill "${name}" not found`));
|
|
2121
2474
|
return false;
|
|
2122
2475
|
}
|
|
2123
|
-
|
|
2476
|
+
fs8.rmSync(targetDir, { recursive: true });
|
|
2124
2477
|
console.log(chalk5.green(`Removed skill "${name}"`));
|
|
2125
2478
|
return true;
|
|
2126
2479
|
}
|
|
@@ -2534,9 +2887,9 @@ Replace \\\`TARGET_ID\\\` or \\\`TARGET_NAME\\\` with the user's selection.
|
|
|
2534
2887
|
|
|
2535
2888
|
// src/index.ts
|
|
2536
2889
|
var __filename2 = fileURLToPath2(import.meta.url);
|
|
2537
|
-
var __dirname2 =
|
|
2538
|
-
var pkgPath =
|
|
2539
|
-
var pkg = JSON.parse(
|
|
2890
|
+
var __dirname2 = path7.dirname(__filename2);
|
|
2891
|
+
var pkgPath = path7.resolve(__dirname2, "..", "package.json");
|
|
2892
|
+
var pkg = JSON.parse(fs9.readFileSync(pkgPath, "utf-8"));
|
|
2540
2893
|
var program = new Command();
|
|
2541
2894
|
var DEFAULT_OFFICIAL_ENV = {
|
|
2542
2895
|
ANTHROPIC_BASE_URL: "https://api.anthropic.com",
|
|
@@ -2600,11 +2953,11 @@ var recoverRegistriesFromLegacy = (registries) => {
|
|
|
2600
2953
|
return registries;
|
|
2601
2954
|
}
|
|
2602
2955
|
const legacyConfigPath = getLegacyConfigPath();
|
|
2603
|
-
if (!
|
|
2956
|
+
if (!fs9.existsSync(legacyConfigPath)) {
|
|
2604
2957
|
return registries;
|
|
2605
2958
|
}
|
|
2606
2959
|
try {
|
|
2607
|
-
const legacyRaw = JSON.parse(
|
|
2960
|
+
const legacyRaw = JSON.parse(fs9.readFileSync(legacyConfigPath, "utf-8"));
|
|
2608
2961
|
const legacyRegistries = legacyRaw.registries ?? {};
|
|
2609
2962
|
let changed = false;
|
|
2610
2963
|
const recovered = { ...registries };
|
|
@@ -2750,7 +3103,7 @@ PERMISSION_MODES.forEach((mode) => {
|
|
|
2750
3103
|
const registries = getRegistries();
|
|
2751
3104
|
const current = config2.get("current");
|
|
2752
3105
|
const envConfig = registries[current];
|
|
2753
|
-
await runWithTempPermissions(mode, envConfig);
|
|
3106
|
+
await runWithTempPermissions(mode, envConfig, current);
|
|
2754
3107
|
});
|
|
2755
3108
|
});
|
|
2756
3109
|
var showCurrentEnv = (usageStats2, usageLoading2) => {
|
|
@@ -2798,14 +3151,14 @@ var switchEnvironment = async (name) => {
|
|
|
2798
3151
|
exportCmds.forEach((cmd) => console.log(cmd));
|
|
2799
3152
|
}
|
|
2800
3153
|
};
|
|
2801
|
-
var getSessionsFilePath = () =>
|
|
2802
|
-
var getRuntimeStateFilePath = () =>
|
|
3154
|
+
var getSessionsFilePath = () => path7.join(getCcemConfigDir(), "sessions.json");
|
|
3155
|
+
var getRuntimeStateFilePath = () => path7.join(getCcemConfigDir(), "runtime-state.json");
|
|
2803
3156
|
var parseJsonFile = (filePath) => {
|
|
2804
|
-
if (!
|
|
3157
|
+
if (!fs9.existsSync(filePath)) {
|
|
2805
3158
|
return null;
|
|
2806
3159
|
}
|
|
2807
3160
|
try {
|
|
2808
|
-
return JSON.parse(
|
|
3161
|
+
return JSON.parse(fs9.readFileSync(filePath, "utf-8"));
|
|
2809
3162
|
} catch {
|
|
2810
3163
|
return null;
|
|
2811
3164
|
}
|
|
@@ -3127,12 +3480,12 @@ setupCmd.command("migrate").description("\u8FC1\u79FB\u65E7\u7248\u914D\u7F6E\u5
|
|
|
3127
3480
|
const newConfigPath = getCcemConfigPath();
|
|
3128
3481
|
const legacyConfigPath = getLegacyConfigPath();
|
|
3129
3482
|
console.log(chalk7.cyan("\n\u{1F504} \u914D\u7F6E\u8FC1\u79FB\n"));
|
|
3130
|
-
if (!
|
|
3483
|
+
if (!fs9.existsSync(legacyConfigPath)) {
|
|
3131
3484
|
console.log(chalk7.yellow("\u672A\u627E\u5230\u65E7\u7248\u914D\u7F6E\u6587\u4EF6"));
|
|
3132
3485
|
console.log(chalk7.gray(` \u65E7\u8DEF\u5F84: ${legacyConfigPath}`));
|
|
3133
3486
|
return;
|
|
3134
3487
|
}
|
|
3135
|
-
if (
|
|
3488
|
+
if (fs9.existsSync(newConfigPath) && !options.force) {
|
|
3136
3489
|
console.log(chalk7.green("\u2713 \u914D\u7F6E\u5DF2\u5728\u65B0\u8DEF\u5F84"));
|
|
3137
3490
|
console.log(chalk7.gray(` \u8DEF\u5F84: ${newConfigPath}`));
|
|
3138
3491
|
console.log(chalk7.gray("\n\u4F7F\u7528 --force \u5F3A\u5236\u91CD\u65B0\u8FC1\u79FB"));
|
|
@@ -3140,15 +3493,15 @@ setupCmd.command("migrate").description("\u8FC1\u79FB\u65E7\u7248\u914D\u7F6E\u5
|
|
|
3140
3493
|
}
|
|
3141
3494
|
try {
|
|
3142
3495
|
ensureCcemDir();
|
|
3143
|
-
|
|
3496
|
+
fs9.copyFileSync(legacyConfigPath, newConfigPath);
|
|
3144
3497
|
console.log(chalk7.green("\u2713 \u914D\u7F6E\u5DF2\u8FC1\u79FB"));
|
|
3145
3498
|
console.log(chalk7.gray(` \u4ECE: ${legacyConfigPath}`));
|
|
3146
3499
|
console.log(chalk7.gray(` \u5230: ${newConfigPath}`));
|
|
3147
3500
|
if (options.clean) {
|
|
3148
|
-
|
|
3149
|
-
const legacyDir =
|
|
3501
|
+
fs9.unlinkSync(legacyConfigPath);
|
|
3502
|
+
const legacyDir = path7.dirname(legacyConfigPath);
|
|
3150
3503
|
try {
|
|
3151
|
-
|
|
3504
|
+
fs9.rmdirSync(legacyDir);
|
|
3152
3505
|
} catch {
|
|
3153
3506
|
}
|
|
3154
3507
|
console.log(chalk7.green("\u2713 \u5DF2\u5220\u9664\u65E7\u914D\u7F6E\u6587\u4EF6"));
|
|
@@ -3159,13 +3512,13 @@ setupCmd.command("migrate").description("\u8FC1\u79FB\u65E7\u7248\u914D\u7F6E\u5
|
|
|
3159
3512
|
});
|
|
3160
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() {
|
|
3161
3514
|
const options = this.opts();
|
|
3162
|
-
const skillDir =
|
|
3163
|
-
const targetPath =
|
|
3164
|
-
if (!
|
|
3165
|
-
|
|
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 });
|
|
3166
3519
|
console.log(chalk7.gray(`\u521B\u5EFA\u76EE\u5F55: ${skillDir}`));
|
|
3167
3520
|
}
|
|
3168
|
-
if (
|
|
3521
|
+
if (fs9.existsSync(targetPath) && !options.force) {
|
|
3169
3522
|
const { overwrite } = await inquirer.prompt([
|
|
3170
3523
|
{
|
|
3171
3524
|
type: "confirm",
|
|
@@ -3179,7 +3532,7 @@ setupCmd.command("cron").description("\u5B89\u88C5 ccem-cron skill \u5230 Claude
|
|
|
3179
3532
|
return;
|
|
3180
3533
|
}
|
|
3181
3534
|
}
|
|
3182
|
-
|
|
3535
|
+
fs9.writeFileSync(targetPath, CCEM_CRON_SKILL_CONTENT, "utf-8");
|
|
3183
3536
|
console.log(chalk7.green(`\u2713 \u5DF2\u5B89\u88C5 ccem-cron skill`));
|
|
3184
3537
|
console.log(chalk7.gray(` \u8DEF\u5F84: ${targetPath}`));
|
|
3185
3538
|
console.log(chalk7.cyan(`
|
|
@@ -3261,6 +3614,7 @@ program.command("launch").description(false).option("--env <name>", "\u73AF\u588
|
|
|
3261
3614
|
const proxyBaseUrl = opts.proxyBaseUrl || opts.anthropicBaseUrl;
|
|
3262
3615
|
const launchEnvConfig = proxyBaseUrl ? { ...envConfig, ANTHROPIC_BASE_URL: proxyBaseUrl } : envConfig;
|
|
3263
3616
|
await launchClaude({
|
|
3617
|
+
envName,
|
|
3264
3618
|
envConfig: launchEnvConfig,
|
|
3265
3619
|
permMode: opts.perm,
|
|
3266
3620
|
workingDir: opts.workingDir,
|
|
@@ -3319,7 +3673,11 @@ program.action(async (options) => {
|
|
|
3319
3673
|
msg.error("No environment configuration found.");
|
|
3320
3674
|
process.exit(1);
|
|
3321
3675
|
}
|
|
3322
|
-
await launchClaude({
|
|
3676
|
+
await launchClaude({
|
|
3677
|
+
envName: current,
|
|
3678
|
+
envConfig,
|
|
3679
|
+
permMode: defaultMode || void 0
|
|
3680
|
+
});
|
|
3323
3681
|
return;
|
|
3324
3682
|
} else if (action === "usage") {
|
|
3325
3683
|
console.clear();
|
|
@@ -3444,7 +3802,7 @@ Editing environment '${result.name}'`));
|
|
|
3444
3802
|
}
|
|
3445
3803
|
]);
|
|
3446
3804
|
if (permMode !== "back") {
|
|
3447
|
-
await runWithTempPermissions(permMode, envConfig);
|
|
3805
|
+
await runWithTempPermissions(permMode, envConfig, current);
|
|
3448
3806
|
return;
|
|
3449
3807
|
}
|
|
3450
3808
|
} else if (action === "setDefault") {
|