instar 0.19.4 → 0.19.7
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 +3 -3
- package/dashboard/index.html +400 -3
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +49 -9
- package/dist/commands/server.js.map +1 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +8 -4
- package/dist/commands/setup.js.map +1 -1
- package/dist/core/AutoUpdater.d.ts.map +1 -1
- package/dist/core/AutoUpdater.js +12 -0
- package/dist/core/AutoUpdater.js.map +1 -1
- package/dist/core/GlobalInstallCleanup.d.ts +23 -0
- package/dist/core/GlobalInstallCleanup.d.ts.map +1 -0
- package/dist/core/GlobalInstallCleanup.js +130 -0
- package/dist/core/GlobalInstallCleanup.js.map +1 -0
- package/dist/core/SessionManager.d.ts +6 -0
- package/dist/core/SessionManager.d.ts.map +1 -1
- package/dist/core/SessionManager.js +20 -0
- package/dist/core/SessionManager.js.map +1 -1
- package/dist/core/UpdateChecker.js +3 -3
- package/dist/core/UpdateChecker.js.map +1 -1
- package/dist/paste/PasteManager.d.ts +154 -0
- package/dist/paste/PasteManager.d.ts.map +1 -0
- package/dist/paste/PasteManager.js +524 -0
- package/dist/paste/PasteManager.js.map +1 -0
- package/dist/paste/TruncationDetector.d.ts +51 -0
- package/dist/paste/TruncationDetector.d.ts.map +1 -0
- package/dist/paste/TruncationDetector.js +220 -0
- package/dist/paste/TruncationDetector.js.map +1 -0
- package/dist/server/AgentServer.d.ts +2 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +12 -3
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/WebSocketManager.d.ts +5 -0
- package/dist/server/WebSocketManager.d.ts.map +1 -1
- package/dist/server/WebSocketManager.js +14 -0
- package/dist/server/WebSocketManager.js.map +1 -1
- package/dist/server/routes.d.ts +4 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +158 -0
- package/dist/server/routes.js.map +1 -1
- package/dist/threadline/relay/RegistryStore.d.ts.map +1 -1
- package/dist/threadline/relay/RegistryStore.js +4 -2
- package/dist/threadline/relay/RegistryStore.js.map +1 -1
- package/package.json +3 -3
- package/src/data/builtin-manifest.json +48 -48
- package/upgrades/0.19.6.md +17 -0
- package/upgrades/0.19.7.md +33 -0
package/README.md
CHANGED
|
@@ -11,14 +11,14 @@
|
|
|
11
11
|
<p align="center">
|
|
12
12
|
<a href="https://www.npmjs.com/package/instar"><img src="https://img.shields.io/npm/v/instar?style=flat-square" alt="npm version"></a>
|
|
13
13
|
<a href="https://www.npmjs.com/package/instar"><img src="https://img.shields.io/npm/dw/instar?style=flat-square" alt="npm downloads"></a>
|
|
14
|
-
<a href="https://github.com/
|
|
15
|
-
<a href="https://github.com/
|
|
14
|
+
<a href="https://github.com/JKHeadley/instar/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/JKHeadley/instar/ci.yml?branch=main&style=flat-square&label=CI" alt="CI"></a>
|
|
15
|
+
<a href="https://github.com/JKHeadley/instar/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-MIT-green?style=flat-square" alt="License"></a>
|
|
16
16
|
<img src="https://img.shields.io/badge/TypeScript-100%25-blue?style=flat-square&logo=typescript&logoColor=white" alt="TypeScript">
|
|
17
17
|
<a href="https://instar.sh/introduction/"><img src="https://img.shields.io/badge/Docs-instar.sh-teal?style=flat-square" alt="Docs"></a>
|
|
18
18
|
</p>
|
|
19
19
|
|
|
20
20
|
<p align="center">
|
|
21
|
-
<a href="https://www.npmjs.com/package/instar">npm</a> · <a href="https://github.com/
|
|
21
|
+
<a href="https://www.npmjs.com/package/instar">npm</a> · <a href="https://github.com/JKHeadley/instar">GitHub</a> · <a href="https://instar.sh">instar.sh</a> · <a href="https://instar.sh/introduction/">Docs</a>
|
|
22
22
|
</p>
|
|
23
23
|
|
|
24
24
|
---
|
package/dashboard/index.html
CHANGED
|
@@ -246,6 +246,190 @@
|
|
|
246
246
|
.empty-state .icon { font-size: 32px; margin-bottom: 12px; opacity: 0.5; }
|
|
247
247
|
.empty-state p { font-size: 13px; line-height: 1.5; }
|
|
248
248
|
|
|
249
|
+
/* Drop Zone */
|
|
250
|
+
.dropzone-container {
|
|
251
|
+
display: flex;
|
|
252
|
+
justify-content: center;
|
|
253
|
+
padding: 24px;
|
|
254
|
+
overflow-y: auto;
|
|
255
|
+
height: calc(100vh - 56px);
|
|
256
|
+
}
|
|
257
|
+
.dropzone-panel {
|
|
258
|
+
width: 100%;
|
|
259
|
+
max-width: 720px;
|
|
260
|
+
}
|
|
261
|
+
.dropzone-header h2 {
|
|
262
|
+
color: var(--text-bright);
|
|
263
|
+
font-size: 20px;
|
|
264
|
+
margin-bottom: 4px;
|
|
265
|
+
}
|
|
266
|
+
.dropzone-subtitle {
|
|
267
|
+
color: var(--text-dim);
|
|
268
|
+
font-size: 13px;
|
|
269
|
+
margin-bottom: 20px;
|
|
270
|
+
}
|
|
271
|
+
.dropzone-form {
|
|
272
|
+
display: flex;
|
|
273
|
+
flex-direction: column;
|
|
274
|
+
gap: 12px;
|
|
275
|
+
}
|
|
276
|
+
.dropzone-label {
|
|
277
|
+
background: var(--surface);
|
|
278
|
+
border: 1px solid var(--border);
|
|
279
|
+
border-radius: 6px;
|
|
280
|
+
padding: 10px 12px;
|
|
281
|
+
color: var(--text);
|
|
282
|
+
font-size: 14px;
|
|
283
|
+
outline: none;
|
|
284
|
+
}
|
|
285
|
+
.dropzone-label:focus { border-color: var(--accent); }
|
|
286
|
+
.dropzone-textarea-wrap {
|
|
287
|
+
position: relative;
|
|
288
|
+
}
|
|
289
|
+
.dropzone-textarea {
|
|
290
|
+
width: 100%;
|
|
291
|
+
min-height: 200px;
|
|
292
|
+
max-height: 60vh;
|
|
293
|
+
background: var(--surface);
|
|
294
|
+
border: 1px solid var(--border);
|
|
295
|
+
border-radius: 6px;
|
|
296
|
+
padding: 12px;
|
|
297
|
+
color: var(--text);
|
|
298
|
+
font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
|
|
299
|
+
font-size: 13px;
|
|
300
|
+
line-height: 1.5;
|
|
301
|
+
resize: vertical;
|
|
302
|
+
outline: none;
|
|
303
|
+
box-sizing: border-box;
|
|
304
|
+
}
|
|
305
|
+
.dropzone-textarea:focus { border-color: var(--accent); }
|
|
306
|
+
.dropzone-count {
|
|
307
|
+
position: absolute;
|
|
308
|
+
bottom: 8px;
|
|
309
|
+
right: 12px;
|
|
310
|
+
font-size: 11px;
|
|
311
|
+
color: var(--text-dim);
|
|
312
|
+
pointer-events: none;
|
|
313
|
+
}
|
|
314
|
+
.dropzone-actions {
|
|
315
|
+
display: flex;
|
|
316
|
+
gap: 10px;
|
|
317
|
+
align-items: center;
|
|
318
|
+
}
|
|
319
|
+
.dropzone-session-select {
|
|
320
|
+
flex: 1;
|
|
321
|
+
background: var(--surface);
|
|
322
|
+
border: 1px solid var(--border);
|
|
323
|
+
border-radius: 6px;
|
|
324
|
+
padding: 10px 12px;
|
|
325
|
+
color: var(--text);
|
|
326
|
+
font-size: 13px;
|
|
327
|
+
outline: none;
|
|
328
|
+
}
|
|
329
|
+
.dropzone-send-btn {
|
|
330
|
+
background: var(--accent);
|
|
331
|
+
color: #fff;
|
|
332
|
+
border: none;
|
|
333
|
+
border-radius: 6px;
|
|
334
|
+
padding: 10px 24px;
|
|
335
|
+
font-size: 14px;
|
|
336
|
+
font-weight: 600;
|
|
337
|
+
cursor: pointer;
|
|
338
|
+
transition: background 0.2s;
|
|
339
|
+
}
|
|
340
|
+
.dropzone-send-btn:hover { background: #4fa0d8; }
|
|
341
|
+
.dropzone-send-btn:disabled { background: #555; cursor: not-allowed; }
|
|
342
|
+
.dropzone-disclosure {
|
|
343
|
+
font-size: 11px;
|
|
344
|
+
color: var(--text-dim);
|
|
345
|
+
font-style: italic;
|
|
346
|
+
}
|
|
347
|
+
.dropzone-status {
|
|
348
|
+
margin-top: 12px;
|
|
349
|
+
padding: 10px 14px;
|
|
350
|
+
border-radius: 6px;
|
|
351
|
+
font-size: 13px;
|
|
352
|
+
}
|
|
353
|
+
.dropzone-status.success {
|
|
354
|
+
background: rgba(46, 160, 67, 0.15);
|
|
355
|
+
color: #3fb950;
|
|
356
|
+
border: 1px solid rgba(46, 160, 67, 0.3);
|
|
357
|
+
}
|
|
358
|
+
.dropzone-status.error {
|
|
359
|
+
background: rgba(248, 81, 73, 0.15);
|
|
360
|
+
color: #f85149;
|
|
361
|
+
border: 1px solid rgba(248, 81, 73, 0.3);
|
|
362
|
+
}
|
|
363
|
+
.dropzone-status.queued {
|
|
364
|
+
background: rgba(210, 153, 34, 0.15);
|
|
365
|
+
color: #d29922;
|
|
366
|
+
border: 1px solid rgba(210, 153, 34, 0.3);
|
|
367
|
+
}
|
|
368
|
+
.dropzone-history {
|
|
369
|
+
margin-top: 28px;
|
|
370
|
+
}
|
|
371
|
+
.dropzone-history h3 {
|
|
372
|
+
color: var(--text-bright);
|
|
373
|
+
font-size: 15px;
|
|
374
|
+
margin-bottom: 12px;
|
|
375
|
+
}
|
|
376
|
+
.dropzone-history-list {
|
|
377
|
+
display: flex;
|
|
378
|
+
flex-direction: column;
|
|
379
|
+
gap: 8px;
|
|
380
|
+
}
|
|
381
|
+
.dropzone-empty {
|
|
382
|
+
color: var(--text-dim);
|
|
383
|
+
font-size: 13px;
|
|
384
|
+
font-style: italic;
|
|
385
|
+
}
|
|
386
|
+
.dz-paste-item {
|
|
387
|
+
display: flex;
|
|
388
|
+
align-items: center;
|
|
389
|
+
justify-content: space-between;
|
|
390
|
+
background: var(--surface);
|
|
391
|
+
border: 1px solid var(--border);
|
|
392
|
+
border-radius: 6px;
|
|
393
|
+
padding: 10px 14px;
|
|
394
|
+
}
|
|
395
|
+
.dz-paste-info {
|
|
396
|
+
display: flex;
|
|
397
|
+
flex-direction: column;
|
|
398
|
+
gap: 2px;
|
|
399
|
+
}
|
|
400
|
+
.dz-paste-label {
|
|
401
|
+
color: var(--text);
|
|
402
|
+
font-size: 13px;
|
|
403
|
+
font-weight: 500;
|
|
404
|
+
}
|
|
405
|
+
.dz-paste-meta {
|
|
406
|
+
color: var(--text-dim);
|
|
407
|
+
font-size: 11px;
|
|
408
|
+
}
|
|
409
|
+
.dz-paste-status {
|
|
410
|
+
font-size: 11px;
|
|
411
|
+
padding: 2px 8px;
|
|
412
|
+
border-radius: 4px;
|
|
413
|
+
font-weight: 500;
|
|
414
|
+
}
|
|
415
|
+
.dz-paste-status.written { background: rgba(210, 153, 34, 0.2); color: #d29922; }
|
|
416
|
+
.dz-paste-status.notified { background: rgba(46, 160, 67, 0.2); color: #3fb950; }
|
|
417
|
+
.dz-paste-status.acknowledged { background: rgba(56, 132, 255, 0.2); color: #58a6ff; }
|
|
418
|
+
.dz-paste-delete {
|
|
419
|
+
background: none;
|
|
420
|
+
border: none;
|
|
421
|
+
color: var(--text-dim);
|
|
422
|
+
cursor: pointer;
|
|
423
|
+
font-size: 14px;
|
|
424
|
+
padding: 4px 8px;
|
|
425
|
+
}
|
|
426
|
+
.dz-paste-delete:hover { color: #f85149; }
|
|
427
|
+
@media (max-width: 768px) {
|
|
428
|
+
.dropzone-container { padding: 16px; }
|
|
429
|
+
.dropzone-actions { flex-direction: column; }
|
|
430
|
+
.dropzone-session-select { width: 100%; }
|
|
431
|
+
}
|
|
432
|
+
|
|
249
433
|
/* WhatsApp QR panel */
|
|
250
434
|
.wa-qr-panel {
|
|
251
435
|
position: fixed;
|
|
@@ -1295,6 +1479,7 @@
|
|
|
1295
1479
|
<nav class="tab-bar">
|
|
1296
1480
|
<button class="tab active" data-tab="sessions" onclick="switchTab('sessions')">Sessions <span class="tab-count" id="tabSessionCount">0</span></button>
|
|
1297
1481
|
<button class="tab" data-tab="files" onclick="switchTab('files')">Files</button>
|
|
1482
|
+
<button class="tab" data-tab="dropzone" onclick="switchTab('dropzone')">Drop Zone</button>
|
|
1298
1483
|
</nav>
|
|
1299
1484
|
</div>
|
|
1300
1485
|
<button class="wa-status-btn" id="waStatusBtn" onclick="toggleQrPanel()">WhatsApp</button>
|
|
@@ -1396,6 +1581,45 @@
|
|
|
1396
1581
|
</div>
|
|
1397
1582
|
</div>
|
|
1398
1583
|
|
|
1584
|
+
<!-- Drop Zone tab -->
|
|
1585
|
+
<div class="dropzone-container" id="dropzoneTab" style="display:none">
|
|
1586
|
+
<div class="dropzone-panel">
|
|
1587
|
+
<div class="dropzone-header">
|
|
1588
|
+
<h2>Drop Zone</h2>
|
|
1589
|
+
<p class="dropzone-subtitle">Send large text content to your agent session</p>
|
|
1590
|
+
</div>
|
|
1591
|
+
|
|
1592
|
+
<div class="dropzone-form">
|
|
1593
|
+
<input type="text" class="dropzone-label" id="dzLabel"
|
|
1594
|
+
placeholder="What is this? (optional)" maxlength="256">
|
|
1595
|
+
|
|
1596
|
+
<div class="dropzone-textarea-wrap">
|
|
1597
|
+
<textarea class="dropzone-textarea" id="dzContent"
|
|
1598
|
+
placeholder="Paste your content here..."
|
|
1599
|
+
oninput="updateDzCount()"></textarea>
|
|
1600
|
+
<div class="dropzone-count" id="dzCount">0 chars · ~0 tokens</div>
|
|
1601
|
+
</div>
|
|
1602
|
+
|
|
1603
|
+
<div class="dropzone-actions">
|
|
1604
|
+
<select class="dropzone-session-select" id="dzSession">
|
|
1605
|
+
<option value="">Auto (most recent session)</option>
|
|
1606
|
+
</select>
|
|
1607
|
+
<button class="dropzone-send-btn" id="dzSendBtn" onclick="sendPaste()">Send</button>
|
|
1608
|
+
</div>
|
|
1609
|
+
<div class="dropzone-disclosure">Stored locally for up to 7 days. Avoid pasting secrets or credentials.</div>
|
|
1610
|
+
</div>
|
|
1611
|
+
|
|
1612
|
+
<div class="dropzone-status" id="dzStatus" style="display:none"></div>
|
|
1613
|
+
|
|
1614
|
+
<div class="dropzone-history">
|
|
1615
|
+
<h3>Recent Pastes</h3>
|
|
1616
|
+
<div class="dropzone-history-list" id="dzHistoryList">
|
|
1617
|
+
<div class="dropzone-empty">No pastes yet</div>
|
|
1618
|
+
</div>
|
|
1619
|
+
</div>
|
|
1620
|
+
</div>
|
|
1621
|
+
</div>
|
|
1622
|
+
|
|
1399
1623
|
<!-- WhatsApp QR panel (hidden by default) -->
|
|
1400
1624
|
<div class="wa-qr-backdrop" id="waQrBackdrop" style="display:none" onclick="closeQrPanel()"></div>
|
|
1401
1625
|
<div class="wa-qr-panel" id="waQrPanel" style="display:none">
|
|
@@ -2011,17 +2235,27 @@
|
|
|
2011
2235
|
const sessionsTab = document.getElementById('sessionsTab');
|
|
2012
2236
|
const mainPanel = document.getElementById('mainPanel');
|
|
2013
2237
|
const filesTab = document.getElementById('filesTab');
|
|
2238
|
+
const dropzoneTab = document.getElementById('dropzoneTab');
|
|
2239
|
+
|
|
2240
|
+
sessionsTab.style.display = 'none';
|
|
2241
|
+
mainPanel.style.display = 'none';
|
|
2242
|
+
filesTab.style.display = 'none';
|
|
2243
|
+
dropzoneTab.style.display = 'none';
|
|
2014
2244
|
|
|
2015
2245
|
if (tabName === 'sessions') {
|
|
2016
2246
|
sessionsTab.style.display = '';
|
|
2017
2247
|
mainPanel.style.display = '';
|
|
2018
|
-
filesTab.style.display = 'none';
|
|
2019
2248
|
} else if (tabName === 'files') {
|
|
2020
|
-
sessionsTab.style.display = 'none';
|
|
2021
|
-
mainPanel.style.display = 'none';
|
|
2022
2249
|
filesTab.style.display = 'flex';
|
|
2023
2250
|
// Load file tree on first switch
|
|
2024
2251
|
if (!fileTreeLoaded) loadFileTree();
|
|
2252
|
+
} else if (tabName === 'dropzone') {
|
|
2253
|
+
dropzoneTab.style.display = 'flex';
|
|
2254
|
+
// Auto-focus textarea
|
|
2255
|
+
setTimeout(() => document.getElementById('dzContent')?.focus(), 100);
|
|
2256
|
+
// Load session list and paste history
|
|
2257
|
+
loadDzSessions();
|
|
2258
|
+
loadDzHistory();
|
|
2025
2259
|
}
|
|
2026
2260
|
|
|
2027
2261
|
// Update URL
|
|
@@ -2759,6 +2993,169 @@
|
|
|
2759
2993
|
deepLinkObserver.observe(document.getElementById('authOverlay'), {
|
|
2760
2994
|
attributes: true, attributeFilter: ['style'],
|
|
2761
2995
|
});
|
|
2996
|
+
|
|
2997
|
+
// ── Drop Zone ──────────────────────────────────────────────
|
|
2998
|
+
|
|
2999
|
+
function updateDzCount() {
|
|
3000
|
+
const content = document.getElementById('dzContent').value;
|
|
3001
|
+
const chars = content.length;
|
|
3002
|
+
// Rough token estimate: ~4 chars per token for English
|
|
3003
|
+
const tokens = Math.round(chars / 4);
|
|
3004
|
+
document.getElementById('dzCount').textContent =
|
|
3005
|
+
`${chars.toLocaleString()} chars \u00b7 ~${tokens.toLocaleString()} tokens`;
|
|
3006
|
+
}
|
|
3007
|
+
|
|
3008
|
+
async function loadDzSessions() {
|
|
3009
|
+
const select = document.getElementById('dzSession');
|
|
3010
|
+
// Keep the first "Auto" option, remove the rest
|
|
3011
|
+
while (select.options.length > 1) select.remove(1);
|
|
3012
|
+
|
|
3013
|
+
// Use the cached session list from WebSocket if available
|
|
3014
|
+
if (sessions && sessions.length > 0) {
|
|
3015
|
+
const interactive = sessions.filter(s => !s.jobSlug);
|
|
3016
|
+
for (const s of interactive) {
|
|
3017
|
+
const opt = document.createElement('option');
|
|
3018
|
+
opt.value = s.name;
|
|
3019
|
+
opt.textContent = s.name + (s.model ? ` (${s.model})` : '');
|
|
3020
|
+
select.appendChild(opt);
|
|
3021
|
+
}
|
|
3022
|
+
}
|
|
3023
|
+
}
|
|
3024
|
+
|
|
3025
|
+
async function loadDzHistory() {
|
|
3026
|
+
try {
|
|
3027
|
+
const resp = await fetch('/pastes', {
|
|
3028
|
+
headers: { 'Authorization': 'Bearer ' + token },
|
|
3029
|
+
});
|
|
3030
|
+
if (!resp.ok) return;
|
|
3031
|
+
const data = await resp.json();
|
|
3032
|
+
renderDzHistory(data.pastes || []);
|
|
3033
|
+
} catch {}
|
|
3034
|
+
}
|
|
3035
|
+
|
|
3036
|
+
function renderDzHistory(pastes) {
|
|
3037
|
+
const list = document.getElementById('dzHistoryList');
|
|
3038
|
+
if (!pastes.length) {
|
|
3039
|
+
list.innerHTML = '<div class="dropzone-empty">No pastes yet</div>';
|
|
3040
|
+
return;
|
|
3041
|
+
}
|
|
3042
|
+
list.innerHTML = pastes.slice(0, 20).map(p => {
|
|
3043
|
+
const age = timeAgo(new Date(p.timestamp));
|
|
3044
|
+
const label = p.label || '(unlabeled)';
|
|
3045
|
+
const chars = p.contentLength.toLocaleString();
|
|
3046
|
+
return `
|
|
3047
|
+
<div class="dz-paste-item" data-paste-id="${esc(p.pasteId)}">
|
|
3048
|
+
<div class="dz-paste-info">
|
|
3049
|
+
<span class="dz-paste-label">${esc(label)}</span>
|
|
3050
|
+
<span class="dz-paste-meta">${chars} chars · ${age}</span>
|
|
3051
|
+
</div>
|
|
3052
|
+
<div style="display:flex;align-items:center;gap:8px">
|
|
3053
|
+
<span class="dz-paste-status ${p.status}">${p.status}</span>
|
|
3054
|
+
<button class="dz-paste-delete" onclick="deletePaste('${esc(p.pasteId)}')" title="Delete">×</button>
|
|
3055
|
+
</div>
|
|
3056
|
+
</div>`;
|
|
3057
|
+
}).join('');
|
|
3058
|
+
}
|
|
3059
|
+
|
|
3060
|
+
async function sendPaste() {
|
|
3061
|
+
const content = document.getElementById('dzContent').value.trim();
|
|
3062
|
+
if (!content) return;
|
|
3063
|
+
|
|
3064
|
+
const label = document.getElementById('dzLabel').value.trim() || undefined;
|
|
3065
|
+
const targetSession = document.getElementById('dzSession').value || undefined;
|
|
3066
|
+
const btn = document.getElementById('dzSendBtn');
|
|
3067
|
+
const status = document.getElementById('dzStatus');
|
|
3068
|
+
|
|
3069
|
+
btn.disabled = true;
|
|
3070
|
+
btn.textContent = 'Sending...';
|
|
3071
|
+
status.style.display = 'none';
|
|
3072
|
+
|
|
3073
|
+
try {
|
|
3074
|
+
const resp = await fetch('/pastes', {
|
|
3075
|
+
method: 'POST',
|
|
3076
|
+
headers: {
|
|
3077
|
+
'Authorization': 'Bearer ' + token,
|
|
3078
|
+
'Content-Type': 'application/json',
|
|
3079
|
+
},
|
|
3080
|
+
body: JSON.stringify({ content, label, targetSession }),
|
|
3081
|
+
});
|
|
3082
|
+
|
|
3083
|
+
const data = await resp.json();
|
|
3084
|
+
|
|
3085
|
+
if (resp.ok && data.ok) {
|
|
3086
|
+
const chars = data.contentLength?.toLocaleString() || content.length.toLocaleString();
|
|
3087
|
+
if (data.status === 'notified') {
|
|
3088
|
+
status.className = 'dropzone-status success';
|
|
3089
|
+
status.textContent = `Sent ${chars} chars to session "${data.sessionName}"`;
|
|
3090
|
+
} else {
|
|
3091
|
+
status.className = 'dropzone-status queued';
|
|
3092
|
+
status.textContent = `Queued ${chars} chars \u2014 will be delivered when a session starts.`;
|
|
3093
|
+
}
|
|
3094
|
+
status.style.display = 'block';
|
|
3095
|
+
|
|
3096
|
+
// Clear form
|
|
3097
|
+
document.getElementById('dzContent').value = '';
|
|
3098
|
+
document.getElementById('dzLabel').value = '';
|
|
3099
|
+
updateDzCount();
|
|
3100
|
+
|
|
3101
|
+
// Refresh history
|
|
3102
|
+
loadDzHistory();
|
|
3103
|
+
} else {
|
|
3104
|
+
status.className = 'dropzone-status error';
|
|
3105
|
+
status.textContent = data.message || data.error || 'Failed to send paste';
|
|
3106
|
+
status.style.display = 'block';
|
|
3107
|
+
}
|
|
3108
|
+
} catch (err) {
|
|
3109
|
+
status.className = 'dropzone-status error';
|
|
3110
|
+
status.textContent = 'Network error: ' + err.message;
|
|
3111
|
+
status.style.display = 'block';
|
|
3112
|
+
}
|
|
3113
|
+
|
|
3114
|
+
btn.disabled = false;
|
|
3115
|
+
btn.textContent = 'Send';
|
|
3116
|
+
}
|
|
3117
|
+
|
|
3118
|
+
async function deletePaste(pasteId) {
|
|
3119
|
+
try {
|
|
3120
|
+
await fetch('/pastes/' + encodeURIComponent(pasteId), {
|
|
3121
|
+
method: 'DELETE',
|
|
3122
|
+
headers: { 'Authorization': 'Bearer ' + token },
|
|
3123
|
+
});
|
|
3124
|
+
loadDzHistory();
|
|
3125
|
+
} catch {}
|
|
3126
|
+
}
|
|
3127
|
+
|
|
3128
|
+
function timeAgo(date) {
|
|
3129
|
+
const seconds = Math.floor((Date.now() - date.getTime()) / 1000);
|
|
3130
|
+
if (seconds < 60) return 'just now';
|
|
3131
|
+
const minutes = Math.floor(seconds / 60);
|
|
3132
|
+
if (minutes < 60) return minutes + 'm ago';
|
|
3133
|
+
const hours = Math.floor(minutes / 60);
|
|
3134
|
+
if (hours < 24) return hours + 'h ago';
|
|
3135
|
+
const days = Math.floor(hours / 24);
|
|
3136
|
+
return days + 'd ago';
|
|
3137
|
+
}
|
|
3138
|
+
|
|
3139
|
+
function esc(s) {
|
|
3140
|
+
const d = document.createElement('div');
|
|
3141
|
+
d.textContent = s;
|
|
3142
|
+
return d.innerHTML;
|
|
3143
|
+
}
|
|
3144
|
+
|
|
3145
|
+
// Listen for WebSocket paste events
|
|
3146
|
+
const origWsOnMessage = ws?.onmessage;
|
|
3147
|
+
if (typeof ws !== 'undefined') {
|
|
3148
|
+
const origOnMessage = ws.onmessage;
|
|
3149
|
+
ws.addEventListener('message', function(event) {
|
|
3150
|
+
try {
|
|
3151
|
+
const msg = JSON.parse(event.data);
|
|
3152
|
+
if (msg.type === 'paste_delivered' || msg.type === 'paste_acknowledged') {
|
|
3153
|
+
loadDzHistory();
|
|
3154
|
+
}
|
|
3155
|
+
} catch {}
|
|
3156
|
+
});
|
|
3157
|
+
}
|
|
3158
|
+
|
|
2762
3159
|
</script>
|
|
2763
3160
|
</body>
|
|
2764
3161
|
</html>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAqPH,UAAU,YAAY;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;2DACuD;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAy+BD,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA0xEtE;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAsDzE;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuD5E"}
|
package/dist/commands/server.js
CHANGED
|
@@ -70,6 +70,7 @@ import { LiveConfig } from '../config/LiveConfig.js';
|
|
|
70
70
|
import { CoherenceMonitor } from '../monitoring/CoherenceMonitor.js';
|
|
71
71
|
import { ProcessIntegrity } from '../core/ProcessIntegrity.js';
|
|
72
72
|
import { StaleProcessGuard } from '../core/StaleProcessGuard.js';
|
|
73
|
+
import { cleanupGlobalInstalls } from '../core/GlobalInstallCleanup.js';
|
|
73
74
|
import { ForegroundRestartWatcher } from '../core/ForegroundRestartWatcher.js';
|
|
74
75
|
import { NotificationBatcher } from '../messaging/NotificationBatcher.js';
|
|
75
76
|
import { MessageStore } from '../messaging/MessageStore.js';
|
|
@@ -730,7 +731,12 @@ function wireTelegramRouting(telegram, sessionManager, quotaTracker, topicMemory
|
|
|
730
731
|
? userManager.resolveFromTelegramUserId(telegramUserId)
|
|
731
732
|
: null;
|
|
732
733
|
// Most commands are handled inside TelegramAdapter.handleCommand().
|
|
733
|
-
// /new
|
|
734
|
+
// /new — create a new topic thread. Does NOT spawn a session immediately.
|
|
735
|
+
// Sessions are spawned on-demand when the user sends their first real message
|
|
736
|
+
// in the new topic (via the auto-spawn path below). This avoids premature
|
|
737
|
+
// session exit: spawning with a meta-message ("new session started") gives
|
|
738
|
+
// Claude nothing real to do, so it responds and exits. The user's actual
|
|
739
|
+
// message then arrives to a dead session.
|
|
734
740
|
const newMatch = text.match(/^\/new(?:\s+(.+))?$/);
|
|
735
741
|
if (newMatch) {
|
|
736
742
|
const sessionName = newMatch[1]?.trim() || null;
|
|
@@ -740,16 +746,15 @@ function wireTelegramRouting(telegram, sessionManager, quotaTracker, topicMemory
|
|
|
740
746
|
(async () => {
|
|
741
747
|
try {
|
|
742
748
|
const topic = await telegram.findOrCreateForumTopic(topicDisplayName, TOPIC_STYLE.SESSION.color);
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
await telegram.sendToTopic(topic.topicId, `
|
|
746
|
-
await telegram.sendToTopic(topicId, `
|
|
747
|
-
console.log(`[telegram]
|
|
749
|
+
// Don't create a session — findOrCreateForumTopic already stored the topic name.
|
|
750
|
+
// The first message in this topic will trigger auto-spawn with real content.
|
|
751
|
+
await telegram.sendToTopic(topic.topicId, `Ready — send your first message to start.`);
|
|
752
|
+
await telegram.sendToTopic(topicId, `Created topic "${topicName}" — head over there.`);
|
|
753
|
+
console.log(`[telegram] Created topic "${topicName}" (${topic.topicId}) — session will spawn on first message`);
|
|
748
754
|
}
|
|
749
755
|
catch (err) {
|
|
750
756
|
console.error(`[telegram] /new failed:`, err);
|
|
751
|
-
|
|
752
|
-
await telegram.sendToTopic(topicId, 'Couldn\'t create the new session. Try again in a moment.').catch(() => { });
|
|
757
|
+
await telegram.sendToTopic(topicId, 'Couldn\'t create the topic. Try again in a moment.').catch(() => { });
|
|
753
758
|
}
|
|
754
759
|
})();
|
|
755
760
|
return;
|
|
@@ -1218,6 +1223,29 @@ export async function startServer(options) {
|
|
|
1218
1223
|
console.warn(pc.red(` rm -rf ${path.join(process.cwd(), 'node_modules')} ${path.join(process.cwd(), 'package.json')} ${path.join(process.cwd(), 'package-lock.json')}`));
|
|
1219
1224
|
console.warn();
|
|
1220
1225
|
}
|
|
1226
|
+
// ── Global install cleanup ─────────────────────────────────────────
|
|
1227
|
+
// Shadow installs are the sole source of truth. Global installs cause
|
|
1228
|
+
// version confusion — agents report stale versions when CLI commands
|
|
1229
|
+
// resolve to a global binary instead of the shadow install.
|
|
1230
|
+
// Clean up any lingering globals at startup (idempotent, safe to run every time).
|
|
1231
|
+
try {
|
|
1232
|
+
const cleanup = cleanupGlobalInstalls();
|
|
1233
|
+
if (cleanup.removed.length > 0) {
|
|
1234
|
+
console.log(pc.green(` ✓ Cleaned up ${cleanup.removed.length} stale global instar install(s):`));
|
|
1235
|
+
for (const r of cleanup.removed) {
|
|
1236
|
+
console.log(pc.green(` - ${r}`));
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
if (cleanup.failed.length > 0) {
|
|
1240
|
+
for (const f of cleanup.failed) {
|
|
1241
|
+
console.warn(pc.yellow(` ⚠ Failed to remove global install at ${f.path}: ${f.error}`));
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
catch (err) {
|
|
1246
|
+
// Non-fatal — log and continue
|
|
1247
|
+
console.warn(`[server] Global install cleanup error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1248
|
+
}
|
|
1221
1249
|
// ── ProcessIntegrity: freeze the running version at startup ────────
|
|
1222
1250
|
// This MUST happen before any version reporting. The version is captured
|
|
1223
1251
|
// from the code loaded into memory, NOT from disk (which changes after
|
|
@@ -2264,6 +2292,18 @@ export async function startServer(options) {
|
|
|
2264
2292
|
viewsDir: path.join(config.stateDir, 'views'),
|
|
2265
2293
|
});
|
|
2266
2294
|
console.log(pc.green(` Private viewer enabled`));
|
|
2295
|
+
// Set up paste manager (Drop Zone — always enabled)
|
|
2296
|
+
const { PasteManager } = await import('../paste/PasteManager.js');
|
|
2297
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- paste config fields are optional extensions
|
|
2298
|
+
const cfgAny = config;
|
|
2299
|
+
const pasteManager = new PasteManager({
|
|
2300
|
+
pasteDir: path.join(config.stateDir, 'paste'),
|
|
2301
|
+
stateDir: path.join(config.stateDir, 'state'),
|
|
2302
|
+
projectDir: config.projectDir,
|
|
2303
|
+
maxSizeBytes: cfgAny.pasteMaxSizeMB ? cfgAny.pasteMaxSizeMB * 1024 * 1024 : undefined,
|
|
2304
|
+
retentionDays: cfgAny.pasteRetentionDays ?? undefined,
|
|
2305
|
+
});
|
|
2306
|
+
console.log(pc.green(` Drop Zone (paste) enabled`));
|
|
2267
2307
|
// Set up Cloudflare Tunnel — enabled by default (quick tunnel, zero-config)
|
|
2268
2308
|
// Only disabled if explicitly set to tunnel.enabled = false
|
|
2269
2309
|
const tunnelEnabled = config.tunnel?.enabled !== false;
|
|
@@ -2987,7 +3027,7 @@ export async function startServer(options) {
|
|
|
2987
3027
|
console.warn(pc.yellow(` Response review pipeline: configured but ANTHROPIC_API_KEY not set`));
|
|
2988
3028
|
}
|
|
2989
3029
|
}
|
|
2990
|
-
const server = new AgentServer({ config, sessionManager, state, scheduler, telegram, relationships, feedback, feedbackAnomalyDetector, dispatches, updateChecker, autoUpdater, autoDispatcher, quotaTracker, quotaManager, publisher, viewer, tunnel, evolution, watchdog, topicMemory, triageNurse, projectMapper, coherenceGate: scopeVerifier, contextHierarchy, canonicalState, operationGate, sentinel, adaptiveTrust, memoryMonitor, orphanReaper, coherenceMonitor, commitmentTracker, semanticMemory, activitySentinel, messageRouter, summarySentinel, spawnManager, systemReviewer, capabilityMapper, selfKnowledgeTree, coverageAuditor, topicResumeMap: _topicResumeMap ?? undefined, autonomyManager, trustElevationTracker, autonomousEvolution, coordinator: coordinator.enabled ? coordinator : undefined, localSigningKeyPem, whatsapp: whatsappAdapter, whatsappBusinessBackend, messageBridge, hookEventReceiver, worktreeMonitor, subagentTracker, instructionsVerifier, handshakeManager: threadlineHandshake, threadlineRelayClient, responseReviewGate, telemetryHeartbeat, liveConfig });
|
|
3030
|
+
const server = new AgentServer({ config, sessionManager, state, scheduler, telegram, relationships, feedback, feedbackAnomalyDetector, dispatches, updateChecker, autoUpdater, autoDispatcher, quotaTracker, quotaManager, publisher, viewer, tunnel, evolution, watchdog, topicMemory, triageNurse, projectMapper, coherenceGate: scopeVerifier, contextHierarchy, canonicalState, operationGate, sentinel, adaptiveTrust, memoryMonitor, orphanReaper, coherenceMonitor, commitmentTracker, semanticMemory, activitySentinel, messageRouter, summarySentinel, spawnManager, systemReviewer, capabilityMapper, selfKnowledgeTree, coverageAuditor, topicResumeMap: _topicResumeMap ?? undefined, autonomyManager, trustElevationTracker, autonomousEvolution, coordinator: coordinator.enabled ? coordinator : undefined, localSigningKeyPem, whatsapp: whatsappAdapter, whatsappBusinessBackend, messageBridge, hookEventReceiver, worktreeMonitor, subagentTracker, instructionsVerifier, handshakeManager: threadlineHandshake, threadlineRelayClient, responseReviewGate, telemetryHeartbeat, pasteManager, liveConfig });
|
|
2991
3031
|
await server.start();
|
|
2992
3032
|
// Connect DegradationReporter downstream systems now that everything is initialized.
|
|
2993
3033
|
// Any degradation events queued during startup will drain to feedback + telegram.
|