@graphty/remote-logger 1.2.3 → 1.3.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 +52 -1
- package/dist/bundle/browser-entry.d.ts +34 -0
- package/dist/bundle/browser-entry.d.ts.map +1 -0
- package/dist/bundle/browser-entry.js +129 -0
- package/dist/bundle/browser-entry.js.map +1 -0
- package/dist/mcp/mcp-server.d.ts +1 -1
- package/dist/mcp/mcp-server.d.ts.map +1 -1
- package/dist/mcp/mcp-server.js +48 -3
- package/dist/mcp/mcp-server.js.map +1 -1
- package/dist/mcp/tools/logs-receive.d.ts +8 -8
- package/dist/mcp/tools/logs-status.d.ts.map +1 -1
- package/dist/mcp/tools/logs-status.js +4 -1
- package/dist/mcp/tools/logs-status.js.map +1 -1
- package/dist/remote-logger.browser.js +166 -0
- package/dist/remote-logger.browser.js.map +1 -0
- package/dist/server/dual-server.d.ts.map +1 -1
- package/dist/server/dual-server.js +16 -4
- package/dist/server/dual-server.js.map +1 -1
- package/dist/server/log-server.d.ts +10 -0
- package/dist/server/log-server.d.ts.map +1 -1
- package/dist/server/log-server.js +77 -8
- package/dist/server/log-server.js.map +1 -1
- package/dist/server/log-storage.d.ts +4 -0
- package/dist/server/log-storage.d.ts.map +1 -1
- package/dist/server/log-storage.js.map +1 -1
- package/dist/server/proxy.d.ts +39 -0
- package/dist/server/proxy.d.ts.map +1 -0
- package/dist/server/proxy.js +290 -0
- package/dist/server/proxy.js.map +1 -0
- package/package.json +6 -2
- package/src/bundle/browser-entry.ts +157 -0
- package/src/mcp/mcp-server.ts +48 -3
- package/src/mcp/tools/logs-status.ts +4 -1
- package/src/server/dual-server.ts +19 -4
- package/src/server/log-server.ts +91 -8
- package/src/server/log-storage.ts +4 -0
- package/src/server/proxy.ts +357 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser bundle entry point for the remote logger.
|
|
3
|
+
*
|
|
4
|
+
* When loaded as a script tag, this auto-initializes a RemoteLogClient
|
|
5
|
+
* that intercepts console methods and forwards them to the server.
|
|
6
|
+
*
|
|
7
|
+
* Server URL detection priority:
|
|
8
|
+
* 1. window.__REMOTE_LOG_SERVER_URL__ (injected by server when serving this file)
|
|
9
|
+
* 2. document.currentScript.src (parse origin from script tag URL)
|
|
10
|
+
* 3. window.location.origin (fallback)
|
|
11
|
+
*
|
|
12
|
+
* Exposes window.__remoteLogger__ for manual control.
|
|
13
|
+
* @module bundle/browser-entry
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { RemoteLogClient } from "../client/RemoteLogClient.js";
|
|
17
|
+
import { ConsoleCaptureUI } from "../ui/ConsoleCaptureUI.js";
|
|
18
|
+
|
|
19
|
+
declare global {
|
|
20
|
+
interface Window {
|
|
21
|
+
__REMOTE_LOG_SERVER_URL__?: string;
|
|
22
|
+
__remoteLogger__?: RemoteLoggerGlobal;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
type ConsoleMethod = "log" | "warn" | "error" | "info" | "debug";
|
|
27
|
+
|
|
28
|
+
const CONSOLE_LEVEL_MAP: Record<ConsoleMethod, string> = {
|
|
29
|
+
log: "LOG",
|
|
30
|
+
warn: "WARN",
|
|
31
|
+
error: "ERROR",
|
|
32
|
+
info: "INFO",
|
|
33
|
+
debug: "DEBUG",
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const INTERCEPTED_METHODS: ConsoleMethod[] = ["log", "warn", "error", "info", "debug"];
|
|
37
|
+
|
|
38
|
+
export interface RemoteLoggerGlobal {
|
|
39
|
+
client: RemoteLogClient;
|
|
40
|
+
ui?: ConsoleCaptureUI;
|
|
41
|
+
destroy: () => void;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function detectServerUrl(): string | undefined {
|
|
45
|
+
if (typeof window !== "undefined" && window.__REMOTE_LOG_SERVER_URL__) {
|
|
46
|
+
return window.__REMOTE_LOG_SERVER_URL__;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (typeof document !== "undefined" && document.currentScript) {
|
|
50
|
+
try {
|
|
51
|
+
const scriptUrl = new URL((document.currentScript as HTMLScriptElement).src);
|
|
52
|
+
return scriptUrl.origin;
|
|
53
|
+
} catch {
|
|
54
|
+
// Invalid URL, fall through
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (typeof window !== "undefined" && window.location) {
|
|
59
|
+
return window.location.origin;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function shouldShowUI(): boolean {
|
|
66
|
+
if (typeof document === "undefined" || !document.currentScript) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
const {src} = (document.currentScript as HTMLScriptElement);
|
|
72
|
+
if (!src) {return false;}
|
|
73
|
+
const params = new URL(src).searchParams;
|
|
74
|
+
return params.get("ui") === "true";
|
|
75
|
+
} catch {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function formatArg(arg: unknown): string {
|
|
81
|
+
if (typeof arg === "string") {return arg;}
|
|
82
|
+
if (arg instanceof Error) {return `${arg.name}: ${arg.message}`;}
|
|
83
|
+
try {
|
|
84
|
+
return JSON.stringify(arg);
|
|
85
|
+
} catch {
|
|
86
|
+
return String(arg);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function formatArgs(args: unknown[]): string {
|
|
91
|
+
return args.map(formatArg).join(" ");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Initialize the remote logger, intercepting console methods.
|
|
96
|
+
* @returns The remote logger global object, or undefined if no server URL detected
|
|
97
|
+
*/
|
|
98
|
+
export function initRemoteLogger(): RemoteLoggerGlobal | undefined {
|
|
99
|
+
const serverUrl = detectServerUrl();
|
|
100
|
+
if (!serverUrl) {
|
|
101
|
+
console.warn("[RemoteLogger] Could not detect server URL. Set window.__REMOTE_LOG_SERVER_URL__ before loading.");
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const client = new RemoteLogClient({
|
|
106
|
+
serverUrl,
|
|
107
|
+
sessionPrefix: "remote",
|
|
108
|
+
batchIntervalMs: 500,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const originalMethods: Record<ConsoleMethod, typeof console.log> = {} as Record<ConsoleMethod, typeof console.log>;
|
|
112
|
+
|
|
113
|
+
for (const method of INTERCEPTED_METHODS) {
|
|
114
|
+
// eslint-disable-next-line no-console
|
|
115
|
+
originalMethods[method] = console[method];
|
|
116
|
+
|
|
117
|
+
// eslint-disable-next-line no-console
|
|
118
|
+
console[method] = (...args: unknown[]) => {
|
|
119
|
+
originalMethods[method].apply(console, args);
|
|
120
|
+
const message = formatArgs(args);
|
|
121
|
+
client.log(CONSOLE_LEVEL_MAP[method], message);
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
let ui: ConsoleCaptureUI | undefined;
|
|
126
|
+
if (shouldShowUI()) {
|
|
127
|
+
ui = new ConsoleCaptureUI();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const destroy = (): void => {
|
|
131
|
+
for (const method of INTERCEPTED_METHODS) {
|
|
132
|
+
// eslint-disable-next-line no-console
|
|
133
|
+
console[method] = originalMethods[method];
|
|
134
|
+
}
|
|
135
|
+
void client.close();
|
|
136
|
+
if (ui) {
|
|
137
|
+
ui.destroy();
|
|
138
|
+
}
|
|
139
|
+
if (typeof window !== "undefined") {
|
|
140
|
+
delete window.__remoteLogger__;
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const global: RemoteLoggerGlobal = { client, ui, destroy };
|
|
145
|
+
|
|
146
|
+
if (typeof window !== "undefined") {
|
|
147
|
+
window.__remoteLogger__ = global;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return global;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Auto-initialize on load
|
|
154
|
+
initRemoteLogger();
|
|
155
|
+
|
|
156
|
+
// Re-export for manual use via the IIFE global
|
|
157
|
+
export { ConsoleCaptureUI,RemoteLogClient };
|
package/src/mcp/mcp-server.ts
CHANGED
|
@@ -108,7 +108,29 @@ logger.interceptConsole();
|
|
|
108
108
|
logger.log("INFO", "Hello from browser!");
|
|
109
109
|
\`\`\`
|
|
110
110
|
|
|
111
|
-
### Option 2:
|
|
111
|
+
### Option 2: Script tag (zero install)
|
|
112
|
+
|
|
113
|
+
The server serves a browser-ready script that auto-configures itself.
|
|
114
|
+
Get the script URL from logs_status - look for server.scriptUrl in the response.
|
|
115
|
+
|
|
116
|
+
Add to HTML:
|
|
117
|
+
\`\`\`html
|
|
118
|
+
<script src="http://localhost:9080/remote-logger.js"></script>
|
|
119
|
+
\`\`\`
|
|
120
|
+
|
|
121
|
+
Or paste in browser console:
|
|
122
|
+
\`\`\`javascript
|
|
123
|
+
var s=document.createElement('script');s.src='http://localhost:9080/remote-logger.js';document.head.appendChild(s);
|
|
124
|
+
\`\`\`
|
|
125
|
+
|
|
126
|
+
The script automatically:
|
|
127
|
+
- Intercepts all console.log/warn/error/info/debug calls
|
|
128
|
+
- Sends them to the server it was loaded from (zero config)
|
|
129
|
+
- Exposes window.__remoteLogger__ for manual control (e.g., window.__remoteLogger__.destroy())
|
|
130
|
+
|
|
131
|
+
Add ?ui=true to the script URL to show a floating console capture widget.
|
|
132
|
+
|
|
133
|
+
### Option 3: Raw fetch() calls
|
|
112
134
|
|
|
113
135
|
\`\`\`typescript
|
|
114
136
|
fetch("http://localhost:9080/log", {
|
|
@@ -123,6 +145,28 @@ fetch("http://localhost:9080/log", {
|
|
|
123
145
|
});
|
|
124
146
|
\`\`\`
|
|
125
147
|
|
|
148
|
+
## Debugging Third-Party Websites
|
|
149
|
+
|
|
150
|
+
The server includes a reverse proxy that injects the remote logger into any website.
|
|
151
|
+
Get the proxy base URL from logs_status - look for server.proxyBaseUrl.
|
|
152
|
+
|
|
153
|
+
To debug a third-party site, prepend the proxy base URL to the target URL:
|
|
154
|
+
{proxyBaseUrl}https://example.com/page
|
|
155
|
+
|
|
156
|
+
Example: http://192.168.1.x:9080/proxy/https://example.com
|
|
157
|
+
|
|
158
|
+
The proxy automatically:
|
|
159
|
+
- Injects the remote-logger script into HTML responses
|
|
160
|
+
- Strips Content-Security-Policy headers that would block the script
|
|
161
|
+
- Forwards cookies, auth headers, and other request data
|
|
162
|
+
- Passes non-HTML resources (CSS, JS, images) through unmodified
|
|
163
|
+
|
|
164
|
+
Limitations:
|
|
165
|
+
- Resources using absolute paths (e.g., /fonts/..., /media/...) bypass the base tag and return 404
|
|
166
|
+
- JavaScript fetch() calls using absolute paths may bypass the proxy
|
|
167
|
+
- OAuth redirect flows that check the origin domain will not work
|
|
168
|
+
- WebSocket connections are not proxied (future enhancement)
|
|
169
|
+
|
|
126
170
|
## Querying Logs (MCP Tools)
|
|
127
171
|
|
|
128
172
|
Once browser logs are flowing to the server:
|
|
@@ -138,8 +182,9 @@ Once browser logs are flowing to the server:
|
|
|
138
182
|
|
|
139
183
|
## Typical Debugging Workflow
|
|
140
184
|
|
|
141
|
-
1. Call logs_status to verify server is running and get the endpoint URL
|
|
142
|
-
2.
|
|
185
|
+
1. Call logs_status to verify server is running and get the endpoint URL, script URL, and proxy URL
|
|
186
|
+
2. Add logging to the browser app using one of the options above (script tag is simplest)
|
|
187
|
+
- For third-party sites, use the proxy: navigate to {proxyBaseUrl}https://target-site.com
|
|
143
188
|
3. Trigger the action in the browser you want to debug
|
|
144
189
|
4. Call logs_get_recent to see what happened
|
|
145
190
|
5. Use logs_search if looking for specific errors or messages`;
|
|
@@ -61,9 +61,12 @@ export const logsStatusTool = {
|
|
|
61
61
|
description:
|
|
62
62
|
"Get the status of the remote log server. " +
|
|
63
63
|
"Returns health metrics (uptime, session count, log count, memory usage), " +
|
|
64
|
-
"HTTP endpoint configuration (port, host, URL for browser clients
|
|
64
|
+
"HTTP endpoint configuration (port, host, URL for browser clients, script URL for zero-config injection, " +
|
|
65
|
+
"proxy base URL for debugging third-party sites), " +
|
|
65
66
|
"and retention settings (how long logs are kept before automatic cleanup). " +
|
|
66
67
|
"Use this to verify the server is running, find the endpoint URL for configuring browser clients, " +
|
|
68
|
+
"get the script URL (server.scriptUrl) for injecting via a script tag, " +
|
|
69
|
+
"get the proxy URL (server.proxyBaseUrl) for debugging third-party sites, " +
|
|
67
70
|
"or check server configuration.",
|
|
68
71
|
inputSchema: logsStatusInputSchema,
|
|
69
72
|
};
|
|
@@ -17,8 +17,9 @@ import * as path from "path";
|
|
|
17
17
|
|
|
18
18
|
import { createMcpServer } from "../mcp/mcp-server.js";
|
|
19
19
|
import { JsonlWriter } from "./jsonl-writer.js";
|
|
20
|
-
import { createLogServer } from "./log-server.js";
|
|
20
|
+
import { createLogServer, setProxy } from "./log-server.js";
|
|
21
21
|
import { LogStorage, type ServerMode } from "./log-storage.js";
|
|
22
|
+
import { createProxy, type ProxyInstance } from "./proxy.js";
|
|
22
23
|
import { certFilesExist } from "./self-signed-cert.js";
|
|
23
24
|
|
|
24
25
|
// ANSI color codes for terminal output
|
|
@@ -291,6 +292,7 @@ export async function createDualServer(options: DualServerOptions = {}): Promise
|
|
|
291
292
|
let httpServer: http.Server | https.Server | undefined;
|
|
292
293
|
let mcpServer: McpServer | undefined;
|
|
293
294
|
let actualHttpPort: number | undefined;
|
|
295
|
+
let proxyInstance: ProxyInstance | undefined;
|
|
294
296
|
|
|
295
297
|
// Track active connections for graceful shutdown
|
|
296
298
|
const activeConnections = new Set<net.Socket>();
|
|
@@ -332,9 +334,8 @@ export async function createDualServer(options: DualServerOptions = {}): Promise
|
|
|
332
334
|
});
|
|
333
335
|
});
|
|
334
336
|
|
|
335
|
-
|
|
336
|
-
httpServer.
|
|
337
|
-
httpServer.headersTimeout = 2000;
|
|
337
|
+
httpServer.keepAliveTimeout = 30000;
|
|
338
|
+
httpServer.headersTimeout = 35000;
|
|
338
339
|
|
|
339
340
|
// Set server config in storage so MCP tools can report it
|
|
340
341
|
// Determine protocol based on whether valid cert files were provided
|
|
@@ -366,13 +367,22 @@ export async function createDualServer(options: DualServerOptions = {}): Promise
|
|
|
366
367
|
}
|
|
367
368
|
}
|
|
368
369
|
}
|
|
370
|
+
const scriptUrl = `${protocol}://${endpointHost}:${actualHttpPort}/remote-logger.js`;
|
|
371
|
+
const proxyBaseUrl = `/proxy/`;
|
|
372
|
+
|
|
369
373
|
storage.setServerConfig({
|
|
370
374
|
httpPort: actualHttpPort,
|
|
371
375
|
httpHost,
|
|
372
376
|
protocol,
|
|
373
377
|
httpEndpoint: `${protocol}://${endpointHost}:${actualHttpPort}/log`,
|
|
378
|
+
scriptUrl,
|
|
379
|
+
proxyBaseUrl: `${protocol}://${endpointHost}:${actualHttpPort}${proxyBaseUrl}`,
|
|
374
380
|
mode,
|
|
375
381
|
});
|
|
382
|
+
|
|
383
|
+
// Create and register the proxy instance
|
|
384
|
+
proxyInstance = createProxy(proxyBaseUrl, quiet);
|
|
385
|
+
setProxy(proxyInstance);
|
|
376
386
|
}
|
|
377
387
|
|
|
378
388
|
// Create and connect MCP server if enabled
|
|
@@ -400,6 +410,11 @@ export async function createDualServer(options: DualServerOptions = {}): Promise
|
|
|
400
410
|
});
|
|
401
411
|
}
|
|
402
412
|
|
|
413
|
+
// Close proxy
|
|
414
|
+
if (proxyInstance) {
|
|
415
|
+
proxyInstance.close();
|
|
416
|
+
}
|
|
417
|
+
|
|
403
418
|
// Close JSONL writer only if we created it internally
|
|
404
419
|
if (ownsJsonlWriter) {
|
|
405
420
|
await jsonlWriter.close();
|
package/src/server/log-server.ts
CHANGED
|
@@ -18,10 +18,11 @@ import * as http from "http";
|
|
|
18
18
|
import * as https from "https";
|
|
19
19
|
import * as os from "os";
|
|
20
20
|
import * as path from "path";
|
|
21
|
-
import { URL } from "url";
|
|
21
|
+
import { fileURLToPath,URL } from "url";
|
|
22
22
|
|
|
23
23
|
import { JsonlWriter } from "./jsonl-writer.js";
|
|
24
24
|
import { type LogEntry, LogStorage } from "./log-storage.js";
|
|
25
|
+
import type { ProxyInstance } from "./proxy.js";
|
|
25
26
|
import { certFilesExist, readCertFiles } from "./self-signed-cert.js";
|
|
26
27
|
|
|
27
28
|
// Shared log storage instance
|
|
@@ -62,6 +63,55 @@ export function setLogStorage(storage: LogStorage): void {
|
|
|
62
63
|
sharedStorage = storage;
|
|
63
64
|
}
|
|
64
65
|
|
|
66
|
+
// Shared proxy instance
|
|
67
|
+
let sharedProxy: ProxyInstance | null = null;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Set the shared proxy instance.
|
|
71
|
+
* @param proxy - The proxy instance to use
|
|
72
|
+
*/
|
|
73
|
+
export function setProxy(proxy: ProxyInstance): void {
|
|
74
|
+
sharedProxy = proxy;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
// Browser bundle cache (loaded on first request)
|
|
79
|
+
let browserBundleCache: string | null | undefined;
|
|
80
|
+
|
|
81
|
+
function loadBrowserBundle(): string | null {
|
|
82
|
+
if (browserBundleCache !== undefined) {
|
|
83
|
+
return browserBundleCache;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const thisDir = path.dirname(fileURLToPath(import.meta.url));
|
|
87
|
+
|
|
88
|
+
// Try multiple locations: dist/ (when running from built output)
|
|
89
|
+
// and the package root dist/ (when running from source during tests)
|
|
90
|
+
const candidates = [
|
|
91
|
+
path.resolve(thisDir, "..", "remote-logger.browser.js"),
|
|
92
|
+
path.resolve(thisDir, "..", "..", "dist", "remote-logger.browser.js"),
|
|
93
|
+
];
|
|
94
|
+
|
|
95
|
+
for (const candidate of candidates) {
|
|
96
|
+
try {
|
|
97
|
+
browserBundleCache = fs.readFileSync(candidate, "utf-8");
|
|
98
|
+
return browserBundleCache;
|
|
99
|
+
} catch {
|
|
100
|
+
// Try next candidate
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
browserBundleCache = null;
|
|
105
|
+
return browserBundleCache;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Reset the browser bundle cache. Used for testing.
|
|
110
|
+
*/
|
|
111
|
+
export function resetBrowserBundleCache(): void {
|
|
112
|
+
browserBundleCache = undefined;
|
|
113
|
+
}
|
|
114
|
+
|
|
65
115
|
// ANSI color codes for terminal output
|
|
66
116
|
const colors = {
|
|
67
117
|
reset: "\x1b[0m",
|
|
@@ -269,7 +319,36 @@ function handleRequest(
|
|
|
269
319
|
return;
|
|
270
320
|
}
|
|
271
321
|
|
|
272
|
-
//
|
|
322
|
+
// Serve browser-ready script bundle (available in all modes including logReceiveOnly)
|
|
323
|
+
if ((url === "/remote-logger.js" || url.startsWith("/remote-logger.js?")) && req.method === "GET") {
|
|
324
|
+
const bundle = loadBrowserBundle();
|
|
325
|
+
if (!bundle) {
|
|
326
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
327
|
+
res.end(JSON.stringify({ error: "Browser bundle not found. Run 'npm run build' first." }));
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const hostHeader = req.headers.host ?? `${host}:${port}`;
|
|
332
|
+
const serverUrl = `${protocol}://${hostHeader}`;
|
|
333
|
+
const configPrefix = `window.__REMOTE_LOG_SERVER_URL__="${serverUrl}";\n`;
|
|
334
|
+
|
|
335
|
+
res.writeHead(200, {
|
|
336
|
+
"Content-Type": "application/javascript",
|
|
337
|
+
"Cache-Control": "no-cache",
|
|
338
|
+
});
|
|
339
|
+
res.end(configPrefix + bundle);
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Reverse proxy with auto-injection (available in all modes including logReceiveOnly)
|
|
344
|
+
if (url.startsWith("/proxy/") && sharedProxy) {
|
|
345
|
+
const targetUrl = decodeURIComponent(url.substring("/proxy/".length));
|
|
346
|
+
const hostHeader = req.headers.host ?? `${host}:${port}`;
|
|
347
|
+
sharedProxy.handleRequest(req, res, targetUrl, protocol, hostHeader);
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// In logReceiveOnly mode, only /log, /health, /remote-logger.js, and /proxy/ are available
|
|
273
352
|
if (logReceiveOnly) {
|
|
274
353
|
res.writeHead(404, { "Content-Type": "application/json" });
|
|
275
354
|
res.end(JSON.stringify({ error: "Not found (log receive only mode)" }));
|
|
@@ -377,17 +456,21 @@ function printBanner(host: string, port: number, useHttps: boolean): void {
|
|
|
377
456
|
// eslint-disable-next-line no-console
|
|
378
457
|
console.log(`${colors.yellow}API Endpoints:${colors.reset}`);
|
|
379
458
|
// eslint-disable-next-line no-console
|
|
380
|
-
console.log(` ${colors.cyan}POST /log
|
|
459
|
+
console.log(` ${colors.cyan}POST /log ${colors.reset} - Receive logs from browser`);
|
|
460
|
+
// eslint-disable-next-line no-console
|
|
461
|
+
console.log(` ${colors.cyan}GET /remote-logger.js ${colors.reset} - Browser-ready auto-config script`);
|
|
462
|
+
// eslint-disable-next-line no-console
|
|
463
|
+
console.log(` ${colors.cyan}* /proxy/<url> ${colors.reset} - Reverse proxy with script injection`);
|
|
381
464
|
// eslint-disable-next-line no-console
|
|
382
|
-
console.log(` ${colors.cyan}GET /logs
|
|
465
|
+
console.log(` ${colors.cyan}GET /logs ${colors.reset} - Get all logs as JSON`);
|
|
383
466
|
// eslint-disable-next-line no-console
|
|
384
|
-
console.log(` ${colors.cyan}GET /logs/recent
|
|
467
|
+
console.log(` ${colors.cyan}GET /logs/recent ${colors.reset} - Get last 50 logs (?n=100 for more)`);
|
|
385
468
|
// eslint-disable-next-line no-console
|
|
386
|
-
console.log(` ${colors.cyan}GET /logs/errors
|
|
469
|
+
console.log(` ${colors.cyan}GET /logs/errors ${colors.reset} - Get only error logs`);
|
|
387
470
|
// eslint-disable-next-line no-console
|
|
388
|
-
console.log(` ${colors.cyan}POST /logs/clear
|
|
471
|
+
console.log(` ${colors.cyan}POST /logs/clear ${colors.reset} - Clear all logs`);
|
|
389
472
|
// eslint-disable-next-line no-console
|
|
390
|
-
console.log(` ${colors.cyan}GET /health
|
|
473
|
+
console.log(` ${colors.cyan}GET /health ${colors.reset} - Health check`);
|
|
391
474
|
// eslint-disable-next-line no-console
|
|
392
475
|
console.log("");
|
|
393
476
|
// eslint-disable-next-line no-console
|
|
@@ -141,6 +141,10 @@ export interface ServerConfig {
|
|
|
141
141
|
protocol: "http" | "https";
|
|
142
142
|
/** Full URL for browser clients to send logs to */
|
|
143
143
|
httpEndpoint: string;
|
|
144
|
+
/** URL to the browser-ready auto-config script */
|
|
145
|
+
scriptUrl?: string;
|
|
146
|
+
/** Base URL for the reverse proxy (e.g., "http://host:port/proxy/") */
|
|
147
|
+
proxyBaseUrl?: string;
|
|
144
148
|
/** Server mode (mcp-only, http-only, or dual) */
|
|
145
149
|
mode: ServerMode;
|
|
146
150
|
}
|