@agent-link/server 0.1.188 → 0.1.189

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.
@@ -0,0 +1,36 @@
1
+ export interface AuthAttemptState {
2
+ failures: number;
3
+ lockedUntil: Date | null;
4
+ }
5
+ export declare class AuthManager {
6
+ readonly authAttempts: Map<string, AuthAttemptState>;
7
+ readonly pendingAuth: Map<string, string>;
8
+ readonly sessionAuth: Map<string, {
9
+ passwordHash: string;
10
+ passwordSalt: string;
11
+ }>;
12
+ private readonly serverSecret;
13
+ hashPassword(password: string): {
14
+ hash: string;
15
+ salt: string;
16
+ };
17
+ verifyPassword(submitted: string, storedHash: string, storedSalt: string): boolean;
18
+ setSessionPassword(sessionId: string, hash: string, salt: string): void;
19
+ getSessionAuth(sessionId: string): {
20
+ passwordHash: string;
21
+ passwordSalt: string;
22
+ } | undefined;
23
+ requiresAuth(sessionId: string): boolean;
24
+ generateAuthToken(sessionId: string): string;
25
+ verifyAuthToken(token: string, expectedSessionId: string): boolean;
26
+ isLocked(sessionId: string): boolean;
27
+ recordFailure(sessionId: string): {
28
+ locked: boolean;
29
+ remaining: number;
30
+ };
31
+ clearFailures(sessionId: string): void;
32
+ setPending(clientId: string, sessionId: string): void;
33
+ getPending(clientId: string): string | undefined;
34
+ removePending(clientId: string): void;
35
+ }
36
+ export declare const auth: AuthManager;
@@ -0,0 +1,96 @@
1
+ import { scryptSync, randomBytes, timingSafeEqual, createHmac } from 'crypto';
2
+ const MAX_FAILURES = 5;
3
+ const LOCKOUT_MS = 15 * 60 * 1000; // 15 minutes
4
+ const TOKEN_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
5
+ export class AuthManager {
6
+ authAttempts = new Map();
7
+ pendingAuth = new Map();
8
+ sessionAuth = new Map();
9
+ serverSecret = randomBytes(32);
10
+ // --- Password ---
11
+ hashPassword(password) {
12
+ const salt = randomBytes(16).toString('base64');
13
+ const hash = scryptSync(password, salt, 32).toString('base64');
14
+ return { hash, salt };
15
+ }
16
+ verifyPassword(submitted, storedHash, storedSalt) {
17
+ const computed = scryptSync(submitted, storedSalt, 32);
18
+ const expected = Buffer.from(storedHash, 'base64');
19
+ if (computed.length !== expected.length)
20
+ return false;
21
+ return timingSafeEqual(computed, expected);
22
+ }
23
+ // --- Session password ---
24
+ setSessionPassword(sessionId, hash, salt) {
25
+ this.sessionAuth.set(sessionId, { passwordHash: hash, passwordSalt: salt });
26
+ }
27
+ getSessionAuth(sessionId) {
28
+ return this.sessionAuth.get(sessionId);
29
+ }
30
+ requiresAuth(sessionId) {
31
+ const a = this.sessionAuth.get(sessionId);
32
+ return !!(a?.passwordHash && a?.passwordSalt);
33
+ }
34
+ // --- Token ---
35
+ generateAuthToken(sessionId) {
36
+ const ts = Date.now().toString();
37
+ const hmac = createHmac('sha256', this.serverSecret).update(`${sessionId}:${ts}`).digest('base64url');
38
+ return `${sessionId}:${ts}:${hmac}`;
39
+ }
40
+ verifyAuthToken(token, expectedSessionId) {
41
+ const idx1 = token.indexOf(':');
42
+ const idx2 = token.indexOf(':', idx1 + 1);
43
+ if (idx1 === -1 || idx2 === -1)
44
+ return false;
45
+ const sessionId = token.slice(0, idx1);
46
+ const ts = token.slice(idx1 + 1, idx2);
47
+ const hmac = token.slice(idx2 + 1);
48
+ if (sessionId !== expectedSessionId)
49
+ return false;
50
+ const age = Date.now() - parseInt(ts, 10);
51
+ if (isNaN(age) || age < 0 || age > TOKEN_TTL_MS)
52
+ return false;
53
+ const expected = createHmac('sha256', this.serverSecret).update(`${sessionId}:${ts}`).digest('base64url');
54
+ return hmac === expected;
55
+ }
56
+ // --- Brute-force protection ---
57
+ isLocked(sessionId) {
58
+ const state = this.authAttempts.get(sessionId);
59
+ if (!state?.lockedUntil)
60
+ return false;
61
+ if (Date.now() > state.lockedUntil.getTime()) {
62
+ this.authAttempts.delete(sessionId);
63
+ return false;
64
+ }
65
+ return true;
66
+ }
67
+ recordFailure(sessionId) {
68
+ let state = this.authAttempts.get(sessionId);
69
+ if (!state) {
70
+ state = { failures: 0, lockedUntil: null };
71
+ this.authAttempts.set(sessionId, state);
72
+ }
73
+ state.failures++;
74
+ if (state.failures >= MAX_FAILURES) {
75
+ state.lockedUntil = new Date(Date.now() + LOCKOUT_MS);
76
+ return { locked: true, remaining: 0 };
77
+ }
78
+ return { locked: false, remaining: MAX_FAILURES - state.failures };
79
+ }
80
+ clearFailures(sessionId) {
81
+ this.authAttempts.delete(sessionId);
82
+ }
83
+ // --- Pending auth ---
84
+ setPending(clientId, sessionId) {
85
+ this.pendingAuth.set(clientId, sessionId);
86
+ }
87
+ getPending(clientId) {
88
+ return this.pendingAuth.get(clientId);
89
+ }
90
+ removePending(clientId) {
91
+ this.pendingAuth.delete(clientId);
92
+ }
93
+ }
94
+ // Singleton instance
95
+ export const auth = new AuthManager();
96
+ //# sourceMappingURL=auth-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-manager.js","sourceRoot":"","sources":["../src/auth-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAO9E,MAAM,YAAY,GAAG,CAAC,CAAC;AACvB,MAAM,UAAU,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAG,aAAa;AAClD,MAAM,YAAY,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,WAAW;AAErD,MAAM,OAAO,WAAW;IACb,YAAY,GAAG,IAAI,GAAG,EAA4B,CAAC;IACnD,WAAW,GAAG,IAAI,GAAG,EAAkB,CAAC;IACxC,WAAW,GAAG,IAAI,GAAG,EAA0D,CAAC;IACxE,YAAY,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;IAEhD,mBAAmB;IAEnB,YAAY,CAAC,QAAgB;QAC3B,MAAM,IAAI,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAChD,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC/D,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACxB,CAAC;IAED,cAAc,CAAC,SAAiB,EAAE,UAAkB,EAAE,UAAkB;QACtE,MAAM,QAAQ,GAAG,UAAU,CAAC,SAAS,EAAE,UAAU,EAAE,EAAE,CAAC,CAAC;QACvD,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QACnD,IAAI,QAAQ,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QACtD,OAAO,eAAe,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC7C,CAAC;IAED,2BAA2B;IAE3B,kBAAkB,CAAC,SAAiB,EAAE,IAAY,EAAE,IAAY;QAC9D,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9E,CAAC;IAED,cAAc,CAAC,SAAiB;QAC9B,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACzC,CAAC;IAED,YAAY,CAAC,SAAiB;QAC5B,MAAM,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC1C,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,YAAY,IAAI,CAAC,EAAE,YAAY,CAAC,CAAC;IAChD,CAAC;IAED,gBAAgB;IAEhB,iBAAiB,CAAC,SAAiB;QACjC,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC;QACjC,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,GAAG,SAAS,IAAI,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QACtG,OAAO,GAAG,SAAS,IAAI,EAAE,IAAI,IAAI,EAAE,CAAC;IACtC,CAAC;IAED,eAAe,CAAC,KAAa,EAAE,iBAAyB;QACtD,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAChC,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC;QAC1C,IAAI,IAAI,KAAK,CAAC,CAAC,IAAI,IAAI,KAAK,CAAC,CAAC;YAAE,OAAO,KAAK,CAAC;QAE7C,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;QACvC,MAAM,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC;QACvC,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;QAEnC,IAAI,SAAS,KAAK,iBAAiB;YAAE,OAAO,KAAK,CAAC;QAElD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QAC1C,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,GAAG,GAAG,YAAY;YAAE,OAAO,KAAK,CAAC;QAE9D,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,GAAG,SAAS,IAAI,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QAC1G,OAAO,IAAI,KAAK,QAAQ,CAAC;IAC3B,CAAC;IAED,iCAAiC;IAEjC,QAAQ,CAAC,SAAiB;QACxB,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC/C,IAAI,CAAC,KAAK,EAAE,WAAW;YAAE,OAAO,KAAK,CAAC;QACtC,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,WAAW,CAAC,OAAO,EAAE,EAAE,CAAC;YAC7C,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YACpC,OAAO,KAAK,CAAC;QACf,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,aAAa,CAAC,SAAiB;QAC7B,IAAI,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC7C,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,KAAK,GAAG,EAAE,QAAQ,EAAE,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;YAC3C,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;QAC1C,CAAC;QACD,KAAK,CAAC,QAAQ,EAAE,CAAC;QACjB,IAAI,KAAK,CAAC,QAAQ,IAAI,YAAY,EAAE,CAAC;YACnC,KAAK,CAAC,WAAW,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,UAAU,CAAC,CAAC;YACtD,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;QACxC,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,YAAY,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC;IACrE,CAAC;IAED,aAAa,CAAC,SAAiB;QAC7B,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACtC,CAAC;IAED,uBAAuB;IAEvB,UAAU,CAAC,QAAgB,EAAE,SAAiB;QAC5C,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAC5C,CAAC;IAED,UAAU,CAAC,QAAgB;QACzB,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACxC,CAAC;IAED,aAAa,CAAC,QAAgB;QAC5B,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACpC,CAAC;CACF;AAED,qBAAqB;AACrB,MAAM,CAAC,MAAM,IAAI,GAAG,IAAI,WAAW,EAAE,CAAC"}
package/dist/http.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import express from 'express';
2
+ export declare function createApp(webDir: string, pkg: {
3
+ version: string;
4
+ }, startedAt: Date): express.Express;
package/dist/http.js ADDED
@@ -0,0 +1,85 @@
1
+ import express from 'express';
2
+ import { join } from 'path';
3
+ import { sessions } from './session-manager.js';
4
+ export function createApp(webDir, pkg, startedAt) {
5
+ const app = express();
6
+ // Landing page at root
7
+ app.get('/', (_req, res) => {
8
+ res.sendFile(join(webDir, 'landing.html'));
9
+ });
10
+ // Chinese landing page
11
+ app.get('/zh', (_req, res) => {
12
+ res.sendFile(join(webDir, 'landing.zh.html'));
13
+ });
14
+ // Serve index.html with no-store (Vite hashed filenames handle cache-busting)
15
+ const sendIndexHtml = (_req, res) => {
16
+ res.setHeader('Cache-Control', 'no-store');
17
+ res.sendFile(join(webDir, 'index.html'));
18
+ };
19
+ // Intercept /index.html before express.static to ensure no-store is applied
20
+ app.get('/index.html', sendIndexHtml);
21
+ // Serve static assets from web/ — hashed assets are immutable, others use no-cache
22
+ app.use(express.static(webDir, {
23
+ setHeaders(res, filePath) {
24
+ if (filePath.includes('assets')) {
25
+ // Vite-hashed files (e.g. index-CqWVRN_z.js) — immutable
26
+ res.setHeader('Cache-Control', 'public, max-age=31536000, immutable');
27
+ }
28
+ else if (filePath.endsWith('.html')) {
29
+ res.setHeader('Cache-Control', 'no-store');
30
+ }
31
+ else {
32
+ res.setHeader('Cache-Control', 'no-cache');
33
+ }
34
+ },
35
+ }));
36
+ // SPA fallback: /s/:sessionId → serve versioned index.html
37
+ app.get('/s/:sessionId', sendIndexHtml);
38
+ // Health check
39
+ app.get('/api/health', (_req, res) => {
40
+ res.json({
41
+ status: 'ok',
42
+ agents: sessions.agents.size,
43
+ webClients: sessions.webClients.size,
44
+ timestamp: new Date().toISOString(),
45
+ });
46
+ });
47
+ // Server status (aggregate stats, no agent details exposed)
48
+ app.get('/api/status', (_req, res) => {
49
+ const mem = process.memoryUsage();
50
+ res.json({
51
+ server: {
52
+ version: pkg.version,
53
+ startedAt: startedAt.toISOString(),
54
+ uptimeSeconds: Math.floor((Date.now() - startedAt.getTime()) / 1000),
55
+ memoryMB: Math.round(mem.rss / 1024 / 1024),
56
+ },
57
+ agents: {
58
+ connected: sessions.agents.size,
59
+ },
60
+ webClients: {
61
+ connected: sessions.webClients.size,
62
+ },
63
+ });
64
+ });
65
+ // Session info API (web client fetches this to know agent details)
66
+ app.get('/api/session/:sessionId', (req, res) => {
67
+ const { sessionId } = req.params;
68
+ const agent = sessions.getAgentBySession(sessionId);
69
+ if (agent) {
70
+ res.json({
71
+ sessionId,
72
+ agent: {
73
+ name: agent.name,
74
+ workDir: agent.workDir,
75
+ connectedAt: agent.connectedAt,
76
+ },
77
+ });
78
+ }
79
+ else {
80
+ res.status(404).json({ error: 'Session not found' });
81
+ }
82
+ });
83
+ return app;
84
+ }
85
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
- import express from 'express';
2
1
  import { createServer } from 'http';
