claude-simple 0.2.35 → 0.2.37
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/README.md +0 -0
- package/client.js +1 -1
- package/dist/addon-fit.min.js +0 -0
- package/dist/{app.0.2.35.min.js → app.0.2.37.min.js} +147 -147
- package/dist/index.html +2 -2
- package/dist/{style.0.2.35.min.css → style.0.2.37.min.css} +23 -0
- package/dist/xterm.min.css +0 -0
- package/dist/xterm.min.js +0 -0
- package/gateway.js +0 -0
- package/package.json +1 -1
- package/public/addon-fit.min.js +0 -0
- package/public/agent-panel.js +0 -0
- package/public/api.js +0 -0
- package/public/app.js +2 -2
- package/public/base.css +0 -0
- package/public/cache.js +0 -0
- package/public/chat.js +0 -0
- package/public/download-status.js +25 -0
- package/public/files.js +71 -38
- package/public/git.js +0 -0
- package/public/icons/icon-180.png +0 -0
- package/public/icons/icon-192.png +0 -0
- package/public/icons/icon-512.png +0 -0
- package/public/icons/icon-maskable-512.png +0 -0
- package/public/index.html +2 -2
- package/public/lib.js +0 -0
- package/public/manifest.webmanifest +0 -0
- package/public/mobile.js +0 -0
- package/public/notifications.js +0 -0
- package/public/offline.html +0 -0
- package/public/pwa.js +1 -1
- package/public/router.js +0 -0
- package/public/settings.js +0 -0
- package/public/shell/boot.js +0 -0
- package/public/shell/dashboard.js +0 -0
- package/public/shell/hub.js +0 -0
- package/public/shell/layout.js +0 -0
- package/public/shell/notes.js +0 -0
- package/public/shell/session-context.js +0 -0
- package/public/shell/session-list.js +0 -0
- package/public/shell/session-nav.js +0 -0
- package/public/shell/templates.js +0 -0
- package/public/shell/util.js +0 -0
- package/public/shell.js +0 -0
- package/public/state.js +0 -0
- package/public/style.css +23 -0
- package/public/sw.js +3 -3
- package/public/terminal.js +0 -0
- package/public/wake-lock.js +0 -0
- package/public/xterm.min.css +0 -0
- package/public/xterm.min.js +0 -0
- package/src/agents/claude.js +0 -0
- package/src/agents/codex.js +5 -1
- package/src/agents/meta-agent.js +0 -0
- package/src/agents/models.js +0 -0
- package/src/agents/sessions.js +54 -25
- package/src/agents/stream-batch.js +0 -0
- package/src/app.js +0 -0
- package/src/auto-update.js +0 -0
- package/src/runtime/approvals.js +0 -0
- package/src/runtime/outbox.js +0 -0
- package/src/runtime/protocol.js +0 -0
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.37.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.37.min.js"></script>
|
|
15
15
|
</body>
|
|
16
16
|
</html>
|
|
@@ -140,6 +140,29 @@
|
|
|
140
140
|
}
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
+
.file-download-toast {
|
|
144
|
+
position: fixed;
|
|
145
|
+
left: 50%;
|
|
146
|
+
bottom: calc(1.25rem + env(safe-area-inset-bottom, 0px));
|
|
147
|
+
transform: translateX(-50%) translateY(8px);
|
|
148
|
+
z-index: 80;
|
|
149
|
+
max-width: min(92vw, 28rem);
|
|
150
|
+
padding: 0.7rem 1rem;
|
|
151
|
+
border-radius: 0.7rem;
|
|
152
|
+
background: var(--color-primary, #3b82f6);
|
|
153
|
+
color: var(--color-primary-content, #fff);
|
|
154
|
+
font-size: 0.85rem;
|
|
155
|
+
line-height: 1.35;
|
|
156
|
+
box-shadow: 0 8px 28px rgba(0, 0, 0, 0.35);
|
|
157
|
+
opacity: 0;
|
|
158
|
+
pointer-events: none;
|
|
159
|
+
transition: opacity .18s ease, transform .18s ease;
|
|
160
|
+
}
|
|
161
|
+
.file-download-toast.visible {
|
|
162
|
+
opacity: 1;
|
|
163
|
+
transform: translateX(-50%) translateY(0);
|
|
164
|
+
}
|
|
165
|
+
|
|
143
166
|
/* File row actions: hover-reveal on desktop only */
|
|
144
167
|
@media (min-width: 641px) {
|
|
145
168
|
.file-row-action {
|
package/dist/xterm.min.css
CHANGED
|
File without changes
|
package/dist/xterm.min.js
CHANGED
|
File without changes
|
package/gateway.js
CHANGED
|
File without changes
|
package/package.json
CHANGED
package/public/addon-fit.min.js
CHANGED
|
File without changes
|
package/public/agent-panel.js
CHANGED
|
File without changes
|
package/public/api.js
CHANGED
|
File without changes
|
package/public/app.js
CHANGED
|
@@ -3,7 +3,7 @@ import { ctx } from './state.js';
|
|
|
3
3
|
import { probeHub } from './api.js?v=ui-24';
|
|
4
4
|
import { initShell, showApp, initAuth, resumeSession, switchTab, shouldRestoreSessionOnBoot } from './shell.js';
|
|
5
5
|
import { initChat } from './chat.js?v=ui-24';
|
|
6
|
-
import { initFilesTab } from './files.js?v=ui-
|
|
6
|
+
import { initFilesTab } from './files.js?v=ui-29';
|
|
7
7
|
import { initGitTab } from './git.js';
|
|
8
8
|
import { initSettings } from './settings.js';
|
|
9
9
|
import { initNotes } from './notes.js';
|
|
@@ -11,7 +11,7 @@ import { initTerminal } from './terminal.js';
|
|
|
11
11
|
import { initImagePaste } from './chat.js?v=ui-24';
|
|
12
12
|
import { initMetaAgent } from './agent-panel.js';
|
|
13
13
|
import { initRouter, applyInitialRoute, parseHashPath } from './router.js';
|
|
14
|
-
import { initPwa } from './pwa.js?v=ui-
|
|
14
|
+
import { initPwa } from './pwa.js?v=ui-29';
|
|
15
15
|
import { initMobileChrome } from './mobile.js';
|
|
16
16
|
|
|
17
17
|
function markBootReady() {
|
package/public/base.css
CHANGED
|
File without changes
|
package/public/cache.js
CHANGED
|
File without changes
|
package/public/chat.js
CHANGED
|
File without changes
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** Shared download status copy for the Files tab toast. */
|
|
2
|
+
export function sizeLabel(size) {
|
|
3
|
+
if (size == null) return '';
|
|
4
|
+
if (size > 1024 * 1024) return `${(size / (1024 * 1024)).toFixed(1)} MB`;
|
|
5
|
+
if (size > 1024) return `${(size / 1024).toFixed(1)} KB`;
|
|
6
|
+
return `${size} B`;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function formatDownloadStatus(name, received, total) {
|
|
10
|
+
const n = name || 'file';
|
|
11
|
+
if (total > 0 && received != null) {
|
|
12
|
+
const pct = Math.min(100, Math.round((received / total) * 100));
|
|
13
|
+
return `正在下载 ${n}(${pct}%)`;
|
|
14
|
+
}
|
|
15
|
+
if (received > 0) return `正在下载 ${n}(${sizeLabel(received)})`;
|
|
16
|
+
return `正在下载 ${n}…`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function formatDownloadDone(name) {
|
|
20
|
+
return `已保存 ${name || 'file'},请查看浏览器下载栏`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function formatDownloadHandedOff(name) {
|
|
24
|
+
return `已交给浏览器下载 ${name || 'file'}`;
|
|
25
|
+
}
|
package/public/files.js
CHANGED
|
@@ -6,6 +6,12 @@ import { resolveWorkspaceFile } from './workspace-path.js?v=ui-23';
|
|
|
6
6
|
import { renderMermaid } from './mermaid.js';
|
|
7
7
|
import { renderMath } from './math.js';
|
|
8
8
|
import { parseMarkdownWithMath } from './markdown.js';
|
|
9
|
+
import {
|
|
10
|
+
sizeLabel,
|
|
11
|
+
formatDownloadStatus,
|
|
12
|
+
formatDownloadDone,
|
|
13
|
+
formatDownloadHandedOff,
|
|
14
|
+
} from './download-status.js?v=ui-29';
|
|
9
15
|
|
|
10
16
|
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
11
17
|
function fileIcon(name) {
|
|
@@ -33,13 +39,6 @@ function isMarkdownFile(path) {
|
|
|
33
39
|
return ext === 'md' || ext === 'markdown' || ext === 'mdx';
|
|
34
40
|
}
|
|
35
41
|
|
|
36
|
-
function sizeLabel(size) {
|
|
37
|
-
if (size == null) return '';
|
|
38
|
-
if (size > 1024 * 1024) return `${(size / (1024 * 1024)).toFixed(1)} MB`;
|
|
39
|
-
if (size > 1024) return `${(size / 1024).toFixed(1)} KB`;
|
|
40
|
-
return `${size} B`;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
42
|
function base64ToArrayBuffer(b64) {
|
|
44
43
|
const bin = atob(b64);
|
|
45
44
|
const bytes = new Uint8Array(bin.length);
|
|
@@ -381,35 +380,75 @@ function triggerUpload() {
|
|
|
381
380
|
input.click();
|
|
382
381
|
}
|
|
383
382
|
|
|
383
|
+
let downloadBusy = false;
|
|
384
|
+
let downloadToastTimer = 0;
|
|
385
|
+
|
|
386
|
+
function downloadToastEl() {
|
|
387
|
+
let el = document.getElementById('file-download-toast');
|
|
388
|
+
if (el) return el;
|
|
389
|
+
el = document.createElement('div');
|
|
390
|
+
el.id = 'file-download-toast';
|
|
391
|
+
el.className = 'file-download-toast';
|
|
392
|
+
el.setAttribute('role', 'status');
|
|
393
|
+
el.setAttribute('aria-live', 'polite');
|
|
394
|
+
el.hidden = true;
|
|
395
|
+
document.body.appendChild(el);
|
|
396
|
+
return el;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function showDownloadToast(text, { sticky = false } = {}) {
|
|
400
|
+
const el = downloadToastEl();
|
|
401
|
+
el.textContent = text;
|
|
402
|
+
el.hidden = false;
|
|
403
|
+
el.classList.add('visible');
|
|
404
|
+
clearTimeout(downloadToastTimer);
|
|
405
|
+
if (!sticky) {
|
|
406
|
+
downloadToastTimer = setTimeout(() => {
|
|
407
|
+
el.classList.remove('visible');
|
|
408
|
+
}, 5000);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function triggerBrowserDownload(href, name) {
|
|
413
|
+
const a = document.createElement('a');
|
|
414
|
+
a.href = href;
|
|
415
|
+
a.download = name;
|
|
416
|
+
a.rel = 'noopener';
|
|
417
|
+
a.style.display = 'none';
|
|
418
|
+
document.body.appendChild(a);
|
|
419
|
+
a.click();
|
|
420
|
+
a.remove();
|
|
421
|
+
}
|
|
422
|
+
|
|
384
423
|
async function downloadFile(proj, filePath) {
|
|
385
424
|
const root = filesRoot.peek() || proj?.path || '';
|
|
386
425
|
if (!root) {
|
|
387
426
|
alert('No path selected — set root first');
|
|
388
427
|
return;
|
|
389
428
|
}
|
|
429
|
+
if (downloadBusy) {
|
|
430
|
+
showDownloadToast('已有文件正在下载');
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
390
433
|
const id = '_';
|
|
391
434
|
const name = (filePath || '').split('/').pop() || 'download';
|
|
392
|
-
// Prefer Authorization header (no token in URL / logs). authQuery kept as
|
|
393
|
-
// fallback for environments that only support plain navigations.
|
|
394
435
|
const qs = new URLSearchParams({ root, path: filePath, download: 'true' });
|
|
395
436
|
const url = `/api/projects/${id}/file?${qs}`;
|
|
437
|
+
downloadBusy = true;
|
|
438
|
+
showDownloadToast(formatDownloadStatus(name), { sticky: true });
|
|
396
439
|
try {
|
|
397
|
-
const handle = typeof window.showSaveFilePicker === 'function'
|
|
398
|
-
? await window.showSaveFilePicker({ suggestedName: name }) : null;
|
|
399
440
|
const res = await fetch(url, { headers: authHeaders(), credentials: 'same-origin' });
|
|
400
441
|
if (!res.ok) {
|
|
401
442
|
const d = await res.json().catch(() => ({}));
|
|
402
443
|
throw new Error(d.error || `HTTP ${res.status}`);
|
|
403
444
|
}
|
|
404
|
-
if (handle && res.body) {
|
|
405
|
-
await res.body.pipeTo(await handle.createWritable());
|
|
406
|
-
return;
|
|
407
|
-
}
|
|
408
|
-
// Browsers without a file sink hand large downloads to their download manager.
|
|
409
445
|
const maxBlob = 16 * 1024 * 1024;
|
|
410
|
-
|
|
446
|
+
const total = Number(res.headers.get('content-length')) || 0;
|
|
447
|
+
if (total > maxBlob || !res.body) {
|
|
411
448
|
await res.body?.cancel();
|
|
412
|
-
|
|
449
|
+
triggerBrowserDownload(`/api/projects/${id}/file?${authQuery({ root, path: filePath, download: 'true' })}`, name);
|
|
450
|
+
showDownloadToast(formatDownloadHandedOff(name));
|
|
451
|
+
return;
|
|
413
452
|
}
|
|
414
453
|
const reader = res.body.getReader();
|
|
415
454
|
const chunks = []; let bytes = 0;
|
|
@@ -417,37 +456,31 @@ async function downloadFile(proj, filePath) {
|
|
|
417
456
|
const { done, value } = await reader.read();
|
|
418
457
|
if (done) break;
|
|
419
458
|
bytes += value.byteLength;
|
|
420
|
-
if (bytes > maxBlob) {
|
|
459
|
+
if (bytes > maxBlob) {
|
|
460
|
+
await reader.cancel();
|
|
461
|
+
triggerBrowserDownload(`/api/projects/${id}/file?${authQuery({ root, path: filePath, download: 'true' })}`, name);
|
|
462
|
+
showDownloadToast(formatDownloadHandedOff(name));
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
421
465
|
chunks.push(value);
|
|
466
|
+
showDownloadToast(formatDownloadStatus(name, bytes, total), { sticky: true });
|
|
422
467
|
}
|
|
423
468
|
const blob = new Blob(chunks, { type: res.headers.get('content-type') || 'application/octet-stream' });
|
|
424
469
|
const objectUrl = URL.createObjectURL(blob);
|
|
425
|
-
|
|
426
|
-
a.href = objectUrl;
|
|
427
|
-
a.download = name;
|
|
428
|
-
a.rel = 'noopener';
|
|
429
|
-
a.style.display = 'none';
|
|
430
|
-
document.body.appendChild(a);
|
|
431
|
-
a.click();
|
|
432
|
-
a.remove();
|
|
433
|
-
// iOS may need the URL briefly; revoke after a beat.
|
|
470
|
+
triggerBrowserDownload(objectUrl, name);
|
|
434
471
|
setTimeout(() => URL.revokeObjectURL(objectUrl), 60_000);
|
|
472
|
+
showDownloadToast(formatDownloadDone(name));
|
|
435
473
|
} catch (e) {
|
|
436
474
|
if (e.name === 'AbortError') return;
|
|
437
|
-
// Last resort: open with token query (works when fetch is blocked, e.g. some WebViews).
|
|
438
475
|
try {
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
a.href = fallback;
|
|
442
|
-
a.download = name;
|
|
443
|
-
a.rel = 'noopener';
|
|
444
|
-
a.target = '_blank';
|
|
445
|
-
document.body.appendChild(a);
|
|
446
|
-
a.click();
|
|
447
|
-
a.remove();
|
|
476
|
+
triggerBrowserDownload(`/api/projects/${id}/file?${authQuery({ root, path: filePath, download: 'true' })}`, name);
|
|
477
|
+
showDownloadToast(formatDownloadHandedOff(name));
|
|
448
478
|
} catch {
|
|
479
|
+
showDownloadToast(`下载失败:${e.message}`);
|
|
449
480
|
alert(`Download failed: ${e.message}`);
|
|
450
481
|
}
|
|
482
|
+
} finally {
|
|
483
|
+
downloadBusy = false;
|
|
451
484
|
}
|
|
452
485
|
}
|
|
453
486
|
|
package/public/git.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/public/index.html
CHANGED
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
<script src="/vendor/mermaid.min.js"></script>
|
|
86
86
|
<link rel="stylesheet" href="/vendor/katex.min.css">
|
|
87
87
|
<script src="/vendor/katex.min.js"></script>
|
|
88
|
-
<link rel="stylesheet" href="style.css?v=ui-
|
|
88
|
+
<link rel="stylesheet" href="style.css?v=ui-29">
|
|
89
89
|
</head>
|
|
90
90
|
<body class="h-dvh overflow-hidden flex flex-col bg-base-100 text-base-content">
|
|
91
91
|
<div id="root" class="flex flex-col flex-1 overflow-hidden"></div>
|
|
@@ -101,6 +101,6 @@
|
|
|
101
101
|
}
|
|
102
102
|
}, 10000);
|
|
103
103
|
</script>
|
|
104
|
-
<script type="module" src="app.js?v=ui-
|
|
104
|
+
<script type="module" src="app.js?v=ui-29"></script>
|
|
105
105
|
</body>
|
|
106
106
|
</html>
|
package/public/lib.js
CHANGED
|
File without changes
|
|
File without changes
|
package/public/mobile.js
CHANGED
|
File without changes
|
package/public/notifications.js
CHANGED
|
File without changes
|
package/public/offline.html
CHANGED
|
File without changes
|
package/public/pwa.js
CHANGED
|
@@ -69,7 +69,7 @@ export function initPwa() {
|
|
|
69
69
|
|
|
70
70
|
// Register after first paint so install doesn't compete with boot.
|
|
71
71
|
const register = () => {
|
|
72
|
-
navigator.serviceWorker.register('/sw.js?v=
|
|
72
|
+
navigator.serviceWorker.register('/sw.js?v=38', { scope: '/' })
|
|
73
73
|
.then((reg) => {
|
|
74
74
|
reg.addEventListener('updatefound', () => {
|
|
75
75
|
const sw = reg.installing;
|
package/public/router.js
CHANGED
|
File without changes
|
package/public/settings.js
CHANGED
|
File without changes
|
package/public/shell/boot.js
CHANGED
|
File without changes
|
|
File without changes
|
package/public/shell/hub.js
CHANGED
|
File without changes
|
package/public/shell/layout.js
CHANGED
|
File without changes
|
package/public/shell/notes.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/public/shell/util.js
CHANGED
|
File without changes
|
package/public/shell.js
CHANGED
|
File without changes
|
package/public/state.js
CHANGED
|
File without changes
|
package/public/style.css
CHANGED
|
@@ -138,6 +138,29 @@
|
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
+
.file-download-toast {
|
|
142
|
+
position: fixed;
|
|
143
|
+
left: 50%;
|
|
144
|
+
bottom: calc(1.25rem + env(safe-area-inset-bottom, 0px));
|
|
145
|
+
transform: translateX(-50%) translateY(8px);
|
|
146
|
+
z-index: 80;
|
|
147
|
+
max-width: min(92vw, 28rem);
|
|
148
|
+
padding: 0.7rem 1rem;
|
|
149
|
+
border-radius: 0.7rem;
|
|
150
|
+
background: var(--color-primary, #3b82f6);
|
|
151
|
+
color: var(--color-primary-content, #fff);
|
|
152
|
+
font-size: 0.85rem;
|
|
153
|
+
line-height: 1.35;
|
|
154
|
+
box-shadow: 0 8px 28px rgba(0, 0, 0, 0.35);
|
|
155
|
+
opacity: 0;
|
|
156
|
+
pointer-events: none;
|
|
157
|
+
transition: opacity .18s ease, transform .18s ease;
|
|
158
|
+
}
|
|
159
|
+
.file-download-toast.visible {
|
|
160
|
+
opacity: 1;
|
|
161
|
+
transform: translateX(-50%) translateY(0);
|
|
162
|
+
}
|
|
163
|
+
|
|
141
164
|
/* File row actions: hover-reveal on desktop only */
|
|
142
165
|
@media (min-width: 641px) {
|
|
143
166
|
.file-row-action {
|
package/public/sw.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/* Agent UI service worker — cache shell assets only.
|
|
2
2
|
* Never intercepts WebSocket or mutates API semantics beyond offline fallback.
|
|
3
3
|
* Bump CACHE when shipping UI that must invalidate old shells. */
|
|
4
|
-
const CACHE = 'agent-ui-shell-
|
|
4
|
+
const CACHE = 'agent-ui-shell-v38';
|
|
5
5
|
const PRECACHE = [
|
|
6
6
|
'/',
|
|
7
7
|
'/index.html',
|
|
8
8
|
'/manifest.webmanifest',
|
|
9
|
-
'/style.css?v=ui-
|
|
9
|
+
'/style.css?v=ui-29',
|
|
10
10
|
'/style.css',
|
|
11
11
|
'/ui.min.css',
|
|
12
12
|
'/vendor/mini-react.min.js',
|
|
@@ -16,7 +16,7 @@ const PRECACHE = [
|
|
|
16
16
|
'/vendor/mermaid.min.js',
|
|
17
17
|
'/vendor/katex.min.js',
|
|
18
18
|
'/vendor/katex.min.css',
|
|
19
|
-
'/app.js?v=ui-
|
|
19
|
+
'/app.js?v=ui-29',
|
|
20
20
|
'/icons/icon-192.png',
|
|
21
21
|
'/icons/icon-512.png',
|
|
22
22
|
'/icons/icon-maskable-512.png',
|
package/public/terminal.js
CHANGED
|
File without changes
|
package/public/wake-lock.js
CHANGED
|
File without changes
|
package/public/xterm.min.css
CHANGED
|
File without changes
|
package/public/xterm.min.js
CHANGED
|
File without changes
|
package/src/agents/claude.js
CHANGED
|
File without changes
|
package/src/agents/codex.js
CHANGED
|
@@ -12,6 +12,10 @@ import crypto from 'crypto';
|
|
|
12
12
|
* result (usage), complete, error
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
+
export function isTransientCodexNotice(msg) {
|
|
16
|
+
return /Reconnecting|Falling back|remote compact task/i.test(String(msg || ''));
|
|
17
|
+
}
|
|
18
|
+
|
|
15
19
|
function mapPermissionToMode(permissionMode) {
|
|
16
20
|
if (permissionMode == null) return 'yolo';
|
|
17
21
|
switch (permissionMode) {
|
|
@@ -244,7 +248,7 @@ export function runCodex(command, options, send, { cliPath, activeSessions }) {
|
|
|
244
248
|
}
|
|
245
249
|
case 'error': {
|
|
246
250
|
const msg = raw.message || 'codex error';
|
|
247
|
-
if (
|
|
251
|
+
if (!isTransientCodexNotice(msg)) {
|
|
248
252
|
send({ type: 'error', message: msg, agent: 'codex' });
|
|
249
253
|
}
|
|
250
254
|
break;
|
package/src/agents/meta-agent.js
CHANGED
|
File without changes
|
package/src/agents/models.js
CHANGED
|
File without changes
|
package/src/agents/sessions.js
CHANGED
|
@@ -313,36 +313,63 @@ async function findCodexRollouts(codexHome) {
|
|
|
313
313
|
return files;
|
|
314
314
|
}
|
|
315
315
|
|
|
316
|
+
const CODEX_CATALOG_FULL_SCAN_MAX = 2 * 1024 * 1024;
|
|
317
|
+
const CODEX_CATALOG_HEAD_BYTES = 256 * 1024;
|
|
318
|
+
const CODEX_LINE_PARSE_MAX = 16 * 1024;
|
|
319
|
+
|
|
316
320
|
const codexMetadata = new Map();
|
|
317
321
|
async function readCodexMeta(filePath) {
|
|
318
322
|
const stat = await fs.stat(filePath);
|
|
319
323
|
const cached = codexMetadata.get(filePath);
|
|
320
324
|
if (cached?.size === stat.size && cached?.mtimeMs === stat.mtimeMs) return cached.meta;
|
|
321
325
|
let id = null, cwd = null, ts = null, turnCount = 0;
|
|
326
|
+
// Native rollouts keep every tool payload. AgentUI / Hub never show that
|
|
327
|
+
// body, so catalog only reads a short head (session_meta is line 0).
|
|
328
|
+
const limit = stat.size > CODEX_CATALOG_FULL_SCAN_MAX
|
|
329
|
+
? Math.min(stat.size, CODEX_CATALOG_HEAD_BYTES)
|
|
330
|
+
: stat.size;
|
|
322
331
|
try {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
const
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
332
|
+
if (limit > 0) {
|
|
333
|
+
const stream = fsSync.createReadStream(filePath, { start: 0, end: limit - 1 });
|
|
334
|
+
let pending = Buffer.alloc(0);
|
|
335
|
+
let skip = false;
|
|
336
|
+
for await (const chunk of stream) {
|
|
337
|
+
pending = pending.length ? Buffer.concat([pending, chunk]) : chunk;
|
|
338
|
+
let start = 0;
|
|
339
|
+
let end;
|
|
340
|
+
while ((end = pending.indexOf(10, start)) !== -1) {
|
|
341
|
+
const len = end - start;
|
|
342
|
+
const overflow = skip || len > CODEX_LINE_PARSE_MAX;
|
|
343
|
+
skip = false;
|
|
344
|
+
if (!overflow) {
|
|
345
|
+
const line = pending.subarray(start, end).toString('utf8');
|
|
346
|
+
const head = line.slice(0, 400);
|
|
347
|
+
if (/"type"\s*:\s*"(?:session_meta|turn_context)"/.test(head) ||
|
|
348
|
+
/"(?:role|type)"\s*:\s*"(?:user|user_message)"/.test(head)) {
|
|
349
|
+
try {
|
|
350
|
+
const d = JSON.parse(line);
|
|
351
|
+
if (d.type === 'session_meta' && d.payload) {
|
|
352
|
+
id = d.payload.id || id;
|
|
353
|
+
cwd = d.payload.cwd || cwd;
|
|
354
|
+
ts = d.payload.timestamp || d.timestamp || ts;
|
|
355
|
+
}
|
|
356
|
+
if (d.type === 'event_msg' && d.payload?.type === 'user_message') turnCount += 1;
|
|
357
|
+
else if (d.type === 'response_item' && d.payload?.role === 'user') turnCount += 1;
|
|
358
|
+
else if (d.type === 'turn_context' && d.payload?.turn_index != null) {
|
|
359
|
+
const idx = Number(d.payload.turn_index) + 1;
|
|
360
|
+
if (idx > turnCount) turnCount = idx;
|
|
361
|
+
}
|
|
362
|
+
} catch {}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
start = end + 1;
|
|
336
366
|
}
|
|
337
|
-
|
|
338
|
-
if (
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
// last turn_index + 1 is a better total; bump max
|
|
342
|
-
const idx = Number(d.payload.turn_index) + 1;
|
|
343
|
-
if (idx > turnCount) turnCount = idx;
|
|
367
|
+
pending = pending.subarray(start);
|
|
368
|
+
if (pending.length > CODEX_LINE_PARSE_MAX) {
|
|
369
|
+
skip = true;
|
|
370
|
+
pending = Buffer.alloc(0);
|
|
344
371
|
}
|
|
345
|
-
}
|
|
372
|
+
}
|
|
346
373
|
}
|
|
347
374
|
} catch {}
|
|
348
375
|
// Fallback: parse id from filename rollout-...-<uuid>.jsonl
|
|
@@ -472,11 +499,13 @@ async function updateCodexTranscript(filePath, entry, stat) {
|
|
|
472
499
|
let start = 0;
|
|
473
500
|
let end;
|
|
474
501
|
while ((end = pending.indexOf(10, start)) !== -1) {
|
|
475
|
-
const
|
|
476
|
-
entry.offset +=
|
|
502
|
+
const lineBytes = end - start;
|
|
503
|
+
entry.offset += lineBytes + 1;
|
|
477
504
|
start = end + 1;
|
|
478
|
-
//
|
|
479
|
-
//
|
|
505
|
+
// Tool outputs are multi-MB JSONL lines. AgentUI does not render them
|
|
506
|
+
// and Hub must not receive them — skip without materializing the body.
|
|
507
|
+
if (lineBytes > 64 * 1024) continue;
|
|
508
|
+
const line = pending.subarray(start - lineBytes - 1, end).toString('utf8');
|
|
480
509
|
const head = line.slice(0, 400);
|
|
481
510
|
if (!/"type"\s*:\s*"(?:response_item|event_msg)"/.test(head) ||
|
|
482
511
|
!/"type"\s*:\s*"(?:message|agent_message|function_call|command_execution|custom_tool_call|task_started|task_complete|turn_aborted)"/.test(head)) continue;
|
|
File without changes
|
package/src/app.js
CHANGED
|
File without changes
|
package/src/auto-update.js
CHANGED
|
File without changes
|
package/src/runtime/approvals.js
CHANGED
|
File without changes
|
package/src/runtime/outbox.js
CHANGED
|
File without changes
|
package/src/runtime/protocol.js
CHANGED
|
File without changes
|