@ekkos/cli 1.0.13 → 1.0.15
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/dashboard.js +64 -19
- package/dist/commands/run.js +23 -10
- package/dist/lib/usage-parser.js +2 -1
- package/package.json +1 -1
|
@@ -301,25 +301,36 @@ function resolveJsonlPath(sessionName, createdAfterMs) {
|
|
|
301
301
|
* This prevents picking up old sessions that are still being modified.
|
|
302
302
|
*/
|
|
303
303
|
function findLatestJsonl(projectPath, createdAfterMs) {
|
|
304
|
-
// Claude encodes project paths by replacing
|
|
305
|
-
// On Windows
|
|
306
|
-
|
|
307
|
-
const
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
.
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
304
|
+
// Claude encodes project paths by replacing separators with '-'.
|
|
305
|
+
// On Windows, ':' is also illegal in directory names so it gets replaced too.
|
|
306
|
+
// Try all plausible encodings since Claude's exact scheme varies by platform.
|
|
307
|
+
const candidateEncodings = new Set([
|
|
308
|
+
projectPath.replace(/[\\/]/g, '-'), // C:-Users-name (backslash only)
|
|
309
|
+
projectPath.replace(/[:\\/]/g, '-'), // C--Users-name (colon + backslash)
|
|
310
|
+
'-' + projectPath.replace(/[:\\/]/g, '-'), // -C--Users-name (leading separator)
|
|
311
|
+
projectPath.replace(/\//g, '-'), // macOS: /Users/name → -Users-name
|
|
312
|
+
]);
|
|
313
|
+
const projectsRoot = path.join(os.homedir(), '.claude', 'projects');
|
|
314
|
+
for (const encoded of candidateEncodings) {
|
|
315
|
+
const projectDir = path.join(projectsRoot, encoded);
|
|
316
|
+
if (!fs.existsSync(projectDir))
|
|
317
|
+
continue;
|
|
318
|
+
const jsonlFiles = fs.readdirSync(projectDir)
|
|
319
|
+
.filter(f => f.endsWith('.jsonl'))
|
|
320
|
+
.map(f => {
|
|
321
|
+
const stat = fs.statSync(path.join(projectDir, f));
|
|
322
|
+
return {
|
|
323
|
+
path: path.join(projectDir, f),
|
|
324
|
+
mtime: stat.mtimeMs,
|
|
325
|
+
birthtime: stat.birthtimeMs,
|
|
326
|
+
};
|
|
327
|
+
})
|
|
328
|
+
.filter(f => !createdAfterMs || f.birthtime > createdAfterMs)
|
|
329
|
+
.sort((a, b) => b.mtime - a.mtime);
|
|
330
|
+
if (jsonlFiles.length > 0)
|
|
331
|
+
return jsonlFiles[0].path;
|
|
332
|
+
}
|
|
333
|
+
return null;
|
|
323
334
|
}
|
|
324
335
|
function getLatestSession() {
|
|
325
336
|
const sessions = (0, state_js_1.getActiveSessions)();
|
|
@@ -406,6 +417,40 @@ async function waitForNewSession() {
|
|
|
406
417
|
return { sessionName: name, jsonlPath: latestJsonl };
|
|
407
418
|
}
|
|
408
419
|
}
|
|
420
|
+
// Broad fallback: scan ALL project directories for any new JSONL (Windows safety net).
|
|
421
|
+
// Claude creates the JSONL immediately when a session starts, before the first message.
|
|
422
|
+
// This catches cases where path encoding doesn't match.
|
|
423
|
+
{
|
|
424
|
+
const projectsRoot = path.join(os.homedir(), '.claude', 'projects');
|
|
425
|
+
try {
|
|
426
|
+
if (fs.existsSync(projectsRoot)) {
|
|
427
|
+
const allNewJsonl = fs.readdirSync(projectsRoot)
|
|
428
|
+
.flatMap(dir => {
|
|
429
|
+
const dirPath = path.join(projectsRoot, dir);
|
|
430
|
+
try {
|
|
431
|
+
return fs.readdirSync(dirPath)
|
|
432
|
+
.filter(f => f.endsWith('.jsonl'))
|
|
433
|
+
.map(f => {
|
|
434
|
+
const fp = path.join(dirPath, f);
|
|
435
|
+
const stat = fs.statSync(fp);
|
|
436
|
+
return { path: fp, birthtime: stat.birthtimeMs, mtime: stat.mtimeMs };
|
|
437
|
+
})
|
|
438
|
+
.filter(f => f.birthtime > launchTs);
|
|
439
|
+
}
|
|
440
|
+
catch {
|
|
441
|
+
return [];
|
|
442
|
+
}
|
|
443
|
+
})
|
|
444
|
+
.sort((a, b) => b.birthtime - a.birthtime);
|
|
445
|
+
if (allNewJsonl.length > 0) {
|
|
446
|
+
const name = candidateName || 'session';
|
|
447
|
+
console.log(chalk_1.default.green(` Found session via scan: ${name}`));
|
|
448
|
+
return { sessionName: name, jsonlPath: allNewJsonl[0].path };
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
catch { /* ignore */ }
|
|
453
|
+
}
|
|
409
454
|
await sleep(pollMs);
|
|
410
455
|
process.stdout.write(chalk_1.default.gray('.'));
|
|
411
456
|
}
|
package/dist/commands/run.js
CHANGED
|
@@ -187,6 +187,10 @@ const transcript_repair_1 = require("../capture/transcript-repair");
|
|
|
187
187
|
let pty = null;
|
|
188
188
|
let ptyLoadPromise = null;
|
|
189
189
|
async function loadPty() {
|
|
190
|
+
// node-pty uses native addons that don't load cleanly on Windows.
|
|
191
|
+
// All PTY code paths already guard with `!isWindows`, so skip the import entirely.
|
|
192
|
+
if (isWindows)
|
|
193
|
+
return null;
|
|
190
194
|
if (pty)
|
|
191
195
|
return pty;
|
|
192
196
|
if (!ptyLoadPromise) {
|
|
@@ -840,18 +844,27 @@ function launchWithDashboardWindows(options) {
|
|
|
840
844
|
fs.writeFileSync(markerPath, `${launchTime}\n${cwd}`);
|
|
841
845
|
}
|
|
842
846
|
catch { }
|
|
843
|
-
// Use
|
|
844
|
-
//
|
|
845
|
-
|
|
846
|
-
const
|
|
847
|
-
const
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
847
|
+
// Use -EncodedCommand (UTF-16LE Base64) to pass PowerShell scripts to wt panes.
|
|
848
|
+
// This completely avoids nested quote hell: cmd.exe sees no " inside the wt command string,
|
|
849
|
+
// and PowerShell decodes the base64 itself, preserving backslashes and special chars exactly.
|
|
850
|
+
const ekkosCmdEscaped = ekkosCmd.replace(/'/g, "''");
|
|
851
|
+
const cwdEscaped = cwd.replace(/'/g, "''");
|
|
852
|
+
function toPsEncoded(script) {
|
|
853
|
+
// PowerShell -EncodedCommand expects UTF-16LE Base64
|
|
854
|
+
return Buffer.from(script, 'utf16le').toString('base64');
|
|
855
|
+
}
|
|
856
|
+
// cd to original CWD first so ekkos run --kickstart registers the correct projectPath
|
|
857
|
+
const runScript = `Set-Location '${cwdEscaped}'; & node '${ekkosCmdEscaped}' ${runArgs.join(' ')}`;
|
|
858
|
+
const dashScript = `& node '${ekkosCmdEscaped}' dashboard --wait-for-new --refresh 2000`;
|
|
859
|
+
const runEncoded = toPsEncoded(runScript);
|
|
860
|
+
const dashEncoded = toPsEncoded(dashScript);
|
|
861
|
+
// Windows Terminal split pane command.
|
|
862
|
+
// No nested double-quotes in the PowerShell portion — only the WT --title/--startingDirectory
|
|
863
|
+
// values need quoting, which cmd.exe handles cleanly.
|
|
851
864
|
const wtCmd = [
|
|
852
865
|
'wt',
|
|
853
|
-
`new-tab --
|
|
854
|
-
`; split-pane -V --size 0.4 --title
|
|
866
|
+
`new-tab --startingDirectory "${cwd}" --title ekkOS powershell -NoExit -EncodedCommand ${runEncoded}`,
|
|
867
|
+
`; split-pane -V --size 0.4 --title Dashboard powershell -NoExit -EncodedCommand ${dashEncoded}`
|
|
855
868
|
].join(' ');
|
|
856
869
|
try {
|
|
857
870
|
(0, child_process_1.execSync)(wtCmd, { stdio: 'inherit', shell: true });
|
package/dist/lib/usage-parser.js
CHANGED
|
@@ -79,7 +79,8 @@ function isEkkosSessionName(name) {
|
|
|
79
79
|
return /^[a-z]+-[a-z]+-[a-z]+$/.test(name);
|
|
80
80
|
}
|
|
81
81
|
function encodeProjectPath(projectPath) {
|
|
82
|
-
|
|
82
|
+
// Replace all path separators (and Windows drive colon) with '-'
|
|
83
|
+
return projectPath.replace(/[:\\/]/g, '-');
|
|
83
84
|
}
|
|
84
85
|
/** Resolve an ekkOS session name to a JSONL UUID */
|
|
85
86
|
function resolveSessionName(name) {
|