3
2
  import { createRequire } from 'module';
4
3
  import { WebSocket, WebSocketServer } from 'ws';
5
4
  import { fileURLToPath } from 'url';
6
5
  import { dirname, join } from 'path';
7
- import { agents, webClients, cleanupDeadConnections } from './context.js';
6
+ import { sessions } from './session-manager.js';
7
+ import { createApp } from './http.js';
8
8
  import { handleAgentConnection } from './ws-agent.js';
9
9
  import { handleWebConnection } from './ws-client.js';
10
10
  import { saveServerRuntimeState, clearServerRuntimeState } from './config.js';
@@ -14,87 +14,10 @@ const require = createRequire(import.meta.url);
14
14
  const pkg = require('../package.json');
15
15
  const startedAt = new Date();
16
16
  const PORT = parseInt(process.env.PORT || '3456', 10);
17
- const app = express();
17
+ const webDir = join(__dirname, '../web/dist');
18
+ const app = createApp(webDir, pkg, startedAt);
18
19
  const server = createServer(app);
19
20
  const wss = new WebSocketServer({ server });
20
- const webDir = join(__dirname, '../web/dist');
21
- // Landing page at root
22
- app.get('/', (_req, res) => {
23
- res.sendFile(join(webDir, 'landing.html'));
24
- });
25
- // Chinese landing page
26
- app.get('/zh', (_req, res) => {
27
- res.sendFile(join(webDir, 'landing.zh.html'));
28
- });
29
- // Serve index.html with no-store (Vite hashed filenames handle cache-busting)
30
- const sendIndexHtml = (_req, res) => {
31
- res.setHeader('Cache-Control', 'no-store');
32
- res.sendFile(join(webDir, 'index.html'));
33
- };
34
- // Intercept /index.html before express.static to ensure no-store is applied
35
- app.get('/index.html', sendIndexHtml);
36
- // Serve static assets from web/ — hashed assets are immutable, others use no-cache
37
- app.use(express.static(webDir, {
38
- setHeaders(res, filePath) {
39
- if (filePath.includes('assets')) {
40
- // Vite-hashed files (e.g. index-CqWVRN_z.js) — immutable
41
- res.setHeader('Cache-Control', 'public, max-age=31536000, immutable');
42
- }
43
- else if (filePath.endsWith('.html')) {
44
- res.setHeader('Cache-Control', 'no-store');
45
- }
46
- else {
47
- res.setHeader('Cache-Control', 'no-cache');
48
- }
49
- },
50
- }));
51
- // SPA fallback: /s/:sessionId → serve versioned index.html
52
- app.get('/s/:sessionId', sendIndexHtml);
53
- // Health check
54
- app.get('/api/health', (_req, res) => {
55
- res.json({
56
- status: 'ok',
57
- agents: agents.size,
58
- webClients: webClients.size,
59
- timestamp: new Date().toISOString(),
60
- });
61
- });
62
- // Server status (aggregate stats, no agent details exposed)
63
- app.get('/api/status', (_req, res) => {
64
- const mem = process.memoryUsage();
65
- res.json({
66
- server: {
67
- version: pkg.version,
68
- startedAt: startedAt.toISOString(),
69
- uptimeSeconds: Math.floor((Date.now() - startedAt.getTime()) / 1000),
70
- memoryMB: Math.round(mem.rss / 1024 / 1024),
71
- },
72
- agents: {
73
- connected: agents.size,
74
- },
75
- webClients: {
76
- connected: webClients.size,
77
- },
78
- });
79
- });
80
- // Session info API (web client fetches this to know agent details)
81
- app.get('/api/session/:sessionId', (req, res) => {
82
- const { sessionId } = req.params;
83
- for (const [, agent] of agents) {
84
- if (agent.sessionId === sessionId) {
85
- res.json({
86
- sessionId,
87
- agent: {
88
- name: agent.name,
89
- workDir: agent.workDir,
90
- connectedAt: agent.connectedAt,
91
- },
92
- });
93
- return;
94
- }
95
- }
96
- res.status(404).json({ error: 'Session not found' });
97
- });
98
21
  // WebSocket routing: agent or web client
