claude-simple 0.2.38 → 0.2.39
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.html
CHANGED
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
|
|
6
6
|
<title>Claude Code</title>
|
|
7
|
-
<link rel="stylesheet" href="/style.0.2.
|
|
7
|
+
<link rel="stylesheet" href="/style.0.2.39.min.css">
|
|
8
8
|
<link rel="stylesheet" href="/highlight.min.css">
|
|
9
9
|
<script src="/marked.min.js"></script>
|
|
10
10
|
<script src="/highlight.min.js"></script>
|
|
11
11
|
</head>
|
|
12
12
|
<body class="h-dvh overflow-hidden flex flex-col bg-base-100 text-base-content">
|
|
13
13
|
<div id="root" class="flex flex-col flex-1 overflow-hidden"></div>
|
|
14
|
-
<script type="module" src="/app.0.2.
|
|
14
|
+
<script type="module" src="/app.0.2.39.min.js"></script>
|
|
15
15
|
</body>
|
|
16
16
|
</html>
|
package/package.json
CHANGED
package/src/agents/sessions.js
CHANGED
|
@@ -5,6 +5,7 @@ import path from 'path';
|
|
|
5
5
|
import os from 'os';
|
|
6
6
|
import crypto from 'crypto';
|
|
7
7
|
import { pathsEqual } from '../platform.js';
|
|
8
|
+
import { JSONL_LINE_PARSE_MAX, scanJsonlFile } from '../jsonl-scan.js';
|
|
8
9
|
|
|
9
10
|
// ─── Claude ───────────────────────────────────────────────────────────────────
|
|
10
11
|
|
|
@@ -318,6 +319,7 @@ const CODEX_CATALOG_HEAD_BYTES = 256 * 1024;
|
|
|
318
319
|
// session_meta includes git + base_instructions and is often 20–40KB.
|
|
319
320
|
// Multi-MB tool outputs still exceed this and are skipped.
|
|
320
321
|
const CODEX_LINE_PARSE_MAX = 256 * 1024;
|
|
322
|
+
const CODEX_TRANSCRIPT_CACHE_MAX = 64;
|
|
321
323
|
|
|
322
324
|
const codexMetadata = new Map();
|
|
323
325
|
async function readCodexMeta(filePath) {
|
|
@@ -445,7 +447,9 @@ export async function parseCodexMessages(filePath) {
|
|
|
445
447
|
}
|
|
446
448
|
codexTranscripts.delete(filePath);
|
|
447
449
|
codexTranscripts.set(filePath, entry);
|
|
448
|
-
while (codexTranscripts.size >
|
|
450
|
+
while (codexTranscripts.size > CODEX_TRANSCRIPT_CACHE_MAX) {
|
|
451
|
+
codexTranscripts.delete(codexTranscripts.keys().next().value);
|
|
452
|
+
}
|
|
449
453
|
if (entry.size === stat.size && entry.mtimeMs === stat.mtimeMs) return entry.messages;
|
|
450
454
|
const loading = updateCodexTranscript(filePath, entry, stat);
|
|
451
455
|
entry.loading = loading;
|
|
@@ -494,25 +498,16 @@ async function updateCodexTranscript(filePath, entry, stat) {
|
|
|
494
498
|
|
|
495
499
|
try {
|
|
496
500
|
if (stat.size <= entry.offset) return;
|
|
497
|
-
const
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
let end;
|
|
503
|
-
while ((end = pending.indexOf(10, start)) !== -1) {
|
|
504
|
-
const lineBytes = end - start;
|
|
505
|
-
entry.offset += lineBytes + 1;
|
|
506
|
-
start = end + 1;
|
|
507
|
-
// Tool outputs are multi-MB JSONL lines. AgentUI does not render them
|
|
508
|
-
// and Hub must not receive them — skip without materializing the body.
|
|
509
|
-
if (lineBytes > 64 * 1024) continue;
|
|
510
|
-
const line = pending.subarray(start - lineBytes - 1, end).toString('utf8');
|
|
501
|
+
const scanned = await scanJsonlFile(filePath, {
|
|
502
|
+
start: entry.offset,
|
|
503
|
+
end: stat.size,
|
|
504
|
+
maxLineBytes: JSONL_LINE_PARSE_MAX,
|
|
505
|
+
onLine: (line) => {
|
|
511
506
|
const head = line.slice(0, 400);
|
|
512
507
|
if (!/"type"\s*:\s*"(?:response_item|event_msg)"/.test(head) ||
|
|
513
|
-
!/"type"\s*:\s*"(?:message|agent_message|function_call|command_execution|custom_tool_call|task_started|task_complete|turn_aborted)"/.test(head))
|
|
508
|
+
!/"type"\s*:\s*"(?:message|agent_message|function_call|command_execution|custom_tool_call|task_started|task_complete|turn_aborted)"/.test(head)) return;
|
|
514
509
|
let d;
|
|
515
|
-
try { d = JSON.parse(line); } catch {
|
|
510
|
+
try { d = JSON.parse(line); } catch { return; }
|
|
516
511
|
|
|
517
512
|
if (d.type === 'event_msg' && d.payload) {
|
|
518
513
|
const p = d.payload;
|
|
@@ -544,13 +539,13 @@ async function updateCodexTranscript(filePath, entry, stat) {
|
|
|
544
539
|
.filter(Boolean);
|
|
545
540
|
pushAssistant(texts.join('\n'), d.timestamp);
|
|
546
541
|
} else if (p.type === 'function_call') {
|
|
547
|
-
if (p.call_id && entry.seenCalls.has(p.call_id))
|
|
542
|
+
if (p.call_id && entry.seenCalls.has(p.call_id)) return;
|
|
548
543
|
if (p.call_id) entry.seenCalls.add(p.call_id);
|
|
549
544
|
let input = p.arguments;
|
|
550
545
|
try { input = typeof input === 'string' ? JSON.parse(input) : input; } catch {}
|
|
551
546
|
messages.push({ role: 'tool', type: 'tool_use', name: p.name || 'function', id: p.call_id, input: input ?? {}, ts: d.timestamp });
|
|
552
547
|
} else if (p.type === 'command_execution' || p.type === 'custom_tool_call') {
|
|
553
|
-
if (p.call_id && entry.seenCalls.has(p.call_id))
|
|
548
|
+
if (p.call_id && entry.seenCalls.has(p.call_id)) return;
|
|
554
549
|
if (p.call_id) entry.seenCalls.add(p.call_id);
|
|
555
550
|
let input = p.input;
|
|
556
551
|
try { input = typeof input === 'string' ? JSON.parse(input) : input; } catch {}
|
|
@@ -570,9 +565,9 @@ async function updateCodexTranscript(filePath, entry, stat) {
|
|
|
570
565
|
const text = d.payload.message || d.payload.text;
|
|
571
566
|
pushAssistant(text, d.timestamp);
|
|
572
567
|
}
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
|
|
568
|
+
},
|
|
569
|
+
});
|
|
570
|
+
entry.offset = scanned.offset;
|
|
576
571
|
entry.size = stat.size;
|
|
577
572
|
entry.mtimeMs = stat.mtimeMs;
|
|
578
573
|
} catch (error) {
|
|
@@ -1922,15 +1917,11 @@ export async function loadSessionJsonl(sessionId, agent, { claudeConfigDirs, cod
|
|
|
1922
1917
|
const budget = Math.min(Math.max(1000, Number(maxChars) || 24000), 64000);
|
|
1923
1918
|
const lines = [];
|
|
1924
1919
|
let n = 0, chars = 0, hasMore = false, clipped = false;
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
try {
|
|
1928
|
-
for await (const line of rl) {
|
|
1920
|
+
await scanJsonlFile(file, {
|
|
1921
|
+
onLine: (line) => {
|
|
1929
1922
|
const index = n++;
|
|
1930
|
-
if (!tail && index < start)
|
|
1931
|
-
if (!tail && (lines.length >= limit || chars >= budget)) { hasMore = true;
|
|
1932
|
-
// Bound huge tool output rows as well as total response size. Clipping
|
|
1933
|
-
// is explicit: a clipped line must not be mistaken for complete JSON.
|
|
1923
|
+
if (!tail && index < start) return;
|
|
1924
|
+
if (!tail && (lines.length >= limit || chars >= budget)) { hasMore = true; return; }
|
|
1934
1925
|
const cap = tail ? budget : budget - chars;
|
|
1935
1926
|
const text = line.slice(0, cap);
|
|
1936
1927
|
if (text.length < line.length) clipped = true;
|
|
@@ -1938,8 +1929,8 @@ export async function loadSessionJsonl(sessionId, agent, { claudeConfigDirs, cod
|
|
|
1938
1929
|
if (tail) while (lines.length > limit || (chars > budget && lines.length > 1)) {
|
|
1939
1930
|
chars -= lines.shift().length;
|
|
1940
1931
|
}
|
|
1941
|
-
}
|
|
1942
|
-
}
|
|
1932
|
+
},
|
|
1933
|
+
});
|
|
1943
1934
|
const stat = await fs.stat(file).catch(() => null);
|
|
1944
1935
|
const actualOffset = tail ? Math.max(0, n - lines.length) : start;
|
|
1945
1936
|
return { agent: a, fileName: path.basename(file), offset: actualOffset, lines,
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import fsSync from 'node:fs';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
|
|
4
|
+
export const JSONL_LINE_PARSE_MAX = 64 * 1024;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Sequential JSONL reader. Oversize records are skipped by searching for the
|
|
8
|
+
* next newline without keeping the body in a JS string.
|
|
9
|
+
*/
|
|
10
|
+
export async function scanJsonlFile(filePath, {
|
|
11
|
+
start = 0,
|
|
12
|
+
end = null,
|
|
13
|
+
maxLineBytes = JSONL_LINE_PARSE_MAX,
|
|
14
|
+
onLine,
|
|
15
|
+
} = {}) {
|
|
16
|
+
const stat = await fs.stat(filePath);
|
|
17
|
+
const stop = end == null ? stat.size : Math.min(end, stat.size);
|
|
18
|
+
let offset = Math.max(0, start);
|
|
19
|
+
let skippedLines = 0;
|
|
20
|
+
if (offset >= stop) return { offset, skippedLines, size: stat.size };
|
|
21
|
+
|
|
22
|
+
const stream = fsSync.createReadStream(filePath, {
|
|
23
|
+
start: offset,
|
|
24
|
+
end: stop - 1,
|
|
25
|
+
highWaterMark: 64 * 1024,
|
|
26
|
+
});
|
|
27
|
+
let pending = Buffer.alloc(0);
|
|
28
|
+
let skip = false;
|
|
29
|
+
try {
|
|
30
|
+
for await (const chunk of stream) {
|
|
31
|
+
let buf = skip ? chunk : (pending.length ? Buffer.concat([pending, chunk]) : chunk);
|
|
32
|
+
pending = Buffer.alloc(0);
|
|
33
|
+
let pos = 0;
|
|
34
|
+
if (skip) {
|
|
35
|
+
const nl = buf.indexOf(10);
|
|
36
|
+
if (nl === -1) {
|
|
37
|
+
offset += buf.length;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
offset += nl + 1;
|
|
41
|
+
skippedLines += 1;
|
|
42
|
+
skip = false;
|
|
43
|
+
pos = nl + 1;
|
|
44
|
+
}
|
|
45
|
+
while (pos < buf.length) {
|
|
46
|
+
const nl = buf.indexOf(10, pos);
|
|
47
|
+
if (nl === -1) break;
|
|
48
|
+
const len = nl - pos;
|
|
49
|
+
offset += len + 1;
|
|
50
|
+
if (len > maxLineBytes) {
|
|
51
|
+
skippedLines += 1;
|
|
52
|
+
} else if (typeof onLine === 'function') {
|
|
53
|
+
onLine(buf.subarray(pos, nl).toString('utf8'), { pendingBytes: buf.length, offset });
|
|
54
|
+
}
|
|
55
|
+
pos = nl + 1;
|
|
56
|
+
}
|
|
57
|
+
if (pos < buf.length) {
|
|
58
|
+
const leftover = buf.subarray(pos);
|
|
59
|
+
if (leftover.length > maxLineBytes) {
|
|
60
|
+
skip = true;
|
|
61
|
+
offset += leftover.length;
|
|
62
|
+
} else {
|
|
63
|
+
pending = leftover;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
} finally {
|
|
68
|
+
stream.destroy();
|
|
69
|
+
}
|
|
70
|
+
return { offset, skippedLines, size: stat.size };
|
|
71
|
+
}
|
|
File without changes
|
|
File without changes
|