supersurf-daemon 1.0.0
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/LICENSE +225 -0
- package/README.md +40 -0
- package/dist/extension-bridge.d.ts +51 -0
- package/dist/extension-bridge.d.ts.map +1 -0
- package/dist/extension-bridge.js +259 -0
- package/dist/extension-bridge.js.map +1 -0
- package/dist/ipc.d.ts +43 -0
- package/dist/ipc.d.ts.map +1 -0
- package/dist/ipc.js +192 -0
- package/dist/ipc.js.map +1 -0
- package/dist/main.d.ts +31 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +224 -0
- package/dist/main.js.map +1 -0
- package/dist/scheduler.d.ts +48 -0
- package/dist/scheduler.d.ts.map +1 -0
- package/dist/scheduler.js +223 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/session.d.ts +43 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +106 -0
- package/dist/session.js.map +1 -0
- package/dist/shared/index.d.ts +3 -0
- package/dist/shared/index.d.ts.map +1 -0
- package/dist/shared/index.js +10 -0
- package/dist/shared/index.js.map +1 -0
- package/dist/shared/logger/logger.d.ts +41 -0
- package/dist/shared/logger/logger.d.ts.map +1 -0
- package/dist/shared/logger/logger.js +110 -0
- package/dist/shared/logger/logger.js.map +1 -0
- package/dist/types.d.ts +29 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/package.json +45 -0
package/dist/ipc.js
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* IPC Server — Unix socket server for MCP session connections.
|
|
4
|
+
*
|
|
5
|
+
* Accepts connections from MCP servers over a Unix domain socket.
|
|
6
|
+
* Protocol:
|
|
7
|
+
* 1. MCP server sends: { type: "session_register", sessionId: "..." }\n
|
|
8
|
+
* 2. Daemon responds: { type: "session_ack", browser: "...", buildTimestamp: "..." }\n
|
|
9
|
+
* or { type: "session_reject", reason: "..." }\n
|
|
10
|
+
* 3. Post-handshake: NDJSON (newline-delimited JSON-RPC 2.0) for tool calls
|
|
11
|
+
*
|
|
12
|
+
* @module ipc
|
|
13
|
+
*/
|
|
14
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
15
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
16
|
+
};
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.IPCServer = void 0;
|
|
19
|
+
const net_1 = __importDefault(require("net"));
|
|
20
|
+
const debugLog = (...args) => {
|
|
21
|
+
const logger = global.DAEMON_LOGGER;
|
|
22
|
+
if (logger)
|
|
23
|
+
logger.log('[IPC]', ...args);
|
|
24
|
+
else if (global.DAEMON_DEBUG)
|
|
25
|
+
console.error('[IPC]', ...args);
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Unix domain socket server for MCP session connections.
|
|
29
|
+
* Handles session handshake, NDJSON message routing, and cleanup.
|
|
30
|
+
*/
|
|
31
|
+
class IPCServer {
|
|
32
|
+
server = null;
|
|
33
|
+
socketPath;
|
|
34
|
+
bridge;
|
|
35
|
+
sessions;
|
|
36
|
+
scheduler;
|
|
37
|
+
onSessionCountChange = null;
|
|
38
|
+
constructor(socketPath, bridge, sessions, scheduler) {
|
|
39
|
+
this.socketPath = socketPath;
|
|
40
|
+
this.bridge = bridge;
|
|
41
|
+
this.sessions = sessions;
|
|
42
|
+
this.scheduler = scheduler;
|
|
43
|
+
}
|
|
44
|
+
/** Set a callback for session count changes (used by idle timeout). */
|
|
45
|
+
setSessionCountCallback(cb) {
|
|
46
|
+
this.onSessionCountChange = cb;
|
|
47
|
+
}
|
|
48
|
+
/** Start listening on the Unix socket. */
|
|
49
|
+
async start() {
|
|
50
|
+
return new Promise((resolve, reject) => {
|
|
51
|
+
this.server = net_1.default.createServer((socket) => this.handleConnection(socket));
|
|
52
|
+
this.server.on('error', (error) => {
|
|
53
|
+
debugLog('IPC server error:', error);
|
|
54
|
+
reject(error);
|
|
55
|
+
});
|
|
56
|
+
this.server.listen(this.socketPath, () => {
|
|
57
|
+
debugLog(`IPC listening on ${this.socketPath}`);
|
|
58
|
+
resolve();
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
/** Handle a new connection from an MCP server. */
|
|
63
|
+
handleConnection(socket) {
|
|
64
|
+
debugLog('New IPC connection');
|
|
65
|
+
let buffer = '';
|
|
66
|
+
let sessionId = null;
|
|
67
|
+
let handshakeComplete = false;
|
|
68
|
+
socket.on('data', (data) => {
|
|
69
|
+
buffer += data.toString();
|
|
70
|
+
let newlineIdx;
|
|
71
|
+
while ((newlineIdx = buffer.indexOf('\n')) !== -1) {
|
|
72
|
+
const line = buffer.slice(0, newlineIdx).trim();
|
|
73
|
+
buffer = buffer.slice(newlineIdx + 1);
|
|
74
|
+
if (!line)
|
|
75
|
+
continue;
|
|
76
|
+
try {
|
|
77
|
+
const msg = JSON.parse(line);
|
|
78
|
+
if (!handshakeComplete) {
|
|
79
|
+
// Expecting session_register handshake
|
|
80
|
+
if (msg.type === 'session_register' && msg.sessionId) {
|
|
81
|
+
sessionId = msg.sessionId;
|
|
82
|
+
if (this.sessions.has(sessionId)) {
|
|
83
|
+
this.sendLine(socket, {
|
|
84
|
+
type: 'session_reject',
|
|
85
|
+
reason: 'Session ID already in use',
|
|
86
|
+
});
|
|
87
|
+
socket.end();
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
// Register the session
|
|
91
|
+
this.sessions.add(sessionId, socket);
|
|
92
|
+
this.scheduler.addSession(sessionId);
|
|
93
|
+
this.sendLine(socket, {
|
|
94
|
+
type: 'session_ack',
|
|
95
|
+
browser: this.bridge.browser,
|
|
96
|
+
buildTimestamp: this.bridge.buildTime,
|
|
97
|
+
});
|
|
98
|
+
handshakeComplete = true;
|
|
99
|
+
debugLog(`Session registered: "${sessionId}"`);
|
|
100
|
+
if (this.onSessionCountChange) {
|
|
101
|
+
this.onSessionCountChange(this.sessions.count);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
this.sendLine(socket, {
|
|
106
|
+
type: 'session_reject',
|
|
107
|
+
reason: 'Expected session_register handshake',
|
|
108
|
+
});
|
|
109
|
+
socket.end();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
// Post-handshake: JSON-RPC 2.0 requests
|
|
114
|
+
this.handleRequest(sessionId, socket, msg);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
debugLog('Parse error:', err.message);
|
|
119
|
+
if (handshakeComplete && sessionId) {
|
|
120
|
+
this.sendLine(socket, {
|
|
121
|
+
jsonrpc: '2.0',
|
|
122
|
+
id: null,
|
|
123
|
+
error: { code: -32700, message: `Parse error: ${err.message}` },
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
socket.on('close', () => {
|
|
130
|
+
if (sessionId) {
|
|
131
|
+
debugLog(`Session disconnected: "${sessionId}"`);
|
|
132
|
+
this.scheduler.removeSession(sessionId);
|
|
133
|
+
this.sessions.remove(sessionId);
|
|
134
|
+
// Notify extension to ungroup the session's tabs
|
|
135
|
+
this.bridge.sendCmd('sessionDisconnect', { sessionId }, 5000).catch(() => { });
|
|
136
|
+
if (this.onSessionCountChange) {
|
|
137
|
+
this.onSessionCountChange(this.sessions.count);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
socket.on('error', (error) => {
|
|
142
|
+
debugLog('Socket error:', error.message);
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
/** Route a JSON-RPC 2.0 request into the scheduler. */
|
|
146
|
+
async handleRequest(sessionId, socket, msg) {
|
|
147
|
+
if (msg.jsonrpc !== '2.0' || !msg.method || msg.id === undefined) {
|
|
148
|
+
this.sendLine(socket, {
|
|
149
|
+
jsonrpc: '2.0',
|
|
150
|
+
id: msg.id ?? null,
|
|
151
|
+
error: { code: -32600, message: 'Invalid JSON-RPC 2.0 request' },
|
|
152
|
+
});
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
try {
|
|
156
|
+
const result = await this.scheduler.enqueue(sessionId, msg.method, msg.params || {}, msg.timeout || 30000);
|
|
157
|
+
this.sendLine(socket, { jsonrpc: '2.0', id: msg.id, result });
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
this.sendLine(socket, {
|
|
161
|
+
jsonrpc: '2.0',
|
|
162
|
+
id: msg.id,
|
|
163
|
+
error: { code: -32000, message: error.message || String(error) },
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/** Write an NDJSON line to a socket. */
|
|
168
|
+
sendLine(socket, data) {
|
|
169
|
+
if (!socket.writable)
|
|
170
|
+
return;
|
|
171
|
+
socket.write(JSON.stringify(data) + '\n');
|
|
172
|
+
}
|
|
173
|
+
/** Gracefully shut down the IPC server. */
|
|
174
|
+
async stop() {
|
|
175
|
+
return new Promise((resolve) => {
|
|
176
|
+
if (!this.server) {
|
|
177
|
+
resolve();
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
// Close all session sockets
|
|
181
|
+
for (const session of this.sessions.values()) {
|
|
182
|
+
session.socket.end();
|
|
183
|
+
}
|
|
184
|
+
this.server.close(() => {
|
|
185
|
+
debugLog('IPC server stopped');
|
|
186
|
+
resolve();
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
exports.IPCServer = IPCServer;
|
|
192
|
+
//# sourceMappingURL=ipc.js.map
|
package/dist/ipc.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ipc.js","sourceRoot":"","sources":["../src/ipc.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;;;;;;AAEH,8CAAsB;AAOtB,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAe,EAAE,EAAE;IACtC,MAAM,MAAM,GAAI,MAAc,CAAC,aAAuC,CAAC;IACvE,IAAI,MAAM;QAAE,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;SACpC,IAAK,MAAc,CAAC,YAAY;QAAE,OAAO,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;AACzE,CAAC,CAAC;AAKF;;;GAGG;AACH,MAAa,SAAS;IACZ,MAAM,GAAsB,IAAI,CAAC;IACjC,UAAU,CAAS;IACnB,MAAM,CAAkB;IACxB,QAAQ,CAAkB;IAC1B,SAAS,CAAmB;IAC5B,oBAAoB,GAAgC,IAAI,CAAC;IAEjE,YACE,UAAkB,EAClB,MAAuB,EACvB,QAAyB,EACzB,SAA2B;QAE3B,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC7B,CAAC;IAED,uEAAuE;IACvE,uBAAuB,CAAC,EAAwB;QAC9C,IAAI,CAAC,oBAAoB,GAAG,EAAE,CAAC;IACjC,CAAC;IAED,0CAA0C;IAC1C,KAAK,CAAC,KAAK;QACT,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,IAAI,CAAC,MAAM,GAAG,aAAG,CAAC,YAAY,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC;YAE1E,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;gBAChC,QAAQ,CAAC,mBAAmB,EAAE,KAAK,CAAC,CAAC;gBACrC,MAAM,CAAC,KAAK,CAAC,CAAC;YAChB,CAAC,CAAC,CAAC;YAEH,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,EAAE;gBACvC,QAAQ,CAAC,oBAAoB,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;gBAChD,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED,kDAAkD;IAC1C,gBAAgB,CAAC,MAAkB;QACzC,QAAQ,CAAC,oBAAoB,CAAC,CAAC;QAE/B,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,SAAS,GAAkB,IAAI,CAAC;QACpC,IAAI,iBAAiB,GAAG,KAAK,CAAC;QAE9B,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;YACzB,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAE1B,IAAI,UAAkB,CAAC;YACvB,OAAO,CAAC,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;gBAClD,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,IAAI,EAAE,CAAC;gBAChD,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;gBAEtC,IAAI,CAAC,IAAI;oBAAE,SAAS;gBAEpB,IAAI,CAAC;oBACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;oBAE7B,IAAI,CAAC,iBAAiB,EAAE,CAAC;wBACvB,uCAAuC;wBACvC,IAAI,GAAG,CAAC,IAAI,KAAK,kBAAkB,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC;4BACrD,SAAS,GAAG,GAAG,CAAC,SAAS,CAAC;4BAE1B,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAU,CAAC,EAAE,CAAC;gCAClC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE;oCACpB,IAAI,EAAE,gBAAgB;oCACtB,MAAM,EAAE,2BAA2B;iCACpC,CAAC,CAAC;gCACH,MAAM,CAAC,GAAG,EAAE,CAAC;gCACb,OAAO;4BACT,CAAC;4BAED,uBAAuB;4BACvB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAU,EAAE,MAAM,CAAC,CAAC;4BACtC,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,SAAU,CAAC,CAAC;4BAEtC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE;gCACpB,IAAI,EAAE,aAAa;gCACnB,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO;gCAC5B,cAAc,EAAE,IAAI,CAAC,MAAM,CAAC,SAAS;6BACtC,CAAC,CAAC;4BAEH,iBAAiB,GAAG,IAAI,CAAC;4BACzB,QAAQ,CAAC,wBAAwB,SAAS,GAAG,CAAC,CAAC;4BAE/C,IAAI,IAAI,CAAC,oBAAoB,EAAE,CAAC;gCAC9B,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;4BACjD,CAAC;wBACH,CAAC;6BAAM,CAAC;4BACN,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE;gCACpB,IAAI,EAAE,gBAAgB;gCACtB,MAAM,EAAE,qCAAqC;6BAC9C,CAAC,CAAC;4BACH,MAAM,CAAC,GAAG,EAAE,CAAC;wBACf,CAAC;oBACH,CAAC;yBAAM,CAAC;wBACN,wCAAwC;wBACxC,IAAI,CAAC,aAAa,CAAC,SAAU,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;oBAC9C,CAAC;gBACH,CAAC;gBAAC,OAAO,GAAQ,EAAE,CAAC;oBAClB,QAAQ,CAAC,cAAc,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;oBACtC,IAAI,iBAAiB,IAAI,SAAS,EAAE,CAAC;wBACnC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE;4BACpB,OAAO,EAAE,KAAK;4BACd,EAAE,EAAE,IAAI;4BACR,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,gBAAgB,GAAG,CAAC,OAAO,EAAE,EAAE;yBAChE,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACtB,IAAI,SAAS,EAAE,CAAC;gBACd,QAAQ,CAAC,0BAA0B,SAAS,GAAG,CAAC,CAAC;gBACjD,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;gBACxC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;gBAEhC,iDAAiD;gBACjD,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,mBAAmB,EAAE,EAAE,SAAS,EAAE,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;gBAE9E,IAAI,IAAI,CAAC,oBAAoB,EAAE,CAAC;oBAC9B,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;gBACjD,CAAC;YACH,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;YAC3B,QAAQ,CAAC,eAAe,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;QAC3C,CAAC,CAAC,CAAC;IACL,CAAC;IAED,uDAAuD;IAC/C,KAAK,CAAC,aAAa,CAAC,SAAiB,EAAE,MAAkB,EAAE,GAAQ;QACzE,IAAI,GAAG,CAAC,OAAO,KAAK,KAAK,IAAI,CAAC,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;YACjE,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE;gBACpB,OAAO,EAAE,KAAK;gBACd,EAAE,EAAE,GAAG,CAAC,EAAE,IAAI,IAAI;gBAClB,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,8BAA8B,EAAE;aACjE,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QAED,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CACzC,SAAS,EACT,GAAG,CAAC,MAAM,EACV,GAAG,CAAC,MAAM,IAAI,EAAE,EAChB,GAAG,CAAC,OAAO,IAAI,KAAK,CACrB,CAAC;YACF,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;QAChE,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE;gBACpB,OAAO,EAAE,KAAK;gBACd,EAAE,EAAE,GAAG,CAAC,EAAE;gBACV,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE;aACjE,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,wCAAwC;IAChC,QAAQ,CAAC,MAAkB,EAAE,IAAS;QAC5C,IAAI,CAAC,MAAM,CAAC,QAAQ;YAAE,OAAO;QAC7B,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;IAC5C,CAAC;IAED,2CAA2C;IAC3C,KAAK,CAAC,IAAI;QACR,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC7B,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACjB,OAAO,EAAE,CAAC;gBACV,OAAO;YACT,CAAC;YAED,4BAA4B;YAC5B,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;gBAC7C,OAAO,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC;YACvB,CAAC;YAED,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE;gBACrB,QAAQ,CAAC,oBAAoB,CAAC,CAAC;gBAC/B,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;CACF;AA9LD,8BA8LC"}
|
package/dist/main.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* SuperSurf Daemon — standalone coordinator for multiple MCP sessions.
|
|
4
|
+
*
|
|
5
|
+
* Manages a single Chrome extension connection (WebSocket) and multiplexes
|
|
6
|
+
* tool calls from multiple MCP servers (Unix domain socket).
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* supersurf-daemon [--port <n>] [--debug]
|
|
10
|
+
*
|
|
11
|
+
* Files:
|
|
12
|
+
* ~/.supersurf/daemon.pid — PID file for process detection
|
|
13
|
+
* ~/.supersurf/daemon.sock — Unix domain socket for MCP server IPC
|
|
14
|
+
* ~/.supersurf/logs/daemon.log — debug log (when --debug)
|
|
15
|
+
*
|
|
16
|
+
* @module main
|
|
17
|
+
*/
|
|
18
|
+
declare const SUPERSURF_DIR: string;
|
|
19
|
+
declare const PID_FILE: string;
|
|
20
|
+
declare const SOCK_FILE: string;
|
|
21
|
+
declare const IDLE_TIMEOUT_MS: number;
|
|
22
|
+
declare function parseArgs(argv: string[]): {
|
|
23
|
+
port: number;
|
|
24
|
+
debug: boolean;
|
|
25
|
+
};
|
|
26
|
+
/** Check if a process with the given PID is alive. */
|
|
27
|
+
declare function isProcessAlive(pid: number): boolean;
|
|
28
|
+
/** Clean stale PID/socket files if the referenced process is dead. */
|
|
29
|
+
declare function cleanStaleFiles(): void;
|
|
30
|
+
export { parseArgs, isProcessAlive, cleanStaleFiles, SUPERSURF_DIR, PID_FILE, SOCK_FILE, IDLE_TIMEOUT_MS };
|
|
31
|
+
//# sourceMappingURL=main.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;GAeG;AAWH,QAAA,MAAM,aAAa,QAAwC,CAAC;AAC5D,QAAA,MAAM,QAAQ,QAAyC,CAAC;AACxD,QAAA,MAAM,SAAS,QAA0C,CAAC;AAE1D,QAAA,MAAM,eAAe,QAAiB,CAAC;AAIvC,iBAAS,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAkBnE;AAID,sDAAsD;AACtD,iBAAS,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAO5C;AAED,sEAAsE;AACtE,iBAAS,eAAe,IAAI,IAAI,CAmB/B;AAiID,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,eAAe,EAAE,aAAa,EAAE,QAAQ,EAAE,SAAS,EAAE,eAAe,EAAE,CAAC"}
|
package/dist/main.js
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* SuperSurf Daemon — standalone coordinator for multiple MCP sessions.
|
|
5
|
+
*
|
|
6
|
+
* Manages a single Chrome extension connection (WebSocket) and multiplexes
|
|
7
|
+
* tool calls from multiple MCP servers (Unix domain socket).
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* supersurf-daemon [--port <n>] [--debug]
|
|
11
|
+
*
|
|
12
|
+
* Files:
|
|
13
|
+
* ~/.supersurf/daemon.pid — PID file for process detection
|
|
14
|
+
* ~/.supersurf/daemon.sock — Unix domain socket for MCP server IPC
|
|
15
|
+
* ~/.supersurf/logs/daemon.log — debug log (when --debug)
|
|
16
|
+
*
|
|
17
|
+
* @module main
|
|
18
|
+
*/
|
|
19
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
20
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
21
|
+
};
|
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
+
exports.IDLE_TIMEOUT_MS = exports.SOCK_FILE = exports.PID_FILE = exports.SUPERSURF_DIR = void 0;
|
|
24
|
+
exports.parseArgs = parseArgs;
|
|
25
|
+
exports.isProcessAlive = isProcessAlive;
|
|
26
|
+
exports.cleanStaleFiles = cleanStaleFiles;
|
|
27
|
+
const fs_1 = __importDefault(require("fs"));
|
|
28
|
+
const path_1 = __importDefault(require("path"));
|
|
29
|
+
const os_1 = __importDefault(require("os"));
|
|
30
|
+
const shared_1 = require("./shared");
|
|
31
|
+
const extension_bridge_1 = require("./extension-bridge");
|
|
32
|
+
const session_1 = require("./session");
|
|
33
|
+
const scheduler_1 = require("./scheduler");
|
|
34
|
+
const ipc_1 = require("./ipc");
|
|
35
|
+
const SUPERSURF_DIR = path_1.default.join(os_1.default.homedir(), '.supersurf');
|
|
36
|
+
exports.SUPERSURF_DIR = SUPERSURF_DIR;
|
|
37
|
+
const PID_FILE = path_1.default.join(SUPERSURF_DIR, 'daemon.pid');
|
|
38
|
+
exports.PID_FILE = PID_FILE;
|
|
39
|
+
const SOCK_FILE = path_1.default.join(SUPERSURF_DIR, 'daemon.sock');
|
|
40
|
+
exports.SOCK_FILE = SOCK_FILE;
|
|
41
|
+
const LOG_FILE = path_1.default.join(SUPERSURF_DIR, 'logs', 'daemon.log');
|
|
42
|
+
const IDLE_TIMEOUT_MS = 10 * 60 * 1000; // 10 minutes
|
|
43
|
+
exports.IDLE_TIMEOUT_MS = IDLE_TIMEOUT_MS;
|
|
44
|
+
// ─── CLI Parsing ──────────────────────────────────────────────
|
|
45
|
+
function parseArgs(argv) {
|
|
46
|
+
let port = 5555;
|
|
47
|
+
let debug = false;
|
|
48
|
+
for (let i = 2; i < argv.length; i++) {
|
|
49
|
+
if (argv[i] === '--port' && argv[i + 1]) {
|
|
50
|
+
port = parseInt(argv[i + 1], 10);
|
|
51
|
+
if (isNaN(port)) {
|
|
52
|
+
console.error('Invalid port number');
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
i++;
|
|
56
|
+
}
|
|
57
|
+
else if (argv[i] === '--debug') {
|
|
58
|
+
debug = true;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return { port, debug };
|
|
62
|
+
}
|
|
63
|
+
// ─── PID File Management ──────────────────────────────────────
|
|
64
|
+
/** Check if a process with the given PID is alive. */
|
|
65
|
+
function isProcessAlive(pid) {
|
|
66
|
+
try {
|
|
67
|
+
process.kill(pid, 0);
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/** Clean stale PID/socket files if the referenced process is dead. */
|
|
75
|
+
function cleanStaleFiles() {
|
|
76
|
+
if (fs_1.default.existsSync(PID_FILE)) {
|
|
77
|
+
try {
|
|
78
|
+
const pid = parseInt(fs_1.default.readFileSync(PID_FILE, 'utf8').trim(), 10);
|
|
79
|
+
if (!isNaN(pid) && !isProcessAlive(pid)) {
|
|
80
|
+
fs_1.default.unlinkSync(PID_FILE);
|
|
81
|
+
if (fs_1.default.existsSync(SOCK_FILE)) {
|
|
82
|
+
fs_1.default.unlinkSync(SOCK_FILE);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// If we can't read the PID file, clean both
|
|
88
|
+
try {
|
|
89
|
+
fs_1.default.unlinkSync(PID_FILE);
|
|
90
|
+
}
|
|
91
|
+
catch { }
|
|
92
|
+
try {
|
|
93
|
+
fs_1.default.unlinkSync(SOCK_FILE);
|
|
94
|
+
}
|
|
95
|
+
catch { }
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
else if (fs_1.default.existsSync(SOCK_FILE)) {
|
|
99
|
+
// Orphaned socket file without a PID file — clean it
|
|
100
|
+
try {
|
|
101
|
+
fs_1.default.unlinkSync(SOCK_FILE);
|
|
102
|
+
}
|
|
103
|
+
catch { }
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/** Write current PID to the PID file. */
|
|
107
|
+
function writePidFile() {
|
|
108
|
+
fs_1.default.writeFileSync(PID_FILE, String(process.pid), 'utf8');
|
|
109
|
+
}
|
|
110
|
+
/** Remove PID and socket files on shutdown. */
|
|
111
|
+
function cleanupFiles() {
|
|
112
|
+
try {
|
|
113
|
+
fs_1.default.unlinkSync(PID_FILE);
|
|
114
|
+
}
|
|
115
|
+
catch { }
|
|
116
|
+
try {
|
|
117
|
+
fs_1.default.unlinkSync(SOCK_FILE);
|
|
118
|
+
}
|
|
119
|
+
catch { }
|
|
120
|
+
}
|
|
121
|
+
// ─── Main ──────────────────────────────────────────────────────
|
|
122
|
+
async function main() {
|
|
123
|
+
const { port, debug } = parseArgs(process.argv);
|
|
124
|
+
// Initialize logger — always create, only enable if --debug
|
|
125
|
+
const logger = new shared_1.FileLogger(LOG_FILE);
|
|
126
|
+
if (debug) {
|
|
127
|
+
logger.enable();
|
|
128
|
+
global.DAEMON_DEBUG = true;
|
|
129
|
+
global.DAEMON_LOGGER = logger;
|
|
130
|
+
}
|
|
131
|
+
logger.log(`[Daemon] Starting daemon (port=${port}, pid=${process.pid})`);
|
|
132
|
+
// Ensure ~/.supersurf/ exists
|
|
133
|
+
if (!fs_1.default.existsSync(SUPERSURF_DIR)) {
|
|
134
|
+
fs_1.default.mkdirSync(SUPERSURF_DIR, { recursive: true });
|
|
135
|
+
}
|
|
136
|
+
// Clean stale files from a crashed previous instance
|
|
137
|
+
cleanStaleFiles();
|
|
138
|
+
// Check if daemon is already running
|
|
139
|
+
if (fs_1.default.existsSync(PID_FILE)) {
|
|
140
|
+
const existingPid = parseInt(fs_1.default.readFileSync(PID_FILE, 'utf8').trim(), 10);
|
|
141
|
+
if (!isNaN(existingPid) && isProcessAlive(existingPid)) {
|
|
142
|
+
console.error(`Daemon already running (pid ${existingPid})`);
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// Write PID file
|
|
147
|
+
writePidFile();
|
|
148
|
+
// Initialize components
|
|
149
|
+
const bridge = new extension_bridge_1.ExtensionBridge(port, '127.0.0.1');
|
|
150
|
+
const sessions = new session_1.SessionRegistry();
|
|
151
|
+
const scheduler = new scheduler_1.RequestScheduler(bridge, sessions);
|
|
152
|
+
const ipc = new ipc_1.IPCServer(SOCK_FILE, bridge, sessions, scheduler);
|
|
153
|
+
// Idle timeout: exit after 10 minutes with no sessions
|
|
154
|
+
let idleTimer = null;
|
|
155
|
+
function resetIdleTimer() {
|
|
156
|
+
if (idleTimer) {
|
|
157
|
+
clearTimeout(idleTimer);
|
|
158
|
+
idleTimer = null;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function startIdleTimer() {
|
|
162
|
+
resetIdleTimer();
|
|
163
|
+
idleTimer = setTimeout(() => {
|
|
164
|
+
logger.log('[Daemon] Idle timeout — no sessions for 10 minutes, exiting');
|
|
165
|
+
shutdown();
|
|
166
|
+
}, IDLE_TIMEOUT_MS);
|
|
167
|
+
}
|
|
168
|
+
ipc.setSessionCountCallback((count) => {
|
|
169
|
+
logger.log(`[Daemon] Session count: ${count}`);
|
|
170
|
+
if (count === 0) {
|
|
171
|
+
startIdleTimer();
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
resetIdleTimer();
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
// Graceful shutdown
|
|
178
|
+
let shuttingDown = false;
|
|
179
|
+
async function shutdown() {
|
|
180
|
+
if (shuttingDown)
|
|
181
|
+
return;
|
|
182
|
+
shuttingDown = true;
|
|
183
|
+
logger.log('[Daemon] Shutting down...');
|
|
184
|
+
resetIdleTimer();
|
|
185
|
+
scheduler.drainAll();
|
|
186
|
+
await ipc.stop();
|
|
187
|
+
await bridge.stop();
|
|
188
|
+
cleanupFiles();
|
|
189
|
+
logger.log('[Daemon] Shutdown complete');
|
|
190
|
+
process.exit(0);
|
|
191
|
+
}
|
|
192
|
+
process.on('SIGTERM', shutdown);
|
|
193
|
+
process.on('SIGINT', shutdown);
|
|
194
|
+
// Start extension WebSocket server
|
|
195
|
+
try {
|
|
196
|
+
await bridge.start();
|
|
197
|
+
logger.log(`[Daemon] Extension WebSocket listening on port ${port}`);
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
console.error(`Failed to start extension WebSocket: ${error.message}`);
|
|
201
|
+
cleanupFiles();
|
|
202
|
+
process.exit(1);
|
|
203
|
+
}
|
|
204
|
+
// Start IPC server
|
|
205
|
+
try {
|
|
206
|
+
await ipc.start();
|
|
207
|
+
logger.log(`[Daemon] IPC listening on ${SOCK_FILE}`);
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
console.error(`Failed to start IPC server: ${error.message}`);
|
|
211
|
+
await bridge.stop();
|
|
212
|
+
cleanupFiles();
|
|
213
|
+
process.exit(1);
|
|
214
|
+
}
|
|
215
|
+
// Start idle timer (no sessions yet)
|
|
216
|
+
startIdleTimer();
|
|
217
|
+
logger.log('[Daemon] Daemon ready');
|
|
218
|
+
}
|
|
219
|
+
main().catch((error) => {
|
|
220
|
+
console.error('Daemon fatal error:', error);
|
|
221
|
+
cleanupFiles();
|
|
222
|
+
process.exit(1);
|
|
223
|
+
});
|
|
224
|
+
//# sourceMappingURL=main.js.map
|
package/dist/main.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";;AACA;;;;;;;;;;;;;;;GAeG;;;;;;AAwMM,8BAAS;AAAE,wCAAc;AAAE,0CAAe;AAtMnD,4CAAoB;AACpB,gDAAwB;AACxB,4CAAoB;AACpB,mCAAoC;AACpC,yDAAqD;AACrD,uCAA4C;AAC5C,2CAA+C;AAC/C,+BAAkC;AAElC,MAAM,aAAa,GAAG,cAAI,CAAC,IAAI,CAAC,YAAE,CAAC,OAAO,EAAE,EAAE,YAAY,CAAC,CAAC;AA6LP,sCAAa;AA5LlE,MAAM,QAAQ,GAAG,cAAI,CAAC,IAAI,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC;AA4LY,4BAAQ;AA3L5E,MAAM,SAAS,GAAG,cAAI,CAAC,IAAI,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;AA2LoB,8BAAS;AA1LvF,MAAM,QAAQ,GAAG,cAAI,CAAC,IAAI,CAAC,aAAa,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC;AAChE,MAAM,eAAe,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,aAAa;AAyLoC,0CAAe;AAvLxG,iEAAiE;AAEjE,SAAS,SAAS,CAAC,IAAc;IAC/B,IAAI,IAAI,GAAG,IAAI,CAAC;IAChB,IAAI,KAAK,GAAG,KAAK,CAAC;IAElB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YACxC,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACjC,IAAI,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;gBAChB,OAAO,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;gBACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;YACD,CAAC,EAAE,CAAC;QACN,CAAC;aAAM,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,SAAS,EAAE,CAAC;YACjC,KAAK,GAAG,IAAI,CAAC;QACf,CAAC;IACH,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AACzB,CAAC;AAED,iEAAiE;AAEjE,sDAAsD;AACtD,SAAS,cAAc,CAAC,GAAW;IACjC,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,sEAAsE;AACtE,SAAS,eAAe;IACtB,IAAI,YAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5B,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,QAAQ,CAAC,YAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;YACnE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC;gBACxC,YAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;gBACxB,IAAI,YAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;oBAC7B,YAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;gBAC3B,CAAC;YACH,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,4CAA4C;YAC5C,IAAI,CAAC;gBAAC,YAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAA,CAAC;YACzC,IAAI,CAAC;gBAAC,YAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAA,CAAC;QAC5C,CAAC;IACH,CAAC;SAAM,IAAI,YAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QACpC,qDAAqD;QACrD,IAAI,CAAC;YAAC,YAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IAC5C,CAAC;AACH,CAAC;AAED,yCAAyC;AACzC,SAAS,YAAY;IACnB,YAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC;AAC1D,CAAC;AAED,+CAA+C;AAC/C,SAAS,YAAY;IACnB,IAAI,CAAC;QAAC,YAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;IACzC,IAAI,CAAC;QAAC,YAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;AAC5C,CAAC;AAED,kEAAkE;AAElE,KAAK,UAAU,IAAI;IACjB,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhD,4DAA4D;IAC5D,MAAM,MAAM,GAAG,IAAI,mBAAU,CAAC,QAAQ,CAAC,CAAC;IACxC,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,CAAC,MAAM,EAAE,CAAC;QACf,MAAc,CAAC,YAAY,GAAG,IAAI,CAAC;QACnC,MAAc,CAAC,aAAa,GAAG,MAAM,CAAC;IACzC,CAAC;IAED,MAAM,CAAC,GAAG,CAAC,kCAAkC,IAAI,SAAS,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC;IAE1E,8BAA8B;IAC9B,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QAClC,YAAE,CAAC,SAAS,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,CAAC;IAED,qDAAqD;IACrD,eAAe,EAAE,CAAC;IAElB,qCAAqC;IACrC,IAAI,YAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5B,MAAM,WAAW,GAAG,QAAQ,CAAC,YAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QAC3E,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,cAAc,CAAC,WAAW,CAAC,EAAE,CAAC;YACvD,OAAO,CAAC,KAAK,CAAC,+BAA+B,WAAW,GAAG,CAAC,CAAC;YAC7D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IAED,iBAAiB;IACjB,YAAY,EAAE,CAAC;IAEf,wBAAwB;IACxB,MAAM,MAAM,GAAG,IAAI,kCAAe,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IACtD,MAAM,QAAQ,GAAG,IAAI,yBAAe,EAAE,CAAC;IACvC,MAAM,SAAS,GAAG,IAAI,4BAAgB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACzD,MAAM,GAAG,GAAG,IAAI,eAAS,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;IAElE,uDAAuD;IACvD,IAAI,SAAS,GAAyC,IAAI,CAAC;IAE3D,SAAS,cAAc;QACrB,IAAI,SAAS,EAAE,CAAC;YACd,YAAY,CAAC,SAAS,CAAC,CAAC;YACxB,SAAS,GAAG,IAAI,CAAC;QACnB,CAAC;IACH,CAAC;IAED,SAAS,cAAc;QACrB,cAAc,EAAE,CAAC;QACjB,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE;YAC1B,MAAM,CAAC,GAAG,CAAC,6DAA6D,CAAC,CAAC;YAC1E,QAAQ,EAAE,CAAC;QACb,CAAC,EAAE,eAAe,CAAC,CAAC;IACtB,CAAC;IAED,GAAG,CAAC,uBAAuB,CAAC,CAAC,KAAa,EAAE,EAAE;QAC5C,MAAM,CAAC,GAAG,CAAC,2BAA2B,KAAK,EAAE,CAAC,CAAC;QAC/C,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;YAChB,cAAc,EAAE,CAAC;QACnB,CAAC;aAAM,CAAC;YACN,cAAc,EAAE,CAAC;QACnB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,oBAAoB;IACpB,IAAI,YAAY,GAAG,KAAK,CAAC;IAEzB,KAAK,UAAU,QAAQ;QACrB,IAAI,YAAY;YAAE,OAAO;QACzB,YAAY,GAAG,IAAI,CAAC;QAEpB,MAAM,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;QACxC,cAAc,EAAE,CAAC;QACjB,SAAS,CAAC,QAAQ,EAAE,CAAC;QACrB,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QACjB,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,YAAY,EAAE,CAAC;QACf,MAAM,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;QACzC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAChC,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAE/B,mCAAmC;IACnC,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACrB,MAAM,CAAC,GAAG,CAAC,kDAAkD,IAAI,EAAE,CAAC,CAAC;IACvE,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QACpB,OAAO,CAAC,KAAK,CAAC,wCAAwC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACvE,YAAY,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,mBAAmB;IACnB,IAAI,CAAC;QACH,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC;QAClB,MAAM,CAAC,GAAG,CAAC,6BAA6B,SAAS,EAAE,CAAC,CAAC;IACvD,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QACpB,OAAO,CAAC,KAAK,CAAC,+BAA+B,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QAC9D,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,YAAY,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,qCAAqC;IACrC,cAAc,EAAE,CAAC;IAEjB,MAAM,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;AACtC,CAAC;AAKD,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,qBAAqB,EAAE,KAAK,CAAC,CAAC;IAC5C,YAAY,EAAE,CAAC;IACf,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RequestScheduler — round-robin scheduler with tab ownership enforcement.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from the multiplexer's leader-mode logic. Serializes all commands
|
|
5
|
+
* to the extension through a single drain loop, ensuring fair round-robin
|
|
6
|
+
* across sessions and automatic tab context-switching.
|
|
7
|
+
*
|
|
8
|
+
* @module scheduler
|
|
9
|
+
*/
|
|
10
|
+
import type { ExtensionBridge } from './extension-bridge';
|
|
11
|
+
import type { SessionRegistry } from './session';
|
|
12
|
+
/**
|
|
13
|
+
* Round-robin request scheduler with tab ownership and auto context-switching.
|
|
14
|
+
*/
|
|
15
|
+
export declare class RequestScheduler {
|
|
16
|
+
private bridge;
|
|
17
|
+
private sessions;
|
|
18
|
+
private requestQueue;
|
|
19
|
+
private sessionOrder;
|
|
20
|
+
private currentSessionIdx;
|
|
21
|
+
private processingQueue;
|
|
22
|
+
private currentExtensionTabId;
|
|
23
|
+
private sessionGroupIds;
|
|
24
|
+
constructor(bridge: ExtensionBridge, sessions: SessionRegistry);
|
|
25
|
+
/** Register a session in the scheduler. */
|
|
26
|
+
addSession(sessionId: string): void;
|
|
27
|
+
/** Remove a session from the scheduler. Rejects queued requests. */
|
|
28
|
+
removeSession(sessionId: string): void;
|
|
29
|
+
/** Enqueue a request and return a promise for the result. */
|
|
30
|
+
enqueue(sessionId: string, method: string, params: Record<string, unknown>, timeout?: number): Promise<any>;
|
|
31
|
+
/** Process queued requests in round-robin order. Serialized — one at a time. */
|
|
32
|
+
private drainQueue;
|
|
33
|
+
private hasQueuedRequests;
|
|
34
|
+
/** Round-robin pick: advance through sessions, skip empty queues. */
|
|
35
|
+
private pickNextRequest;
|
|
36
|
+
/**
|
|
37
|
+
* Execute a single queued request:
|
|
38
|
+
* 1. Tab ownership check for tab-management methods
|
|
39
|
+
* 2. Auto context-switch if this session's tab != current extension tab
|
|
40
|
+
* 3. Inject _sessionId for extension-side group isolation
|
|
41
|
+
* 4. Execute the actual command
|
|
42
|
+
* 5. Track ownership changes
|
|
43
|
+
*/
|
|
44
|
+
private executeRequest;
|
|
45
|
+
/** Drain and reject all queued requests. Called during shutdown. */
|
|
46
|
+
drainAll(): void;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=scheduler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../src/scheduler.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAsBjD;;GAEG;AACH,qBAAa,gBAAgB;IASzB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,QAAQ;IATlB,OAAO,CAAC,YAAY,CAA2C;IAC/D,OAAO,CAAC,YAAY,CAAgB;IACpC,OAAO,CAAC,iBAAiB,CAAa;IACtC,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,qBAAqB,CAAuB;IACpD,OAAO,CAAC,eAAe,CAAkC;gBAG/C,MAAM,EAAE,eAAe,EACvB,QAAQ,EAAE,eAAe;IAGnC,2CAA2C;IAC3C,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAKnC,oEAAoE;IACpE,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAatC,6DAA6D;IAC7D,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAE,MAAc,GAAG,OAAO,CAAC,GAAG,CAAC;IAalH,gFAAgF;YAClE,UAAU;IAexB,OAAO,CAAC,iBAAiB;IAOzB,qEAAqE;IACrE,OAAO,CAAC,eAAe;IAmBvB;;;;;;;OAOG;YACW,cAAc;IA6F5B,oEAAoE;IACpE,QAAQ,IAAI,IAAI;CASjB"}
|