99
22
  wss.on('connection', (ws, req) => {
100
23
  const url = new URL(req.url || '/', `http://${req.headers.host}`);
@@ -112,7 +35,7 @@ wss.on('connection', (ws, req) => {
112
35
  });
113
36
  // Heartbeat every 30s to detect dead connections
114
37
  setInterval(() => {
115
- cleanupDeadConnections((client, msg) => {
38
+ sessions.cleanupDeadConnections((client, msg) => {
116
39
  if (client.ws.readyState === WebSocket.OPEN) {
117
40
  encryptAndSend(client.ws, msg, client.sessionKey);
118
41
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,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,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,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,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;AAC9C,MAAM,GAAG,GAAG,SAAS,CAAC,MAAM,EAAE,GAAG,EAAE,SAAS,CAAC,CAAC;AAC9C,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;AACjC,MAAM,GAAG,GAAG,IAAI,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;AAE5C,yCAAyC;AACzC,GAAG,CAAC,EAAE,CAAC,YAAY,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE;IAC/B,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,EAAE,UAAU,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAClE,MAAM,IAAI,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAE1C,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;QACrB,qBAAqB,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;IACjC,CAAC;SAAM,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;QAC1B,mBAAmB,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;IAC/B,CAAC;SAAM,CAAC;QACN,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,4CAA4C,EAAE,CAAC,CAAC,CAAC;QAClG,EAAE,CAAC,KAAK,EAAE,CAAC;IACb,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,iDAAiD;AACjD,WAAW,CAAC,GAAG,EAAE;IACf,QAAQ,CAAC,sBAAsB,CAAC,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE;QAC9C,IAAI,MAAM,CAAC,EAAE,CAAC,UAAU,KAAK,SAAS,CAAC,IAAI,EAAE,CAAC;YAC5C,cAAc,CAAC,MAAM,CAAC,EAAE,EAAE,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;QACpD,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,EAAE,MAAM,CAAC,CAAC;AAEX,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;IACvB,OAAO,CAAC,GAAG,CAAC,oDAAoD,IAAI,EAAE,CAAC,CAAC;IACxE,OAAO,CAAC,GAAG,CAAC,uBAAuB,MAAM,EAAE,CAAC,CAAC;IAE7C,kDAAkD;IAClD,sBAAsB,CAAC;QACrB,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,IAAI,EAAE,IAAI;QACV,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACpC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,iCAAiC;AACjC,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAC;AAC5C,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,GAAG,uBAAuB,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC5E,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,GAAG,uBAAuB,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Per-ID ordered message relay queue.
3
+ * Guarantees messages for the same ID are processed sequentially,
4
+ * preventing out-of-order delivery caused by async encryption/compression.
5
+ */
6
+ export declare class MessageRelay {
7
+ private queues;
8
+ /**
9
+ * Enqueue a message handler for the given ID.
10
+ * Handlers for the same ID execute in order; different IDs run concurrently.
11
+ */
12
+ enqueue(id: string, handler: () => Promise<void>): void;
13
+ /**
14
+ * Remove the queue for the given ID (call on disconnect).
15
+ */
16
+ cleanup(id: string): void;
17
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Per-ID ordered message relay queue.
3
+ * Guarantees messages for the same ID are processed sequentially,
4
+ * preventing out-of-order delivery caused by async encryption/compression.
5
+ */
6
+ export class MessageRelay {
7
+ queues = new Map();
8
+ /**
9
+ * Enqueue a message handler for the given ID.
10
+ * Handlers for the same ID execute in order; different IDs run concurrently.
11
+ */
12
+ enqueue(id, handler) {
13
+ const prev = this.queues.get(id) || Promise.resolve();
14
+ this.queues.set(id, prev.then(handler).catch(() => { }));
15
+ }
16
+ /**
17
+ * Remove the queue for the given ID (call on disconnect).
18
+ */
19
+ cleanup(id) {
20
+ this.queues.delete(id);
21
+ }
22
+ }
23
+ //# sourceMappingURL=message-relay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message-relay.js","sourceRoot":"","sources":["../src/message-relay.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,OAAO,YAAY;IACf,MAAM,GAAG,IAAI,GAAG,EAAyB,CAAC;IAElD;;;OAGG;IACH,OAAO,CAAC,EAAU,EAAE,OAA4B;QAC9C,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACtD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC,CAAC;IAC1D,CAAC;IAED;;OAEG;IACH,OAAO,CAAC,EAAU;QAChB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACzB,CAAC;CACF"}
@@ -0,0 +1,44 @@
1
+ import { WebSocket } from 'ws';
2
+ export interface AgentSession {
3
+ ws: WebSocket;
4
+ agentId: string;
5
+ name: string;
6
+ hostname: string;
7
+ workDir: string;
8
+ version: string;
9
+ sessionId: string;
10
+ sessionKey: Uint8Array | null;
11
+ connectedAt: Date;
12
+ isAlive: boolean;
13
+ passwordHash: string | null;
14
+ passwordSalt: string | null;
15
+ }
16
+ export interface WebClient {
17
+ ws: WebSocket;
18
+ clientId: string;
19
+ sessionId: string;
20
+ sessionKey: Uint8Array | null;
21
+ connectedAt: Date;
22
+ isAlive: boolean;
23
+ }
24
+ export declare class SessionManager {
25
+ readonly agents: Map<string, AgentSession>;
26
+ readonly sessionToAgent: Map<string, string>;
27
+ readonly webClients: Map<string, WebClient>;
28
+ generateSessionId(): string;
29
+ registerAgent(agentId: string, agent: AgentSession): void;
30
+ removeAgent(agentId: string): void;
31
+ getAgent(agentId: string): AgentSession | undefined;
32
+ getAgentBySession(sessionId: string): AgentSession | undefined;
33
+ registerClient(clientId: string, client: WebClient): void;
34
+ removeClient(clientId: string): void;
35
+ getClient(clientId: string): WebClient | undefined;
36
+ getClientsForSession(sessionId: string): WebClient[];
37
+ cleanupDeadConnections(notifyFn: (client: WebClient, msg: {
38
+ type: string;
39
+ }) => void): {
40
+ removedAgents: string[];
41
+ removedClients: string[];
42
+ };
43
+ }
44
+ export declare const sessions: SessionManager;
@@ -0,0 +1,83 @@
1
+ import { randomBytes } from 'crypto';
2
+ export class SessionManager {
3
+ agents = new Map();
4
+ sessionToAgent = new Map();
5
+ webClients = new Map();
6
+ generateSessionId() {
7
+ return randomBytes(12).toString('base64url');
8
+ }
9
+ // --- Agent operations ---
10
+ registerAgent(agentId, agent) {
11
+ this.agents.set(agentId, agent);
12
+ this.sessionToAgent.set(agent.sessionId, agentId);
13
+ }
14
+ removeAgent(agentId) {
15
+ const agent = this.agents.get(agentId);
16
+ if (agent) {
17
+ this.sessionToAgent.delete(agent.sessionId);
18
+ this.agents.delete(agentId);
19
+ }
20
+ }
21
+ getAgent(agentId) {
22
+ return this.agents.get(agentId);
23
+ }
24
+ getAgentBySession(sessionId) {
25
+ const agentId = this.sessionToAgent.get(sessionId);
26
+ return agentId ? this.agents.get(agentId) : undefined;
27
+ }
28
+ // --- Client operations ---
29
+ registerClient(clientId, client) {
30
+ this.webClients.set(clientId, client);
31
+ }
32
+ removeClient(clientId) {
33
+ this.webClients.delete(clientId);
34
+ }
35
+ getClient(clientId) {
36
+ return this.webClients.get(clientId);
37
+ }
38
+ getClientsForSession(sessionId) {
39
+ const result = [];
40
+ for (const [, client] of this.webClients) {
41
+ if (client.sessionId === sessionId) {
42
+ result.push(client);
43
+ }
44
+ }
45
+ return result;
46
+ }
47
+ // --- Heartbeat ---
48
+ cleanupDeadConnections(notifyFn) {
49
+ const removedAgents = [];
50
+ const removedClients = [];
51
+ for (const [agentId, agent] of this.agents) {
52
+ if (!agent.isAlive) {
53
+ console.log(`[Heartbeat] Agent ${agent.name} timed out`);
54
+ agent.ws.terminate();
55
+ this.sessionToAgent.delete(agent.sessionId);
56
+ this.agents.delete(agentId);
57
+ removedAgents.push(agentId);
58
+ for (const [, client] of this.webClients) {
59
+ if (client.sessionId === agent.sessionId) {
60
+ notifyFn(client, { type: 'agent_disconnected' });
61
+ }
62
+ }
63
+ continue;
64
+ }
65
+ agent.isAlive = false;
66
+ agent.ws.ping();
67
+ }
68
+ for (const [clientId, client] of this.webClients) {
69
+ if (!client.isAlive) {
70
+ client.ws.terminate();
71
+ this.webClients.delete(clientId);
72
+ removedClients.push(clientId);
73
+ continue;
74
+ }
75
+ client.isAlive = false;
76
+ client.ws.ping();
77
+ }
78
+ return { removedAgents, removedClients };
79
+ }
80
+ }
81
+ // Singleton instance for backward compatibility
82
+ export const sessions = new SessionManager();
83
+ //# sourceMappingURL=session-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-manager.js","sourceRoot":"","sources":["../src/session-manager.ts"],"names":[],"mappings":"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"}