@arvoretech/runtime-lens-mcp 1.0.0 → 1.2.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/README.md +10 -3
- package/agent/index.ts +281 -212
- package/dist/agent/index.js +282 -0
- package/dist/agent/index.js.map +7 -0
- package/dist/agent-bridge.d.ts +20 -0
- package/dist/agent-bridge.d.ts.map +1 -0
- package/dist/agent-bridge.js +185 -0
- package/dist/agent-bridge.js.map +1 -0
- package/dist/extension/extension.js +433 -0
- package/dist/extension/extension.js.map +7 -0
- package/dist/server.d.ts +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +115 -100
- package/dist/server.js.map +1 -1
- package/package.json +2 -2
- package/src/agent-bridge.ts +218 -0
- package/src/server.ts +405 -358
- package/.vscodeignore +0 -21
- package/agent/tsconfig.json +0 -17
- package/eslint.config.js +0 -41
- package/extension/decorator.ts +0 -144
- package/extension/extension.ts +0 -98
- package/extension/runtime-bridge.ts +0 -206
- package/extension/tsconfig.json +0 -17
- package/tsconfig.json +0 -20
- package/vitest.config.ts +0 -13
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// agent/index.ts
|
|
4
|
+
var import_node_crypto = require("node:crypto");
|
|
5
|
+
var import_node_http = require("node:http");
|
|
6
|
+
var PORT = parseInt(process.env.RUNTIME_LENS_PORT || "9500", 10);
|
|
7
|
+
var clients = /* @__PURE__ */ new Set();
|
|
8
|
+
var buffer = [];
|
|
9
|
+
var MAX_BUFFER = 1e3;
|
|
10
|
+
function serialize(value, depth = 0) {
|
|
11
|
+
if (depth > 3) return "[...]";
|
|
12
|
+
if (value === null) return "null";
|
|
13
|
+
if (value === void 0) return "undefined";
|
|
14
|
+
if (typeof value === "string")
|
|
15
|
+
return value.length > 100 ? `"${value.slice(0, 100)}..."` : `"${value}"`;
|
|
16
|
+
if (typeof value === "number" || typeof value === "boolean")
|
|
17
|
+
return String(value);
|
|
18
|
+
if (typeof value === "function") return `fn ${value.name || "anonymous"}()`;
|
|
19
|
+
if (typeof value === "symbol") return value.toString();
|
|
20
|
+
if (typeof value === "bigint") return `${value}n`;
|
|
21
|
+
if (value instanceof Error)
|
|
22
|
+
return `${value.name}: ${value.message}${value.stack ? "\n" + value.stack : ""}`;
|
|
23
|
+
if (value instanceof Date) return value.toISOString();
|
|
24
|
+
if (value instanceof RegExp) return value.toString();
|
|
25
|
+
if (value instanceof Map) return `Map(${value.size})`;
|
|
26
|
+
if (value instanceof Set) return `Set(${value.size})`;
|
|
27
|
+
if (value instanceof Promise) return "Promise";
|
|
28
|
+
if (Array.isArray(value)) {
|
|
29
|
+
if (value.length === 0) return "[]";
|
|
30
|
+
const items = value.slice(0, 5).map((v) => serialize(v, depth + 1));
|
|
31
|
+
const suffix = value.length > 5 ? `, ...+${value.length - 5}` : "";
|
|
32
|
+
return `[${items.join(", ")}${suffix}]`;
|
|
33
|
+
}
|
|
34
|
+
if (typeof value === "object") {
|
|
35
|
+
const keys = Object.keys(value);
|
|
36
|
+
if (keys.length === 0) return "{}";
|
|
37
|
+
const entries = keys.slice(0, 5).map(
|
|
38
|
+
(k) => `${k}: ${serialize(value[k], depth + 1)}`
|
|
39
|
+
);
|
|
40
|
+
const suffix = keys.length > 5 ? `, ...+${keys.length - 5}` : "";
|
|
41
|
+
return `{${entries.join(", ")}${suffix}}`;
|
|
42
|
+
}
|
|
43
|
+
return typeof value === "object" ? Object.prototype.toString.call(value) : String(value);
|
|
44
|
+
}
|
|
45
|
+
function broadcast(msg) {
|
|
46
|
+
const payload = JSON.stringify(msg);
|
|
47
|
+
const buf = Buffer.from(payload, "utf-8");
|
|
48
|
+
const frame = buildWsFrame(buf);
|
|
49
|
+
for (const client of clients) {
|
|
50
|
+
if (!client.destroyed) {
|
|
51
|
+
client.write(frame);
|
|
52
|
+
} else {
|
|
53
|
+
clients.delete(client);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
buffer.push(msg);
|
|
57
|
+
if (buffer.length > MAX_BUFFER) buffer.shift();
|
|
58
|
+
}
|
|
59
|
+
function flushBuffer(socket) {
|
|
60
|
+
for (const msg of buffer) {
|
|
61
|
+
const buf = Buffer.from(JSON.stringify(msg), "utf-8");
|
|
62
|
+
socket.write(buildWsFrame(buf));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function buildWsFrame(data) {
|
|
66
|
+
const len = data.length;
|
|
67
|
+
let header;
|
|
68
|
+
if (len < 126) {
|
|
69
|
+
header = Buffer.alloc(2);
|
|
70
|
+
header[0] = 129;
|
|
71
|
+
header[1] = len;
|
|
72
|
+
} else if (len < 65536) {
|
|
73
|
+
header = Buffer.alloc(4);
|
|
74
|
+
header[0] = 129;
|
|
75
|
+
header[1] = 126;
|
|
76
|
+
header.writeUInt16BE(len, 2);
|
|
77
|
+
} else {
|
|
78
|
+
header = Buffer.alloc(10);
|
|
79
|
+
header[0] = 129;
|
|
80
|
+
header[1] = 127;
|
|
81
|
+
header.writeBigUInt64BE(BigInt(len), 2);
|
|
82
|
+
}
|
|
83
|
+
return Buffer.concat([header, data]);
|
|
84
|
+
}
|
|
85
|
+
function parseWsFrame(data) {
|
|
86
|
+
if (data.length < 2) return null;
|
|
87
|
+
const masked = (data[1] & 128) !== 0;
|
|
88
|
+
let payloadLen = data[1] & 127;
|
|
89
|
+
let offset = 2;
|
|
90
|
+
if (payloadLen === 126) {
|
|
91
|
+
payloadLen = data.readUInt16BE(2);
|
|
92
|
+
offset = 4;
|
|
93
|
+
} else if (payloadLen === 127) {
|
|
94
|
+
payloadLen = Number(data.readBigUInt64BE(2));
|
|
95
|
+
offset = 10;
|
|
96
|
+
}
|
|
97
|
+
let maskKey = null;
|
|
98
|
+
if (masked) {
|
|
99
|
+
maskKey = data.subarray(offset, offset + 4);
|
|
100
|
+
offset += 4;
|
|
101
|
+
}
|
|
102
|
+
const payload = data.subarray(offset, offset + payloadLen);
|
|
103
|
+
if (maskKey) {
|
|
104
|
+
for (let i = 0; i < payload.length; i++) {
|
|
105
|
+
payload[i] ^= maskKey[i % 4];
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return payload.toString("utf-8");
|
|
109
|
+
}
|
|
110
|
+
function extractCallSite() {
|
|
111
|
+
const stack = new Error().stack || "";
|
|
112
|
+
const lines = stack.split("\n");
|
|
113
|
+
for (let i = 2; i < lines.length; i++) {
|
|
114
|
+
const line = lines[i];
|
|
115
|
+
if (line.includes("node:") || line.includes("dist/agent/index.js"))
|
|
116
|
+
continue;
|
|
117
|
+
const match = /\((.+):(\d+):(\d+)\)/.exec(line) || /at (.+):(\d+):(\d+)/.exec(line);
|
|
118
|
+
if (match) {
|
|
119
|
+
return {
|
|
120
|
+
file: match[1],
|
|
121
|
+
line: parseInt(match[2], 10),
|
|
122
|
+
column: parseInt(match[3], 10)
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return { file: "unknown", line: 0, column: 0 };
|
|
127
|
+
}
|
|
128
|
+
var originalConsole = {
|
|
129
|
+
log: console.log.bind(console),
|
|
130
|
+
info: console.info.bind(console),
|
|
131
|
+
warn: console.warn.bind(console),
|
|
132
|
+
error: console.error.bind(console),
|
|
133
|
+
debug: console.debug.bind(console)
|
|
134
|
+
};
|
|
135
|
+
function patchConsole() {
|
|
136
|
+
const methods = ["log", "info", "warn", "error", "debug"];
|
|
137
|
+
for (const method of methods) {
|
|
138
|
+
console[method] = (...args) => {
|
|
139
|
+
const site = extractCallSite();
|
|
140
|
+
broadcast({
|
|
141
|
+
type: method,
|
|
142
|
+
file: site.file,
|
|
143
|
+
line: site.line,
|
|
144
|
+
column: site.column,
|
|
145
|
+
values: args.map((a) => serialize(a)),
|
|
146
|
+
timestamp: Date.now()
|
|
147
|
+
});
|
|
148
|
+
originalConsole[method](...args);
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
var server = (0, import_node_http.createServer)((_req, res) => {
|
|
153
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
154
|
+
res.end(
|
|
155
|
+
JSON.stringify({
|
|
156
|
+
status: "ok",
|
|
157
|
+
pid: process.pid,
|
|
158
|
+
uptime: process.uptime(),
|
|
159
|
+
clients: clients.size,
|
|
160
|
+
buffered: buffer.length
|
|
161
|
+
})
|
|
162
|
+
);
|
|
163
|
+
});
|
|
164
|
+
server.on("upgrade", (req, socket) => {
|
|
165
|
+
const key = req.headers["sec-websocket-key"];
|
|
166
|
+
if (!key) {
|
|
167
|
+
socket.destroy();
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
const acceptKey = (0, import_node_crypto.createHash)("sha1").update(key + "258EAFA5-E914-47DA-95CA-5AB5DC11650B").digest("base64");
|
|
171
|
+
socket.write(
|
|
172
|
+
`HTTP/1.1 101 Switching Protocols\r
|
|
173
|
+
Upgrade: websocket\r
|
|
174
|
+
Connection: Upgrade\r
|
|
175
|
+
Sec-WebSocket-Accept: ${acceptKey}\r
|
|
176
|
+
\r
|
|
177
|
+
`
|
|
178
|
+
);
|
|
179
|
+
clients.add(socket);
|
|
180
|
+
originalConsole.log(
|
|
181
|
+
`[runtime-lens] client connected (${clients.size} total)`
|
|
182
|
+
);
|
|
183
|
+
flushBuffer(socket);
|
|
184
|
+
socket.on("data", (data) => {
|
|
185
|
+
const text = parseWsFrame(data);
|
|
186
|
+
if (!text) return;
|
|
187
|
+
try {
|
|
188
|
+
const msg = JSON.parse(text);
|
|
189
|
+
if (msg.type === "eval") {
|
|
190
|
+
try {
|
|
191
|
+
const fn = new Function(`return (${msg.expression})`);
|
|
192
|
+
const result = fn();
|
|
193
|
+
broadcast({
|
|
194
|
+
type: "result",
|
|
195
|
+
file: msg.file || "eval",
|
|
196
|
+
line: msg.line || 0,
|
|
197
|
+
column: msg.column || 0,
|
|
198
|
+
values: [serialize(result)],
|
|
199
|
+
timestamp: Date.now(),
|
|
200
|
+
expression: msg.expression
|
|
201
|
+
});
|
|
202
|
+
} catch (err) {
|
|
203
|
+
broadcast({
|
|
204
|
+
type: "error",
|
|
205
|
+
file: msg.file || "eval",
|
|
206
|
+
line: msg.line || 0,
|
|
207
|
+
column: msg.column || 0,
|
|
208
|
+
values: [err instanceof Error ? err.message : String(err)],
|
|
209
|
+
timestamp: Date.now(),
|
|
210
|
+
expression: msg.expression
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
} else if (msg.type === "get_buffer") {
|
|
214
|
+
const payload = JSON.stringify({ type: "buffer", logs: buffer });
|
|
215
|
+
const buf = Buffer.from(payload, "utf-8");
|
|
216
|
+
socket.write(buildWsFrame(buf));
|
|
217
|
+
}
|
|
218
|
+
} catch {
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
socket.on("close", () => {
|
|
222
|
+
clients.delete(socket);
|
|
223
|
+
originalConsole.log(
|
|
224
|
+
`[runtime-lens] client disconnected (${clients.size} total)`
|
|
225
|
+
);
|
|
226
|
+
});
|
|
227
|
+
socket.on("error", () => {
|
|
228
|
+
clients.delete(socket);
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
server.on("error", (err) => {
|
|
232
|
+
if (err.code === "EADDRINUSE") {
|
|
233
|
+
originalConsole.log(
|
|
234
|
+
`[runtime-lens] port ${PORT} in use, skipping agent server`
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
server.listen(PORT, () => {
|
|
239
|
+
originalConsole.log(
|
|
240
|
+
`[runtime-lens] agent listening on ws://localhost:${PORT}`
|
|
241
|
+
);
|
|
242
|
+
});
|
|
243
|
+
var keepAliveTimer = null;
|
|
244
|
+
var KEEP_ALIVE_MS = parseInt(
|
|
245
|
+
process.env.RUNTIME_LENS_KEEP_ALIVE || "30000",
|
|
246
|
+
10
|
|
247
|
+
);
|
|
248
|
+
function scheduleShutdown() {
|
|
249
|
+
if (keepAliveTimer) return;
|
|
250
|
+
keepAliveTimer = setTimeout(() => {
|
|
251
|
+
if (clients.size === 0) {
|
|
252
|
+
originalConsole.log(
|
|
253
|
+
`[runtime-lens] no clients connected, shutting down agent`
|
|
254
|
+
);
|
|
255
|
+
server.close();
|
|
256
|
+
process.exit(0);
|
|
257
|
+
} else {
|
|
258
|
+
keepAliveTimer = null;
|
|
259
|
+
scheduleShutdown();
|
|
260
|
+
}
|
|
261
|
+
}, KEEP_ALIVE_MS);
|
|
262
|
+
keepAliveTimer.unref();
|
|
263
|
+
}
|
|
264
|
+
process.on("beforeExit", () => {
|
|
265
|
+
server.ref();
|
|
266
|
+
scheduleShutdown();
|
|
267
|
+
});
|
|
268
|
+
patchConsole();
|
|
269
|
+
globalThis.__runtimeLens = {
|
|
270
|
+
log: (...args) => {
|
|
271
|
+
const site = extractCallSite();
|
|
272
|
+
broadcast({
|
|
273
|
+
type: "result",
|
|
274
|
+
file: site.file,
|
|
275
|
+
line: site.line,
|
|
276
|
+
column: site.column,
|
|
277
|
+
values: args.map((a) => serialize(a)),
|
|
278
|
+
timestamp: Date.now()
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../agent/index.ts"],
|
|
4
|
+
"sourcesContent": ["import { createHash } from \"node:crypto\";\nimport type { IncomingMessage } from \"node:http\";\nimport { createServer } from \"node:http\";\nimport type { Socket } from \"node:net\";\n\nconst PORT = parseInt(process.env.RUNTIME_LENS_PORT || \"9500\", 10);\n\ninterface LogMessage {\n\ttype: \"log\" | \"error\" | \"warn\" | \"info\" | \"debug\" | \"result\";\n\tfile: string;\n\tline: number;\n\tcolumn: number;\n\tvalues: string[];\n\ttimestamp: number;\n\texpression?: string;\n\tcount?: number;\n}\n\nconst clients: Set<Socket> = new Set();\nconst buffer: LogMessage[] = [];\nconst MAX_BUFFER = 1000;\n\nfunction serialize(value: unknown, depth = 0): string {\n\tif (depth > 3) return \"[...]\";\n\tif (value === null) return \"null\";\n\tif (value === undefined) return \"undefined\";\n\tif (typeof value === \"string\")\n\t\treturn value.length > 100 ? `\"${value.slice(0, 100)}...\"` : `\"${value}\"`;\n\tif (typeof value === \"number\" || typeof value === \"boolean\")\n\t\treturn String(value);\n\tif (typeof value === \"function\") return `fn ${value.name || \"anonymous\"}()`;\n\tif (typeof value === \"symbol\") return value.toString();\n\tif (typeof value === \"bigint\") return `${value}n`;\n\tif (value instanceof Error)\n\t\treturn `${value.name}: ${value.message}${value.stack ? \"\\n\" + value.stack : \"\"}`;\n\tif (value instanceof Date) return value.toISOString();\n\tif (value instanceof RegExp) return value.toString();\n\tif (value instanceof Map) return `Map(${value.size})`;\n\tif (value instanceof Set) return `Set(${value.size})`;\n\tif (value instanceof Promise) return \"Promise\";\n\tif (Array.isArray(value)) {\n\t\tif (value.length === 0) return \"[]\";\n\t\tconst items = value.slice(0, 5).map((v) => serialize(v, depth + 1));\n\t\tconst suffix = value.length > 5 ? `, ...+${value.length - 5}` : \"\";\n\t\treturn `[${items.join(\", \")}${suffix}]`;\n\t}\n\tif (typeof value === \"object\") {\n\t\tconst keys = Object.keys(value);\n\t\tif (keys.length === 0) return \"{}\";\n\t\tconst entries = keys\n\t\t\t.slice(0, 5)\n\t\t\t.map(\n\t\t\t\t(k) =>\n\t\t\t\t\t`${k}: ${serialize((value as Record<string, unknown>)[k], depth + 1)}`,\n\t\t\t);\n\t\tconst suffix = keys.length > 5 ? `, ...+${keys.length - 5}` : \"\";\n\t\treturn `{${entries.join(\", \")}${suffix}}`;\n\t}\n\treturn typeof value === \"object\"\n\t\t? Object.prototype.toString.call(value)\n\t\t: String(value);\n}\n\nfunction broadcast(msg: LogMessage): void {\n\tconst payload = JSON.stringify(msg);\n\tconst buf = Buffer.from(payload, \"utf-8\");\n\tconst frame = buildWsFrame(buf);\n\n\tfor (const client of clients) {\n\t\tif (!client.destroyed) {\n\t\t\tclient.write(frame);\n\t\t} else {\n\t\t\tclients.delete(client);\n\t\t}\n\t}\n\n\t// Always buffer for late-connecting clients (MCP server, extension)\n\tbuffer.push(msg);\n\tif (buffer.length > MAX_BUFFER) buffer.shift();\n}\n\nfunction flushBuffer(socket: Socket): void {\n\tfor (const msg of buffer) {\n\t\tconst buf = Buffer.from(JSON.stringify(msg), \"utf-8\");\n\t\tsocket.write(buildWsFrame(buf));\n\t}\n}\n\nfunction buildWsFrame(data: Buffer): Buffer {\n\tconst len = data.length;\n\tlet header: Buffer;\n\tif (len < 126) {\n\t\theader = Buffer.alloc(2);\n\t\theader[0] = 0x81;\n\t\theader[1] = len;\n\t} else if (len < 65536) {\n\t\theader = Buffer.alloc(4);\n\t\theader[0] = 0x81;\n\t\theader[1] = 126;\n\t\theader.writeUInt16BE(len, 2);\n\t} else {\n\t\theader = Buffer.alloc(10);\n\t\theader[0] = 0x81;\n\t\theader[1] = 127;\n\t\theader.writeBigUInt64BE(BigInt(len), 2);\n\t}\n\treturn Buffer.concat([header, data]);\n}\n\nfunction parseWsFrame(data: Buffer): string | null {\n\tif (data.length < 2) return null;\n\tconst masked = (data[1] & 0x80) !== 0;\n\tlet payloadLen = data[1] & 0x7f;\n\tlet offset = 2;\n\n\tif (payloadLen === 126) {\n\t\tpayloadLen = data.readUInt16BE(2);\n\t\toffset = 4;\n\t} else if (payloadLen === 127) {\n\t\tpayloadLen = Number(data.readBigUInt64BE(2));\n\t\toffset = 10;\n\t}\n\n\tlet maskKey: Buffer | null = null;\n\tif (masked) {\n\t\tmaskKey = data.subarray(offset, offset + 4);\n\t\toffset += 4;\n\t}\n\n\tconst payload = data.subarray(offset, offset + payloadLen);\n\tif (maskKey) {\n\t\tfor (let i = 0; i < payload.length; i++) {\n\t\t\tpayload[i] ^= maskKey[i % 4];\n\t\t}\n\t}\n\treturn payload.toString(\"utf-8\");\n}\n\nfunction extractCallSite(): { file: string; line: number; column: number } {\n\tconst stack = new Error().stack || \"\";\n\tconst lines = stack.split(\"\\n\");\n\tfor (let i = 2; i < lines.length; i++) {\n\t\tconst line = lines[i];\n\t\tif (line.includes(\"node:\") || line.includes(\"dist/agent/index.js\"))\n\t\t\tcontinue;\n\t\tconst match =\n\t\t\t/\\((.+):(\\d+):(\\d+)\\)/.exec(line) || /at (.+):(\\d+):(\\d+)/.exec(line);\n\t\tif (match) {\n\t\t\treturn {\n\t\t\t\tfile: match[1],\n\t\t\t\tline: parseInt(match[2], 10),\n\t\t\t\tcolumn: parseInt(match[3], 10),\n\t\t\t};\n\t\t}\n\t}\n\treturn { file: \"unknown\", line: 0, column: 0 };\n}\n\ntype ConsoleMethod = \"log\" | \"info\" | \"warn\" | \"error\" | \"debug\";\nconst originalConsole: Record<ConsoleMethod, (...args: unknown[]) => void> = {\n\tlog: console.log.bind(console),\n\tinfo: console.info.bind(console),\n\twarn: console.warn.bind(console),\n\terror: console.error.bind(console),\n\tdebug: console.debug.bind(console),\n};\n\nfunction patchConsole(): void {\n\tconst methods: ConsoleMethod[] = [\"log\", \"info\", \"warn\", \"error\", \"debug\"];\n\tfor (const method of methods) {\n\t\tconsole[method] = (...args: unknown[]) => {\n\t\t\tconst site = extractCallSite();\n\t\t\tbroadcast({\n\t\t\t\ttype: method,\n\t\t\t\tfile: site.file,\n\t\t\t\tline: site.line,\n\t\t\t\tcolumn: site.column,\n\t\t\t\tvalues: args.map((a) => serialize(a)),\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t});\n\t\t\toriginalConsole[method](...args);\n\t\t};\n\t}\n}\n\nconst server = createServer((_req, res) => {\n\tres.writeHead(200, { \"Content-Type\": \"application/json\" });\n\tres.end(\n\t\tJSON.stringify({\n\t\t\tstatus: \"ok\",\n\t\t\tpid: process.pid,\n\t\t\tuptime: process.uptime(),\n\t\t\tclients: clients.size,\n\t\t\tbuffered: buffer.length,\n\t\t}),\n\t);\n});\n\nserver.on(\"upgrade\", (req: IncomingMessage, socket: Socket) => {\n\tconst key = req.headers[\"sec-websocket-key\"];\n\tif (!key) {\n\t\tsocket.destroy();\n\t\treturn;\n\t}\n\n\tconst acceptKey = createHash(\"sha1\")\n\t\t.update(key + \"258EAFA5-E914-47DA-95CA-5AB5DC11650B\")\n\t\t.digest(\"base64\");\n\n\tsocket.write(\n\t\t\"HTTP/1.1 101 Switching Protocols\\r\\n\" +\n\t\t\t\"Upgrade: websocket\\r\\n\" +\n\t\t\t\"Connection: Upgrade\\r\\n\" +\n\t\t\t`Sec-WebSocket-Accept: ${acceptKey}\\r\\n` +\n\t\t\t\"\\r\\n\",\n\t);\n\n\tclients.add(socket);\n\toriginalConsole.log(\n\t\t`[runtime-lens] client connected (${clients.size} total)`,\n\t);\n\n\tflushBuffer(socket);\n\n\tsocket.on(\"data\", (data: Buffer) => {\n\t\tconst text = parseWsFrame(data);\n\t\tif (!text) return;\n\t\ttry {\n\t\t\tconst msg = JSON.parse(text);\n\t\t\tif (msg.type === \"eval\") {\n\t\t\t\ttry {\n\t\t\t\t\tconst fn = new Function(`return (${msg.expression})`);\n\t\t\t\t\tconst result = fn();\n\t\t\t\t\tbroadcast({\n\t\t\t\t\t\ttype: \"result\",\n\t\t\t\t\t\tfile: msg.file || \"eval\",\n\t\t\t\t\t\tline: msg.line || 0,\n\t\t\t\t\t\tcolumn: msg.column || 0,\n\t\t\t\t\t\tvalues: [serialize(result)],\n\t\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t\t\texpression: msg.expression,\n\t\t\t\t\t});\n\t\t\t\t} catch (err: unknown) {\n\t\t\t\t\tbroadcast({\n\t\t\t\t\t\ttype: \"error\",\n\t\t\t\t\t\tfile: msg.file || \"eval\",\n\t\t\t\t\t\tline: msg.line || 0,\n\t\t\t\t\t\tcolumn: msg.column || 0,\n\t\t\t\t\t\tvalues: [err instanceof Error ? err.message : String(err)],\n\t\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t\t\texpression: msg.expression,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} else if (msg.type === \"get_buffer\") {\n\t\t\t\tconst payload = JSON.stringify({ type: \"buffer\", logs: buffer });\n\t\t\t\tconst buf = Buffer.from(payload, \"utf-8\");\n\t\t\t\tsocket.write(buildWsFrame(buf));\n\t\t\t}\n\t\t} catch {\n\t\t\t// invalid JSON\n\t\t}\n\t});\n\n\tsocket.on(\"close\", () => {\n\t\tclients.delete(socket);\n\t\toriginalConsole.log(\n\t\t\t`[runtime-lens] client disconnected (${clients.size} total)`,\n\t\t);\n\t});\n\n\tsocket.on(\"error\", () => {\n\t\tclients.delete(socket);\n\t});\n});\n\nserver.on(\"error\", (err: NodeJS.ErrnoException) => {\n\tif (err.code === \"EADDRINUSE\") {\n\t\toriginalConsole.log(\n\t\t\t`[runtime-lens] port ${PORT} in use, skipping agent server`,\n\t\t);\n\t}\n});\n\nserver.listen(PORT, () => {\n\toriginalConsole.log(\n\t\t`[runtime-lens] agent listening on ws://localhost:${PORT}`,\n\t);\n});\n\nlet keepAliveTimer: ReturnType<typeof setTimeout> | null = null;\nconst KEEP_ALIVE_MS = parseInt(\n\tprocess.env.RUNTIME_LENS_KEEP_ALIVE || \"30000\",\n\t10,\n);\n\nfunction scheduleShutdown(): void {\n\tif (keepAliveTimer) return;\n\tkeepAliveTimer = setTimeout(() => {\n\t\tif (clients.size === 0) {\n\t\t\toriginalConsole.log(\n\t\t\t\t`[runtime-lens] no clients connected, shutting down agent`,\n\t\t\t);\n\t\t\tserver.close();\n\t\t\tprocess.exit(0);\n\t\t} else {\n\t\t\tkeepAliveTimer = null;\n\t\t\tscheduleShutdown();\n\t\t}\n\t}, KEEP_ALIVE_MS);\n\tkeepAliveTimer.unref();\n}\n\nprocess.on(\"beforeExit\", () => {\n\tserver.ref();\n\tscheduleShutdown();\n});\n\npatchConsole();\n\n(globalThis as Record<string, unknown>).__runtimeLens = {\n\tlog: (...args: unknown[]) => {\n\t\tconst site = extractCallSite();\n\t\tbroadcast({\n\t\t\ttype: \"result\",\n\t\t\tfile: site.file,\n\t\t\tline: site.line,\n\t\t\tcolumn: site.column,\n\t\t\tvalues: args.map((a) => serialize(a)),\n\t\t\ttimestamp: Date.now(),\n\t\t});\n\t},\n};\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { LogCollector } from "./log-collector.js";
|
|
2
|
+
export declare class AgentBridge {
|
|
3
|
+
private socket;
|
|
4
|
+
private reconnectTimer;
|
|
5
|
+
private connected;
|
|
6
|
+
private readonly port;
|
|
7
|
+
private readonly collector;
|
|
8
|
+
private dataBuffer;
|
|
9
|
+
private stopped;
|
|
10
|
+
constructor(collector: LogCollector, port?: number);
|
|
11
|
+
connect(): void;
|
|
12
|
+
disconnect(): void;
|
|
13
|
+
isConnected(): boolean;
|
|
14
|
+
private processFrames;
|
|
15
|
+
private handleMessage;
|
|
16
|
+
private ingestLog;
|
|
17
|
+
private sendFrame;
|
|
18
|
+
private scheduleReconnect;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=agent-bridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-bridge.d.ts","sourceRoot":"","sources":["../src/agent-bridge.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAuBvD,qBAAa,WAAW;IACvB,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,cAAc,CAA8C;IACpE,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,UAAU,CAA2B;IAC7C,OAAO,CAAC,OAAO,CAAS;gBAEZ,SAAS,EAAE,YAAY,EAAE,IAAI,SAAO;IAKhD,OAAO,IAAI,IAAI;IAyDf,UAAU,IAAI,IAAI;IAalB,WAAW,IAAI,OAAO;IAItB,OAAO,CAAC,aAAa;IA2CrB,OAAO,CAAC,aAAa;IAWrB,OAAO,CAAC,SAAS;IAiBjB,OAAO,CAAC,SAAS;IAyBjB,OAAO,CAAC,iBAAiB;CAOzB"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { request } from "node:http";
|
|
3
|
+
const LEVEL_MAP = {
|
|
4
|
+
log: "info",
|
|
5
|
+
info: "info",
|
|
6
|
+
warn: "warn",
|
|
7
|
+
error: "error",
|
|
8
|
+
debug: "debug",
|
|
9
|
+
result: "info",
|
|
10
|
+
};
|
|
11
|
+
export class AgentBridge {
|
|
12
|
+
socket = null;
|
|
13
|
+
reconnectTimer = null;
|
|
14
|
+
connected = false;
|
|
15
|
+
port;
|
|
16
|
+
collector;
|
|
17
|
+
dataBuffer = Buffer.alloc(0);
|
|
18
|
+
stopped = false;
|
|
19
|
+
constructor(collector, port = 9500) {
|
|
20
|
+
this.collector = collector;
|
|
21
|
+
this.port = parseInt(process.env.RUNTIME_LENS_PORT || String(port), 10);
|
|
22
|
+
}
|
|
23
|
+
connect() {
|
|
24
|
+
if (this.socket || this.stopped)
|
|
25
|
+
return;
|
|
26
|
+
const key = randomBytes(16).toString("base64");
|
|
27
|
+
const req = request({
|
|
28
|
+
hostname: "localhost",
|
|
29
|
+
port: this.port,
|
|
30
|
+
path: "/",
|
|
31
|
+
method: "GET",
|
|
32
|
+
timeout: 3000,
|
|
33
|
+
headers: {
|
|
34
|
+
Upgrade: "websocket",
|
|
35
|
+
Connection: "Upgrade",
|
|
36
|
+
"Sec-WebSocket-Key": key,
|
|
37
|
+
"Sec-WebSocket-Version": "13",
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
req.on("upgrade", (_res, socket) => {
|
|
41
|
+
this.socket = socket;
|
|
42
|
+
this.connected = true;
|
|
43
|
+
this.dataBuffer = Buffer.alloc(0);
|
|
44
|
+
console.error("[runtime-lens-mcp] Connected to agent on port", this.port);
|
|
45
|
+
// Request buffered logs
|
|
46
|
+
this.sendFrame(JSON.stringify({ type: "get_buffer" }));
|
|
47
|
+
socket.on("data", (data) => {
|
|
48
|
+
this.dataBuffer = Buffer.concat([this.dataBuffer, data]);
|
|
49
|
+
this.processFrames();
|
|
50
|
+
});
|
|
51
|
+
socket.on("close", () => {
|
|
52
|
+
this.connected = false;
|
|
53
|
+
this.socket = null;
|
|
54
|
+
this.scheduleReconnect();
|
|
55
|
+
});
|
|
56
|
+
socket.on("error", () => {
|
|
57
|
+
this.connected = false;
|
|
58
|
+
this.socket = null;
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
req.on("error", () => {
|
|
62
|
+
this.scheduleReconnect();
|
|
63
|
+
});
|
|
64
|
+
req.on("timeout", () => {
|
|
65
|
+
req.destroy();
|
|
66
|
+
this.scheduleReconnect();
|
|
67
|
+
});
|
|
68
|
+
req.end();
|
|
69
|
+
}
|
|
70
|
+
disconnect() {
|
|
71
|
+
this.stopped = true;
|
|
72
|
+
if (this.reconnectTimer) {
|
|
73
|
+
clearTimeout(this.reconnectTimer);
|
|
74
|
+
this.reconnectTimer = null;
|
|
75
|
+
}
|
|
76
|
+
if (this.socket) {
|
|
77
|
+
this.socket.end();
|
|
78
|
+
this.socket = null;
|
|
79
|
+
}
|
|
80
|
+
this.connected = false;
|
|
81
|
+
}
|
|
82
|
+
isConnected() {
|
|
83
|
+
return this.connected;
|
|
84
|
+
}
|
|
85
|
+
processFrames() {
|
|
86
|
+
while (this.dataBuffer.length >= 2) {
|
|
87
|
+
const firstByte = this.dataBuffer[0];
|
|
88
|
+
const secondByte = this.dataBuffer[1];
|
|
89
|
+
const isFin = (firstByte & 0x80) !== 0;
|
|
90
|
+
const opcode = firstByte & 0x0f;
|
|
91
|
+
if (opcode === 0x08) {
|
|
92
|
+
this.socket?.end();
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
let payloadLen = secondByte & 0x7f;
|
|
96
|
+
let headerLen = 2;
|
|
97
|
+
if (payloadLen === 126) {
|
|
98
|
+
if (this.dataBuffer.length < 4)
|
|
99
|
+
return;
|
|
100
|
+
payloadLen = this.dataBuffer.readUInt16BE(2);
|
|
101
|
+
headerLen = 4;
|
|
102
|
+
}
|
|
103
|
+
else if (payloadLen === 127) {
|
|
104
|
+
if (this.dataBuffer.length < 10)
|
|
105
|
+
return;
|
|
106
|
+
payloadLen = Number(this.dataBuffer.readBigUInt64BE(2));
|
|
107
|
+
headerLen = 10;
|
|
108
|
+
}
|
|
109
|
+
const totalLen = headerLen + payloadLen;
|
|
110
|
+
if (this.dataBuffer.length < totalLen)
|
|
111
|
+
return;
|
|
112
|
+
const payload = this.dataBuffer.subarray(headerLen, totalLen);
|
|
113
|
+
this.dataBuffer = this.dataBuffer.subarray(totalLen);
|
|
114
|
+
if (isFin && (opcode === 0x01 || opcode === 0x02)) {
|
|
115
|
+
const text = payload.toString("utf-8");
|
|
116
|
+
try {
|
|
117
|
+
const msg = JSON.parse(text);
|
|
118
|
+
this.handleMessage(msg);
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
// invalid JSON
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
handleMessage(msg) {
|
|
127
|
+
if (msg.type === "buffer" && msg.logs) {
|
|
128
|
+
for (const log of msg.logs) {
|
|
129
|
+
this.ingestLog(log);
|
|
130
|
+
}
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
this.ingestLog(msg);
|
|
134
|
+
}
|
|
135
|
+
ingestLog(msg) {
|
|
136
|
+
const level = LEVEL_MAP[msg.type] || "info";
|
|
137
|
+
this.collector.addLog({
|
|
138
|
+
level,
|
|
139
|
+
message: msg.values.join(", "),
|
|
140
|
+
source: msg.file || "agent",
|
|
141
|
+
framework: "unknown",
|
|
142
|
+
metadata: {
|
|
143
|
+
line: msg.line,
|
|
144
|
+
column: msg.column,
|
|
145
|
+
agentType: msg.type,
|
|
146
|
+
expression: msg.expression,
|
|
147
|
+
originalTimestamp: msg.timestamp,
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
sendFrame(text) {
|
|
152
|
+
if (!this.socket)
|
|
153
|
+
return;
|
|
154
|
+
const data = Buffer.from(text, "utf-8");
|
|
155
|
+
const len = data.length;
|
|
156
|
+
let header;
|
|
157
|
+
if (len < 126) {
|
|
158
|
+
header = Buffer.alloc(2);
|
|
159
|
+
header[0] = 0x81;
|
|
160
|
+
header[1] = len;
|
|
161
|
+
}
|
|
162
|
+
else if (len < 65536) {
|
|
163
|
+
header = Buffer.alloc(4);
|
|
164
|
+
header[0] = 0x81;
|
|
165
|
+
header[1] = 126;
|
|
166
|
+
header.writeUInt16BE(len, 2);
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
header = Buffer.alloc(10);
|
|
170
|
+
header[0] = 0x81;
|
|
171
|
+
header[1] = 127;
|
|
172
|
+
header.writeBigUInt64BE(BigInt(len), 2);
|
|
173
|
+
}
|
|
174
|
+
this.socket.write(Buffer.concat([header, data]));
|
|
175
|
+
}
|
|
176
|
+
scheduleReconnect() {
|
|
177
|
+
if (this.reconnectTimer || this.stopped)
|
|
178
|
+
return;
|
|
179
|
+
this.reconnectTimer = setTimeout(() => {
|
|
180
|
+
this.reconnectTimer = null;
|
|
181
|
+
this.connect();
|
|
182
|
+
}, 5000);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=agent-bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-bridge.js","sourceRoot":"","sources":["../src/agent-bridge.ts"],"names":[],"mappings":"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"}
|