claude-simple 0.2.34 → 0.2.36
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.34.min.js → app.0.2.36.min.js} +147 -147
- package/dist/index.html +2 -2
- package/dist/{style.0.2.34.min.css → style.0.2.36.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/meta-agent.js +0 -0
- package/src/agents/models.js +0 -0
- package/src/agents/sessions.js +0 -0
- 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/http-transfer.js +16 -7
- 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.36.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.36.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/meta-agent.js
CHANGED
|
File without changes
|
package/src/agents/models.js
CHANGED
|
File without changes
|
package/src/agents/sessions.js
CHANGED
|
File without changes
|
|
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
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { WebSocket } from 'ws';
|
|
2
|
+
import { request } from 'node:http';
|
|
2
3
|
import { setTimeout as sleep } from 'node:timers/promises';
|
|
3
4
|
|
|
4
5
|
// Aggregate wire budget across downloads/previews, separate from chat traffic.
|
|
@@ -46,15 +47,22 @@ export function createHTTPTransfer({ gatewayURL, token, machineId, budget = new
|
|
|
46
47
|
ws.send(JSON.stringify({ ...data, reqId: msg.reqId }), err => err ? reject(err) : resolve());
|
|
47
48
|
});
|
|
48
49
|
try {
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
// A paced consumer can outlive the local server's keep-alive timeout.
|
|
51
|
+
// Read the dedicated HTTP stream directly instead of fetch's pooled parser.
|
|
52
|
+
const res = await new Promise((resolve, reject) => {
|
|
53
|
+
const req = request(url, { method: options.method, headers: { ...options.headers, Connection: 'close', 'Accept-Encoding': 'identity' }, signal: controller.signal }, resolve);
|
|
54
|
+
req.on('error', reject);
|
|
55
|
+
req.end(options.body);
|
|
56
|
+
});
|
|
57
|
+
const upstreamHeaders = Object.fromEntries(Object.entries(res.headers).filter(([, value]) => value != null).map(([key, value]) => [key, Array.isArray(value) ? value.join(', ') : String(value)]));
|
|
58
|
+
const headers = sanitizeHeaders(upstreamHeaders);
|
|
51
59
|
// Identity bodies retain their exact length (ordinary proxy replies recompute it).
|
|
52
|
-
if (!
|
|
53
|
-
const length =
|
|
54
|
-
if (length
|
|
60
|
+
if (!upstreamHeaders['content-encoding'] || upstreamHeaders['content-encoding'] === 'identity') {
|
|
61
|
+
const length = upstreamHeaders['content-length'];
|
|
62
|
+
if (length != null) headers['content-length'] = length;
|
|
55
63
|
}
|
|
56
|
-
await send({ type: 'http-res-start', status: res.
|
|
57
|
-
for await (const value of res
|
|
64
|
+
await send({ type: 'http-res-start', status: res.statusCode, headers });
|
|
65
|
+
for await (const value of res) {
|
|
58
66
|
// Each frame is small; sends wait for socket backpressure and share a rate cap.
|
|
59
67
|
for (let offset = 0; offset < value.byteLength; offset += 24 * 1024) {
|
|
60
68
|
const chunk = Buffer.from(value.subarray(offset, offset + 24 * 1024));
|
|
@@ -65,6 +73,7 @@ export function createHTTPTransfer({ gatewayURL, token, machineId, budget = new
|
|
|
65
73
|
await send({ type: 'http-end' });
|
|
66
74
|
} catch (err) {
|
|
67
75
|
if (ws.readyState === WebSocket.OPEN) await send({ type: 'http-error', message: err.message }).catch(() => {});
|
|
76
|
+
console.warn(`[transfer] ${msg.reqId}: ${err.message}`);
|
|
68
77
|
throw err;
|
|
69
78
|
}
|
|
70
79
|
} finally {
|
package/src/runtime/outbox.js
CHANGED
|
File without changes
|
package/src/runtime/protocol.js
CHANGED
|
File without changes
|