@arvoretech/runtime-lens-mcp 1.1.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/agent/index.ts +281 -212
- package/dist/agent/index.js +92 -36
- package/dist/agent/index.js.map +2 -2
- 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 +58 -32
- package/dist/extension/extension.js.map +2 -2
- 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 +1 -1
- package/src/agent-bridge.ts +218 -0
- package/src/server.ts +405 -358
|
@@ -34,8 +34,8 @@ __export(extension_exports, {
|
|
|
34
34
|
deactivate: () => deactivate
|
|
35
35
|
});
|
|
36
36
|
module.exports = __toCommonJS(extension_exports);
|
|
37
|
-
var vscode3 = __toESM(require("vscode"));
|
|
38
37
|
var import_node_path = require("node:path");
|
|
38
|
+
var vscode3 = __toESM(require("vscode"));
|
|
39
39
|
|
|
40
40
|
// extension/decorator.ts
|
|
41
41
|
var vscode = __toESM(require("vscode"));
|
|
@@ -47,6 +47,7 @@ var TYPE_COLORS = {
|
|
|
47
47
|
debug: "#9CDCFE",
|
|
48
48
|
result: "#DCDCAA"
|
|
49
49
|
};
|
|
50
|
+
var MAX_HISTORY_PER_LINE = 10;
|
|
50
51
|
var InlineDecorator = class {
|
|
51
52
|
decorationTypes = /* @__PURE__ */ new Map();
|
|
52
53
|
values = /* @__PURE__ */ new Map();
|
|
@@ -65,17 +66,16 @@ var InlineDecorator = class {
|
|
|
65
66
|
addValue(value) {
|
|
66
67
|
const key = value.file;
|
|
67
68
|
if (!this.values.has(key)) {
|
|
68
|
-
this.values.set(key,
|
|
69
|
+
this.values.set(key, /* @__PURE__ */ new Map());
|
|
69
70
|
}
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
existing[idx] = value;
|
|
74
|
-
} else {
|
|
75
|
-
existing.push(value);
|
|
71
|
+
const lineMap = this.values.get(key);
|
|
72
|
+
if (!lineMap.has(value.line)) {
|
|
73
|
+
lineMap.set(value.line, []);
|
|
76
74
|
}
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
const history = lineMap.get(value.line);
|
|
76
|
+
history.push(value);
|
|
77
|
+
if (history.length > MAX_HISTORY_PER_LINE) {
|
|
78
|
+
history.splice(0, history.length - MAX_HISTORY_PER_LINE);
|
|
79
79
|
}
|
|
80
80
|
this.refresh();
|
|
81
81
|
}
|
|
@@ -92,39 +92,51 @@ var InlineDecorator = class {
|
|
|
92
92
|
if (!editor) return;
|
|
93
93
|
this.clearDecorations();
|
|
94
94
|
const filePath = editor.document.uri.fsPath;
|
|
95
|
-
const
|
|
96
|
-
if (!
|
|
95
|
+
const lineMap = this.values.get(filePath);
|
|
96
|
+
if (!lineMap || lineMap.size === 0) return;
|
|
97
97
|
const decorationsByType = /* @__PURE__ */ new Map();
|
|
98
|
-
for (const
|
|
99
|
-
|
|
98
|
+
for (const [lineNum, history] of lineMap) {
|
|
99
|
+
if (history.length === 0) continue;
|
|
100
|
+
const lineIndex = lineNum - 1;
|
|
100
101
|
if (lineIndex < 0 || lineIndex >= editor.document.lineCount) continue;
|
|
101
102
|
const lineText = editor.document.lineAt(lineIndex).text;
|
|
102
103
|
const range = new vscode.Range(
|
|
103
104
|
new vscode.Position(lineIndex, lineText.length),
|
|
104
105
|
new vscode.Position(lineIndex, lineText.length)
|
|
105
106
|
);
|
|
106
|
-
const
|
|
107
|
+
const latest = history[history.length - 1];
|
|
108
|
+
const maxLen = vscode.workspace.getConfiguration("runtimeLens").get("maxInlineLength", 80);
|
|
109
|
+
let inlineText = latest.text;
|
|
110
|
+
if (inlineText.length > maxLen) {
|
|
111
|
+
inlineText = inlineText.slice(0, maxLen) + "\u2026";
|
|
112
|
+
}
|
|
113
|
+
if (history.length > 1) {
|
|
114
|
+
inlineText = `${inlineText} (\xD7${history.length})`;
|
|
115
|
+
}
|
|
116
|
+
const hoverLines = history.slice().reverse().map((v, i) => {
|
|
117
|
+
const time = new Date(v.timestamp).toLocaleTimeString();
|
|
118
|
+
const prefix = i === 0 ? "\u25B6" : " ";
|
|
119
|
+
return `${prefix} \`[${time}]\` ${v.type}: \`${v.text}\``;
|
|
120
|
+
}).join("\n\n");
|
|
121
|
+
const hover = new vscode.MarkdownString(
|
|
122
|
+
`**Runtime Lens** \u2014 ${history.length} log(s)
|
|
123
|
+
|
|
124
|
+
${hoverLines}`
|
|
125
|
+
);
|
|
126
|
+
hover.isTrusted = true;
|
|
107
127
|
const decoration = {
|
|
108
128
|
range,
|
|
109
|
-
hoverMessage:
|
|
110
|
-
`**Runtime Lens** (${value.type})
|
|
111
|
-
|
|
112
|
-
\`\`\`
|
|
113
|
-
${value.text}
|
|
114
|
-
\`\`\`
|
|
115
|
-
|
|
116
|
-
*${new Date(value.timestamp).toLocaleTimeString()}*`
|
|
117
|
-
),
|
|
129
|
+
hoverMessage: hover,
|
|
118
130
|
renderOptions: {
|
|
119
131
|
after: {
|
|
120
|
-
contentText: ` // \u2192 ${
|
|
121
|
-
color: TYPE_COLORS[
|
|
132
|
+
contentText: ` // \u2192 ${inlineText}`,
|
|
133
|
+
color: TYPE_COLORS[latest.type] || "#6A9955",
|
|
122
134
|
fontStyle: "italic",
|
|
123
135
|
margin: "0 0 0 1em"
|
|
124
136
|
}
|
|
125
137
|
}
|
|
126
138
|
};
|
|
127
|
-
const typeKey =
|
|
139
|
+
const typeKey = latest.type;
|
|
128
140
|
if (!decorationsByType.has(typeKey)) {
|
|
129
141
|
decorationsByType.set(typeKey, []);
|
|
130
142
|
}
|
|
@@ -331,7 +343,10 @@ function activate(context) {
|
|
|
331
343
|
decorator = new InlineDecorator();
|
|
332
344
|
decorator.activate(context);
|
|
333
345
|
bridge = new RuntimeBridge(decorator, outputChannel);
|
|
334
|
-
statusBarItem = vscode3.window.createStatusBarItem(
|
|
346
|
+
statusBarItem = vscode3.window.createStatusBarItem(
|
|
347
|
+
vscode3.StatusBarAlignment.Right,
|
|
348
|
+
100
|
|
349
|
+
);
|
|
335
350
|
statusBarItem.text = "$(eye) Lens";
|
|
336
351
|
statusBarItem.tooltip = "Runtime Lens - Click to toggle";
|
|
337
352
|
statusBarItem.command = "runtimeLens.toggle";
|
|
@@ -342,7 +357,9 @@ function activate(context) {
|
|
|
342
357
|
vscode3.commands.registerCommand("runtimeLens.start", () => {
|
|
343
358
|
bridge.connect();
|
|
344
359
|
updateStatusBar(true);
|
|
345
|
-
vscode3.window.showInformationMessage(
|
|
360
|
+
vscode3.window.showInformationMessage(
|
|
361
|
+
"Runtime Lens: Listening for logs..."
|
|
362
|
+
);
|
|
346
363
|
}),
|
|
347
364
|
vscode3.commands.registerCommand("runtimeLens.stop", () => {
|
|
348
365
|
bridge.disconnect();
|
|
@@ -376,20 +393,29 @@ function activate(context) {
|
|
|
376
393
|
}
|
|
377
394
|
terminal.sendText(`export NODE_OPTIONS="--require ${agentPath}"`, true);
|
|
378
395
|
terminal.sendText(`export RUNTIME_LENS_PORT="${port}"`, true);
|
|
379
|
-
vscode3.window.showInformationMessage(
|
|
396
|
+
vscode3.window.showInformationMessage(
|
|
397
|
+
"Runtime Lens: Environment injected. Run your app now."
|
|
398
|
+
);
|
|
399
|
+
})
|
|
400
|
+
);
|
|
401
|
+
context.subscriptions.push(
|
|
402
|
+
vscode3.workspace.onDidSaveTextDocument((doc) => {
|
|
403
|
+
decorator.clearFile(doc.uri.fsPath);
|
|
380
404
|
})
|
|
381
405
|
);
|
|
382
406
|
const autoStart = vscode3.workspace.getConfiguration("runtimeLens").get("autoStart", false);
|
|
383
407
|
if (autoStart) {
|
|
384
408
|
vscode3.commands.executeCommand("runtimeLens.start");
|
|
385
409
|
}
|
|
386
|
-
outputChannel.appendLine(
|
|
410
|
+
outputChannel.appendLine("[runtime-lens] Extension activated");
|
|
387
411
|
outputChannel.appendLine(`[runtime-lens] Agent path: ${agentPath}`);
|
|
388
412
|
}
|
|
389
413
|
function updateStatusBar(active) {
|
|
390
414
|
if (active) {
|
|
391
415
|
statusBarItem.text = "$(eye) Lens \u25CF";
|
|
392
|
-
statusBarItem.backgroundColor = new vscode3.ThemeColor(
|
|
416
|
+
statusBarItem.backgroundColor = new vscode3.ThemeColor(
|
|
417
|
+
"statusBarItem.warningBackground"
|
|
418
|
+
);
|
|
393
419
|
} else {
|
|
394
420
|
statusBarItem.text = "$(eye) Lens";
|
|
395
421
|
statusBarItem.backgroundColor = void 0;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../extension/extension.ts", "../../extension/decorator.ts", "../../extension/runtime-bridge.ts"],
|
|
4
|
-
"sourcesContent": ["import * as vscode from \"vscode\";\nimport { join } from \"node:path\";\nimport { InlineDecorator } from \"./decorator.js\";\nimport { RuntimeBridge } from \"./runtime-bridge.js\";\n\nlet decorator: InlineDecorator;\nlet bridge: RuntimeBridge;\nlet statusBarItem: vscode.StatusBarItem;\n\nexport function activate(context: vscode.ExtensionContext): void {\n const outputChannel = vscode.window.createOutputChannel(\"Runtime Lens\");\n const agentPath = join(context.extensionPath, \"dist\", \"agent\", \"index.js\");\n\n decorator = new InlineDecorator();\n decorator.activate(context);\n\n bridge = new RuntimeBridge(decorator, outputChannel);\n\n statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 100);\n statusBarItem.text = \"$(eye) Lens\";\n statusBarItem.tooltip = \"Runtime Lens - Click to toggle\";\n statusBarItem.command = \"runtimeLens.toggle\";\n statusBarItem.show();\n context.subscriptions.push(statusBarItem);\n\n const port = vscode.workspace.getConfiguration(\"runtimeLens\").get(\"port\", 9500);\n\n context.subscriptions.push(\n vscode.commands.registerCommand(\"runtimeLens.start\", () => {\n bridge.connect();\n updateStatusBar(true);\n vscode.window.showInformationMessage(\"Runtime Lens: Listening for logs...\");\n }),\n\n vscode.commands.registerCommand(\"runtimeLens.stop\", () => {\n bridge.disconnect();\n decorator.clearAll();\n updateStatusBar(false);\n vscode.window.showInformationMessage(\"Runtime Lens: Stopped\");\n }),\n\n vscode.commands.registerCommand(\"runtimeLens.toggle\", () => {\n if (bridge.isConnected()) {\n vscode.commands.executeCommand(\"runtimeLens.stop\");\n } else {\n vscode.commands.executeCommand(\"runtimeLens.start\");\n }\n }),\n\n vscode.commands.registerCommand(\"runtimeLens.clear\", () => {\n decorator.clearAll();\n vscode.window.showInformationMessage(\"Runtime Lens: Cleared\");\n }),\n\n vscode.commands.registerCommand(\"runtimeLens.connect\", () => {\n bridge.connect();\n updateStatusBar(true);\n }),\n\n vscode.commands.registerCommand(\"runtimeLens.showOutput\", () => {\n outputChannel.show();\n }),\n\n vscode.commands.registerCommand(\"runtimeLens.injectEnv\", () => {\n const terminal = vscode.window.activeTerminal;\n if (!terminal) {\n vscode.window.showWarningMessage(\"Runtime Lens: No active terminal\");\n return;\n }\n terminal.sendText(`export NODE_OPTIONS=\"--require ${agentPath}\"`, true);\n terminal.sendText(`export RUNTIME_LENS_PORT=\"${port}\"`, true);\n vscode.window.showInformationMessage(\"Runtime Lens: Environment injected. Run your app now.\");\n })\n );\n\n const autoStart = vscode.workspace.getConfiguration(\"runtimeLens\").get(\"autoStart\", false);\n if (autoStart) {\n vscode.commands.executeCommand(\"runtimeLens.start\");\n }\n\n outputChannel.appendLine(`[runtime-lens] Extension activated`);\n outputChannel.appendLine(`[runtime-lens] Agent path: ${agentPath}`);\n}\n\nfunction updateStatusBar(active: boolean): void {\n if (active) {\n statusBarItem.text = \"$(eye) Lens \u25CF\";\n statusBarItem.backgroundColor = new vscode.ThemeColor(\"statusBarItem.warningBackground\");\n } else {\n statusBarItem.text = \"$(eye) Lens\";\n statusBarItem.backgroundColor = undefined;\n }\n}\n\nexport function deactivate(): void {\n bridge?.disconnect();\n decorator?.dispose();\n}\n", "import * as vscode from \"vscode\";\n\ninterface InlineValue {\n file: string;\n line: number;\n column: number;\n text: string;\n type: \"log\" | \"error\" | \"warn\" | \"info\" | \"debug\" | \"result\";\n timestamp: number;\n}\n\nconst TYPE_COLORS: Record<string, string> = {\n log: \"#6A9955\",\n info: \"#569CD6\",\n warn: \"#CE9178\",\n error: \"#F44747\",\n debug: \"#9CDCFE\",\n result: \"#DCDCAA\",\n};\n\nexport class InlineDecorator {\n private decorationTypes: Map<string, vscode.TextEditorDecorationType> = new Map();\n private values: Map<string, InlineValue[]> = new Map();\n private disposables: vscode.Disposable[] = [];\n\n activate(context: vscode.ExtensionContext): void {\n this.disposables.push(\n vscode.window.onDidChangeActiveTextEditor(() => this.refresh()),\n vscode.workspace.onDidChangeTextDocument((e) => {\n if (e.document === vscode.window.activeTextEditor?.document) {\n this.refresh();\n }\n })\n );\n context.subscriptions.push(...this.disposables);\n }\n\n addValue(value: InlineValue): void {\n const key = value.file;\n if (!this.values.has(key)) {\n this.values.set(key, []);\n }\n\n const existing = this.values.get(key)!;\n const idx = existing.findIndex((v) => v.line === value.line);\n if (idx >= 0) {\n existing[idx] = value;\n } else {\n existing.push(value);\n }\n\n if (existing.length > 1000) {\n existing.splice(0, existing.length - 1000);\n }\n\n this.refresh();\n }\n\n clearFile(file: string): void {\n this.values.delete(file);\n this.refresh();\n }\n\n clearAll(): void {\n this.values.clear();\n this.clearDecorations();\n }\n\n refresh(): void {\n const editor = vscode.window.activeTextEditor;\n if (!editor) return;\n\n this.clearDecorations();\n\n const filePath = editor.document.uri.fsPath;\n const fileValues = this.values.get(filePath);\n if (!fileValues || fileValues.length === 0) return;\n\n const decorationsByType = new Map<string, vscode.DecorationOptions[]>();\n\n for (const value of fileValues) {\n const lineIndex = value.line - 1;\n if (lineIndex < 0 || lineIndex >= editor.document.lineCount) continue;\n\n const lineText = editor.document.lineAt(lineIndex).text;\n const range = new vscode.Range(\n new vscode.Position(lineIndex, lineText.length),\n new vscode.Position(lineIndex, lineText.length)\n );\n\n const truncated = value.text.length > 80\n ? value.text.slice(0, 80) + \"\u2026\"\n : value.text;\n\n const decoration: vscode.DecorationOptions = {\n range,\n hoverMessage: new vscode.MarkdownString(\n `**Runtime Lens** (${value.type})\\n\\n\\`\\`\\`\\n${value.text}\\n\\`\\`\\`\\n\\n*${new Date(value.timestamp).toLocaleTimeString()}*`\n ),\n renderOptions: {\n after: {\n contentText: ` // \u2192 ${truncated}`,\n color: TYPE_COLORS[value.type] || \"#6A9955\",\n fontStyle: \"italic\",\n margin: \"0 0 0 1em\",\n },\n },\n };\n\n const typeKey = value.type;\n if (!decorationsByType.has(typeKey)) {\n decorationsByType.set(typeKey, []);\n }\n decorationsByType.get(typeKey)!.push(decoration);\n }\n\n for (const [typeKey, decorations] of decorationsByType) {\n const decorationType = vscode.window.createTextEditorDecorationType({\n after: {\n color: TYPE_COLORS[typeKey] || \"#6A9955\",\n fontStyle: \"italic\",\n },\n isWholeLine: false,\n });\n\n this.decorationTypes.set(typeKey, decorationType);\n editor.setDecorations(decorationType, decorations);\n }\n }\n\n private clearDecorations(): void {\n for (const decorationType of this.decorationTypes.values()) {\n decorationType.dispose();\n }\n this.decorationTypes.clear();\n }\n\n dispose(): void {\n this.clearDecorations();\n for (const d of this.disposables) {\n d.dispose();\n }\n }\n}\n", "import * as vscode from \"vscode\";\nimport { request } from \"node:http\";\nimport { randomBytes } from \"node:crypto\";\nimport type { Socket } from \"node:net\";\nimport { InlineDecorator } from \"./decorator.js\";\n\ninterface AgentMessage {\n type: \"log\" | \"error\" | \"warn\" | \"info\" | \"debug\" | \"result\";\n file: string;\n line: number;\n column: number;\n values: string[];\n timestamp: number;\n expression?: string;\n}\n\nexport class RuntimeBridge {\n private socket: Socket | null = null;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private readonly decorator: InlineDecorator;\n private readonly port: number;\n private connected = false;\n private readonly outputChannel: vscode.OutputChannel;\n private dataBuffer: Buffer = Buffer.alloc(0);\n\n constructor(decorator: InlineDecorator, outputChannel: vscode.OutputChannel) {\n this.decorator = decorator;\n this.port = vscode.workspace.getConfiguration(\"runtimeLens\").get(\"port\", 9500);\n this.outputChannel = outputChannel;\n }\n\n connect(): void {\n if (this.socket) return;\n\n const key = randomBytes(16).toString(\"base64\");\n\n const req = request({\n hostname: \"localhost\",\n port: this.port,\n path: \"/\",\n method: \"GET\",\n headers: {\n Upgrade: \"websocket\",\n Connection: \"Upgrade\",\n \"Sec-WebSocket-Key\": key,\n \"Sec-WebSocket-Version\": \"13\",\n },\n });\n\n req.on(\"upgrade\", (_res, socket) => {\n this.socket = socket;\n this.connected = true;\n this.dataBuffer = Buffer.alloc(0);\n this.outputChannel.appendLine(`[runtime-lens] Connected to agent on port ${this.port}`);\n vscode.window.setStatusBarMessage(\"$(eye) Runtime Lens: Connected\", 3000);\n\n socket.on(\"data\", (data: Buffer) => {\n this.dataBuffer = Buffer.concat([this.dataBuffer, data]);\n this.processFrames();\n });\n\n socket.on(\"close\", () => {\n this.connected = false;\n this.socket = null;\n this.scheduleReconnect();\n });\n\n socket.on(\"error\", () => {\n this.connected = false;\n this.socket = null;\n });\n });\n\n req.on(\"error\", () => {\n this.scheduleReconnect();\n });\n\n req.end();\n }\n\n disconnect(): void {\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n if (this.socket) {\n this.socket.end();\n this.socket = null;\n }\n this.connected = false;\n }\n\n isConnected(): boolean {\n return this.connected;\n }\n\n evaluate(expression: string, file: string, line: number): void {\n if (!this.socket || !this.connected) return;\n const payload = JSON.stringify({ type: \"eval\", expression, file, line });\n this.sendFrame(payload);\n }\n\n private processFrames(): void {\n while (this.dataBuffer.length >= 2) {\n const firstByte = this.dataBuffer[0];\n const secondByte = this.dataBuffer[1];\n const isFin = (firstByte & 0x80) !== 0;\n const opcode = firstByte & 0x0f;\n\n if (opcode === 0x08) {\n this.socket?.end();\n return;\n }\n\n let payloadLen = secondByte & 0x7f;\n let headerLen = 2;\n\n if (payloadLen === 126) {\n if (this.dataBuffer.length < 4) return;\n payloadLen = this.dataBuffer.readUInt16BE(2);\n headerLen = 4;\n } else if (payloadLen === 127) {\n if (this.dataBuffer.length < 10) return;\n payloadLen = Number(this.dataBuffer.readBigUInt64BE(2));\n headerLen = 10;\n }\n\n const totalLen = headerLen + payloadLen;\n if (this.dataBuffer.length < totalLen) return;\n\n const payload = this.dataBuffer.subarray(headerLen, totalLen);\n this.dataBuffer = this.dataBuffer.subarray(totalLen);\n\n if (isFin && (opcode === 0x01 || opcode === 0x02)) {\n const text = payload.toString(\"utf-8\");\n try {\n const msg: AgentMessage = JSON.parse(text);\n this.handleMessage(msg);\n } catch {\n // invalid JSON\n }\n }\n }\n }\n\n private sendFrame(text: string): void {\n if (!this.socket) return;\n const data = Buffer.from(text, \"utf-8\");\n const len = data.length;\n let header: Buffer;\n\n if (len < 126) {\n header = Buffer.alloc(2);\n header[0] = 0x81;\n header[1] = len;\n } else if (len < 65536) {\n header = Buffer.alloc(4);\n header[0] = 0x81;\n header[1] = 126;\n header.writeUInt16BE(len, 2);\n } else {\n header = Buffer.alloc(10);\n header[0] = 0x81;\n header[1] = 127;\n header.writeBigUInt64BE(BigInt(len), 2);\n }\n\n this.socket.write(Buffer.concat([header, data]));\n }\n\n private handleMessage(msg: AgentMessage): void {\n const workspaceFolders = vscode.workspace.workspaceFolders;\n if (!workspaceFolders) return;\n\n let resolvedFile = msg.file;\n for (const folder of workspaceFolders) {\n if (msg.file.startsWith(folder.uri.fsPath)) {\n resolvedFile = msg.file;\n break;\n }\n }\n\n const text = msg.values.join(\", \");\n\n this.decorator.addValue({\n file: resolvedFile,\n line: msg.line,\n column: msg.column,\n text,\n type: msg.type,\n timestamp: msg.timestamp,\n });\n\n this.outputChannel.appendLine(\n `[${msg.type}] ${resolvedFile}:${msg.line} \u2192 ${text}`\n );\n }\n\n private scheduleReconnect(): void {\n if (this.reconnectTimer) return;\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = null;\n this.connect();\n }, 3000);\n }\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAAA,UAAwB
|
|
4
|
+
"sourcesContent": ["import { join } from \"node:path\";\nimport * as vscode from \"vscode\";\nimport { InlineDecorator } from \"./decorator.js\";\nimport { RuntimeBridge } from \"./runtime-bridge.js\";\n\nlet decorator: InlineDecorator;\nlet bridge: RuntimeBridge;\nlet statusBarItem: vscode.StatusBarItem;\n\nexport function activate(context: vscode.ExtensionContext): void {\n\tconst outputChannel = vscode.window.createOutputChannel(\"Runtime Lens\");\n\tconst agentPath = join(context.extensionPath, \"dist\", \"agent\", \"index.js\");\n\n\tdecorator = new InlineDecorator();\n\tdecorator.activate(context);\n\n\tbridge = new RuntimeBridge(decorator, outputChannel);\n\n\tstatusBarItem = vscode.window.createStatusBarItem(\n\t\tvscode.StatusBarAlignment.Right,\n\t\t100,\n\t);\n\tstatusBarItem.text = \"$(eye) Lens\";\n\tstatusBarItem.tooltip = \"Runtime Lens - Click to toggle\";\n\tstatusBarItem.command = \"runtimeLens.toggle\";\n\tstatusBarItem.show();\n\tcontext.subscriptions.push(statusBarItem);\n\n\tconst port = vscode.workspace\n\t\t.getConfiguration(\"runtimeLens\")\n\t\t.get(\"port\", 9500);\n\n\tcontext.subscriptions.push(\n\t\tvscode.commands.registerCommand(\"runtimeLens.start\", () => {\n\t\t\tbridge.connect();\n\t\t\tupdateStatusBar(true);\n\t\t\tvscode.window.showInformationMessage(\n\t\t\t\t\"Runtime Lens: Listening for logs...\",\n\t\t\t);\n\t\t}),\n\n\t\tvscode.commands.registerCommand(\"runtimeLens.stop\", () => {\n\t\t\tbridge.disconnect();\n\t\t\tdecorator.clearAll();\n\t\t\tupdateStatusBar(false);\n\t\t\tvscode.window.showInformationMessage(\"Runtime Lens: Stopped\");\n\t\t}),\n\n\t\tvscode.commands.registerCommand(\"runtimeLens.toggle\", () => {\n\t\t\tif (bridge.isConnected()) {\n\t\t\t\tvscode.commands.executeCommand(\"runtimeLens.stop\");\n\t\t\t} else {\n\t\t\t\tvscode.commands.executeCommand(\"runtimeLens.start\");\n\t\t\t}\n\t\t}),\n\n\t\tvscode.commands.registerCommand(\"runtimeLens.clear\", () => {\n\t\t\tdecorator.clearAll();\n\t\t\tvscode.window.showInformationMessage(\"Runtime Lens: Cleared\");\n\t\t}),\n\n\t\tvscode.commands.registerCommand(\"runtimeLens.connect\", () => {\n\t\t\tbridge.connect();\n\t\t\tupdateStatusBar(true);\n\t\t}),\n\n\t\tvscode.commands.registerCommand(\"runtimeLens.showOutput\", () => {\n\t\t\toutputChannel.show();\n\t\t}),\n\n\t\tvscode.commands.registerCommand(\"runtimeLens.injectEnv\", () => {\n\t\t\tconst terminal = vscode.window.activeTerminal;\n\t\t\tif (!terminal) {\n\t\t\t\tvscode.window.showWarningMessage(\"Runtime Lens: No active terminal\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tterminal.sendText(`export NODE_OPTIONS=\"--require ${agentPath}\"`, true);\n\t\t\tterminal.sendText(`export RUNTIME_LENS_PORT=\"${port}\"`, true);\n\t\t\tvscode.window.showInformationMessage(\n\t\t\t\t\"Runtime Lens: Environment injected. Run your app now.\",\n\t\t\t);\n\t\t}),\n\t);\n\n\tcontext.subscriptions.push(\n\t\tvscode.workspace.onDidSaveTextDocument((doc) => {\n\t\t\tdecorator.clearFile(doc.uri.fsPath);\n\t\t}),\n\t);\n\n\tconst autoStart = vscode.workspace\n\t\t.getConfiguration(\"runtimeLens\")\n\t\t.get(\"autoStart\", false);\n\tif (autoStart) {\n\t\tvscode.commands.executeCommand(\"runtimeLens.start\");\n\t}\n\n\toutputChannel.appendLine(\"[runtime-lens] Extension activated\");\n\toutputChannel.appendLine(`[runtime-lens] Agent path: ${agentPath}`);\n}\n\nfunction updateStatusBar(active: boolean): void {\n\tif (active) {\n\t\tstatusBarItem.text = \"$(eye) Lens \u25CF\";\n\t\tstatusBarItem.backgroundColor = new vscode.ThemeColor(\n\t\t\t\"statusBarItem.warningBackground\",\n\t\t);\n\t} else {\n\t\tstatusBarItem.text = \"$(eye) Lens\";\n\t\tstatusBarItem.backgroundColor = undefined;\n\t}\n}\n\nexport function deactivate(): void {\n\tbridge?.disconnect();\n\tdecorator?.dispose();\n}\n", "import * as vscode from \"vscode\";\n\ninterface InlineValue {\n\tfile: string;\n\tline: number;\n\tcolumn: number;\n\ttext: string;\n\ttype: \"log\" | \"error\" | \"warn\" | \"info\" | \"debug\" | \"result\";\n\ttimestamp: number;\n}\n\nconst TYPE_COLORS: Record<string, string> = {\n\tlog: \"#6A9955\",\n\tinfo: \"#569CD6\",\n\twarn: \"#CE9178\",\n\terror: \"#F44747\",\n\tdebug: \"#9CDCFE\",\n\tresult: \"#DCDCAA\",\n};\n\nconst MAX_HISTORY_PER_LINE = 10;\n\nexport class InlineDecorator {\n\tprivate decorationTypes: Map<string, vscode.TextEditorDecorationType> =\n\t\tnew Map();\n\tprivate values: Map<string, Map<number, InlineValue[]>> = new Map();\n\tprivate disposables: vscode.Disposable[] = [];\n\n\tactivate(context: vscode.ExtensionContext): void {\n\t\tthis.disposables.push(\n\t\t\tvscode.window.onDidChangeActiveTextEditor(() => this.refresh()),\n\t\t\tvscode.workspace.onDidChangeTextDocument((e) => {\n\t\t\t\tif (e.document === vscode.window.activeTextEditor?.document) {\n\t\t\t\t\tthis.refresh();\n\t\t\t\t}\n\t\t\t}),\n\t\t);\n\t\tcontext.subscriptions.push(...this.disposables);\n\t}\n\n\taddValue(value: InlineValue): void {\n\t\tconst key = value.file;\n\t\tif (!this.values.has(key)) {\n\t\t\tthis.values.set(key, new Map());\n\t\t}\n\t\tconst lineMap = this.values.get(key)!;\n\t\tif (!lineMap.has(value.line)) {\n\t\t\tlineMap.set(value.line, []);\n\t\t}\n\n\t\tconst history = lineMap.get(value.line)!;\n\t\thistory.push(value);\n\n\t\tif (history.length > MAX_HISTORY_PER_LINE) {\n\t\t\thistory.splice(0, history.length - MAX_HISTORY_PER_LINE);\n\t\t}\n\n\t\tthis.refresh();\n\t}\n\n\tclearFile(file: string): void {\n\t\tthis.values.delete(file);\n\t\tthis.refresh();\n\t}\n\n\tclearAll(): void {\n\t\tthis.values.clear();\n\t\tthis.clearDecorations();\n\t}\n\n\trefresh(): void {\n\t\tconst editor = vscode.window.activeTextEditor;\n\t\tif (!editor) return;\n\n\t\tthis.clearDecorations();\n\n\t\tconst filePath = editor.document.uri.fsPath;\n\t\tconst lineMap = this.values.get(filePath);\n\t\tif (!lineMap || lineMap.size === 0) return;\n\n\t\tconst decorationsByType = new Map<string, vscode.DecorationOptions[]>();\n\n\t\tfor (const [lineNum, history] of lineMap) {\n\t\t\tif (history.length === 0) continue;\n\n\t\t\tconst lineIndex = lineNum - 1;\n\t\t\tif (lineIndex < 0 || lineIndex >= editor.document.lineCount) continue;\n\n\t\t\tconst lineText = editor.document.lineAt(lineIndex).text;\n\t\t\tconst range = new vscode.Range(\n\t\t\t\tnew vscode.Position(lineIndex, lineText.length),\n\t\t\t\tnew vscode.Position(lineIndex, lineText.length),\n\t\t\t);\n\n\t\t\tconst latest = history[history.length - 1];\n\t\t\tconst maxLen = vscode.workspace\n\t\t\t\t.getConfiguration(\"runtimeLens\")\n\t\t\t\t.get(\"maxInlineLength\", 80);\n\n\t\t\tlet inlineText = latest.text;\n\t\t\tif (inlineText.length > maxLen) {\n\t\t\t\tinlineText = inlineText.slice(0, maxLen) + \"\u2026\";\n\t\t\t}\n\t\t\tif (history.length > 1) {\n\t\t\t\tinlineText = `${inlineText} (\u00D7${history.length})`;\n\t\t\t}\n\n\t\t\tconst hoverLines = history\n\t\t\t\t.slice()\n\t\t\t\t.reverse()\n\t\t\t\t.map((v, i) => {\n\t\t\t\t\tconst time = new Date(v.timestamp).toLocaleTimeString();\n\t\t\t\t\tconst prefix = i === 0 ? \"\u25B6\" : \" \";\n\t\t\t\t\treturn `${prefix} \\`[${time}]\\` ${v.type}: \\`${v.text}\\``;\n\t\t\t\t})\n\t\t\t\t.join(\"\\n\\n\");\n\n\t\t\tconst hover = new vscode.MarkdownString(\n\t\t\t\t`**Runtime Lens** \u2014 ${history.length} log(s)\\n\\n${hoverLines}`,\n\t\t\t);\n\t\t\thover.isTrusted = true;\n\n\t\t\tconst decoration: vscode.DecorationOptions = {\n\t\t\t\trange,\n\t\t\t\thoverMessage: hover,\n\t\t\t\trenderOptions: {\n\t\t\t\t\tafter: {\n\t\t\t\t\t\tcontentText: ` // \u2192 ${inlineText}`,\n\t\t\t\t\t\tcolor: TYPE_COLORS[latest.type] || \"#6A9955\",\n\t\t\t\t\t\tfontStyle: \"italic\",\n\t\t\t\t\t\tmargin: \"0 0 0 1em\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t};\n\n\t\t\tconst typeKey = latest.type;\n\t\t\tif (!decorationsByType.has(typeKey)) {\n\t\t\t\tdecorationsByType.set(typeKey, []);\n\t\t\t}\n\t\t\tdecorationsByType.get(typeKey)!.push(decoration);\n\t\t}\n\n\t\tfor (const [typeKey, decorations] of decorationsByType) {\n\t\t\tconst decorationType = vscode.window.createTextEditorDecorationType({\n\t\t\t\tafter: {\n\t\t\t\t\tcolor: TYPE_COLORS[typeKey] || \"#6A9955\",\n\t\t\t\t\tfontStyle: \"italic\",\n\t\t\t\t},\n\t\t\t\tisWholeLine: false,\n\t\t\t});\n\t\t\tthis.decorationTypes.set(typeKey, decorationType);\n\t\t\teditor.setDecorations(decorationType, decorations);\n\t\t}\n\t}\n\n\tprivate clearDecorations(): void {\n\t\tfor (const decorationType of this.decorationTypes.values()) {\n\t\t\tdecorationType.dispose();\n\t\t}\n\t\tthis.decorationTypes.clear();\n\t}\n\n\tdispose(): void {\n\t\tthis.clearDecorations();\n\t\tfor (const d of this.disposables) {\n\t\t\td.dispose();\n\t\t}\n\t}\n}\n", "import * as vscode from \"vscode\";\nimport { request } from \"node:http\";\nimport { randomBytes } from \"node:crypto\";\nimport type { Socket } from \"node:net\";\nimport { InlineDecorator } from \"./decorator.js\";\n\ninterface AgentMessage {\n type: \"log\" | \"error\" | \"warn\" | \"info\" | \"debug\" | \"result\";\n file: string;\n line: number;\n column: number;\n values: string[];\n timestamp: number;\n expression?: string;\n}\n\nexport class RuntimeBridge {\n private socket: Socket | null = null;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private readonly decorator: InlineDecorator;\n private readonly port: number;\n private connected = false;\n private readonly outputChannel: vscode.OutputChannel;\n private dataBuffer: Buffer = Buffer.alloc(0);\n\n constructor(decorator: InlineDecorator, outputChannel: vscode.OutputChannel) {\n this.decorator = decorator;\n this.port = vscode.workspace.getConfiguration(\"runtimeLens\").get(\"port\", 9500);\n this.outputChannel = outputChannel;\n }\n\n connect(): void {\n if (this.socket) return;\n\n const key = randomBytes(16).toString(\"base64\");\n\n const req = request({\n hostname: \"localhost\",\n port: this.port,\n path: \"/\",\n method: \"GET\",\n headers: {\n Upgrade: \"websocket\",\n Connection: \"Upgrade\",\n \"Sec-WebSocket-Key\": key,\n \"Sec-WebSocket-Version\": \"13\",\n },\n });\n\n req.on(\"upgrade\", (_res, socket) => {\n this.socket = socket;\n this.connected = true;\n this.dataBuffer = Buffer.alloc(0);\n this.outputChannel.appendLine(`[runtime-lens] Connected to agent on port ${this.port}`);\n vscode.window.setStatusBarMessage(\"$(eye) Runtime Lens: Connected\", 3000);\n\n socket.on(\"data\", (data: Buffer) => {\n this.dataBuffer = Buffer.concat([this.dataBuffer, data]);\n this.processFrames();\n });\n\n socket.on(\"close\", () => {\n this.connected = false;\n this.socket = null;\n this.scheduleReconnect();\n });\n\n socket.on(\"error\", () => {\n this.connected = false;\n this.socket = null;\n });\n });\n\n req.on(\"error\", () => {\n this.scheduleReconnect();\n });\n\n req.end();\n }\n\n disconnect(): void {\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n if (this.socket) {\n this.socket.end();\n this.socket = null;\n }\n this.connected = false;\n }\n\n isConnected(): boolean {\n return this.connected;\n }\n\n evaluate(expression: string, file: string, line: number): void {\n if (!this.socket || !this.connected) return;\n const payload = JSON.stringify({ type: \"eval\", expression, file, line });\n this.sendFrame(payload);\n }\n\n private processFrames(): void {\n while (this.dataBuffer.length >= 2) {\n const firstByte = this.dataBuffer[0];\n const secondByte = this.dataBuffer[1];\n const isFin = (firstByte & 0x80) !== 0;\n const opcode = firstByte & 0x0f;\n\n if (opcode === 0x08) {\n this.socket?.end();\n return;\n }\n\n let payloadLen = secondByte & 0x7f;\n let headerLen = 2;\n\n if (payloadLen === 126) {\n if (this.dataBuffer.length < 4) return;\n payloadLen = this.dataBuffer.readUInt16BE(2);\n headerLen = 4;\n } else if (payloadLen === 127) {\n if (this.dataBuffer.length < 10) return;\n payloadLen = Number(this.dataBuffer.readBigUInt64BE(2));\n headerLen = 10;\n }\n\n const totalLen = headerLen + payloadLen;\n if (this.dataBuffer.length < totalLen) return;\n\n const payload = this.dataBuffer.subarray(headerLen, totalLen);\n this.dataBuffer = this.dataBuffer.subarray(totalLen);\n\n if (isFin && (opcode === 0x01 || opcode === 0x02)) {\n const text = payload.toString(\"utf-8\");\n try {\n const msg: AgentMessage = JSON.parse(text);\n this.handleMessage(msg);\n } catch {\n // invalid JSON\n }\n }\n }\n }\n\n private sendFrame(text: string): void {\n if (!this.socket) return;\n const data = Buffer.from(text, \"utf-8\");\n const len = data.length;\n let header: Buffer;\n\n if (len < 126) {\n header = Buffer.alloc(2);\n header[0] = 0x81;\n header[1] = len;\n } else if (len < 65536) {\n header = Buffer.alloc(4);\n header[0] = 0x81;\n header[1] = 126;\n header.writeUInt16BE(len, 2);\n } else {\n header = Buffer.alloc(10);\n header[0] = 0x81;\n header[1] = 127;\n header.writeBigUInt64BE(BigInt(len), 2);\n }\n\n this.socket.write(Buffer.concat([header, data]));\n }\n\n private handleMessage(msg: AgentMessage): void {\n const workspaceFolders = vscode.workspace.workspaceFolders;\n if (!workspaceFolders) return;\n\n let resolvedFile = msg.file;\n for (const folder of workspaceFolders) {\n if (msg.file.startsWith(folder.uri.fsPath)) {\n resolvedFile = msg.file;\n break;\n }\n }\n\n const text = msg.values.join(\", \");\n\n this.decorator.addValue({\n file: resolvedFile,\n line: msg.line,\n column: msg.column,\n text,\n type: msg.type,\n timestamp: msg.timestamp,\n });\n\n this.outputChannel.appendLine(\n `[${msg.type}] ${resolvedFile}:${msg.line} \u2192 ${text}`\n );\n }\n\n private scheduleReconnect(): void {\n if (this.reconnectTimer) return;\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = null;\n this.connect();\n }, 3000);\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["vscode", "vscode", "decorator"]
|
|
7
7
|
}
|
package/dist/server.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export declare class RuntimeLensMCPServer {
|
|
|
3
3
|
private readonly collector;
|
|
4
4
|
private readonly inspector;
|
|
5
5
|
private readonly interceptor;
|
|
6
|
+
private readonly agentBridge;
|
|
6
7
|
constructor(projectRoot?: string, logPaths?: string[]);
|
|
7
8
|
static fromEnvironment(): RuntimeLensMCPServer;
|
|
8
9
|
private setupTools;
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAcA,qBAAa,oBAAoB;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAY;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAmB;IAC7C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqB;IACjD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;gBAE9B,WAAW,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE;IAYrD,MAAM,CAAC,eAAe,IAAI,oBAAoB;IAO9C,OAAO,CAAC,UAAU;IAiBlB,OAAO,CAAC,gBAAgB;IAyBxB,OAAO,CAAC,kBAAkB;IA8B1B,OAAO,CAAC,iBAAiB;IAwBzB,OAAO,CAAC,uBAAuB;IA0B/B,OAAO,CAAC,sBAAsB;IA2B9B,OAAO,CAAC,kBAAkB;IAwB1B,OAAO,CAAC,iBAAiB;IAyBzB,OAAO,CAAC,gBAAgB;IAoBxB,OAAO,CAAC,wBAAwB;IA0BhC,OAAO,CAAC,uBAAuB;IA0B/B,OAAO,CAAC,mBAAmB;IA0B3B,OAAO,CAAC,mBAAmB;IAwB3B,OAAO,CAAC,qBAAqB;IAwB7B,OAAO,CAAC,gBAAgB;IAwBlB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAe5B,qBAAqB,IAAI,IAAI;CAU7B"}
|
package/dist/server.js
CHANGED
|
@@ -1,22 +1,25 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { AgentBridge } from "./agent-bridge.js";
|
|
3
4
|
import { LogCollector } from "./log-collector.js";
|
|
4
5
|
import { ProcessInspector } from "./process-inspector.js";
|
|
5
6
|
import { RuntimeInterceptor } from "./runtime-interceptor.js";
|
|
6
|
-
import {
|
|
7
|
+
import { GetErrorsParamsSchema, GetPerformanceParamsSchema, InspectRequestParamsSchema, SearchLogsParamsSchema, TailLogsParamsSchema, } from "./types.js";
|
|
7
8
|
export class RuntimeLensMCPServer {
|
|
8
9
|
server;
|
|
9
10
|
collector;
|
|
10
11
|
inspector;
|
|
11
12
|
interceptor;
|
|
13
|
+
agentBridge;
|
|
12
14
|
constructor(projectRoot, logPaths) {
|
|
13
15
|
this.server = new McpServer({
|
|
14
16
|
name: "runtime-lens-mcp",
|
|
15
|
-
version: "1.
|
|
17
|
+
version: "1.1.0",
|
|
16
18
|
});
|
|
17
19
|
this.collector = new LogCollector(projectRoot, logPaths);
|
|
18
20
|
this.inspector = new ProcessInspector(projectRoot);
|
|
19
21
|
this.interceptor = new RuntimeInterceptor(this.collector);
|
|
22
|
+
this.agentBridge = new AgentBridge(this.collector);
|
|
20
23
|
this.setupTools();
|
|
21
24
|
}
|
|
22
25
|
static fromEnvironment() {
|
|
@@ -50,15 +53,14 @@ export class RuntimeLensMCPServer {
|
|
|
50
53
|
framework: TailLogsParamsSchema.shape.framework,
|
|
51
54
|
source: TailLogsParamsSchema.shape.source,
|
|
52
55
|
},
|
|
53
|
-
}, async (params) => {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
});
|
|
56
|
+
}, async (params) => ({
|
|
57
|
+
content: [
|
|
58
|
+
{
|
|
59
|
+
type: "text",
|
|
60
|
+
text: JSON.stringify(this.collector.getLogs(params), null, 2),
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
}));
|
|
62
64
|
}
|
|
63
65
|
registerSearchLogs() {
|
|
64
66
|
this.server.registerTool("search_logs", {
|
|
@@ -71,15 +73,14 @@ export class RuntimeLensMCPServer {
|
|
|
71
73
|
limit: SearchLogsParamsSchema.shape.limit,
|
|
72
74
|
since: SearchLogsParamsSchema.shape.since,
|
|
73
75
|
},
|
|
74
|
-
}, async (params) => {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
});
|
|
76
|
+
}, async (params) => ({
|
|
77
|
+
content: [
|
|
78
|
+
{
|
|
79
|
+
type: "text",
|
|
80
|
+
text: JSON.stringify(this.collector.searchLogs(params), null, 2),
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
}));
|
|
83
84
|
}
|
|
84
85
|
registerGetErrors() {
|
|
85
86
|
this.server.registerTool("get_errors", {
|
|
@@ -90,15 +91,14 @@ export class RuntimeLensMCPServer {
|
|
|
90
91
|
framework: GetErrorsParamsSchema.shape.framework,
|
|
91
92
|
grouped: GetErrorsParamsSchema.shape.grouped,
|
|
92
93
|
},
|
|
93
|
-
}, async (params) => {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
});
|
|
94
|
+
}, async (params) => ({
|
|
95
|
+
content: [
|
|
96
|
+
{
|
|
97
|
+
type: "text",
|
|
98
|
+
text: JSON.stringify(this.collector.getErrors(params), null, 2),
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
}));
|
|
102
102
|
}
|
|
103
103
|
registerInspectRequests() {
|
|
104
104
|
this.server.registerTool("inspect_requests", {
|
|
@@ -111,15 +111,14 @@ export class RuntimeLensMCPServer {
|
|
|
111
111
|
statusCode: InspectRequestParamsSchema.shape.statusCode,
|
|
112
112
|
limit: InspectRequestParamsSchema.shape.limit,
|
|
113
113
|
},
|
|
114
|
-
}, async (params) => {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
});
|
|
114
|
+
}, async (params) => ({
|
|
115
|
+
content: [
|
|
116
|
+
{
|
|
117
|
+
type: "text",
|
|
118
|
+
text: JSON.stringify(this.collector.getRequests(params), null, 2),
|
|
119
|
+
},
|
|
120
|
+
],
|
|
121
|
+
}));
|
|
123
122
|
}
|
|
124
123
|
registerGetPerformance() {
|
|
125
124
|
this.server.registerTool("get_performance", {
|
|
@@ -132,12 +131,13 @@ export class RuntimeLensMCPServer {
|
|
|
132
131
|
},
|
|
133
132
|
}, async (params) => {
|
|
134
133
|
await this.collector.collectFromProcess();
|
|
135
|
-
const metrics = this.collector.getMetrics(params);
|
|
136
134
|
return {
|
|
137
|
-
content: [
|
|
135
|
+
content: [
|
|
136
|
+
{
|
|
138
137
|
type: "text",
|
|
139
|
-
text: JSON.stringify(
|
|
140
|
-
}
|
|
138
|
+
text: JSON.stringify(this.collector.getMetrics(params), null, 2),
|
|
139
|
+
},
|
|
140
|
+
],
|
|
141
141
|
};
|
|
142
142
|
});
|
|
143
143
|
}
|
|
@@ -146,15 +146,14 @@ export class RuntimeLensMCPServer {
|
|
|
146
146
|
title: "Get Environment Info",
|
|
147
147
|
description: "Get comprehensive environment information including running Node.js processes, listening ports, project framework detection, and system resources.",
|
|
148
148
|
inputSchema: {},
|
|
149
|
-
}, async () => {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
});
|
|
149
|
+
}, async () => ({
|
|
150
|
+
content: [
|
|
151
|
+
{
|
|
152
|
+
type: "text",
|
|
153
|
+
text: JSON.stringify(await this.inspector.getEnvironmentInfo(), null, 2),
|
|
154
|
+
},
|
|
155
|
+
],
|
|
156
|
+
}));
|
|
158
157
|
}
|
|
159
158
|
registerClearLogs() {
|
|
160
159
|
this.server.registerTool("clear_logs", {
|
|
@@ -164,10 +163,14 @@ export class RuntimeLensMCPServer {
|
|
|
164
163
|
}, async () => {
|
|
165
164
|
const result = this.collector.clearLogs();
|
|
166
165
|
return {
|
|
167
|
-
content: [
|
|
166
|
+
content: [
|
|
167
|
+
{
|
|
168
168
|
type: "text",
|
|
169
|
-
text: JSON.stringify({
|
|
170
|
-
|
|
169
|
+
text: JSON.stringify({
|
|
170
|
+
message: `Cleared ${result.cleared} entries`,
|
|
171
|
+
}),
|
|
172
|
+
},
|
|
173
|
+
],
|
|
171
174
|
};
|
|
172
175
|
});
|
|
173
176
|
}
|
|
@@ -176,15 +179,14 @@ export class RuntimeLensMCPServer {
|
|
|
176
179
|
title: "Get Log Statistics",
|
|
177
180
|
description: "Get statistics about collected logs including counts by level and framework.",
|
|
178
181
|
inputSchema: {},
|
|
179
|
-
}, async () => {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
});
|
|
182
|
+
}, async () => ({
|
|
183
|
+
content: [
|
|
184
|
+
{
|
|
185
|
+
type: "text",
|
|
186
|
+
text: JSON.stringify(this.collector.getStats(), null, 2),
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
}));
|
|
188
190
|
}
|
|
189
191
|
registerStartInterceptor() {
|
|
190
192
|
this.server.registerTool("start_interceptor", {
|
|
@@ -194,10 +196,15 @@ export class RuntimeLensMCPServer {
|
|
|
194
196
|
}, async () => {
|
|
195
197
|
this.interceptor.startIntercepting();
|
|
196
198
|
return {
|
|
197
|
-
content: [
|
|
199
|
+
content: [
|
|
200
|
+
{
|
|
198
201
|
type: "text",
|
|
199
|
-
text: JSON.stringify({
|
|
200
|
-
|
|
202
|
+
text: JSON.stringify({
|
|
203
|
+
message: "Console interceptor started",
|
|
204
|
+
active: true,
|
|
205
|
+
}),
|
|
206
|
+
},
|
|
207
|
+
],
|
|
201
208
|
};
|
|
202
209
|
});
|
|
203
210
|
}
|
|
@@ -209,10 +216,15 @@ export class RuntimeLensMCPServer {
|
|
|
209
216
|
}, async () => {
|
|
210
217
|
this.interceptor.stopIntercepting();
|
|
211
218
|
return {
|
|
212
|
-
content: [
|
|
219
|
+
content: [
|
|
220
|
+
{
|
|
213
221
|
type: "text",
|
|
214
|
-
text: JSON.stringify({
|
|
215
|
-
|
|
222
|
+
text: JSON.stringify({
|
|
223
|
+
message: "Console interceptor stopped",
|
|
224
|
+
active: false,
|
|
225
|
+
}),
|
|
226
|
+
},
|
|
227
|
+
],
|
|
216
228
|
};
|
|
217
229
|
});
|
|
218
230
|
}
|
|
@@ -223,12 +235,16 @@ export class RuntimeLensMCPServer {
|
|
|
223
235
|
inputSchema: {},
|
|
224
236
|
}, async () => {
|
|
225
237
|
await this.collector.collectFromFiles();
|
|
226
|
-
const stats = this.collector.getStats();
|
|
227
238
|
return {
|
|
228
|
-
content: [
|
|
239
|
+
content: [
|
|
240
|
+
{
|
|
229
241
|
type: "text",
|
|
230
|
-
text: JSON.stringify({
|
|
231
|
-
|
|
242
|
+
text: JSON.stringify({
|
|
243
|
+
message: "Log collection complete",
|
|
244
|
+
stats: this.collector.getStats(),
|
|
245
|
+
}),
|
|
246
|
+
},
|
|
247
|
+
],
|
|
232
248
|
};
|
|
233
249
|
});
|
|
234
250
|
}
|
|
@@ -237,48 +253,46 @@ export class RuntimeLensMCPServer {
|
|
|
237
253
|
title: "Scan Project Structure",
|
|
238
254
|
description: "Analyze the project to detect framework (React/Next.js/NestJS), find log files, and list configuration files.",
|
|
239
255
|
inputSchema: {},
|
|
240
|
-
}, async () => {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
});
|
|
256
|
+
}, async () => ({
|
|
257
|
+
content: [
|
|
258
|
+
{
|
|
259
|
+
type: "text",
|
|
260
|
+
text: JSON.stringify(await this.collector.scanProjectStructure(), null, 2),
|
|
261
|
+
},
|
|
262
|
+
],
|
|
263
|
+
}));
|
|
249
264
|
}
|
|
250
265
|
registerFindProcesses() {
|
|
251
266
|
this.server.registerTool("find_processes", {
|
|
252
267
|
title: "Find Running Node Processes",
|
|
253
268
|
description: "Find running Node.js processes related to React, Next.js, or NestJS applications.",
|
|
254
269
|
inputSchema: {},
|
|
255
|
-
}, async () => {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
});
|
|
270
|
+
}, async () => ({
|
|
271
|
+
content: [
|
|
272
|
+
{
|
|
273
|
+
type: "text",
|
|
274
|
+
text: JSON.stringify(await this.inspector.findRunningProcesses(), null, 2),
|
|
275
|
+
},
|
|
276
|
+
],
|
|
277
|
+
}));
|
|
264
278
|
}
|
|
265
279
|
registerGetPorts() {
|
|
266
280
|
this.server.registerTool("get_listening_ports", {
|
|
267
281
|
title: "Get Listening Ports",
|
|
268
282
|
description: "List all TCP ports currently being listened on by Node.js processes.",
|
|
269
283
|
inputSchema: {},
|
|
270
|
-
}, async () => {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
});
|
|
284
|
+
}, async () => ({
|
|
285
|
+
content: [
|
|
286
|
+
{
|
|
287
|
+
type: "text",
|
|
288
|
+
text: JSON.stringify(await this.inspector.getPortListeners(), null, 2),
|
|
289
|
+
},
|
|
290
|
+
],
|
|
291
|
+
}));
|
|
279
292
|
}
|
|
280
293
|
async start() {
|
|
281
294
|
try {
|
|
295
|
+
this.agentBridge.connect();
|
|
282
296
|
const transport = new StdioServerTransport();
|
|
283
297
|
await this.server.connect(transport);
|
|
284
298
|
console.error("Runtime Lens MCP Server started successfully");
|
|
@@ -291,6 +305,7 @@ export class RuntimeLensMCPServer {
|
|
|
291
305
|
setupGracefulShutdown() {
|
|
292
306
|
const shutdown = async (signal) => {
|
|
293
307
|
console.error(`Received ${signal}, shutting down gracefully...`);
|
|
308
|
+
this.agentBridge.disconnect();
|
|
294
309
|
this.interceptor.stopIntercepting();
|
|
295
310
|
process.exit(0);
|
|
296
311
|
};
|