@agent-link/server 0.1.88 → 0.1.90
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.js +18 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/web/app.js +45 -16
- package/web/modules/connection.js +7 -1
- package/web/style.css +40 -0
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import express from 'express';
|
|
2
2
|
import { createServer } from 'http';
|
|
3
3
|
import { createRequire } from 'module';
|
|
4
|
+
import { readFileSync } from 'fs';
|
|
4
5
|
import { WebSocket, WebSocketServer } from 'ws';
|
|
5
6
|
import { fileURLToPath } from 'url';
|
|
6
7
|
import { dirname, join } from 'path';
|
|
@@ -13,6 +14,13 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
13
14
|
const require = createRequire(import.meta.url);
|
|
14
15
|
const pkg = require('../package.json');
|
|
15
16
|
const startedAt = new Date();
|
|
17
|
+
// Build versioned index.html at startup (cache-busting for browsers like iPhone Safari)
|
|
18
|
+
const versionTag = `v=${pkg.version}`;
|
|
19
|
+
const indexHtml = readFileSync(join(__dirname, '../web/index.html'), 'utf-8')
|
|
20
|
+
// Version-stamp local asset URLs in src="..." and href="..." attributes
|
|
21
|
+
.replace(/((?:src|href)\s*=\s*")(\/(?!\/)[^"?]+\.(?:js|css))(")/g, `$1$2?${versionTag}$3`)
|
|
22
|
+
// Version-stamp JS string literals referencing local .css/.js paths (e.g. theme switching)
|
|
23
|
+
.replace(/(')(\/(?!\/)[^'?]+\.(?:js|css))(')/g, `$1$2?${versionTag}$3`);
|
|
16
24
|
const PORT = parseInt(process.env.PORT || '3456', 10);
|
|
17
25
|
const app = express();
|
|
18
26
|
const server = createServer(app);
|
|
@@ -22,6 +30,14 @@ const webDir = join(__dirname, '../web');
|
|
|
22
30
|
app.get('/', (_req, res) => {
|
|
23
31
|
res.sendFile(join(webDir, 'landing.html'));
|
|
24
32
|
});
|
|
33
|
+
// Serve versioned index.html with no-store (cache-busting for browsers like iPhone Safari)
|
|
34
|
+
const sendIndexHtml = (_req, res) => {
|
|
35
|
+
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
|
36
|
+
res.setHeader('Cache-Control', 'no-store');
|
|
37
|
+
res.send(indexHtml);
|
|
38
|
+
};
|
|
39
|
+
// Intercept /index.html before express.static to ensure versioned HTML is served
|
|
40
|
+
app.get('/index.html', sendIndexHtml);
|
|
25
41
|
// Serve static assets from web/ with no-cache for JS/CSS (ensures updates are picked up)
|
|
26
42
|
app.use(express.static(webDir, {
|
|
27
43
|
setHeaders(res, filePath) {
|
|
@@ -30,10 +46,8 @@ app.use(express.static(webDir, {
|
|
|
30
46
|
}
|
|
31
47
|
},
|
|
32
48
|
}));
|
|
33
|
-
// SPA fallback: /s/:sessionId → serve index.html
|
|
34
|
-
app.get('/s/:sessionId',
|
|
35
|
-
res.sendFile(join(webDir, 'index.html'));
|
|
36
|
-
});
|
|
49
|
+
// SPA fallback: /s/:sessionId → serve versioned index.html
|
|
50
|
+
app.get('/s/:sessionId', sendIndexHtml);
|
|
37
51
|
// Health check
|
|
38
52
|
app.get('/api/health', (_req, res) => {
|
|
39
53
|
res.json({
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,IAAI,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAC1E,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1D,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,MAAM,GAAG,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;AACvC,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC;AAE7B,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,MAAM,EAAE,EAAE,CAAC,CAAC;AAEtD,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;AACtB,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;AACjC,MAAM,GAAG,GAAG,IAAI,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;AAE5C,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AAEzC,uBAAuB;AACvB,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;IACzB,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC;AAC7C,CAAC,CAAC,CAAC;AAEH,yFAAyF;AACzF,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE;IAC7B,UAAU,CAAC,GAAG,EAAE,QAAQ;QACtB,IAAI,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YACxF,GAAG,CAAC,SAAS,CAAC,eAAe,EAAE,UAAU,CAAC,CAAC;QAC7C,CAAC;IACH,CAAC;CACF,CAAC,CAAC,CAAC;AAEJ
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/web/app.js
CHANGED
|
@@ -154,7 +154,7 @@ const App = {
|
|
|
154
154
|
hostname, workdirHistory,
|
|
155
155
|
});
|
|
156
156
|
|
|
157
|
-
const { connect, wsSend, closeWs, submitPassword } = createConnection({
|
|
157
|
+
const { connect, wsSend, closeWs, submitPassword, setDequeueNext } = createConnection({
|
|
158
158
|
status, agentName, hostname, workDir, sessionId, error,
|
|
159
159
|
serverVersion, agentVersion,
|
|
160
160
|
messages, isProcessing, isCompacting, visibleLimit,
|
|
@@ -166,10 +166,12 @@ const App = {
|
|
|
166
166
|
|
|
167
167
|
// Now wire up the forwarding function
|
|
168
168
|
_wsSend = wsSend;
|
|
169
|
+
setDequeueNext(dequeueNext);
|
|
169
170
|
|
|
170
171
|
// ── Computed ──
|
|
172
|
+
const hasInput = computed(() => !!(inputText.value.trim() || attachments.value.length > 0));
|
|
171
173
|
const canSend = computed(() =>
|
|
172
|
-
status.value === 'Connected' &&
|
|
174
|
+
status.value === 'Connected' && hasInput.value && !isCompacting.value
|
|
173
175
|
&& !messages.value.some(m => m.role === 'ask-question' && !m.answered)
|
|
174
176
|
);
|
|
175
177
|
|
|
@@ -195,15 +197,6 @@ const App = {
|
|
|
195
197
|
name: f.name, size: f.size, isImage: f.isImage, thumbUrl: f.thumbUrl,
|
|
196
198
|
}));
|
|
197
199
|
|
|
198
|
-
messages.value.push({
|
|
199
|
-
id: streaming.nextId(), role: 'user',
|
|
200
|
-
content: text || (files.length > 0 ? `[${files.length} file${files.length > 1 ? 's' : ''} attached]` : ''),
|
|
201
|
-
attachments: msgAttachments.length > 0 ? msgAttachments : undefined,
|
|
202
|
-
timestamp: new Date(),
|
|
203
|
-
});
|
|
204
|
-
isProcessing.value = true;
|
|
205
|
-
scrollToBottom(true);
|
|
206
|
-
|
|
207
200
|
const payload = { type: 'chat', prompt: text || '(see attached files)' };
|
|
208
201
|
if (needsResume.value && currentClaudeSessionId.value) {
|
|
209
202
|
payload.resumeSessionId = currentClaudeSessionId.value;
|
|
@@ -214,7 +207,25 @@ const App = {
|
|
|
214
207
|
name: f.name, mimeType: f.mimeType, data: f.data,
|
|
215
208
|
}));
|
|
216
209
|
}
|
|
217
|
-
|
|
210
|
+
|
|
211
|
+
const userMsg = {
|
|
212
|
+
id: streaming.nextId(), role: 'user',
|
|
213
|
+
content: text || (files.length > 0 ? `[${files.length} file${files.length > 1 ? 's' : ''} attached]` : ''),
|
|
214
|
+
attachments: msgAttachments.length > 0 ? msgAttachments : undefined,
|
|
215
|
+
timestamp: new Date(),
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
if (isProcessing.value) {
|
|
219
|
+
userMsg.status = 'queued';
|
|
220
|
+
userMsg._queuedPayload = payload;
|
|
221
|
+
messages.value.push(userMsg);
|
|
222
|
+
} else {
|
|
223
|
+
userMsg.status = 'sent';
|
|
224
|
+
messages.value.push(userMsg);
|
|
225
|
+
isProcessing.value = true;
|
|
226
|
+
wsSend(payload);
|
|
227
|
+
}
|
|
228
|
+
scrollToBottom(true);
|
|
218
229
|
attachments.value = [];
|
|
219
230
|
}
|
|
220
231
|
|
|
@@ -223,6 +234,22 @@ const App = {
|
|
|
223
234
|
wsSend({ type: 'cancel_execution' });
|
|
224
235
|
}
|
|
225
236
|
|
|
237
|
+
function dequeueNext() {
|
|
238
|
+
const idx = messages.value.findIndex(m => m.status === 'queued');
|
|
239
|
+
if (idx === -1) return;
|
|
240
|
+
const msg = messages.value[idx];
|
|
241
|
+
msg.status = 'sent';
|
|
242
|
+
isProcessing.value = true;
|
|
243
|
+
wsSend(msg._queuedPayload);
|
|
244
|
+
delete msg._queuedPayload;
|
|
245
|
+
scrollToBottom(true);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function removeQueuedMessage(msgId) {
|
|
249
|
+
const idx = messages.value.findIndex(m => m.id === msgId);
|
|
250
|
+
if (idx !== -1) messages.value.splice(idx, 1);
|
|
251
|
+
}
|
|
252
|
+
|
|
226
253
|
function handleKeydown(e) {
|
|
227
254
|
if (e.key === 'Enter' && !e.shiftKey) {
|
|
228
255
|
e.preventDefault();
|
|
@@ -256,8 +283,8 @@ const App = {
|
|
|
256
283
|
status, agentName, hostname, workDir, sessionId, error,
|
|
257
284
|
serverVersion, agentVersion,
|
|
258
285
|
messages, visibleMessages, hasMoreMessages, loadMoreMessages,
|
|
259
|
-
inputText, isProcessing, isCompacting, canSend, inputRef,
|
|
260
|
-
sendMessage, handleKeydown, cancelExecution, onMessageListScroll,
|
|
286
|
+
inputText, isProcessing, isCompacting, canSend, hasInput, inputRef,
|
|
287
|
+
sendMessage, handleKeydown, cancelExecution, removeQueuedMessage, onMessageListScroll,
|
|
261
288
|
getRenderedContent, copyMessage, toggleTool,
|
|
262
289
|
isPrevAssistant: _isPrevAssistant,
|
|
263
290
|
toggleContextSummary, formatTimestamp,
|
|
@@ -457,7 +484,8 @@ const App = {
|
|
|
457
484
|
<!-- User message -->
|
|
458
485
|
<template v-if="msg.role === 'user'">
|
|
459
486
|
<div class="message-role-label user-label">You</div>
|
|
460
|
-
<div class="message-bubble user-bubble" :title="formatTimestamp(msg.timestamp)">
|
|
487
|
+
<div :class="['message-bubble', 'user-bubble', { queued: msg.status === 'queued' }]" :title="formatTimestamp(msg.timestamp)">
|
|
488
|
+
<button v-if="msg.status === 'queued'" class="queue-remove-btn" @click="removeQueuedMessage(msg.id)" title="Remove from queue">×</button>
|
|
461
489
|
<div class="message-content">{{ msg.content }}</div>
|
|
462
490
|
<div v-if="msg.attachments && msg.attachments.length" class="message-attachments">
|
|
463
491
|
<div v-for="(att, ai) in msg.attachments" :key="ai" class="message-attachment-chip">
|
|
@@ -468,6 +496,7 @@ const App = {
|
|
|
468
496
|
<span>{{ att.name }}</span>
|
|
469
497
|
</div>
|
|
470
498
|
</div>
|
|
499
|
+
<div v-if="msg.status === 'queued'" class="queue-badge">queued</div>
|
|
471
500
|
</div>
|
|
472
501
|
</template>
|
|
473
502
|
|
|
@@ -620,7 +649,7 @@ const App = {
|
|
|
620
649
|
<button class="attach-btn" @click="triggerFileInput" :disabled="status !== 'Connected' || isCompacting || attachments.length >= 5" title="Attach files">
|
|
621
650
|
<svg viewBox="0 0 24 24" width="16" height="16"><path fill="currentColor" d="M16.5 6v11.5c0 2.21-1.79 4-4 4s-4-1.79-4-4V5a2.5 2.5 0 0 1 5 0v10.5c0 .55-.45 1-1 1s-1-.45-1-1V6H10v9.5a2.5 2.5 0 0 0 5 0V5c0-2.21-1.79-4-4-4S7 2.79 7 5v12.5c0 3.04 2.46 5.5 5.5 5.5s5.5-2.46 5.5-5.5V6h-1.5z"/></svg>
|
|
622
651
|
</button>
|
|
623
|
-
<button v-if="isProcessing" @click="cancelExecution" class="send-btn stop-btn" title="Stop generation">
|
|
652
|
+
<button v-if="isProcessing && !hasInput" @click="cancelExecution" class="send-btn stop-btn" title="Stop generation">
|
|
624
653
|
<svg viewBox="0 0 24 24" width="14" height="14"><rect x="6" y="6" width="12" height="12" rx="2" fill="currentColor"/></svg>
|
|
625
654
|
</button>
|
|
626
655
|
<button v-else @click="sendMessage" :disabled="!canSend" class="send-btn" title="Send">
|
|
@@ -22,6 +22,10 @@ export function createConnection(deps) {
|
|
|
22
22
|
scrollToBottom,
|
|
23
23
|
} = deps;
|
|
24
24
|
|
|
25
|
+
// Dequeue callback — set after creation to resolve circular dependency
|
|
26
|
+
let _dequeueNext = () => {};
|
|
27
|
+
function setDequeueNext(fn) { _dequeueNext = fn; }
|
|
28
|
+
|
|
25
29
|
let ws = null;
|
|
26
30
|
let sessionKey = null;
|
|
27
31
|
let reconnectAttempts = 0;
|
|
@@ -226,6 +230,7 @@ export function createConnection(deps) {
|
|
|
226
230
|
scrollToBottom();
|
|
227
231
|
isProcessing.value = false;
|
|
228
232
|
isCompacting.value = false;
|
|
233
|
+
_dequeueNext();
|
|
229
234
|
} else if (msg.type === 'claude_output') {
|
|
230
235
|
handleClaudeOutput(msg, scheduleHighlight);
|
|
231
236
|
} else if (msg.type === 'command_output') {
|
|
@@ -269,6 +274,7 @@ export function createConnection(deps) {
|
|
|
269
274
|
});
|
|
270
275
|
scrollToBottom();
|
|
271
276
|
}
|
|
277
|
+
_dequeueNext();
|
|
272
278
|
} else if (msg.type === 'ask_user_question') {
|
|
273
279
|
streaming.flushReveal();
|
|
274
280
|
finalizeStreamingMsg(scheduleHighlight);
|
|
@@ -446,5 +452,5 @@ export function createConnection(deps) {
|
|
|
446
452
|
ws.send(JSON.stringify({ type: 'authenticate', password: pwd }));
|
|
447
453
|
}
|
|
448
454
|
|
|
449
|
-
return { connect, wsSend, closeWs, submitPassword };
|
|
455
|
+
return { connect, wsSend, closeWs, submitPassword, setDequeueNext };
|
|
450
456
|
}
|
package/web/style.css
CHANGED
|
@@ -787,6 +787,46 @@ body {
|
|
|
787
787
|
color: var(--text-primary);
|
|
788
788
|
}
|
|
789
789
|
|
|
790
|
+
/* ── Queued message (pending send) ── */
|
|
791
|
+
.user-bubble.queued {
|
|
792
|
+
background: transparent;
|
|
793
|
+
border: 1px dashed var(--border);
|
|
794
|
+
opacity: 0.7;
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
.queue-badge {
|
|
798
|
+
font-size: 0.65rem;
|
|
799
|
+
font-weight: 600;
|
|
800
|
+
text-transform: uppercase;
|
|
801
|
+
letter-spacing: 0.05em;
|
|
802
|
+
color: var(--text-secondary);
|
|
803
|
+
text-align: right;
|
|
804
|
+
margin-top: 0.2rem;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
.queue-remove-btn {
|
|
808
|
+
position: absolute;
|
|
809
|
+
top: 4px;
|
|
810
|
+
right: 6px;
|
|
811
|
+
background: none;
|
|
812
|
+
border: none;
|
|
813
|
+
color: var(--text-secondary);
|
|
814
|
+
font-size: 1rem;
|
|
815
|
+
line-height: 1;
|
|
816
|
+
cursor: pointer;
|
|
817
|
+
padding: 0 2px;
|
|
818
|
+
opacity: 0;
|
|
819
|
+
transition: color 0.15s, opacity 0.15s;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
.user-bubble.queued:hover .queue-remove-btn {
|
|
823
|
+
opacity: 1;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
.queue-remove-btn:hover {
|
|
827
|
+
color: var(--error);
|
|
828
|
+
}
|
|
829
|
+
|
|
790
830
|
.assistant-bubble {
|
|
791
831
|
background: transparent;
|
|
792
832
|
padding: 0.2rem 0;
|