@agent-relay/dashboard-server 2.0.93 → 2.0.94
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/routes/models.d.ts.map +1 -1
- package/dist/routes/models.js +39 -3
- package/dist/routes/models.js.map +1 -1
- package/dist/services/index.d.ts +0 -1
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +0 -1
- package/dist/services/index.js.map +1 -1
- package/dist/types/index.d.ts +0 -75
- package/dist/types/index.d.ts.map +1 -1
- package/dist/websocket/logs.d.ts +1 -25
- package/dist/websocket/logs.d.ts.map +1 -1
- package/dist/websocket/logs.js +0 -397
- package/dist/websocket/logs.js.map +1 -1
- package/out/404.html +1 -1
- package/out/_next/static/chunks/{3663-47290254b8f6f5dd.js → 3663-191a9aa9104061af.js} +1 -1
- package/out/_next/static/chunks/4201-d11188cf35739bff.js +1 -0
- package/out/_next/static/chunks/5787-f6fd7a3fbfe5eae6.js +73 -0
- package/out/_next/static/chunks/app/app/[[...slug]]/{page-c1376e695ba19e38.js → page-2b872f60e3d64014.js} +1 -1
- package/out/_next/static/chunks/app/{page-f2ebc7d0bc08e395.js → page-ca5511e5d65100a5.js} +1 -1
- package/out/about.html +1 -1
- package/out/about.txt +1 -1
- package/out/app/onboarding.html +1 -1
- package/out/app/onboarding.txt +1 -1
- package/out/app.html +1 -1
- package/out/app.txt +2 -2
- package/out/blog/go-to-bed-wake-up-to-a-finished-product.html +1 -1
- package/out/blog/go-to-bed-wake-up-to-a-finished-product.txt +1 -1
- package/out/blog/let-them-cook-multi-agent-orchestration.html +1 -1
- package/out/blog/let-them-cook-multi-agent-orchestration.txt +1 -1
- package/out/blog.html +1 -1
- package/out/blog.txt +1 -1
- package/out/careers.html +1 -1
- package/out/careers.txt +1 -1
- package/out/changelog.html +1 -1
- package/out/changelog.txt +1 -1
- package/out/cloud/link.html +1 -1
- package/out/cloud/link.txt +1 -1
- package/out/complete-profile.html +1 -1
- package/out/complete-profile.txt +1 -1
- package/out/connect-repos.html +1 -1
- package/out/connect-repos.txt +1 -1
- package/out/contact.html +1 -1
- package/out/contact.txt +1 -1
- package/out/dev/cli-tools.html +1 -1
- package/out/dev/cli-tools.txt +1 -1
- package/out/dev/log-viewer.html +1 -1
- package/out/dev/log-viewer.txt +1 -1
- package/out/docs.html +1 -1
- package/out/docs.txt +1 -1
- package/out/history.html +1 -1
- package/out/history.txt +2 -2
- package/out/index.html +1 -1
- package/out/index.txt +2 -2
- package/out/login.html +1 -1
- package/out/login.txt +1 -1
- package/out/metrics.html +1 -1
- package/out/metrics.txt +2 -2
- package/out/pricing.html +1 -1
- package/out/pricing.txt +1 -1
- package/out/privacy.html +1 -1
- package/out/privacy.txt +1 -1
- package/out/providers/setup/claude.html +1 -1
- package/out/providers/setup/claude.txt +1 -1
- package/out/providers/setup/codex.html +1 -1
- package/out/providers/setup/codex.txt +1 -1
- package/out/providers/setup/cursor.html +1 -1
- package/out/providers/setup/cursor.txt +1 -1
- package/out/providers.html +1 -1
- package/out/providers.txt +1 -1
- package/out/security.html +1 -1
- package/out/security.txt +1 -1
- package/out/signup.html +1 -1
- package/out/signup.txt +1 -1
- package/out/terms.html +1 -1
- package/out/terms.txt +1 -1
- package/package.json +6 -9
- package/dist/lib/attachment-storage.d.ts +0 -10
- package/dist/lib/attachment-storage.d.ts.map +0 -1
- package/dist/lib/attachment-storage.js +0 -53
- package/dist/lib/attachment-storage.js.map +0 -1
- package/dist/lib/broadcast.d.ts +0 -18
- package/dist/lib/broadcast.d.ts.map +0 -1
- package/dist/lib/broadcast.js +0 -118
- package/dist/lib/broadcast.js.map +0 -1
- package/dist/lib/channel-state.d.ts +0 -32
- package/dist/lib/channel-state.d.ts.map +0 -1
- package/dist/lib/channel-state.js +0 -146
- package/dist/lib/channel-state.js.map +0 -1
- package/dist/lib/cli-auth.d.ts +0 -40
- package/dist/lib/cli-auth.d.ts.map +0 -1
- package/dist/lib/cli-auth.js +0 -144
- package/dist/lib/cli-auth.js.map +0 -1
- package/dist/lib/data-assembly.d.ts +0 -136
- package/dist/lib/data-assembly.d.ts.map +0 -1
- package/dist/lib/data-assembly.js +0 -550
- package/dist/lib/data-assembly.js.map +0 -1
- package/dist/lib/server-state.d.ts +0 -115
- package/dist/lib/server-state.d.ts.map +0 -1
- package/dist/lib/server-state.js +0 -138
- package/dist/lib/server-state.js.map +0 -1
- package/dist/lib/websocket-runtime.d.ts +0 -17
- package/dist/lib/websocket-runtime.d.ts.map +0 -1
- package/dist/lib/websocket-runtime.js +0 -76
- package/dist/lib/websocket-runtime.js.map +0 -1
- package/dist/routes/auth.d.ts +0 -45
- package/dist/routes/auth.d.ts.map +0 -1
- package/dist/routes/auth.js +0 -261
- package/dist/routes/auth.js.map +0 -1
- package/dist/routes/channels-integrated.d.ts +0 -84
- package/dist/routes/channels-integrated.d.ts.map +0 -1
- package/dist/routes/channels-integrated.js +0 -644
- package/dist/routes/channels-integrated.js.map +0 -1
- package/dist/routes/decisions.d.ts +0 -31
- package/dist/routes/decisions.d.ts.map +0 -1
- package/dist/routes/decisions.js +0 -109
- package/dist/routes/decisions.js.map +0 -1
- package/dist/routes/fleet.d.ts +0 -24
- package/dist/routes/fleet.d.ts.map +0 -1
- package/dist/routes/fleet.js +0 -131
- package/dist/routes/fleet.js.map +0 -1
- package/dist/routes/history.d.ts +0 -13
- package/dist/routes/history.d.ts.map +0 -1
- package/dist/routes/history.js +0 -205
- package/dist/routes/history.js.map +0 -1
- package/dist/routes/messaging.d.ts +0 -34
- package/dist/routes/messaging.d.ts.map +0 -1
- package/dist/routes/messaging.js +0 -306
- package/dist/routes/messaging.js.map +0 -1
- package/dist/routes/settings.d.ts +0 -6
- package/dist/routes/settings.d.ts.map +0 -1
- package/dist/routes/settings.js +0 -119
- package/dist/routes/settings.js.map +0 -1
- package/dist/routes/spawn.d.ts +0 -75
- package/dist/routes/spawn.d.ts.map +0 -1
- package/dist/routes/spawn.js +0 -521
- package/dist/routes/spawn.js.map +0 -1
- package/dist/routes/system.d.ts +0 -21
- package/dist/routes/system.d.ts.map +0 -1
- package/dist/routes/system.js +0 -186
- package/dist/routes/system.js.map +0 -1
- package/dist/routes/tasks.d.ts +0 -27
- package/dist/routes/tasks.d.ts.map +0 -1
- package/dist/routes/tasks.js +0 -103
- package/dist/routes/tasks.js.map +0 -1
- package/dist/routes/ui.d.ts +0 -6
- package/dist/routes/ui.d.ts.map +0 -1
- package/dist/routes/ui.js +0 -114
- package/dist/routes/ui.js.map +0 -1
- package/dist/server.d.ts +0 -4
- package/dist/server.d.ts.map +0 -1
- package/dist/server.js +0 -481
- package/dist/server.js.map +0 -1
- package/dist/services/broker-spawn-reader.d.ts +0 -40
- package/dist/services/broker-spawn-reader.d.ts.map +0 -1
- package/dist/services/broker-spawn-reader.js +0 -155
- package/dist/services/broker-spawn-reader.js.map +0 -1
- package/dist/services/user-bridge.d.ts +0 -155
- package/dist/services/user-bridge.d.ts.map +0 -1
- package/dist/services/user-bridge.js +0 -385
- package/dist/services/user-bridge.js.map +0 -1
- package/dist/websocket/bridge.d.ts +0 -12
- package/dist/websocket/bridge.d.ts.map +0 -1
- package/dist/websocket/bridge.js +0 -33
- package/dist/websocket/bridge.js.map +0 -1
- package/dist/websocket/main.d.ts +0 -15
- package/dist/websocket/main.d.ts.map +0 -1
- package/dist/websocket/main.js +0 -84
- package/dist/websocket/main.js.map +0 -1
- package/dist/websocket/presence.d.ts +0 -74
- package/dist/websocket/presence.d.ts.map +0 -1
- package/dist/websocket/presence.js +0 -330
- package/dist/websocket/presence.js.map +0 -1
- package/out/_next/static/chunks/270-8c0b8109123a0c5f.js +0 -73
- package/out/_next/static/chunks/5518-3b96a248632a79c0.js +0 -1
- /package/out/_next/static/{9CykW6n4dJn75_XoFVN_X → wgSCX8AQnjVl_Rnx1gUp5}/_buildManifest.js +0 -0
- /package/out/_next/static/{9CykW6n4dJn75_XoFVN_X → wgSCX8AQnjVl_Rnx1gUp5}/_ssgManifest.js +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../src/routes/models.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../src/routes/models.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAmE3C;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,WAAW,GAAG,IAAI,CAsB3D"}
|
package/dist/routes/models.js
CHANGED
|
@@ -1,3 +1,38 @@
|
|
|
1
|
+
function inferCodexReasoningEfforts(model) {
|
|
2
|
+
if (model === 'gpt-5.1-codex-mini') {
|
|
3
|
+
return ['medium', 'high'];
|
|
4
|
+
}
|
|
5
|
+
if (model.startsWith('gpt-5')) {
|
|
6
|
+
return ['low', 'medium', 'high', 'xhigh'];
|
|
7
|
+
}
|
|
8
|
+
return undefined;
|
|
9
|
+
}
|
|
10
|
+
function enrichCodexModelOptions(modelOptions, config) {
|
|
11
|
+
const codexOptions = Array.isArray(modelOptions.Codex) ? modelOptions.Codex : null;
|
|
12
|
+
if (!codexOptions) {
|
|
13
|
+
return modelOptions;
|
|
14
|
+
}
|
|
15
|
+
const enrichedCodexOptions = codexOptions.map((option) => {
|
|
16
|
+
const reasoningEfforts = option.reasoningEfforts
|
|
17
|
+
?? config.getSupportedReasoningEfforts?.('codex', option.value)
|
|
18
|
+
?? inferCodexReasoningEfforts(option.value);
|
|
19
|
+
if (!reasoningEfforts || reasoningEfforts.length === 0) {
|
|
20
|
+
return option;
|
|
21
|
+
}
|
|
22
|
+
const defaultReasoningEffort = option.defaultReasoningEffort
|
|
23
|
+
?? config.getDefaultReasoningEffort?.('codex', option.value)
|
|
24
|
+
?? reasoningEfforts[reasoningEfforts.length - 1];
|
|
25
|
+
return {
|
|
26
|
+
...option,
|
|
27
|
+
reasoningEfforts,
|
|
28
|
+
defaultReasoningEffort,
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
|
+
return {
|
|
32
|
+
...modelOptions,
|
|
33
|
+
Codex: enrichedCodexOptions,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
1
36
|
/**
|
|
2
37
|
* Model options route.
|
|
3
38
|
* Serves model options from @agent-relay/config (generated from cli-registry.yaml).
|
|
@@ -5,11 +40,12 @@
|
|
|
5
40
|
export function registerModelsRoutes(app) {
|
|
6
41
|
app.get('/api/models', async (_req, res) => {
|
|
7
42
|
try {
|
|
8
|
-
const
|
|
43
|
+
const config = await import('@agent-relay/config');
|
|
44
|
+
const modelOptions = enrichCodexModelOptions(config.ModelOptions, config);
|
|
9
45
|
return res.json({
|
|
10
46
|
success: true,
|
|
11
|
-
modelOptions
|
|
12
|
-
defaultModels: DefaultModels,
|
|
47
|
+
modelOptions,
|
|
48
|
+
defaultModels: config.DefaultModels,
|
|
13
49
|
});
|
|
14
50
|
}
|
|
15
51
|
catch (err) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"models.js","sourceRoot":"","sources":["../../src/routes/models.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"models.js","sourceRoot":"","sources":["../../src/routes/models.ts"],"names":[],"mappings":"AAkBA,SAAS,0BAA0B,CAAC,KAAa;IAC/C,IAAI,KAAK,KAAK,oBAAoB,EAAE,CAAC;QACnC,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC5B,CAAC;IAED,IAAI,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QAC9B,OAAO,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5C,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,uBAAuB,CAC9B,YAAkC,EAClC,MAAoB;IAEpB,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IACnF,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,MAAM,oBAAoB,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE;QACvD,MAAM,gBAAgB,GACpB,MAAM,CAAC,gBAAgB;eACpB,MAAM,CAAC,4BAA4B,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC;eAC5D,0BAA0B,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAE9C,IAAI,CAAC,gBAAgB,IAAI,gBAAgB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvD,OAAO,MAAM,CAAC;QAChB,CAAC;QAED,MAAM,sBAAsB,GAC1B,MAAM,CAAC,sBAAsB;eAC1B,MAAM,CAAC,yBAAyB,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC;eACzD,gBAAgB,CAAC,gBAAgB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAEnD,OAAO;YACL,GAAG,MAAM;YACT,gBAAgB;YAChB,sBAAsB;SACvB,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,GAAG,YAAY;QACf,KAAK,EAAE,oBAAoB;KAC5B,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,GAAgB;IACnD,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;QACzC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,qBAAqB,CAAiB,CAAC;YACnE,MAAM,YAAY,GAAG,uBAAuB,CAC1C,MAAM,CAAC,YAAoC,EAC3C,MAAM,CACP,CAAC;YACF,OAAO,GAAG,CAAC,IAAI,CAAC;gBACd,OAAO,EAAE,IAAI;gBACb,YAAY;gBACZ,aAAa,EAAE,MAAM,CAAC,aAAsC;aAC7D,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACjE,OAAO,CAAC,KAAK,CAAC,qBAAqB,EAAE,OAAO,CAAC,CAAC;YAC9C,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBAC1B,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,OAAO;aACf,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/dist/services/index.d.ts
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Re-exports all service modules for the dashboard server.
|
|
5
5
|
*/
|
|
6
|
-
export { UserBridge, type IRelayClient } from './user-bridge.js';
|
|
7
6
|
export { fetchCloudNeedsAttention, parseNeedsAttentionAgents, type NeedsAttentionProxyRequest, type NeedsAttentionPayload, } from './needs-attention.js';
|
|
8
7
|
export { fetchCloudMetrics, type MetricsProxyRequest } from './metrics.js';
|
|
9
8
|
export { fetchBrokerHealth, type BrokerHealthProxyRequest } from './health-worker-manager.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/services/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/services/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,wBAAwB,EACxB,yBAAyB,EACzB,KAAK,0BAA0B,EAC/B,KAAK,qBAAqB,GAC3B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,iBAAiB,EAAE,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAC3E,OAAO,EAAE,iBAAiB,EAAE,KAAK,wBAAwB,EAAE,MAAM,4BAA4B,CAAC"}
|
package/dist/services/index.js
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Re-exports all service modules for the dashboard server.
|
|
5
5
|
*/
|
|
6
|
-
export { UserBridge } from './user-bridge.js';
|
|
7
6
|
export { fetchCloudNeedsAttention, parseNeedsAttentionAgents, } from './needs-attention.js';
|
|
8
7
|
export { fetchCloudMetrics } from './metrics.js';
|
|
9
8
|
export { fetchBrokerHealth } from './health-worker-manager.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/services/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/services/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,wBAAwB,EACxB,yBAAyB,GAG1B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,iBAAiB,EAA4B,MAAM,cAAc,CAAC;AAC3E,OAAO,EAAE,iBAAiB,EAAiC,MAAM,4BAA4B,CAAC"}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -6,64 +6,6 @@
|
|
|
6
6
|
import type { Express } from 'express';
|
|
7
7
|
import type { Server } from 'http';
|
|
8
8
|
import type { WebSocketServer } from 'ws';
|
|
9
|
-
import type { RelayAdapter } from '@agent-relay/sdk';
|
|
10
|
-
/**
|
|
11
|
-
* Interface for spawn manager read operations.
|
|
12
|
-
* When the broker SpawnManager is passed in, the dashboard uses it
|
|
13
|
-
* for read operations (logs, worker listing).
|
|
14
|
-
* Spawn/release go through the SDK client → broker socket.
|
|
15
|
-
*/
|
|
16
|
-
export interface SpawnManagerLike {
|
|
17
|
-
hasWorker(name: string): boolean;
|
|
18
|
-
getWorkerOutput(name: string, limit?: number): string[] | undefined;
|
|
19
|
-
getWorkerRawOutput(name: string): string | undefined;
|
|
20
|
-
getActiveWorkers(): Array<{
|
|
21
|
-
name: string;
|
|
22
|
-
cli: string;
|
|
23
|
-
task: string;
|
|
24
|
-
team?: string;
|
|
25
|
-
spawnerName?: string;
|
|
26
|
-
spawnedAt: number;
|
|
27
|
-
pid?: number;
|
|
28
|
-
}>;
|
|
29
|
-
sendWorkerInput(name: string, data: string): boolean | Promise<boolean>;
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Options for starting the dashboard server
|
|
33
|
-
*/
|
|
34
|
-
export interface DashboardOptions {
|
|
35
|
-
/** Port to listen on */
|
|
36
|
-
port: number;
|
|
37
|
-
/** Data directory for storage */
|
|
38
|
-
dataDir: string;
|
|
39
|
-
/** Team directory for configuration */
|
|
40
|
-
teamDir: string;
|
|
41
|
-
/** Path to SQLite database (defaults to dataDir/messages.sqlite - same as broker runtime) */
|
|
42
|
-
dbPath?: string;
|
|
43
|
-
/** Enable agent spawning API */
|
|
44
|
-
enableSpawner?: boolean;
|
|
45
|
-
/** Project root for spawner (defaults to dataDir) */
|
|
46
|
-
projectRoot?: string;
|
|
47
|
-
/** Tmux session name for workers */
|
|
48
|
-
tmuxSession?: string;
|
|
49
|
-
/** Enable verbose logging (WebSocket connections, broadcasts, etc.) */
|
|
50
|
-
verbose?: boolean;
|
|
51
|
-
/**
|
|
52
|
-
* Callback to mark an agent as spawning (before HELLO completes).
|
|
53
|
-
* Messages sent to this agent can be queued for delivery after registration.
|
|
54
|
-
*/
|
|
55
|
-
onMarkSpawning?: (agentName: string) => void;
|
|
56
|
-
/**
|
|
57
|
-
* Callback to clear the spawning flag for an agent.
|
|
58
|
-
* Called when spawn fails or is cancelled.
|
|
59
|
-
*/
|
|
60
|
-
onClearSpawning?: (agentName: string) => void;
|
|
61
|
-
/**
|
|
62
|
-
* RelayAdapter instance for broker mode.
|
|
63
|
-
* Provides spawn/release/list/messaging via the broker binary.
|
|
64
|
-
*/
|
|
65
|
-
relayAdapter?: RelayAdapter;
|
|
66
|
-
}
|
|
67
9
|
/**
|
|
68
10
|
* Options for the proxy/mock server (simpler mode)
|
|
69
11
|
*/
|
|
@@ -134,21 +76,4 @@ export interface ThreadMetadata {
|
|
|
134
76
|
lastReplyAt?: string;
|
|
135
77
|
participants?: string[];
|
|
136
78
|
}
|
|
137
|
-
/**
|
|
138
|
-
* Server context passed to route handlers
|
|
139
|
-
*/
|
|
140
|
-
export interface ServerContext {
|
|
141
|
-
/** Data directory */
|
|
142
|
-
dataDir: string;
|
|
143
|
-
/** Team directory */
|
|
144
|
-
teamDir: string;
|
|
145
|
-
/** Database path */
|
|
146
|
-
dbPath?: string;
|
|
147
|
-
/** Project root */
|
|
148
|
-
projectRoot?: string;
|
|
149
|
-
/** Default workspace ID */
|
|
150
|
-
defaultWorkspaceId?: string;
|
|
151
|
-
/** Enable spawner */
|
|
152
|
-
enableSpawner?: boolean;
|
|
153
|
-
}
|
|
154
79
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AACnC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AACnC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,IAAI,CAAC;AAE1C;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,wCAAwC;IACxC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6BAA6B;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,kDAAkD;IAClD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,6DAA6D;IAC7D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uDAAuD;IACvD,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,OAAO,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,eAAe,CAAC;IACrB,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,IAAI,EAAE,OAAO,GAAG,YAAY,GAAG,MAAM,CAAC;CACvC;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;IACjD,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,QAAQ,GAAG,SAAS,CAAC;IACjC,MAAM,EAAE,QAAQ,GAAG,UAAU,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IACnE,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB"}
|
package/dist/websocket/logs.d.ts
CHANGED
|
@@ -1,30 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { WebSocket, type WebSocketServer } from 'ws';
|
|
3
|
-
import type { MessageBuffer } from '../messageBuffer.js';
|
|
1
|
+
import { WebSocket } from 'ws';
|
|
4
2
|
/**
|
|
5
3
|
* Standalone log WebSocket handler — tails local worker log files.
|
|
6
4
|
*/
|
|
7
5
|
export declare function handleStandaloneLogWebSocket(ws: WebSocket, pathname: string, dataDir: string, getLocalAgentNames: () => Set<string> | null, verbose: boolean): void;
|
|
8
|
-
interface SpawnReaderLike {
|
|
9
|
-
hasWorker: (name: string) => boolean;
|
|
10
|
-
getWorkerOutput: (name: string, maxLines?: number) => string[] | undefined;
|
|
11
|
-
getWorkerRawOutput?: (name: string) => string | undefined;
|
|
12
|
-
sendWorkerInput: (name: string, input: string) => Promise<boolean>;
|
|
13
|
-
}
|
|
14
|
-
export interface LogsWebSocketDeps {
|
|
15
|
-
wssLogs: WebSocketServer;
|
|
16
|
-
teamDir: string;
|
|
17
|
-
debug: (message: string) => void;
|
|
18
|
-
logSubscriptions: Map<string, Set<WebSocket>>;
|
|
19
|
-
fileWatchers: Map<string, fs.FSWatcher>;
|
|
20
|
-
fileLastSize: Map<string, number>;
|
|
21
|
-
agentLogBuffers: Map<string, MessageBuffer>;
|
|
22
|
-
getAgentLogBuffer: (agentName: string) => MessageBuffer;
|
|
23
|
-
spawnReader?: SpawnReaderLike;
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Logs WebSocket handler for live output streaming and replay.
|
|
27
|
-
*/
|
|
28
|
-
export declare function setupLogsWebSocket(deps: LogsWebSocketDeps): void;
|
|
29
|
-
export {};
|
|
30
6
|
//# sourceMappingURL=logs.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logs.d.ts","sourceRoot":"","sources":["../../src/websocket/logs.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"logs.d.ts","sourceRoot":"","sources":["../../src/websocket/logs.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AA8C/B;;GAEG;AACH,wBAAgB,4BAA4B,CAC1C,EAAE,EAAE,SAAS,EACb,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,kBAAkB,EAAE,MAAM,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,EAC5C,OAAO,EAAE,OAAO,GACf,IAAI,CA8IN"}
|
package/dist/websocket/logs.js
CHANGED
|
@@ -177,401 +177,4 @@ export function handleStandaloneLogWebSocket(ws, pathname, dataDir, getLocalAgen
|
|
|
177
177
|
clearInterval(interval);
|
|
178
178
|
});
|
|
179
179
|
}
|
|
180
|
-
/**
|
|
181
|
-
* Logs WebSocket handler for live output streaming and replay.
|
|
182
|
-
*/
|
|
183
|
-
export function setupLogsWebSocket(deps) {
|
|
184
|
-
const { wssLogs, teamDir, debug, logSubscriptions, fileWatchers, fileLastSize, agentLogBuffers, getAgentLogBuffer, spawnReader, } = deps;
|
|
185
|
-
// Track alive status for ping/pong keepalive on log connections.
|
|
186
|
-
const logClientAlive = new WeakMap();
|
|
187
|
-
// Ping interval for log WebSocket connections (15 seconds).
|
|
188
|
-
// Reduced from 30s to detect disconnects faster and minimize message loss window.
|
|
189
|
-
const LOG_PING_INTERVAL_MS = 15000;
|
|
190
|
-
const logPingInterval = setInterval(() => {
|
|
191
|
-
wssLogs.clients.forEach((ws) => {
|
|
192
|
-
if (logClientAlive.get(ws) === false) {
|
|
193
|
-
// Client didn't respond to last ping - close gracefully.
|
|
194
|
-
debug('[dashboard] Logs WebSocket client unresponsive, closing gracefully');
|
|
195
|
-
ws.close(1000, 'unresponsive');
|
|
196
|
-
return;
|
|
197
|
-
}
|
|
198
|
-
// Mark as not alive until we get a pong.
|
|
199
|
-
logClientAlive.set(ws, false);
|
|
200
|
-
ws.ping();
|
|
201
|
-
});
|
|
202
|
-
}, LOG_PING_INTERVAL_MS);
|
|
203
|
-
// Clean up ping interval on server close.
|
|
204
|
-
wssLogs.on('close', () => {
|
|
205
|
-
clearInterval(logPingInterval);
|
|
206
|
-
});
|
|
207
|
-
// Handle logs WebSocket connections for live log streaming.
|
|
208
|
-
wssLogs.on('connection', (ws, req) => {
|
|
209
|
-
debug('[dashboard] Logs WebSocket client connected');
|
|
210
|
-
const clientSubscriptions = new Set();
|
|
211
|
-
// Mark client as alive initially.
|
|
212
|
-
logClientAlive.set(ws, true);
|
|
213
|
-
// Handle pong responses (keep connection alive).
|
|
214
|
-
ws.on('pong', () => {
|
|
215
|
-
logClientAlive.set(ws, true);
|
|
216
|
-
});
|
|
217
|
-
// Send sync message with current server timestamp so client can track its position.
|
|
218
|
-
if (ws.readyState === WebSocket.OPEN) {
|
|
219
|
-
ws.send(JSON.stringify({ type: 'sync', serverTimestamp: Date.now(), sequenceId: 0 }));
|
|
220
|
-
}
|
|
221
|
-
// Helper to check if agent is daemon-connected (from agents.json).
|
|
222
|
-
const isDaemonConnected = (agentName) => {
|
|
223
|
-
const agentsPath = path.join(teamDir, 'agents.json');
|
|
224
|
-
if (!fs.existsSync(agentsPath))
|
|
225
|
-
return false;
|
|
226
|
-
try {
|
|
227
|
-
const data = JSON.parse(fs.readFileSync(agentsPath, 'utf-8'));
|
|
228
|
-
return data.agents?.some((a) => a.name === agentName) ?? false;
|
|
229
|
-
}
|
|
230
|
-
catch {
|
|
231
|
-
return false;
|
|
232
|
-
}
|
|
233
|
-
};
|
|
234
|
-
// Helper to get worker info from workers.json (for externally-spawned workers).
|
|
235
|
-
const getExternalWorkerInfo = (agentName) => {
|
|
236
|
-
const workersPath = path.join(teamDir, 'workers.json');
|
|
237
|
-
if (!fs.existsSync(workersPath))
|
|
238
|
-
return null;
|
|
239
|
-
try {
|
|
240
|
-
const data = JSON.parse(fs.readFileSync(workersPath, 'utf-8'));
|
|
241
|
-
const worker = data.workers?.find((w) => w.name === agentName);
|
|
242
|
-
return worker ?? null;
|
|
243
|
-
}
|
|
244
|
-
catch {
|
|
245
|
-
return null;
|
|
246
|
-
}
|
|
247
|
-
};
|
|
248
|
-
// Helper to read raw log tail for externally-spawned workers.
|
|
249
|
-
const readLogFileTail = (logFile, maxBytes = LOG_HISTORY_MAX_BYTES) => {
|
|
250
|
-
return readLogTailContent(logFile, maxBytes);
|
|
251
|
-
};
|
|
252
|
-
const buildLogPayload = (agentName, content, messageType = 'output') => {
|
|
253
|
-
const base = {
|
|
254
|
-
type: messageType,
|
|
255
|
-
agent: agentName,
|
|
256
|
-
data: content,
|
|
257
|
-
content,
|
|
258
|
-
timestamp: new Date().toISOString(),
|
|
259
|
-
};
|
|
260
|
-
const serializedBase = JSON.stringify(base);
|
|
261
|
-
const seq = getAgentLogBuffer(agentName).push(messageType, serializedBase);
|
|
262
|
-
return JSON.stringify({ ...base, seq });
|
|
263
|
-
};
|
|
264
|
-
// Helper to start watching a log file for live updates.
|
|
265
|
-
const watchLogFile = (agentName, logFile) => {
|
|
266
|
-
if (fileWatchers.has(agentName))
|
|
267
|
-
return; // Already watching.
|
|
268
|
-
if (!fs.existsSync(logFile))
|
|
269
|
-
return;
|
|
270
|
-
try {
|
|
271
|
-
// Track current file size for incremental reads.
|
|
272
|
-
const stats = fs.statSync(logFile);
|
|
273
|
-
fileLastSize.set(agentName, stats.size);
|
|
274
|
-
const watcher = fs.watch(logFile, (eventType) => {
|
|
275
|
-
if (eventType !== 'change')
|
|
276
|
-
return;
|
|
277
|
-
const clients = logSubscriptions.get(agentName);
|
|
278
|
-
if (!clients || clients.size === 0) {
|
|
279
|
-
// No subscribers, stop watching.
|
|
280
|
-
watcher.close();
|
|
281
|
-
fileWatchers.delete(agentName);
|
|
282
|
-
fileLastSize.delete(agentName);
|
|
283
|
-
return;
|
|
284
|
-
}
|
|
285
|
-
try {
|
|
286
|
-
const newStats = fs.statSync(logFile);
|
|
287
|
-
const lastSize = fileLastSize.get(agentName) || 0;
|
|
288
|
-
if (newStats.size > lastSize) {
|
|
289
|
-
// Read only the new content.
|
|
290
|
-
const fd = fs.openSync(logFile, 'r');
|
|
291
|
-
const buffer = Buffer.alloc(newStats.size - lastSize);
|
|
292
|
-
fs.readSync(fd, buffer, 0, buffer.length, lastSize);
|
|
293
|
-
fs.closeSync(fd);
|
|
294
|
-
const newContent = buffer.toString('utf-8');
|
|
295
|
-
fileLastSize.set(agentName, newStats.size);
|
|
296
|
-
if (!newContent) {
|
|
297
|
-
return;
|
|
298
|
-
}
|
|
299
|
-
const payload = buildLogPayload(agentName, newContent, 'output');
|
|
300
|
-
for (const client of clients) {
|
|
301
|
-
if (client.readyState === WebSocket.OPEN) {
|
|
302
|
-
client.send(payload);
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
catch (err) {
|
|
308
|
-
console.error(`[dashboard] Error reading log file updates for ${agentName}:`, err);
|
|
309
|
-
}
|
|
310
|
-
});
|
|
311
|
-
fileWatchers.set(agentName, watcher);
|
|
312
|
-
}
|
|
313
|
-
catch (err) {
|
|
314
|
-
console.error(`[dashboard] Failed to watch log file for ${agentName}:`, err);
|
|
315
|
-
}
|
|
316
|
-
};
|
|
317
|
-
const cleanupAgentSubscription = (agentName) => {
|
|
318
|
-
const remainingClients = logSubscriptions.get(agentName);
|
|
319
|
-
if (!remainingClients || remainingClients.size === 0) {
|
|
320
|
-
const watcher = fileWatchers.get(agentName);
|
|
321
|
-
if (watcher) {
|
|
322
|
-
watcher.close();
|
|
323
|
-
fileWatchers.delete(agentName);
|
|
324
|
-
fileLastSize.delete(agentName);
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
};
|
|
328
|
-
// Helper to subscribe to an agent (async to handle spawn timing).
|
|
329
|
-
const subscribeToAgent = async (agentName) => {
|
|
330
|
-
let isSpawned = spawnReader?.hasWorker(agentName) ?? false;
|
|
331
|
-
const isDaemon = isDaemonConnected(agentName);
|
|
332
|
-
// Check if agent exists (either spawned or daemon-connected).
|
|
333
|
-
if (!isSpawned && !isDaemon) {
|
|
334
|
-
ws.send(JSON.stringify({
|
|
335
|
-
type: 'error',
|
|
336
|
-
agent: agentName,
|
|
337
|
-
error: `Agent ${agentName} not found`,
|
|
338
|
-
}));
|
|
339
|
-
// Close with custom code 4404 to signal "agent not found" - client should not reconnect.
|
|
340
|
-
ws.close(4404, 'Agent not found');
|
|
341
|
-
return false;
|
|
342
|
-
}
|
|
343
|
-
// If agent is daemon-connected but not yet in spawner's activeWorkers, poll for registration.
|
|
344
|
-
if (!isSpawned && isDaemon && spawnReader) {
|
|
345
|
-
const isSetupAgent = agentName.startsWith('__setup__');
|
|
346
|
-
const maxWaitMs = isSetupAgent ? 90000 : 5000; // 90s for setup agents (CLI auth can be slow), 5s otherwise.
|
|
347
|
-
const pollIntervalMs = 100;
|
|
348
|
-
const startTime = Date.now();
|
|
349
|
-
while (Date.now() - startTime < maxWaitMs) {
|
|
350
|
-
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
351
|
-
isSpawned = spawnReader.hasWorker(agentName);
|
|
352
|
-
if (isSpawned) {
|
|
353
|
-
console.log(`[dashboard] Agent ${agentName} appeared in spawner after ${Date.now() - startTime}ms`);
|
|
354
|
-
break;
|
|
355
|
-
}
|
|
356
|
-
// Check if WebSocket was closed during wait.
|
|
357
|
-
if (ws.readyState !== WebSocket.OPEN) {
|
|
358
|
-
return false;
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
// Add to subscriptions.
|
|
363
|
-
clientSubscriptions.add(agentName);
|
|
364
|
-
if (!logSubscriptions.has(agentName)) {
|
|
365
|
-
logSubscriptions.set(agentName, new Set());
|
|
366
|
-
}
|
|
367
|
-
logSubscriptions.get(agentName).add(ws);
|
|
368
|
-
debug(`[dashboard] Client subscribed to logs for: ${agentName} (spawned: ${isSpawned}, daemon: ${isDaemon})`);
|
|
369
|
-
if (isSpawned && spawnReader) {
|
|
370
|
-
const rawHistory = spawnReader.getWorkerRawOutput?.(agentName)
|
|
371
|
-
?? (spawnReader.getWorkerOutput(agentName, 5000) || []).join('\n');
|
|
372
|
-
const historyTail = takeTail(rawHistory, LOG_HISTORY_MAX_BYTES);
|
|
373
|
-
ws.send(JSON.stringify({
|
|
374
|
-
type: 'history',
|
|
375
|
-
agent: agentName,
|
|
376
|
-
lines: historyTail ? [historyTail] : [],
|
|
377
|
-
}));
|
|
378
|
-
}
|
|
379
|
-
else {
|
|
380
|
-
// Check if this is an externally-spawned worker with a log file.
|
|
381
|
-
const externalWorker = getExternalWorkerInfo(agentName);
|
|
382
|
-
let logFile = externalWorker?.logFile;
|
|
383
|
-
// If no explicit logFile in workers.json, try conventional path.
|
|
384
|
-
if (!logFile) {
|
|
385
|
-
const conventionalPath = path.join(teamDir, 'worker-logs', `${agentName}.log`);
|
|
386
|
-
if (fs.existsSync(conventionalPath)) {
|
|
387
|
-
logFile = conventionalPath;
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
if (logFile && fs.existsSync(logFile)) {
|
|
391
|
-
// Read logs from the external worker's log file.
|
|
392
|
-
const historyContent = readLogFileTail(logFile, LOG_HISTORY_MAX_BYTES);
|
|
393
|
-
ws.send(JSON.stringify({
|
|
394
|
-
type: 'history',
|
|
395
|
-
agent: agentName,
|
|
396
|
-
lines: historyContent ? [historyContent] : [],
|
|
397
|
-
}));
|
|
398
|
-
// Start watching the log file for live updates.
|
|
399
|
-
watchLogFile(agentName, logFile);
|
|
400
|
-
}
|
|
401
|
-
else {
|
|
402
|
-
// For daemon-connected agents without log files, explain PTY output limitations.
|
|
403
|
-
ws.send(JSON.stringify({
|
|
404
|
-
type: 'history',
|
|
405
|
-
agent: agentName,
|
|
406
|
-
lines: [`[${agentName} is a daemon-connected agent - PTY output not available. Showing relay messages only.]`],
|
|
407
|
-
}));
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
ws.send(JSON.stringify({
|
|
411
|
-
type: 'sync',
|
|
412
|
-
serverTimestamp: Date.now(),
|
|
413
|
-
sequenceId: getAgentLogBuffer(agentName).currentId(),
|
|
414
|
-
}));
|
|
415
|
-
ws.send(JSON.stringify({
|
|
416
|
-
type: 'subscribed',
|
|
417
|
-
agent: agentName,
|
|
418
|
-
}));
|
|
419
|
-
return true;
|
|
420
|
-
};
|
|
421
|
-
// Check if agent name is in URL path: /ws/logs/:agentName.
|
|
422
|
-
const pathname = new URL(req.url || '', `http://${req.headers.host}`).pathname;
|
|
423
|
-
const pathMatch = pathname.match(/^\/ws\/logs\/(.+)$/);
|
|
424
|
-
if (pathMatch) {
|
|
425
|
-
let agentName;
|
|
426
|
-
try {
|
|
427
|
-
agentName = decodeURIComponent(pathMatch[1]);
|
|
428
|
-
}
|
|
429
|
-
catch {
|
|
430
|
-
ws.send(JSON.stringify({
|
|
431
|
-
type: 'error',
|
|
432
|
-
error: 'Invalid log stream path encoding',
|
|
433
|
-
}));
|
|
434
|
-
ws.close(1003, 'invalid-path');
|
|
435
|
-
return;
|
|
436
|
-
}
|
|
437
|
-
subscribeToAgent(agentName).catch((err) => {
|
|
438
|
-
console.error(`[dashboard] Error subscribing to ${agentName}:`, err);
|
|
439
|
-
});
|
|
440
|
-
}
|
|
441
|
-
ws.on('message', async (data) => {
|
|
442
|
-
try {
|
|
443
|
-
const msg = JSON.parse(data.toString());
|
|
444
|
-
// Subscribe to agent logs.
|
|
445
|
-
if (msg.subscribe && typeof msg.subscribe === 'string') {
|
|
446
|
-
subscribeToAgent(msg.subscribe).catch((err) => {
|
|
447
|
-
console.error(`[dashboard] Error subscribing to ${msg.subscribe}:`, err);
|
|
448
|
-
});
|
|
449
|
-
}
|
|
450
|
-
// Unsubscribe from agent logs.
|
|
451
|
-
if (msg.unsubscribe && typeof msg.unsubscribe === 'string') {
|
|
452
|
-
const agentName = msg.unsubscribe;
|
|
453
|
-
clientSubscriptions.delete(agentName);
|
|
454
|
-
logSubscriptions.get(agentName)?.delete(ws);
|
|
455
|
-
cleanupAgentSubscription(agentName);
|
|
456
|
-
debug(`[dashboard] Client unsubscribed from logs for: ${agentName}`);
|
|
457
|
-
ws.send(JSON.stringify({
|
|
458
|
-
type: 'unsubscribed',
|
|
459
|
-
agent: agentName,
|
|
460
|
-
}));
|
|
461
|
-
}
|
|
462
|
-
// Handle interactive terminal input.
|
|
463
|
-
if (msg.type === 'input' && typeof msg.data === 'string') {
|
|
464
|
-
// Get agent name from message or use first subscribed agent.
|
|
465
|
-
const agentName = msg.agent || [...clientSubscriptions][0];
|
|
466
|
-
if (!agentName) {
|
|
467
|
-
ws.send(JSON.stringify({
|
|
468
|
-
type: 'error',
|
|
469
|
-
error: 'No agent subscribed for input',
|
|
470
|
-
}));
|
|
471
|
-
return;
|
|
472
|
-
}
|
|
473
|
-
// Check if this is a spawned agent (we can only send input to spawned agents).
|
|
474
|
-
if (spawnReader?.hasWorker(agentName)) {
|
|
475
|
-
const success = await spawnReader.sendWorkerInput(agentName, msg.data);
|
|
476
|
-
if (!success) {
|
|
477
|
-
console.warn(`[dashboard] Failed to send input to agent ${agentName}`);
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
else {
|
|
481
|
-
// Daemon-connected agents don't support direct input.
|
|
482
|
-
ws.send(JSON.stringify({
|
|
483
|
-
type: 'error',
|
|
484
|
-
agent: agentName,
|
|
485
|
-
error: 'Interactive input not supported for daemon-connected agents',
|
|
486
|
-
}));
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
// Handle replay request:
|
|
490
|
-
// { type: "replay", agent: "name", lastSequenceId: N } (preferred)
|
|
491
|
-
// { type: "replay", agent: "name", lastTimestamp: N } (legacy fallback)
|
|
492
|
-
if (msg.type === 'replay' && typeof msg.agent === 'string') {
|
|
493
|
-
const logBuffer = agentLogBuffers.get(msg.agent);
|
|
494
|
-
if (!logBuffer || ws.readyState !== WebSocket.OPEN) {
|
|
495
|
-
return;
|
|
496
|
-
}
|
|
497
|
-
try {
|
|
498
|
-
const missed = typeof msg.lastSequenceId === 'number'
|
|
499
|
-
? logBuffer.getAfter(msg.lastSequenceId)
|
|
500
|
-
: typeof msg.lastTimestamp === 'number'
|
|
501
|
-
? logBuffer.getAfterTimestamp(msg.lastTimestamp)
|
|
502
|
-
: [];
|
|
503
|
-
const messages = missed.map((m) => {
|
|
504
|
-
try {
|
|
505
|
-
const parsed = JSON.parse(m.payload);
|
|
506
|
-
if (parsed && typeof parsed === 'object') {
|
|
507
|
-
return { ...parsed, seq: m.id };
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
catch {
|
|
511
|
-
// Fallback to raw payload.
|
|
512
|
-
}
|
|
513
|
-
return {
|
|
514
|
-
type: 'output',
|
|
515
|
-
agent: msg.agent,
|
|
516
|
-
data: m.payload,
|
|
517
|
-
content: m.payload,
|
|
518
|
-
timestamp: new Date(m.timestamp).toISOString(),
|
|
519
|
-
seq: m.id,
|
|
520
|
-
};
|
|
521
|
-
});
|
|
522
|
-
const entries = messages.map((message) => {
|
|
523
|
-
const typed = message;
|
|
524
|
-
const content = typeof typed.content === 'string'
|
|
525
|
-
? typed.content
|
|
526
|
-
: typeof typed.data === 'string'
|
|
527
|
-
? typed.data
|
|
528
|
-
: '';
|
|
529
|
-
const timestamp = typeof typed.timestamp === 'string'
|
|
530
|
-
? Date.parse(typed.timestamp)
|
|
531
|
-
: NaN;
|
|
532
|
-
return {
|
|
533
|
-
content,
|
|
534
|
-
timestamp: Number.isFinite(timestamp) ? timestamp : Date.now(),
|
|
535
|
-
};
|
|
536
|
-
});
|
|
537
|
-
ws.send(JSON.stringify({
|
|
538
|
-
type: 'replay',
|
|
539
|
-
agent: msg.agent,
|
|
540
|
-
messages,
|
|
541
|
-
entries,
|
|
542
|
-
}));
|
|
543
|
-
}
|
|
544
|
-
catch (err) {
|
|
545
|
-
console.error('[dashboard] Failed to replay log messages:', err);
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
catch (err) {
|
|
550
|
-
console.error('[dashboard] Invalid logs WebSocket message:', err);
|
|
551
|
-
}
|
|
552
|
-
});
|
|
553
|
-
ws.on('error', (err) => {
|
|
554
|
-
console.error('[dashboard] Logs WebSocket client error:', err);
|
|
555
|
-
});
|
|
556
|
-
ws.on('close', (code, reason) => {
|
|
557
|
-
// Clean up subscriptions on disconnect.
|
|
558
|
-
for (const agentName of clientSubscriptions) {
|
|
559
|
-
logSubscriptions.get(agentName)?.delete(ws);
|
|
560
|
-
const remainingClients = logSubscriptions.get(agentName);
|
|
561
|
-
if (!remainingClients || remainingClients.size === 0) {
|
|
562
|
-
const watcher = fileWatchers.get(agentName);
|
|
563
|
-
if (watcher) {
|
|
564
|
-
watcher.close();
|
|
565
|
-
fileWatchers.delete(agentName);
|
|
566
|
-
fileLastSize.delete(agentName);
|
|
567
|
-
agentLogBuffers.delete(agentName);
|
|
568
|
-
console.log(`[dashboard] Stopped watching log file for: ${agentName}`);
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
const reasonStr = reason?.toString() || 'no reason';
|
|
573
|
-
console.log(`[dashboard] Logs WebSocket client disconnected (code: ${code}, reason: ${reasonStr})`);
|
|
574
|
-
});
|
|
575
|
-
});
|
|
576
|
-
}
|
|
577
180
|
//# sourceMappingURL=logs.js.map
|