@ekkos/cli 1.2.16 → 1.2.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/commands/run.js +57 -27
- package/package.json +1 -1
package/dist/commands/run.js
CHANGED
|
@@ -469,13 +469,12 @@ function getEkkosEnv() {
|
|
|
469
469
|
env.EKKOS_PROXY_MODE = '1';
|
|
470
470
|
// Enable ultra-minimal mode by default (30%→20% eviction for constant-cost infinite context)
|
|
471
471
|
env.EKKOS_ULTRA_MINIMAL = '1';
|
|
472
|
-
//
|
|
473
|
-
//
|
|
472
|
+
// Generate a deterministic word-triple session name from a fresh UUID.
|
|
473
|
+
// This eliminates _pending-* placeholders — proxy sees a real name from request #1.
|
|
474
474
|
if (!cliSessionName) {
|
|
475
|
-
|
|
476
|
-
cliSessionName =
|
|
477
|
-
|
|
478
|
-
console.log(chalk_1.default.gray(` 📂 Session: pending (will bind to Claude session)`));
|
|
475
|
+
cliSessionId = crypto.randomUUID();
|
|
476
|
+
cliSessionName = (0, state_1.uuidToWords)(cliSessionId);
|
|
477
|
+
console.log(chalk_1.default.gray(` 📂 Session: ${cliSessionName}`));
|
|
479
478
|
}
|
|
480
479
|
// Get full userId from config (NOT the truncated version from auth token)
|
|
481
480
|
// Config has full UUID like "d4532ba0-0a86-42ce-bab4-22aa62b55ce6"
|
|
@@ -499,7 +498,7 @@ function getEkkosEnv() {
|
|
|
499
498
|
// Project path is base64-encoded to handle special chars safely
|
|
500
499
|
const projectPath = process.cwd();
|
|
501
500
|
const projectPathEncoded = Buffer.from(projectPath).toString('base64url');
|
|
502
|
-
const proxyUrl = `${EKKOS_PROXY_URL}/proxy/${encodeURIComponent(userId)}/${encodeURIComponent(cliSessionName)}?project=${projectPathEncoded}`;
|
|
501
|
+
const proxyUrl = `${EKKOS_PROXY_URL}/proxy/${encodeURIComponent(userId)}/${encodeURIComponent(cliSessionName)}?project=${projectPathEncoded}&sid=${encodeURIComponent(cliSessionId)}`;
|
|
503
502
|
env.ANTHROPIC_BASE_URL = proxyUrl;
|
|
504
503
|
// Proxy URL contains userId + project path — don't leak to terminal
|
|
505
504
|
}
|
|
@@ -1352,7 +1351,7 @@ async function run(options) {
|
|
|
1352
1351
|
}
|
|
1353
1352
|
// Track state — only use explicit -s option; never inherit stale session from state.json
|
|
1354
1353
|
// The real session name will be detected from Claude Code's output (line ~2552)
|
|
1355
|
-
let currentSession = options.session || null;
|
|
1354
|
+
let currentSession = options.session || cliSessionName || null;
|
|
1356
1355
|
// Write initial instance file
|
|
1357
1356
|
const startedAt = new Date().toISOString();
|
|
1358
1357
|
writeInstanceFile(instanceId, {
|
|
@@ -1419,18 +1418,50 @@ async function run(options) {
|
|
|
1419
1418
|
// Claude creates the transcript file BEFORE outputting the session name
|
|
1420
1419
|
// So we watch for new files rather than parsing TUI output (which is slower)
|
|
1421
1420
|
// ════════════════════════════════════════════════════════════════════════════
|
|
1422
|
-
const
|
|
1423
|
-
const
|
|
1421
|
+
const projectPath = process.cwd();
|
|
1422
|
+
const projectsRoot = path.join(os.homedir(), '.claude', 'projects');
|
|
1423
|
+
const projectDirCandidates = (() => {
|
|
1424
|
+
// Claude's project-dir encoding is platform/version dependent.
|
|
1425
|
+
// Probe a small set of known-safe variants to avoid missing the session file.
|
|
1426
|
+
const encodings = new Set([
|
|
1427
|
+
projectPath.replace(/\//g, '-'),
|
|
1428
|
+
projectPath.replace(/[\\/]/g, '-'),
|
|
1429
|
+
projectPath.replace(/[:\\/]/g, '-'),
|
|
1430
|
+
`-${projectPath.replace(/[:\\/]/g, '-').replace(/^-+/, '')}`,
|
|
1431
|
+
projectPath.replace(/[^a-zA-Z0-9]/g, '-'),
|
|
1432
|
+
`-${projectPath.replace(/^[\\/]+/, '').replace(/[^a-zA-Z0-9]/g, '-')}`,
|
|
1433
|
+
]);
|
|
1434
|
+
return [...encodings]
|
|
1435
|
+
.filter(Boolean)
|
|
1436
|
+
.map(encoded => path.join(projectsRoot, encoded));
|
|
1437
|
+
})();
|
|
1424
1438
|
const launchTime = Date.now();
|
|
1439
|
+
function listCandidateJsonlFiles() {
|
|
1440
|
+
const jsonlFiles = [];
|
|
1441
|
+
for (const candidateDir of projectDirCandidates) {
|
|
1442
|
+
if (!fs.existsSync(candidateDir))
|
|
1443
|
+
continue;
|
|
1444
|
+
try {
|
|
1445
|
+
for (const file of fs.readdirSync(candidateDir)) {
|
|
1446
|
+
if (file.endsWith('.jsonl')) {
|
|
1447
|
+
jsonlFiles.push(path.join(candidateDir, file));
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
catch {
|
|
1452
|
+
// Ignore candidate-dir read errors and keep scanning others.
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
return jsonlFiles;
|
|
1456
|
+
}
|
|
1425
1457
|
// Track existing jsonl files at startup
|
|
1426
1458
|
let existingJsonlFiles = new Set();
|
|
1427
1459
|
try {
|
|
1428
|
-
|
|
1429
|
-
existingJsonlFiles = new Set(files.filter(f => f.endsWith('.jsonl')));
|
|
1460
|
+
existingJsonlFiles = new Set(listCandidateJsonlFiles());
|
|
1430
1461
|
dlog(`[TRANSCRIPT] Found ${existingJsonlFiles.size} existing jsonl files at startup`);
|
|
1431
1462
|
}
|
|
1432
1463
|
catch {
|
|
1433
|
-
dlog('[TRANSCRIPT]
|
|
1464
|
+
dlog('[TRANSCRIPT] No candidate project dir exists yet');
|
|
1434
1465
|
}
|
|
1435
1466
|
// Poll for new transcript file every 500ms for up to 30 seconds.
|
|
1436
1467
|
// Safety rule: do NOT guess using "most recent" files; that can cross-bind sessions.
|
|
@@ -1450,13 +1481,11 @@ async function run(options) {
|
|
|
1450
1481
|
// In proxy mode this is intentionally disabled to avoid cross-session mixing.
|
|
1451
1482
|
if (!proxyModeEnabled && !transcriptPath) {
|
|
1452
1483
|
try {
|
|
1453
|
-
const
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
path: path.join(projectDir, f),
|
|
1459
|
-
mtime: fs.statSync(path.join(projectDir, f)).mtimeMs
|
|
1484
|
+
const jsonlFiles = listCandidateJsonlFiles()
|
|
1485
|
+
.map(fullPath => ({
|
|
1486
|
+
name: path.basename(fullPath),
|
|
1487
|
+
path: fullPath,
|
|
1488
|
+
mtime: fs.statSync(fullPath).mtimeMs
|
|
1460
1489
|
}))
|
|
1461
1490
|
.sort((a, b) => b.mtime - a.mtime);
|
|
1462
1491
|
if (jsonlFiles.length > 0) {
|
|
@@ -1486,14 +1515,13 @@ async function run(options) {
|
|
|
1486
1515
|
return;
|
|
1487
1516
|
}
|
|
1488
1517
|
try {
|
|
1489
|
-
const
|
|
1490
|
-
const jsonlFiles = currentFiles.filter(f => f.endsWith('.jsonl'));
|
|
1518
|
+
const jsonlFiles = listCandidateJsonlFiles();
|
|
1491
1519
|
// Find NEW files (created after we started)
|
|
1492
1520
|
for (const file of jsonlFiles) {
|
|
1493
1521
|
if (!existingJsonlFiles.has(file)) {
|
|
1494
1522
|
// New file! This is our transcript
|
|
1495
|
-
const fullPath =
|
|
1496
|
-
const sessionId = file.replace('.jsonl', '');
|
|
1523
|
+
const fullPath = file;
|
|
1524
|
+
const sessionId = path.basename(file).replace('.jsonl', '');
|
|
1497
1525
|
transcriptPath = fullPath;
|
|
1498
1526
|
currentSessionId = sessionId;
|
|
1499
1527
|
currentSession = (0, state_1.uuidToWords)(sessionId);
|
|
@@ -1608,8 +1636,10 @@ async function run(options) {
|
|
|
1608
1636
|
function resolveTranscriptFromSessionId(source) {
|
|
1609
1637
|
if (!currentSessionId || transcriptPath)
|
|
1610
1638
|
return;
|
|
1611
|
-
const candidate =
|
|
1612
|
-
|
|
1639
|
+
const candidate = projectDirCandidates
|
|
1640
|
+
.map(projectDir => path.join(projectDir, `${currentSessionId}.jsonl`))
|
|
1641
|
+
.find(fullPath => fs.existsSync(fullPath));
|
|
1642
|
+
if (!candidate)
|
|
1613
1643
|
return;
|
|
1614
1644
|
transcriptPath = candidate;
|
|
1615
1645
|
evictionDebugLog('TRANSCRIPT_SET', `Set from session ID (${source})`, {
|
|
@@ -1626,7 +1656,7 @@ async function run(options) {
|
|
|
1626
1656
|
return;
|
|
1627
1657
|
if (boundProxySession === sessionName || bindingSessionInFlight === sessionName)
|
|
1628
1658
|
return;
|
|
1629
|
-
const pendingSession =
|
|
1659
|
+
const pendingSession = undefined; // CLI session names are real word-triples from the start
|
|
1630
1660
|
const bindSessionId = sessionIdHint || currentSessionId || undefined;
|
|
1631
1661
|
bindingSessionInFlight = sessionName;
|
|
1632
1662
|
void (async () => {
|