@yeaft/webchat-agent 1.0.445 → 1.0.447
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/Dockerfile +5 -2
- package/cli.js +9 -0
- package/connection/upgrade.js +18 -0
- package/container-manager.js +1 -0
- package/index.js +5 -2
- package/local-runtime/agent/container-manager.js +1 -0
- package/local-runtime/server/context.js +3 -22
- package/local-runtime/server/database.js +1 -0
- package/local-runtime/server/db/agent-inventory-db.js +122 -0
- package/local-runtime/server/db/connection.js +59 -0
- package/local-runtime/server/handlers/agent-sync.js +12 -1
- package/local-runtime/server/handlers/client-conversation.js +1 -0
- package/local-runtime/server/handlers/client-misc.js +15 -0
- package/local-runtime/server/routes/admin-routes.js +78 -15
- package/local-runtime/server/ws-agent.js +41 -3
- package/local-runtime/server/ws-client.js +1 -7
- package/local-runtime/version.json +1 -1
- package/local-runtime/web/app.bundle.js +217 -242
- package/local-runtime/web/app.bundle.js.gz +0 -0
- package/local-runtime/web/index.html +2 -2
- package/local-runtime/web/style.bundle.css +1 -1
- package/local-runtime/web/style.bundle.css.gz +0 -0
- package/package.json +1 -1
- package/upgrade-command.js +19 -0
|
@@ -5,7 +5,7 @@ import { verifyAgent } from './auth.js';
|
|
|
5
5
|
|
|
6
6
|
import { encodeKey } from './encryption.js';
|
|
7
7
|
import { agents, pendingAgentConnections } from './context.js';
|
|
8
|
-
import { userDb } from './database.js';
|
|
8
|
+
import { agentInventoryDb, userDb } from './database.js';
|
|
9
9
|
import {
|
|
10
10
|
parseMessage, broadcastAgentList, clearAgentDirCache
|
|
11
11
|
} from './ws-utils.js';
|
|
@@ -52,6 +52,35 @@ function pruneAgentConnectionGenerations() {
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
function persistAgentInventory(agentId, agent) {
|
|
56
|
+
if (!agentInventoryDb?.upsert || !agent?.name) return;
|
|
57
|
+
try {
|
|
58
|
+
agentInventoryDb.upsert({
|
|
59
|
+
...agent,
|
|
60
|
+
id: agentId,
|
|
61
|
+
lastSeenAt: agent.lastSeenAt,
|
|
62
|
+
lastConnectedAt: agent.lastConnectedAt,
|
|
63
|
+
});
|
|
64
|
+
} catch (error) {
|
|
65
|
+
// The inventory is an admin read model. Never reject a live Agent because
|
|
66
|
+
// this best-effort durability write failed.
|
|
67
|
+
console.error(`[AgentInventory] Failed to persist ${agentId}:`, error.message);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function persistAgentHeartbeat(agentId, agent, now = Date.now()) {
|
|
72
|
+
if (!agentInventoryDb?.touch || !agent) return;
|
|
73
|
+
const lastPersistedAt = Number(agent.inventoryLastPersistedAt) || 0;
|
|
74
|
+
if (lastPersistedAt && now - lastPersistedAt < 10_000) return;
|
|
75
|
+
agent.inventoryLastPersistedAt = now;
|
|
76
|
+
try {
|
|
77
|
+
const touched = agentInventoryDb.touch(agentId, now);
|
|
78
|
+
if (touched === false) persistAgentInventory(agentId, agent);
|
|
79
|
+
} catch (error) {
|
|
80
|
+
console.error(`[AgentInventory] Failed to persist heartbeat for ${agentId}:`, error.message);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
55
84
|
export function handleAgentConnection(ws, url) {
|
|
56
85
|
const clientAgentId = url.searchParams.get('id') || randomUUID();
|
|
57
86
|
const agentName = url.searchParams.get('name') || `Agent-${clientAgentId.slice(0, 8)}`;
|
|
@@ -183,7 +212,9 @@ export function handleAgentConnection(ws, url) {
|
|
|
183
212
|
ws.close(1008, 'Account disabled');
|
|
184
213
|
return;
|
|
185
214
|
}
|
|
186
|
-
|
|
215
|
+
const now = Date.now();
|
|
216
|
+
markAgentHeartbeatSeen(agent, now);
|
|
217
|
+
persistAgentHeartbeat(resolvedAgentId, agent, now);
|
|
187
218
|
const msg = await parseMessage(data, agent.sessionKey);
|
|
188
219
|
if (msg) {
|
|
189
220
|
console.log(`[Agent] Received message from ${resolvedAgentId}: ${msg.type}`);
|
|
@@ -235,6 +266,9 @@ export function handleAgentConnection(ws, url) {
|
|
|
235
266
|
function handleAgentDisconnect(agentId, agentName, ws) {
|
|
236
267
|
const agent = agents.get(agentId);
|
|
237
268
|
if (!agent || agent.ws !== ws) return;
|
|
269
|
+
// Capture the last in-memory heartbeat before removing the live socket. The
|
|
270
|
+
// durable row remains an offline historical record; it never grants access.
|
|
271
|
+
persistAgentInventory(agentId, agent);
|
|
238
272
|
// Phase 4: 清理目录缓存
|
|
239
273
|
clearAgentDirCache(agentId);
|
|
240
274
|
clearWorkbenchCorrelationsForAgent(agentId);
|
|
@@ -280,6 +314,7 @@ function completeAgentRegistration(ws, agentId, agentName, workDir, sessionKey,
|
|
|
280
314
|
sessionKey,
|
|
281
315
|
isAlive: true,
|
|
282
316
|
lastSeenAt: Date.now(),
|
|
317
|
+
lastConnectedAt: Date.now(),
|
|
283
318
|
capabilities: effectiveCapabilities,
|
|
284
319
|
// The fallback list supports old UI features but cannot prove that an
|
|
285
320
|
// Agent made an explicit capability claim.
|
|
@@ -305,6 +340,7 @@ function completeAgentRegistration(ws, agentId, agentName, workDir, sessionKey,
|
|
|
305
340
|
}
|
|
306
341
|
}, 30000);
|
|
307
342
|
agents.get(agentId)._syncTimeout = syncTimeout;
|
|
343
|
+
persistAgentInventory(agentId, agents.get(agentId));
|
|
308
344
|
|
|
309
345
|
if (existingAgent?.ws && existingAgent.ws !== ws) {
|
|
310
346
|
clearBrowserRuntimeForAgent(agentId);
|
|
@@ -315,7 +351,9 @@ function completeAgentRegistration(ws, agentId, agentName, workDir, sessionKey,
|
|
|
315
351
|
ws.on('pong', () => {
|
|
316
352
|
const agent = agents.get(agentId);
|
|
317
353
|
if (agent?.ws === ws) {
|
|
318
|
-
|
|
354
|
+
const now = Date.now();
|
|
355
|
+
markAgentHeartbeatSeen(agent, now);
|
|
356
|
+
persistAgentHeartbeat(agentId, agent, now);
|
|
319
357
|
if (agent.pingSentAt) {
|
|
320
358
|
agent.latency = Date.now() - agent.pingSentAt;
|
|
321
359
|
agent.pingSentAt = null;
|
|
@@ -5,7 +5,7 @@ import { generateSkipAuthSession } from './auth.js';
|
|
|
5
5
|
import { authenticateRequest } from './auth/request-auth.js';
|
|
6
6
|
import { encodeKey } from './encryption.js';
|
|
7
7
|
import { userDb } from './database.js';
|
|
8
|
-
import { agents, clearYeaftDebugRequestsForClient, webClients
|
|
8
|
+
import { agents, clearYeaftDebugRequestsForClient, webClients } from './context.js';
|
|
9
9
|
import {
|
|
10
10
|
applyClientHello,
|
|
11
11
|
BROWSER_RUNTIME_PROTOCOL,
|
|
@@ -136,12 +136,6 @@ export function handleWebConnection(ws, url, req = {}) {
|
|
|
136
136
|
const client = webClients.get(clientId);
|
|
137
137
|
const msg = await parseMessage(data, client?.sessionKey);
|
|
138
138
|
if (!msg) return;
|
|
139
|
-
// Stats tracking: exclude heartbeat/control frames. User-turn bytes are
|
|
140
|
-
// accounted at the send handlers, not here, so pings and dashboard polling
|
|
141
|
-
// don't inflate traffic.
|
|
142
|
-
if (!isHeartbeatMessageType(msg.type)) {
|
|
143
|
-
trackRequest(client?.userId, data.length || 0, msg.type);
|
|
144
|
-
}
|
|
145
139
|
if (msg.perfTraceId) {
|
|
146
140
|
recordPerfTraceEvent({
|
|
147
141
|
traceId: msg.perfTraceId,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"1.0.
|
|
1
|
+
{"version":"1.0.447"}
|