@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.
Files changed (37) hide show
  1. package/README.md +52 -1
  2. package/dist/bundle/browser-entry.d.ts +34 -0
  3. package/dist/bundle/browser-entry.d.ts.map +1 -0
  4. package/dist/bundle/browser-entry.js +129 -0
  5. package/dist/bundle/browser-entry.js.map +1 -0
  6. package/dist/mcp/mcp-server.d.ts +1 -1
  7. package/dist/mcp/mcp-server.d.ts.map +1 -1
  8. package/dist/mcp/mcp-server.js +48 -3
  9. package/dist/mcp/mcp-server.js.map +1 -1
  10. package/dist/mcp/tools/logs-receive.d.ts +8 -8
  11. package/dist/mcp/tools/logs-status.d.ts.map +1 -1
  12. package/dist/mcp/tools/logs-status.js +4 -1
  13. package/dist/mcp/tools/logs-status.js.map +1 -1
  14. package/dist/remote-logger.browser.js +166 -0
  15. package/dist/remote-logger.browser.js.map +1 -0
  16. package/dist/server/dual-server.d.ts.map +1 -1
  17. package/dist/server/dual-server.js +16 -4
  18. package/dist/server/dual-server.js.map +1 -1
  19. package/dist/server/log-server.d.ts +10 -0
  20. package/dist/server/log-server.d.ts.map +1 -1
  21. package/dist/server/log-server.js +77 -8
  22. package/dist/server/log-server.js.map +1 -1
  23. package/dist/server/log-storage.d.ts +4 -0
  24. package/dist/server/log-storage.d.ts.map +1 -1
  25. package/dist/server/log-storage.js.map +1 -1
  26. package/dist/server/proxy.d.ts +39 -0
  27. package/dist/server/proxy.d.ts.map +1 -0
  28. package/dist/server/proxy.js +290 -0
  29. package/dist/server/proxy.js.map +1 -0
  30. package/package.json +6 -2
  31. package/src/bundle/browser-entry.ts +157 -0
  32. package/src/mcp/mcp-server.ts +48 -3
  33. package/src/mcp/tools/logs-status.ts +4 -1
  34. package/src/server/dual-server.ts +19 -4
  35. package/src/server/log-server.ts +91 -8
  36. package/src/server/log-storage.ts +4 -0
  37. package/src/server/proxy.ts +357 -0
@@ -0,0 +1,166 @@
1
+ var RemoteLogger=(function(l){"use strict";function a(s){try{if(s==="__REMOTE_LOG_PROJECT_MARKER__")return typeof __REMOTE_LOG_PROJECT_MARKER__<"u"?__REMOTE_LOG_PROJECT_MARKER__:void 0;if(s==="__REMOTE_LOG_WORKTREE_PATH__")return typeof __REMOTE_LOG_WORKTREE_PATH__<"u"?__REMOTE_LOG_WORKTREE_PATH__:void 0}catch{return}}const f=1e3,m=3,y=1e3,b="session",w=5e3;function _(s){const e=Date.now().toString(36),t=Math.random().toString(36).substring(2,8);return`${s}-${e}-${t}`}class u{constructor(e){this.pendingLogs=[],this.batchTimer=null,this.isClosed=!1,this.flushPromise=null,this.throttleLastTimes=new Map,this.serverUrl=e.serverUrl,this.sessionId=_(e.sessionPrefix??b),this.batchIntervalMs=e.batchIntervalMs??f,this.maxRetries=e.maxRetries??m,this.retryDelayMs=e.retryDelayMs??y,this.throttlePatterns=e.throttlePatterns??[],this.timeoutMs=e.timeoutMs??w,this.projectMarker=e.projectMarker??a("__REMOTE_LOG_PROJECT_MARKER__"),this.worktreePath=e.worktreePath??a("__REMOTE_LOG_WORKTREE_PATH__")}shouldThrottle(e){const t=Date.now();for(const{pattern:o,intervalMs:i}of this.throttlePatterns)if(o.test(e)){const d=o.source,r=this.throttleLastTimes.get(d)??0;return t-r<i?!0:(this.throttleLastTimes.set(d,t),!1)}return!1}log(e,t,o){if(this.isClosed||this.shouldThrottle(t))return;const i={time:new Date().toISOString(),level:e,message:t,...o!==void 0&&{data:o}};this.pendingLogs.push(i),this.scheduleBatchSend()}scheduleBatchSend(){this.batchTimer!==null||this.isClosed||(this.batchTimer=setTimeout(()=>{this.batchTimer=null,this.sendBatch()},this.batchIntervalMs))}async sendBatch(){if(this.pendingLogs.length===0)return;const e=this.pendingLogs;this.pendingLogs=[],await this.sendWithRetry(e)}async sendWithRetry(e){let t=null;for(let o=0;o<=this.maxRetries;o++)try{await this.sendRequest(e);return}catch(i){if(t=i instanceof Error?i:new Error(String(i)),o<this.maxRetries){const d=this.retryDelayMs*Math.pow(2,o);await this.sleep(d)}}console.error("[RemoteLogClient] Failed to send logs after retries:",t?.message)}async sendRequest(e){const t={sessionId:this.sessionId,logs:e};this.projectMarker!==void 0&&(t.projectMarker=this.projectMarker),this.worktreePath!==void 0&&(t.worktreePath=this.worktreePath);const o=new AbortController,i=setTimeout(()=>{o.abort()},this.timeoutMs);try{const d=await fetch(`${this.serverUrl}/log`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t),signal:o.signal});if(!d.ok)throw new Error(`HTTP ${d.status}: ${d.statusText}`)}finally{clearTimeout(i)}}sleep(e){return new Promise(t=>setTimeout(t,e))}async flush(){this.batchTimer!==null&&(clearTimeout(this.batchTimer),this.batchTimer=null),this.flushPromise!==null&&await this.flushPromise,this.pendingLogs.length>0&&(this.flushPromise=this.sendBatch(),await this.flushPromise,this.flushPromise=null)}async close(){this.isClosed||(this.isClosed=!0,await this.flush())}}const n=globalThis.console;class h{constructor(){this.logs=[],this.buttonContainer=null,this.originalMethods={log:n.log.bind(n),error:n.error.bind(n),warn:n.warn.bind(n),info:n.info.bind(n),debug:n.debug.bind(n)},this.interceptConsole(),this.createUI(),this.setupGlobalMethods()}interceptConsole(){const e=["log","error","warn","info","debug"];for(const t of e){const o=this.originalMethods[t];n[t]=(...i)=>{this.logs.push({type:t,args:i,timestamp:new Date().toISOString()}),this.updateButtonBadge(),o.apply(n,i)}}}createUI(){if(typeof document>"u")return;this.buttonContainer=document.createElement("div"),this.buttonContainer.id="console-capture-container",this.buttonContainer.innerHTML=`
2
+ <button id="console-capture-btn" style="
3
+ position: fixed;
4
+ top: 20px;
5
+ right: 20px;
6
+ width: 32px;
7
+ height: 32px;
8
+ border-radius: 16px;
9
+ background: rgba(128, 128, 128, 0.3);
10
+ color: rgba(255, 255, 255, 0.8);
11
+ border: 1px solid rgba(255, 255, 255, 0.2);
12
+ cursor: pointer;
13
+ box-shadow: 0 1px 4px rgba(0,0,0,0.1);
14
+ z-index: 99999;
15
+ font-size: 14px;
16
+ display: flex;
17
+ align-items: center;
18
+ justify-content: center;
19
+ transition: all 0.2s;
20
+ opacity: 0.6;
21
+ ">📋</button>
22
+ <div id="console-capture-menu" style="
23
+ position: fixed;
24
+ top: 60px;
25
+ right: 20px;
26
+ background: white;
27
+ border-radius: 8px;
28
+ box-shadow: 0 2px 20px rgba(0,0,0,0.2);
29
+ padding: 10px;
30
+ z-index: 99998;
31
+ display: none;
32
+ ">
33
+ <button id="cc-copy" style="
34
+ display: block;
35
+ width: 100%;
36
+ padding: 8px 16px;
37
+ margin: 4px 0;
38
+ border: none;
39
+ background: #4CAF50;
40
+ color: white;
41
+ border-radius: 4px;
42
+ cursor: pointer;
43
+ ">📋 Copy Logs</button>
44
+ <button id="cc-download" style="
45
+ display: block;
46
+ width: 100%;
47
+ padding: 8px 16px;
48
+ margin: 4px 0;
49
+ border: none;
50
+ background: #FF9800;
51
+ color: white;
52
+ border-radius: 4px;
53
+ cursor: pointer;
54
+ ">💾 Download</button>
55
+ <button id="cc-clear" style="
56
+ display: block;
57
+ width: 100%;
58
+ padding: 8px 16px;
59
+ margin: 4px 0;
60
+ border: none;
61
+ background: #f44336;
62
+ color: white;
63
+ border-radius: 4px;
64
+ cursor: pointer;
65
+ ">🗑️ Clear</button>
66
+ <button id="cc-show" style="
67
+ display: block;
68
+ width: 100%;
69
+ padding: 8px 16px;
70
+ margin: 4px 0;
71
+ border: none;
72
+ background: #9C27B0;
73
+ color: white;
74
+ border-radius: 4px;
75
+ cursor: pointer;
76
+ ">👁️ Show Logs</button>
77
+ <div style="
78
+ margin-top: 10px;
79
+ padding-top: 10px;
80
+ border-top: 1px solid #ddd;
81
+ font-size: 12px;
82
+ color: #666;
83
+ text-align: center;
84
+ ">
85
+ <div>Logs: <span id="cc-count">0</span></div>
86
+ </div>
87
+ </div>
88
+ `,document.body.appendChild(this.buttonContainer);const e=document.getElementById("console-capture-btn"),t=document.getElementById("console-capture-menu");if(e&&t){e.addEventListener("mouseenter",()=>{e.style.opacity="1",e.style.background="rgba(128, 128, 128, 0.5)"}),e.addEventListener("mouseleave",()=>{e.style.opacity="0.6",e.style.background="rgba(128, 128, 128, 0.3)"}),e.addEventListener("click",()=>{t.style.display=t.style.display==="none"?"block":"none"});const o=document.getElementById("cc-copy"),i=document.getElementById("cc-download"),d=document.getElementById("cc-clear"),r=document.getElementById("cc-show");o&&o.addEventListener("click",()=>{this.copyLogs(),t.style.display="none"}),i&&i.addEventListener("click",()=>{this.downloadLogs(),t.style.display="none"}),d&&d.addEventListener("click",()=>{this.clearLogs(),t.style.display="none"}),r&&r.addEventListener("click",()=>{this.showLogsModal(),t.style.display="none"}),document.addEventListener("click",c=>{this.buttonContainer&&!this.buttonContainer.contains(c.target)&&(t.style.display="none")})}}updateButtonBadge(){if(typeof document>"u")return;const e=document.getElementById("cc-count");e&&(e.textContent=String(this.logs.length))}formatLog(e){const t=e.args.map(o=>{if(typeof o=="string")return o;if(o instanceof Error)return`${o.name}: ${o.message}
89
+ ${o.stack??""}`;try{return JSON.stringify(o,null,2)}catch{return String(o)}}).join(" ");return`[${e.timestamp}] [${e.type.toUpperCase()}] ${t}`}setupGlobalMethods(){if(typeof window<"u"){const e=()=>[...this.logs];window.__console__={copy:()=>this.copyLogs(),download:()=>{this.downloadLogs()},clear:()=>{this.clearLogs()},get:()=>this.getLogs(),get logs(){return e()}}}}showCopySuccess(){if(typeof document>"u")return;const e=document.getElementById("console-capture-btn");e&&(e.innerHTML="✅",setTimeout(()=>{e.innerHTML="📋"},1500)),n.log("📋 Console logs copied to clipboard!")}showCopyError(){if(typeof document>"u")return;const e=document.getElementById("console-capture-btn");e&&(e.innerHTML="❌",setTimeout(()=>{e.innerHTML="📋"},1500)),n.log("❌ Failed to copy to clipboard. Here are your logs:"),n.log("═".repeat(50)),n.log(this.getLogs()),n.log("═".repeat(50))}copyUsingTextarea(e){if(typeof document>"u")return;const t=document.createElement("textarea");t.value=e,t.style.cssText="position: absolute; left: -9999px; top: -9999px;",document.body.appendChild(t),t.select(),t.setSelectionRange(0,99999);try{document.execCommand("copy")?this.showCopySuccess():this.showCopyError()}catch{this.showCopyError()}finally{document.body.removeChild(t)}}showLogsModal(){if(typeof document>"u")return;const e=document.getElementById("console-logs-modal");e&&e.remove();const t=document.createElement("div");t.id="console-logs-modal",t.innerHTML=`
90
+ <div style="
91
+ position: fixed;
92
+ top: 0;
93
+ left: 0;
94
+ right: 0;
95
+ bottom: 0;
96
+ background: rgba(0,0,0,0.5);
97
+ z-index: 100001;
98
+ display: flex;
99
+ align-items: center;
100
+ justify-content: center;
101
+ ">
102
+ <div style="
103
+ background: white;
104
+ border-radius: 8px;
105
+ width: 90%;
106
+ max-width: 800px;
107
+ height: 85vh;
108
+ max-height: 85vh;
109
+ display: flex;
110
+ flex-direction: column;
111
+ box-shadow: 0 4px 20px rgba(0,0,0,0.3);
112
+ ">
113
+ <div style="
114
+ padding: 16px;
115
+ border-bottom: 1px solid #ddd;
116
+ display: flex;
117
+ justify-content: space-between;
118
+ align-items: center;
119
+ ">
120
+ <h3 style="margin: 0; color: #333;">Console Logs</h3>
121
+ <button id="close-modal" style="
122
+ background: none;
123
+ border: none;
124
+ font-size: 24px;
125
+ cursor: pointer;
126
+ color: #666;
127
+ ">×</button>
128
+ </div>
129
+ <textarea id="logs-textarea" style="
130
+ flex: 1;
131
+ margin: 16px;
132
+ padding: 12px;
133
+ border: 1px solid #ddd;
134
+ border-radius: 4px;
135
+ font-family: monospace;
136
+ font-size: 12px;
137
+ resize: none;
138
+ background: #f5f5f5;
139
+ min-height: 50vh;
140
+ overflow-y: auto;
141
+ " readonly>${this.getLogs()}</textarea>
142
+ <div style="
143
+ padding: 16px;
144
+ border-top: 1px solid #ddd;
145
+ text-align: center;
146
+ ">
147
+ <button id="select-all-logs" style="
148
+ padding: 8px 24px;
149
+ background: #2196F3;
150
+ color: white;
151
+ border: none;
152
+ border-radius: 4px;
153
+ cursor: pointer;
154
+ font-size: 14px;
155
+ ">Select All</button>
156
+ <span style="
157
+ margin-left: 16px;
158
+ color: #666;
159
+ font-size: 14px;
160
+ ">Press Ctrl+C (or Cmd+C) to copy</span>
161
+ </div>
162
+ </div>
163
+ </div>
164
+ `,document.body.appendChild(t);const o=document.getElementById("close-modal");o&&o.addEventListener("click",()=>{t.remove()}),t.addEventListener("click",r=>{r.target===t.firstElementChild&&t.remove()});const i=document.getElementById("logs-textarea"),d=document.getElementById("select-all-logs");d&&i&&d.addEventListener("click",()=>{i.select(),i.focus()}),i&&setTimeout(()=>{i.select(),i.focus()},100)}getLogs(){return this.logs.map(e=>this.formatLog(e)).join(`
165
+ `)}clearLogs(){this.logs=[],this.updateButtonBadge(),n.log("🗑️ Console logs cleared")}async copyLogs(){const e=this.getLogs();try{typeof navigator<"u"&&navigator.clipboard&&navigator.clipboard.writeText?(await navigator.clipboard.writeText(e),this.showCopySuccess()):this.copyUsingTextarea(e)}catch{n.warn("Clipboard API failed, using fallback"),this.copyUsingTextarea(e)}}downloadLogs(){if(typeof document>"u")return;const e=this.getLogs(),t=new Blob([e],{type:"text/plain"}),o=URL.createObjectURL(t),d=`console-logs-${new Date().toISOString().replace(/[:.]/g,"-")}.txt`,r=document.createElement("a");r.href=o,r.download=d,document.body.appendChild(r),r.click(),document.body.removeChild(r),URL.revokeObjectURL(o),n.log("💾 Console logs downloaded!")}destroy(){if(n.log=this.originalMethods.log,n.error=this.originalMethods.error,n.warn=this.originalMethods.warn,n.info=this.originalMethods.info,n.debug=this.originalMethods.debug,typeof document<"u"&&this.buttonContainer&&this.buttonContainer.parentNode&&this.buttonContainer.parentNode.removeChild(this.buttonContainer),typeof document<"u"){const e=document.getElementById("console-logs-modal");e&&e.remove()}typeof window<"u"&&delete window.__console__}}const E={log:"LOG",warn:"WARN",error:"ERROR",info:"INFO",debug:"DEBUG"},p=["log","warn","error","info","debug"];function x(){if(typeof window<"u"&&window.__REMOTE_LOG_SERVER_URL__)return window.__REMOTE_LOG_SERVER_URL__;if(typeof document<"u"&&document.currentScript)try{return new URL(document.currentScript.src).origin}catch{}if(typeof window<"u"&&window.location)return window.location.origin}function L(){if(typeof document>"u"||!document.currentScript)return!1;try{const{src:s}=document.currentScript;return s?new URL(s).searchParams.get("ui")==="true":!1}catch{return!1}}function T(s){if(typeof s=="string")return s;if(s instanceof Error)return`${s.name}: ${s.message}`;try{return JSON.stringify(s)}catch{return String(s)}}function R(s){return s.map(T).join(" ")}function g(){const s=x();if(!s){console.warn("[RemoteLogger] Could not detect server URL. Set window.__REMOTE_LOG_SERVER_URL__ before loading.");return}const e=new u({serverUrl:s,sessionPrefix:"remote",batchIntervalMs:500}),t={};for(const r of p)t[r]=console[r],console[r]=(...c)=>{t[r].apply(console,c);const v=R(c);e.log(E[r],v)};let o;L()&&(o=new h);const d={client:e,ui:o,destroy:()=>{for(const r of p)console[r]=t[r];e.close(),o&&o.destroy(),typeof window<"u"&&delete window.__remoteLogger__}};return typeof window<"u"&&(window.__remoteLogger__=d),d}return g(),l.ConsoleCaptureUI=h,l.RemoteLogClient=u,l.initRemoteLogger=g,Object.defineProperty(l,Symbol.toStringTag,{value:"Module"}),l})({});
166
+ //# sourceMappingURL=remote-logger.browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote-logger.browser.js","sources":["../src/client/RemoteLogClient.ts","../src/ui/ConsoleCaptureUI.ts","../src/bundle/browser-entry.ts"],"sourcesContent":["/**\n * Browser client for sending log messages to a remote server.\n * Provides batching and automatic retry with exponential backoff.\n * @module client/RemoteLogClient\n */\n\nimport type { LogEntry, RemoteLogClientOptions, ThrottlePattern } from \"./types.js\";\n\n/**\n * Global variables that may be injected by the Vite plugin.\n * These provide automatic project marker detection.\n */\ndeclare const __REMOTE_LOG_PROJECT_MARKER__: string | undefined;\ndeclare const __REMOTE_LOG_WORKTREE_PATH__: string | undefined;\n\n/**\n * Safely read a global variable that may or may not be defined.\n * @param name - Name of the global variable\n * @returns The value of the global variable, or undefined if not defined\n */\nfunction getGlobalValue(name: \"__REMOTE_LOG_PROJECT_MARKER__\" | \"__REMOTE_LOG_WORKTREE_PATH__\"): string | undefined {\n try {\n if (name === \"__REMOTE_LOG_PROJECT_MARKER__\") {\n return typeof __REMOTE_LOG_PROJECT_MARKER__ !== \"undefined\" ? __REMOTE_LOG_PROJECT_MARKER__ : undefined;\n }\n if (name === \"__REMOTE_LOG_WORKTREE_PATH__\") {\n return typeof __REMOTE_LOG_WORKTREE_PATH__ !== \"undefined\" ? __REMOTE_LOG_WORKTREE_PATH__ : undefined;\n }\n } catch {\n // ReferenceError if the global is not defined at all\n return undefined;\n }\n return undefined;\n}\n\n/** Default configuration values */\nconst DEFAULT_BATCH_INTERVAL_MS = 1000;\nconst DEFAULT_MAX_RETRIES = 3;\nconst DEFAULT_RETRY_DELAY_MS = 1000;\nconst DEFAULT_SESSION_PREFIX = \"session\";\nconst DEFAULT_TIMEOUT_MS = 5000;\n\n/**\n * Generates a unique session ID with the given prefix.\n * Format: {prefix}-{timestamp}-{random}\n * @param prefix - The prefix to use for the session ID\n * @returns A unique session ID string\n */\nfunction generateSessionId(prefix: string): string {\n const timestamp = Date.now().toString(36);\n const random = Math.random().toString(36).substring(2, 8);\n return `${prefix}-${timestamp}-${random}`;\n}\n\n/**\n * Client for sending log messages to a remote logging server.\n *\n * Features:\n * - Batches multiple log entries before sending\n * - Automatic retry with exponential backoff on network failures\n * - Unique session ID for correlating logs\n * @example\n * ```typescript\n * const client = new RemoteLogClient({\n * serverUrl: \"http://localhost:9080\",\n * sessionPrefix: \"myapp\",\n * });\n *\n * client.log(\"INFO\", \"User logged in\", { userId: 123 });\n * client.log(\"DEBUG\", \"Loading data...\");\n *\n * // Flush immediately when needed\n * await client.flush();\n *\n * // Clean up when done\n * await client.close();\n * ```\n */\nexport class RemoteLogClient {\n /** Unique identifier for this logging session */\n readonly sessionId: string;\n\n private readonly serverUrl: string;\n private readonly batchIntervalMs: number;\n private readonly maxRetries: number;\n private readonly retryDelayMs: number;\n private readonly throttlePatterns: ThrottlePattern[];\n private readonly projectMarker: string | undefined;\n private readonly worktreePath: string | undefined;\n private readonly timeoutMs: number;\n\n private pendingLogs: LogEntry[] = [];\n private batchTimer: ReturnType<typeof setTimeout> | null = null;\n private isClosed = false;\n private flushPromise: Promise<void> | null = null;\n\n /** Tracks when each throttle pattern was last allowed through */\n private throttleLastTimes = new Map<string, number>();\n\n /**\n * Creates a new RemoteLogClient.\n * @param options - Configuration options\n */\n constructor(options: RemoteLogClientOptions) {\n this.serverUrl = options.serverUrl;\n this.sessionId = generateSessionId(options.sessionPrefix ?? DEFAULT_SESSION_PREFIX);\n this.batchIntervalMs = options.batchIntervalMs ?? DEFAULT_BATCH_INTERVAL_MS;\n this.maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;\n this.retryDelayMs = options.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS;\n this.throttlePatterns = options.throttlePatterns ?? [];\n this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\n // Priority: explicit option > global variable\n this.projectMarker = options.projectMarker ?? getGlobalValue(\"__REMOTE_LOG_PROJECT_MARKER__\");\n this.worktreePath = options.worktreePath ?? getGlobalValue(\"__REMOTE_LOG_WORKTREE_PATH__\");\n }\n\n /**\n * Checks if a message should be throttled based on configured patterns.\n * Updates the last time tracking if the message is allowed through.\n * @param message - The message to check\n * @returns true if the message should be dropped (throttled)\n */\n private shouldThrottle(message: string): boolean {\n const now = Date.now();\n\n for (const { pattern, intervalMs } of this.throttlePatterns) {\n if (pattern.test(message)) {\n const key = pattern.source;\n const lastTime = this.throttleLastTimes.get(key) ?? 0;\n\n if (now - lastTime < intervalMs) {\n // Within throttle window, drop this message\n return true;\n }\n\n // Outside throttle window, update timestamp and allow through\n this.throttleLastTimes.set(key, now);\n return false;\n }\n }\n\n // No matching pattern, allow through\n return false;\n }\n\n /**\n * Logs a message at the specified level.\n * @param level - Log level (e.g., \"INFO\", \"DEBUG\", \"WARN\", \"ERROR\")\n * @param message - The log message\n * @param data - Optional additional data to include\n */\n log(level: string, message: string, data?: Record<string, unknown>): void {\n if (this.isClosed) {\n return;\n }\n\n // Check throttling before adding to buffer\n if (this.shouldThrottle(message)) {\n return;\n }\n\n const entry: LogEntry = {\n time: new Date().toISOString(),\n level,\n message,\n ...(data !== undefined && { data }),\n };\n\n this.pendingLogs.push(entry);\n this.scheduleBatchSend();\n }\n\n /**\n * Schedules a batch send after the configured interval.\n * If a timer is already scheduled, does nothing.\n */\n private scheduleBatchSend(): void {\n if (this.batchTimer !== null || this.isClosed) {\n return;\n }\n\n this.batchTimer = setTimeout(() => {\n this.batchTimer = null;\n void this.sendBatch();\n }, this.batchIntervalMs);\n }\n\n /**\n * Sends the current batch of logs to the server.\n * Uses retry logic with exponential backoff on failure.\n */\n private async sendBatch(): Promise<void> {\n if (this.pendingLogs.length === 0) {\n return;\n }\n\n // Take the current logs and clear the pending array\n const logsToSend = this.pendingLogs;\n this.pendingLogs = [];\n\n await this.sendWithRetry(logsToSend);\n }\n\n /**\n * Sends logs with retry logic using exponential backoff.\n * @param logs - The log entries to send\n */\n private async sendWithRetry(logs: LogEntry[]): Promise<void> {\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\n try {\n await this.sendRequest(logs);\n return; // Success!\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n\n if (attempt < this.maxRetries) {\n // Exponential backoff: delay * 2^attempt\n const delay = this.retryDelayMs * Math.pow(2, attempt);\n await this.sleep(delay);\n }\n }\n }\n\n // All retries exhausted - log the error but don't throw\n // In a browser context, we don't want to break the application\n console.error(\"[RemoteLogClient] Failed to send logs after retries:\", lastError?.message);\n }\n\n /**\n * Makes the actual HTTP request to send logs.\n * @param logs - The log entries to send\n * @throws Error if the request fails or times out\n */\n private async sendRequest(logs: LogEntry[]): Promise<void> {\n const requestBody: {\n sessionId: string;\n logs: LogEntry[];\n projectMarker?: string;\n worktreePath?: string;\n } = {\n sessionId: this.sessionId,\n logs,\n };\n\n // Only include these fields if they have values\n if (this.projectMarker !== undefined) {\n requestBody.projectMarker = this.projectMarker;\n }\n if (this.worktreePath !== undefined) {\n requestBody.worktreePath = this.worktreePath;\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => { controller.abort(); }, this.timeoutMs);\n\n try {\n const response = await fetch(`${this.serverUrl}/log`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(requestBody),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n /**\n * Helper to sleep for the specified duration.\n * @param ms - Milliseconds to sleep\n * @returns A promise that resolves after the specified time\n */\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n /**\n * Immediately flushes all pending logs to the server.\n * Useful before page unload or when immediate delivery is needed.\n */\n async flush(): Promise<void> {\n // Cancel any scheduled batch\n if (this.batchTimer !== null) {\n clearTimeout(this.batchTimer);\n this.batchTimer = null;\n }\n\n // Wait for any in-progress flush\n if (this.flushPromise !== null) {\n await this.flushPromise;\n }\n\n // Send any remaining logs\n if (this.pendingLogs.length > 0) {\n this.flushPromise = this.sendBatch();\n await this.flushPromise;\n this.flushPromise = null;\n }\n }\n\n /**\n * Closes the client, flushing any pending logs and stopping the batch timer.\n * After calling close(), no more logs will be accepted.\n */\n async close(): Promise<void> {\n if (this.isClosed) {\n return;\n }\n\n this.isClosed = true;\n\n // Flush remaining logs\n await this.flush();\n }\n}\n\n/**\n * Factory function to create a RemoteLogClient.\n * @param options - Configuration options\n * @returns A new RemoteLogClient instance\n * @example\n * ```typescript\n * const client = createRemoteLogClient({\n * serverUrl: \"http://localhost:9080\",\n * });\n * ```\n */\nexport function createRemoteLogClient(options: RemoteLogClientOptions): RemoteLogClient {\n return new RemoteLogClient(options);\n}\n","/**\n * ConsoleCaptureUI - Console capture with floating UI widget\n *\n * Intercepts console output (log, error, warn, info, debug) and provides\n * a floating button UI with copy, download, view, and clear functionality.\n *\n * Features:\n * - Floating button in top-right corner\n * - Menu with Copy, Download, Clear, and Show Logs options\n * - Works on mobile devices and in XR environments\n * - Global window.__console__ methods for programmatic access\n *\n * Usage:\n * import { initConsoleCaptureUI } from \"@graphty/remote-logger/ui\";\n * initConsoleCaptureUI();\n *\n * Or programmatically:\n * const ui = new ConsoleCaptureUI();\n * console.log(\"This is captured\");\n * ui.destroy(); // Clean up when done\n */\n\n// Extended window interface for global console methods\ndeclare global {\n interface Window {\n __console__?: {\n copy: () => Promise<void>;\n download: () => void;\n clear: () => void;\n get: () => string;\n logs: CapturedLogEntry[];\n };\n }\n}\n\n/** A single captured log entry */\ninterface CapturedLogEntry {\n type: string;\n args: unknown[];\n timestamp: string;\n}\n\n/** Console method names we intercept */\ntype ConsoleMethod = \"log\" | \"error\" | \"warn\" | \"info\" | \"debug\";\n\n// Get reference to the global console object\nconst globalConsole = globalThis.console;\n\n/**\n * ConsoleCaptureUI class - captures console output and provides floating UI widget\n */\nexport class ConsoleCaptureUI {\n private logs: CapturedLogEntry[] = [];\n private originalMethods: Record<ConsoleMethod, typeof globalConsole.log>;\n private buttonContainer: HTMLElement | null = null;\n\n /**\n * Creates a new ConsoleCaptureUI instance that intercepts console methods\n * and creates a floating UI widget\n */\n constructor() {\n // Store original methods at construction time\n this.originalMethods = {\n log: globalConsole.log.bind(globalConsole),\n error: globalConsole.error.bind(globalConsole),\n warn: globalConsole.warn.bind(globalConsole),\n info: globalConsole.info.bind(globalConsole),\n debug: globalConsole.debug.bind(globalConsole),\n };\n\n this.interceptConsole();\n this.createUI();\n this.setupGlobalMethods();\n }\n\n /**\n * Intercept all console methods to capture output\n */\n private interceptConsole(): void {\n const methods: ConsoleMethod[] = [\n \"log\",\n \"error\",\n \"warn\",\n \"info\",\n \"debug\",\n ];\n for (const method of methods) {\n const original = this.originalMethods[method];\n\n // Override the console method - this is intentional for this module\n globalConsole[method] = (...args: unknown[]): void => {\n // Capture the log entry\n this.logs.push({\n type: method,\n args: args,\n timestamp: new Date().toISOString(),\n });\n this.updateButtonBadge();\n // Still call the original method\n original.apply(globalConsole, args);\n };\n }\n }\n\n /**\n * Create the floating UI button and menu\n */\n private createUI(): void {\n if (typeof document === \"undefined\") {\n return;\n }\n\n // Create floating button container\n this.buttonContainer = document.createElement(\"div\");\n this.buttonContainer.id = \"console-capture-container\";\n this.buttonContainer.innerHTML = `\n <button id=\"console-capture-btn\" style=\"\n position: fixed;\n top: 20px;\n right: 20px;\n width: 32px;\n height: 32px;\n border-radius: 16px;\n background: rgba(128, 128, 128, 0.3);\n color: rgba(255, 255, 255, 0.8);\n border: 1px solid rgba(255, 255, 255, 0.2);\n cursor: pointer;\n box-shadow: 0 1px 4px rgba(0,0,0,0.1);\n z-index: 99999;\n font-size: 14px;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s;\n opacity: 0.6;\n \">📋</button>\n <div id=\"console-capture-menu\" style=\"\n position: fixed;\n top: 60px;\n right: 20px;\n background: white;\n border-radius: 8px;\n box-shadow: 0 2px 20px rgba(0,0,0,0.2);\n padding: 10px;\n z-index: 99998;\n display: none;\n \">\n <button id=\"cc-copy\" style=\"\n display: block;\n width: 100%;\n padding: 8px 16px;\n margin: 4px 0;\n border: none;\n background: #4CAF50;\n color: white;\n border-radius: 4px;\n cursor: pointer;\n \">📋 Copy Logs</button>\n <button id=\"cc-download\" style=\"\n display: block;\n width: 100%;\n padding: 8px 16px;\n margin: 4px 0;\n border: none;\n background: #FF9800;\n color: white;\n border-radius: 4px;\n cursor: pointer;\n \">💾 Download</button>\n <button id=\"cc-clear\" style=\"\n display: block;\n width: 100%;\n padding: 8px 16px;\n margin: 4px 0;\n border: none;\n background: #f44336;\n color: white;\n border-radius: 4px;\n cursor: pointer;\n \">🗑️ Clear</button>\n <button id=\"cc-show\" style=\"\n display: block;\n width: 100%;\n padding: 8px 16px;\n margin: 4px 0;\n border: none;\n background: #9C27B0;\n color: white;\n border-radius: 4px;\n cursor: pointer;\n \">👁️ Show Logs</button>\n <div style=\"\n margin-top: 10px;\n padding-top: 10px;\n border-top: 1px solid #ddd;\n font-size: 12px;\n color: #666;\n text-align: center;\n \">\n <div>Logs: <span id=\"cc-count\">0</span></div>\n </div>\n </div>\n `;\n\n document.body.appendChild(this.buttonContainer);\n\n // Add event listeners\n const btn = document.getElementById(\"console-capture-btn\");\n const menu = document.getElementById(\"console-capture-menu\");\n\n if (btn && menu) {\n // Add hover effect to make button more visible on hover\n btn.addEventListener(\"mouseenter\", () => {\n btn.style.opacity = \"1\";\n btn.style.background = \"rgba(128, 128, 128, 0.5)\";\n });\n\n btn.addEventListener(\"mouseleave\", () => {\n btn.style.opacity = \"0.6\";\n btn.style.background = \"rgba(128, 128, 128, 0.3)\";\n });\n\n btn.addEventListener(\"click\", () => {\n menu.style.display =\n menu.style.display === \"none\" ? \"block\" : \"none\";\n });\n\n const copyBtn = document.getElementById(\"cc-copy\");\n const downloadBtn = document.getElementById(\"cc-download\");\n const clearBtn = document.getElementById(\"cc-clear\");\n const showBtn = document.getElementById(\"cc-show\");\n\n if (copyBtn) {\n copyBtn.addEventListener(\"click\", () => {\n void this.copyLogs();\n menu.style.display = \"none\";\n });\n }\n\n if (downloadBtn) {\n downloadBtn.addEventListener(\"click\", () => {\n this.downloadLogs();\n menu.style.display = \"none\";\n });\n }\n\n if (clearBtn) {\n clearBtn.addEventListener(\"click\", () => {\n this.clearLogs();\n menu.style.display = \"none\";\n });\n }\n\n if (showBtn) {\n showBtn.addEventListener(\"click\", () => {\n this.showLogsModal();\n menu.style.display = \"none\";\n });\n }\n\n // Close menu when clicking outside\n document.addEventListener(\"click\", (e) => {\n if (\n this.buttonContainer &&\n !this.buttonContainer.contains(e.target as Node)\n ) {\n menu.style.display = \"none\";\n }\n });\n }\n }\n\n /**\n * Update the log count badge in the menu\n */\n private updateButtonBadge(): void {\n if (typeof document === \"undefined\") {\n return;\n }\n const count = document.getElementById(\"cc-count\");\n if (count) {\n count.textContent = String(this.logs.length);\n }\n }\n\n /**\n * Format a single log entry as a string\n * @param log - The log entry to format\n * @returns Formatted log string\n */\n private formatLog(log: CapturedLogEntry): string {\n const args = log.args\n .map((arg) => {\n if (typeof arg === \"string\") {\n return arg;\n }\n if (arg instanceof Error) {\n return `${arg.name}: ${arg.message}\\n${arg.stack ?? \"\"}`;\n }\n try {\n return JSON.stringify(arg, null, 2);\n } catch {\n return String(arg);\n }\n })\n .join(\" \");\n\n return `[${log.timestamp}] [${log.type.toUpperCase()}] ${args}`;\n }\n\n /**\n * Setup global window.__console__ methods for easy access\n */\n private setupGlobalMethods(): void {\n if (typeof window !== \"undefined\") {\n // Create a function that returns current logs to avoid stale reference\n const getCurrentLogs = (): CapturedLogEntry[] => [...this.logs];\n window.__console__ = {\n copy: () => this.copyLogs(),\n download: () => {\n this.downloadLogs();\n },\n clear: () => {\n this.clearLogs();\n },\n get: () => this.getLogs(),\n // Use getter to always return fresh copy of current logs\n get logs() {\n return getCurrentLogs();\n },\n };\n }\n }\n\n /**\n * Show success indicator on the button\n */\n private showCopySuccess(): void {\n if (typeof document === \"undefined\") {\n return;\n }\n const btn = document.getElementById(\"console-capture-btn\");\n if (btn) {\n btn.innerHTML = \"✅\";\n setTimeout(() => {\n btn.innerHTML = \"📋\";\n }, 1500);\n }\n globalConsole.log(\"📋 Console logs copied to clipboard!\");\n }\n\n /**\n * Show error indicator on the button and fallback message\n */\n private showCopyError(): void {\n if (typeof document === \"undefined\") {\n return;\n }\n const btn = document.getElementById(\"console-capture-btn\");\n if (btn) {\n btn.innerHTML = \"❌\";\n setTimeout(() => {\n btn.innerHTML = \"📋\";\n }, 1500);\n }\n\n // Show logs in console as fallback\n globalConsole.log(\n \"❌ Failed to copy to clipboard. Here are your logs:\"\n );\n globalConsole.log(\"═\".repeat(50));\n globalConsole.log(this.getLogs());\n globalConsole.log(\"═\".repeat(50));\n }\n\n /**\n * Fallback copy method using textarea (for mobile devices)\n * @param text - The text to copy\n */\n private copyUsingTextarea(text: string): void {\n if (typeof document === \"undefined\") {\n return;\n }\n // Create temporary textarea\n const textarea = document.createElement(\"textarea\");\n textarea.value = text;\n textarea.style.cssText =\n \"position: absolute; left: -9999px; top: -9999px;\";\n document.body.appendChild(textarea);\n\n // Select and copy\n textarea.select();\n textarea.setSelectionRange(0, 99999); // For mobile devices\n\n try {\n // execCommand is deprecated but still necessary as a fallback for:\n // - Older mobile browsers that don't support Clipboard API\n // - iOS Safari in certain contexts where Clipboard API fails\n // - XR/VR browsers with limited API support\n // This provides the best compatibility across all target platforms\n // eslint-disable-next-line @typescript-eslint/no-deprecated -- Required fallback for mobile/XR compatibility\n const success = document.execCommand(\"copy\");\n if (success) {\n this.showCopySuccess();\n } else {\n this.showCopyError();\n }\n } catch {\n this.showCopyError();\n } finally {\n document.body.removeChild(textarea);\n }\n }\n\n /**\n * Show a modal dialog with all captured logs\n */\n private showLogsModal(): void {\n if (typeof document === \"undefined\") {\n return;\n }\n\n // Remove existing modal if any\n const existing = document.getElementById(\"console-logs-modal\");\n if (existing) {\n existing.remove();\n }\n\n const modal = document.createElement(\"div\");\n modal.id = \"console-logs-modal\";\n modal.innerHTML = `\n <div style=\"\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0,0,0,0.5);\n z-index: 100001;\n display: flex;\n align-items: center;\n justify-content: center;\n \">\n <div style=\"\n background: white;\n border-radius: 8px;\n width: 90%;\n max-width: 800px;\n height: 85vh;\n max-height: 85vh;\n display: flex;\n flex-direction: column;\n box-shadow: 0 4px 20px rgba(0,0,0,0.3);\n \">\n <div style=\"\n padding: 16px;\n border-bottom: 1px solid #ddd;\n display: flex;\n justify-content: space-between;\n align-items: center;\n \">\n <h3 style=\"margin: 0; color: #333;\">Console Logs</h3>\n <button id=\"close-modal\" style=\"\n background: none;\n border: none;\n font-size: 24px;\n cursor: pointer;\n color: #666;\n \">×</button>\n </div>\n <textarea id=\"logs-textarea\" style=\"\n flex: 1;\n margin: 16px;\n padding: 12px;\n border: 1px solid #ddd;\n border-radius: 4px;\n font-family: monospace;\n font-size: 12px;\n resize: none;\n background: #f5f5f5;\n min-height: 50vh;\n overflow-y: auto;\n \" readonly>${this.getLogs()}</textarea>\n <div style=\"\n padding: 16px;\n border-top: 1px solid #ddd;\n text-align: center;\n \">\n <button id=\"select-all-logs\" style=\"\n padding: 8px 24px;\n background: #2196F3;\n color: white;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n font-size: 14px;\n \">Select All</button>\n <span style=\"\n margin-left: 16px;\n color: #666;\n font-size: 14px;\n \">Press Ctrl+C (or Cmd+C) to copy</span>\n </div>\n </div>\n </div>\n `;\n\n document.body.appendChild(modal);\n\n // Event listeners\n const closeBtn = document.getElementById(\"close-modal\");\n if (closeBtn) {\n closeBtn.addEventListener(\"click\", () => {\n modal.remove();\n });\n }\n\n modal.addEventListener(\"click\", (e) => {\n if (e.target === modal.firstElementChild) {\n modal.remove();\n }\n });\n\n const textarea = document.getElementById(\n \"logs-textarea\"\n ) as HTMLTextAreaElement | null;\n const selectAllBtn = document.getElementById(\"select-all-logs\");\n\n if (selectAllBtn && textarea) {\n selectAllBtn.addEventListener(\"click\", () => {\n textarea.select();\n textarea.focus();\n });\n }\n\n // Auto-select on open\n if (textarea) {\n setTimeout(() => {\n textarea.select();\n textarea.focus();\n }, 100);\n }\n }\n\n /**\n * Get all captured logs as a formatted string\n * @returns Formatted log string with timestamps and levels\n */\n getLogs(): string {\n return this.logs.map((log) => this.formatLog(log)).join(\"\\n\");\n }\n\n /**\n * Clear all captured logs\n */\n clearLogs(): void {\n this.logs = [];\n this.updateButtonBadge();\n globalConsole.log(\"🗑️ Console logs cleared\");\n }\n\n /**\n * Copy logs to clipboard\n */\n async copyLogs(): Promise<void> {\n const text = this.getLogs();\n\n try {\n // Try modern clipboard API first\n if (\n typeof navigator !== \"undefined\" &&\n navigator.clipboard &&\n navigator.clipboard.writeText\n ) {\n await navigator.clipboard.writeText(text);\n this.showCopySuccess();\n } else {\n // Fallback method using textarea\n this.copyUsingTextarea(text);\n }\n } catch {\n // If modern API fails, try fallback\n globalConsole.warn(\"Clipboard API failed, using fallback\");\n this.copyUsingTextarea(text);\n }\n }\n\n /**\n * Download logs as a text file\n */\n downloadLogs(): void {\n if (typeof document === \"undefined\") {\n return;\n }\n\n const text = this.getLogs();\n const blob = new Blob([text], { type: \"text/plain\" });\n const url = URL.createObjectURL(blob);\n const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n const filename = `console-logs-${timestamp}.txt`;\n\n const link = document.createElement(\"a\");\n link.href = url;\n link.download = filename;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n\n URL.revokeObjectURL(url);\n globalConsole.log(\"💾 Console logs downloaded!\");\n }\n\n /**\n * Restore original console methods and clean up UI\n */\n destroy(): void {\n // Restore original console methods\n globalConsole.log = this.originalMethods.log;\n globalConsole.error = this.originalMethods.error;\n globalConsole.warn = this.originalMethods.warn;\n globalConsole.info = this.originalMethods.info;\n globalConsole.debug = this.originalMethods.debug;\n\n // Remove UI elements\n if (\n typeof document !== \"undefined\" &&\n this.buttonContainer &&\n this.buttonContainer.parentNode\n ) {\n this.buttonContainer.parentNode.removeChild(this.buttonContainer);\n }\n\n // Remove modal if open\n if (typeof document !== \"undefined\") {\n const modal = document.getElementById(\"console-logs-modal\");\n if (modal) {\n modal.remove();\n }\n }\n\n // Remove global methods\n if (typeof window !== \"undefined\") {\n delete window.__console__;\n }\n }\n}\n\n/**\n * Convenience function to initialize ConsoleCaptureUI\n * @returns A new ConsoleCaptureUI instance\n */\nexport function initConsoleCaptureUI(): ConsoleCaptureUI {\n return new ConsoleCaptureUI();\n}\n","/**\n * Browser bundle entry point for the remote logger.\n *\n * When loaded as a script tag, this auto-initializes a RemoteLogClient\n * that intercepts console methods and forwards them to the server.\n *\n * Server URL detection priority:\n * 1. window.__REMOTE_LOG_SERVER_URL__ (injected by server when serving this file)\n * 2. document.currentScript.src (parse origin from script tag URL)\n * 3. window.location.origin (fallback)\n *\n * Exposes window.__remoteLogger__ for manual control.\n * @module bundle/browser-entry\n */\n\nimport { RemoteLogClient } from \"../client/RemoteLogClient.js\";\nimport { ConsoleCaptureUI } from \"../ui/ConsoleCaptureUI.js\";\n\ndeclare global {\n interface Window {\n __REMOTE_LOG_SERVER_URL__?: string;\n __remoteLogger__?: RemoteLoggerGlobal;\n }\n}\n\ntype ConsoleMethod = \"log\" | \"warn\" | \"error\" | \"info\" | \"debug\";\n\nconst CONSOLE_LEVEL_MAP: Record<ConsoleMethod, string> = {\n log: \"LOG\",\n warn: \"WARN\",\n error: \"ERROR\",\n info: \"INFO\",\n debug: \"DEBUG\",\n};\n\nconst INTERCEPTED_METHODS: ConsoleMethod[] = [\"log\", \"warn\", \"error\", \"info\", \"debug\"];\n\nexport interface RemoteLoggerGlobal {\n client: RemoteLogClient;\n ui?: ConsoleCaptureUI;\n destroy: () => void;\n}\n\nfunction detectServerUrl(): string | undefined {\n if (typeof window !== \"undefined\" && window.__REMOTE_LOG_SERVER_URL__) {\n return window.__REMOTE_LOG_SERVER_URL__;\n }\n\n if (typeof document !== \"undefined\" && document.currentScript) {\n try {\n const scriptUrl = new URL((document.currentScript as HTMLScriptElement).src);\n return scriptUrl.origin;\n } catch {\n // Invalid URL, fall through\n }\n }\n\n if (typeof window !== \"undefined\" && window.location) {\n return window.location.origin;\n }\n\n return undefined;\n}\n\nfunction shouldShowUI(): boolean {\n if (typeof document === \"undefined\" || !document.currentScript) {\n return false;\n }\n\n try {\n const {src} = (document.currentScript as HTMLScriptElement);\n if (!src) {return false;}\n const params = new URL(src).searchParams;\n return params.get(\"ui\") === \"true\";\n } catch {\n return false;\n }\n}\n\nfunction formatArg(arg: unknown): string {\n if (typeof arg === \"string\") {return arg;}\n if (arg instanceof Error) {return `${arg.name}: ${arg.message}`;}\n try {\n return JSON.stringify(arg);\n } catch {\n return String(arg);\n }\n}\n\nfunction formatArgs(args: unknown[]): string {\n return args.map(formatArg).join(\" \");\n}\n\n/**\n * Initialize the remote logger, intercepting console methods.\n * @returns The remote logger global object, or undefined if no server URL detected\n */\nexport function initRemoteLogger(): RemoteLoggerGlobal | undefined {\n const serverUrl = detectServerUrl();\n if (!serverUrl) {\n console.warn(\"[RemoteLogger] Could not detect server URL. Set window.__REMOTE_LOG_SERVER_URL__ before loading.\");\n return undefined;\n }\n\n const client = new RemoteLogClient({\n serverUrl,\n sessionPrefix: \"remote\",\n batchIntervalMs: 500,\n });\n\n const originalMethods: Record<ConsoleMethod, typeof console.log> = {} as Record<ConsoleMethod, typeof console.log>;\n\n for (const method of INTERCEPTED_METHODS) {\n // eslint-disable-next-line no-console\n originalMethods[method] = console[method];\n\n // eslint-disable-next-line no-console\n console[method] = (...args: unknown[]) => {\n originalMethods[method].apply(console, args);\n const message = formatArgs(args);\n client.log(CONSOLE_LEVEL_MAP[method], message);\n };\n }\n\n let ui: ConsoleCaptureUI | undefined;\n if (shouldShowUI()) {\n ui = new ConsoleCaptureUI();\n }\n\n const destroy = (): void => {\n for (const method of INTERCEPTED_METHODS) {\n // eslint-disable-next-line no-console\n console[method] = originalMethods[method];\n }\n void client.close();\n if (ui) {\n ui.destroy();\n }\n if (typeof window !== \"undefined\") {\n delete window.__remoteLogger__;\n }\n };\n\n const global: RemoteLoggerGlobal = { client, ui, destroy };\n\n if (typeof window !== \"undefined\") {\n window.__remoteLogger__ = global;\n }\n\n return global;\n}\n\n// Auto-initialize on load\ninitRemoteLogger();\n\n// Re-export for manual use via the IIFE global\nexport { ConsoleCaptureUI,RemoteLogClient };\n"],"names":["getGlobalValue","name","DEFAULT_BATCH_INTERVAL_MS","DEFAULT_MAX_RETRIES","DEFAULT_RETRY_DELAY_MS","DEFAULT_SESSION_PREFIX","DEFAULT_TIMEOUT_MS","generateSessionId","prefix","timestamp","random","RemoteLogClient","options","message","now","pattern","intervalMs","key","lastTime","level","data","entry","logsToSend","logs","lastError","attempt","error","delay","requestBody","controller","timeoutId","response","ms","resolve","globalConsole","ConsoleCaptureUI","methods","method","original","args","btn","menu","copyBtn","downloadBtn","clearBtn","showBtn","e","count","log","arg","getCurrentLogs","text","textarea","existing","modal","closeBtn","selectAllBtn","blob","url","filename","link","CONSOLE_LEVEL_MAP","INTERCEPTED_METHODS","detectServerUrl","shouldShowUI","src","formatArg","formatArgs","initRemoteLogger","serverUrl","client","originalMethods","ui","global"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"dual-server.d.ts","sourceRoot":"","sources":["../../src/server/dual-server.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAO/B,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,EAAE,UAAU,EAAmB,MAAM,kBAAkB,CAAC;AAqI/D;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,CACnC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,KAAK,GAAE,OAAe,EACtB,OAAO,GAAE,MAAwB,GAClC,OAAO,CAAC,MAAM,CAAC,CA0CjB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAC9B,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wCAAwC;IACxC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,yCAAyC;IACzC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,gDAAgD;IAChD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,2FAA2F;IAC3F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2FAA2F;IAC3F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+CAA+C;IAC/C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,UAAU,CAAC;IACrB,iFAAiF;IACjF,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,sEAAsE;IACtE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,wDAAwD;IACxD,aAAa,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC7B,qDAAqD;IACrD,UAAU,CAAC,EAAE,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IACxC,0CAA0C;IAC1C,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,kCAAkC;IAClC,OAAO,EAAE,UAAU,CAAC;IACpB,4BAA4B;IAC5B,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iCAAiC;IACjC,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACjC;AAED;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAyJjG"}
1
+ {"version":3,"file":"dual-server.d.ts","sourceRoot":"","sources":["../../src/server/dual-server.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAO/B,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,EAAE,UAAU,EAAmB,MAAM,kBAAkB,CAAC;AAsI/D;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,CACnC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,KAAK,GAAE,OAAe,EACtB,OAAO,GAAE,MAAwB,GAClC,OAAO,CAAC,MAAM,CAAC,CA0CjB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAC9B,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wCAAwC;IACxC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,yCAAyC;IACzC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,gDAAgD;IAChD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,2FAA2F;IAC3F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2FAA2F;IAC3F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+CAA+C;IAC/C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,UAAU,CAAC;IACrB,iFAAiF;IACjF,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,sEAAsE;IACtE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,wDAAwD;IACxD,aAAa,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC7B,qDAAqD;IACrD,UAAU,CAAC,EAAE,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IACxC,0CAA0C;IAC1C,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,kCAAkC;IAClC,OAAO,EAAE,UAAU,CAAC;IACpB,4BAA4B;IAC5B,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iCAAiC;IACjC,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACjC;AAED;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAuKjG"}
@@ -12,8 +12,9 @@ import * as os from "os";
12
12
  import * as path from "path";
13
13
  import { createMcpServer } from "../mcp/mcp-server.js";
14
14
  import { JsonlWriter } from "./jsonl-writer.js";
15
- import { createLogServer } from "./log-server.js";
15
+ import { createLogServer, setProxy } from "./log-server.js";
16
16
  import { LogStorage } from "./log-storage.js";
17
+ import { createProxy } from "./proxy.js";
17
18
  import { certFilesExist } from "./self-signed-cert.js";
18
19
  // ANSI color codes for terminal output
19
20
  const colors = {
@@ -180,6 +181,7 @@ export async function createDualServer(options = {}) {
180
181
  let httpServer;
181
182
  let mcpServer;
182
183
  let actualHttpPort;
184
+ let proxyInstance;
183
185
  // Track active connections for graceful shutdown
184
186
  const activeConnections = new Set();
185
187
  // Start HTTP server if enabled
@@ -209,9 +211,8 @@ export async function createDualServer(options = {}) {
209
211
  activeConnections.delete(socket);
210
212
  });
211
213
  });
212
- // Set short keep-alive timeout for tests (connections close faster)
213
- httpServer.keepAliveTimeout = 1000;
214
- httpServer.headersTimeout = 2000;
214
+ httpServer.keepAliveTimeout = 30000;
215
+ httpServer.headersTimeout = 35000;
215
216
  // Set server config in storage so MCP tools can report it
216
217
  // Determine protocol based on whether valid cert files were provided
217
218
  const useHttps = certPath && keyPath && certFilesExist(certPath, keyPath);
@@ -243,13 +244,20 @@ export async function createDualServer(options = {}) {
243
244
  }
244
245
  }
245
246
  }
247
+ const scriptUrl = `${protocol}://${endpointHost}:${actualHttpPort}/remote-logger.js`;
248
+ const proxyBaseUrl = `/proxy/`;
246
249
  storage.setServerConfig({
247
250
  httpPort: actualHttpPort,
248
251
  httpHost,
249
252
  protocol,
250
253
  httpEndpoint: `${protocol}://${endpointHost}:${actualHttpPort}/log`,
254
+ scriptUrl,
255
+ proxyBaseUrl: `${protocol}://${endpointHost}:${actualHttpPort}${proxyBaseUrl}`,
251
256
  mode,
252
257
  });
258
+ // Create and register the proxy instance
259
+ proxyInstance = createProxy(proxyBaseUrl, quiet);
260
+ setProxy(proxyInstance);
253
261
  }
254
262
  // Create and connect MCP server if enabled
255
263
  if (mcpEnabled) {
@@ -272,6 +280,10 @@ export async function createDualServer(options = {}) {
272
280
  httpServer.close(() => { resolve(); });
273
281
  });
274
282
  }
283
+ // Close proxy
284
+ if (proxyInstance) {
285
+ proxyInstance.close();
286
+ }
275
287
  // Close JSONL writer only if we created it internally
276
288
  if (ownsJsonlWriter) {
277
289
  await jsonlWriter.close();
@@ -1 +1 @@
1
- {"version":3,"file":"dual-server.js","sourceRoot":"","sources":["../../src/server/dual-server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dual-server.js","sourceRoot":"","sources":["../../src/server/dual-server.ts"],"names":[],"mappings":"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"}
@@ -16,6 +16,7 @@ import * as http from "http";
16
16
  import * as https from "https";
17
17
  import { JsonlWriter } from "./jsonl-writer.js";
18
18
  import { LogStorage } from "./log-storage.js";
19
+ import type { ProxyInstance } from "./proxy.js";
19
20
  /**
20
21
  * Get the shared LogStorage instance, creating it if needed.
21
22
  * Creates a JsonlWriter for JSONL file streaming by default.
@@ -32,6 +33,15 @@ export declare function getJsonlWriter(): JsonlWriter | null;
32
33
  * @param storage - The LogStorage instance to use
33
34
  */
34
35
  export declare function setLogStorage(storage: LogStorage): void;
36
+ /**
37
+ * Set the shared proxy instance.
38
+ * @param proxy - The proxy instance to use
39
+ */
40
+ export declare function setProxy(proxy: ProxyInstance): void;
41
+ /**
42
+ * Reset the browser bundle cache. Used for testing.
43
+ */
44
+ export declare function resetBrowserBundleCache(): void;
35
45
  export interface LogServerOptions {
36
46
  /** Port to listen on (default: 9080) */
37
47
  port?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"log-server.d.ts","sourceRoot":"","sources":["../../src/server/log-server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAK/B,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAiB,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAQ7D;;;;GAIG;AACH,wBAAgB,aAAa,IAAI,UAAU,CAU1C;AAED;;;GAGG;AACH,wBAAgB,cAAc,IAAI,WAAW,GAAG,IAAI,CAEnD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,UAAU,GAAG,IAAI,CAEvD;AAkBD,MAAM,WAAW,gBAAgB;IAC7B,wCAAwC;IACxC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2FAA2F;IAC3F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2FAA2F;IAC3F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+CAA+C;IAC/C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gFAAgF;IAChF,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,sCAAsC;IACtC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,oDAAoD;IACpD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+CAA+C;IAC/C,KAAK,CAAC,EAAE,OAAO,CAAC;CACnB;AAGD,YAAY,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAajD;;;GAGG;AACH,wBAAgB,SAAS,IAAI,IAAI,CAEhC;AAqRD;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACnC,wBAAwB;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,kCAAkC;IAClC,OAAO,EAAE,UAAU,CAAC;IACpB,gDAAgD;IAChD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,sEAAsE;IACtE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,2FAA2F;IAC3F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2FAA2F;IAC3F,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IAClC,4DAA4D;IAC5D,MAAM,EAAE,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;CACtC;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,qBAAqB,CAsCtF;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,GAAE,gBAAqB,GAAG,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAkEzF;AAED;;GAEG;AACH,eAAO,MAAM,SAAS,oiDAkCrB,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,eAAe;IAC5B,wCAAwC;IACxC,OAAO,EAAE,gBAAgB,CAAC;IAC1B,mCAAmC;IACnC,QAAQ,EAAE,OAAO,CAAC;IAClB,sCAAsC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,eAAe,CAuDzD;AAED;;GAEG;AACH,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CA2E1C"}
1
+ {"version":3,"file":"log-server.d.ts","sourceRoot":"","sources":["../../src/server/log-server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAK/B,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAiB,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAQhD;;;;GAIG;AACH,wBAAgB,aAAa,IAAI,UAAU,CAU1C;AAED;;;GAGG;AACH,wBAAgB,cAAc,IAAI,WAAW,GAAG,IAAI,CAEnD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,UAAU,GAAG,IAAI,CAEvD;AAKD;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAEnD;AAiCD;;GAEG;AACH,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C;AAkBD,MAAM,WAAW,gBAAgB;IAC7B,wCAAwC;IACxC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2FAA2F;IAC3F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2FAA2F;IAC3F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+CAA+C;IAC/C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gFAAgF;IAChF,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,sCAAsC;IACtC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,oDAAoD;IACpD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+CAA+C;IAC/C,KAAK,CAAC,EAAE,OAAO,CAAC;CACnB;AAGD,YAAY,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAajD;;;GAGG;AACH,wBAAgB,SAAS,IAAI,IAAI,CAEhC;AAsTD;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACnC,wBAAwB;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,kCAAkC;IAClC,OAAO,EAAE,UAAU,CAAC;IACpB,gDAAgD;IAChD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,sEAAsE;IACtE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,2FAA2F;IAC3F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2FAA2F;IAC3F,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IAClC,4DAA4D;IAC5D,MAAM,EAAE,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;CACtC;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,qBAAqB,CAsCtF;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,GAAE,gBAAqB,GAAG,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAkEzF;AAED;;GAEG;AACH,eAAO,MAAM,SAAS,oiDAkCrB,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,eAAe;IAC5B,wCAAwC;IACxC,OAAO,EAAE,gBAAgB,CAAC;IAC1B,mCAAmC;IACnC,QAAQ,EAAE,OAAO,CAAC;IAClB,sCAAsC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,eAAe,CAuDzD;AAED;;GAEG;AACH,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CA2E1C"}
@@ -17,7 +17,7 @@ import * as http from "http";
17
17
  import * as https from "https";
18
18
  import * as os from "os";
19
19
  import * as path from "path";
20
- import { URL } from "url";
20
+ import { fileURLToPath, URL } from "url";
21
21
  import { JsonlWriter } from "./jsonl-writer.js";
22
22
  import { LogStorage } from "./log-storage.js";
23
23
  import { certFilesExist, readCertFiles } from "./self-signed-cert.js";
@@ -54,6 +54,46 @@ export function getJsonlWriter() {
54
54
  export function setLogStorage(storage) {
55
55
  sharedStorage = storage;
56
56
  }
57
+ // Shared proxy instance
58
+ let sharedProxy = null;
59
+ /**
60
+ * Set the shared proxy instance.
61
+ * @param proxy - The proxy instance to use
62
+ */
63
+ export function setProxy(proxy) {
64
+ sharedProxy = proxy;
65
+ }
66
+ // Browser bundle cache (loaded on first request)
67
+ let browserBundleCache;
68
+ function loadBrowserBundle() {
69
+ if (browserBundleCache !== undefined) {
70
+ return browserBundleCache;
71
+ }
72
+ const thisDir = path.dirname(fileURLToPath(import.meta.url));
73
+ // Try multiple locations: dist/ (when running from built output)
74
+ // and the package root dist/ (when running from source during tests)
75
+ const candidates = [
76
+ path.resolve(thisDir, "..", "remote-logger.browser.js"),
77
+ path.resolve(thisDir, "..", "..", "dist", "remote-logger.browser.js"),
78
+ ];
79
+ for (const candidate of candidates) {
80
+ try {
81
+ browserBundleCache = fs.readFileSync(candidate, "utf-8");
82
+ return browserBundleCache;
83
+ }
84
+ catch {
85
+ // Try next candidate
86
+ }
87
+ }
88
+ browserBundleCache = null;
89
+ return browserBundleCache;
90
+ }
91
+ /**
92
+ * Reset the browser bundle cache. Used for testing.
93
+ */
94
+ export function resetBrowserBundleCache() {
95
+ browserBundleCache = undefined;
96
+ }
57
97
  // ANSI color codes for terminal output
58
98
  const colors = {
59
99
  reset: "\x1b[0m",
@@ -200,7 +240,32 @@ function handleRequest(req, res, host, port, useHttps, quiet, logReceiveOnly = f
200
240
  res.end(JSON.stringify({ status: health.status, sessions: health.sessionCount }));
201
241
  return;
202
242
  }
203
- // In logReceiveOnly mode, only /log and /health are available
243
+ // Serve browser-ready script bundle (available in all modes including logReceiveOnly)
244
+ if ((url === "/remote-logger.js" || url.startsWith("/remote-logger.js?")) && req.method === "GET") {
245
+ const bundle = loadBrowserBundle();
246
+ if (!bundle) {
247
+ res.writeHead(404, { "Content-Type": "application/json" });
248
+ res.end(JSON.stringify({ error: "Browser bundle not found. Run 'npm run build' first." }));
249
+ return;
250
+ }
251
+ const hostHeader = req.headers.host ?? `${host}:${port}`;
252
+ const serverUrl = `${protocol}://${hostHeader}`;
253
+ const configPrefix = `window.__REMOTE_LOG_SERVER_URL__="${serverUrl}";\n`;
254
+ res.writeHead(200, {
255
+ "Content-Type": "application/javascript",
256
+ "Cache-Control": "no-cache",
257
+ });
258
+ res.end(configPrefix + bundle);
259
+ return;
260
+ }
261
+ // Reverse proxy with auto-injection (available in all modes including logReceiveOnly)
262
+ if (url.startsWith("/proxy/") && sharedProxy) {
263
+ const targetUrl = decodeURIComponent(url.substring("/proxy/".length));
264
+ const hostHeader = req.headers.host ?? `${host}:${port}`;
265
+ sharedProxy.handleRequest(req, res, targetUrl, protocol, hostHeader);
266
+ return;
267
+ }
268
+ // In logReceiveOnly mode, only /log, /health, /remote-logger.js, and /proxy/ are available
204
269
  if (logReceiveOnly) {
205
270
  res.writeHead(404, { "Content-Type": "application/json" });
206
271
  res.end(JSON.stringify({ error: "Not found (log receive only mode)" }));
@@ -280,17 +345,21 @@ function printBanner(host, port, useHttps) {
280
345
  // eslint-disable-next-line no-console
281
346
  console.log(`${colors.yellow}API Endpoints:${colors.reset}`);
282
347
  // eslint-disable-next-line no-console
283
- console.log(` ${colors.cyan}POST /log ${colors.reset} - Receive logs from browser`);
348
+ console.log(` ${colors.cyan}POST /log ${colors.reset} - Receive logs from browser`);
349
+ // eslint-disable-next-line no-console
350
+ console.log(` ${colors.cyan}GET /remote-logger.js ${colors.reset} - Browser-ready auto-config script`);
351
+ // eslint-disable-next-line no-console
352
+ console.log(` ${colors.cyan}* /proxy/<url> ${colors.reset} - Reverse proxy with script injection`);
284
353
  // eslint-disable-next-line no-console
285
- console.log(` ${colors.cyan}GET /logs ${colors.reset} - Get all logs as JSON`);
354
+ console.log(` ${colors.cyan}GET /logs ${colors.reset} - Get all logs as JSON`);
286
355
  // eslint-disable-next-line no-console
287
- console.log(` ${colors.cyan}GET /logs/recent ${colors.reset} - Get last 50 logs (?n=100 for more)`);
356
+ console.log(` ${colors.cyan}GET /logs/recent ${colors.reset} - Get last 50 logs (?n=100 for more)`);
288
357
  // eslint-disable-next-line no-console
289
- console.log(` ${colors.cyan}GET /logs/errors ${colors.reset} - Get only error logs`);
358
+ console.log(` ${colors.cyan}GET /logs/errors ${colors.reset} - Get only error logs`);
290
359
  // eslint-disable-next-line no-console
291
- console.log(` ${colors.cyan}POST /logs/clear ${colors.reset} - Clear all logs`);
360
+ console.log(` ${colors.cyan}POST /logs/clear ${colors.reset} - Clear all logs`);
292
361
  // eslint-disable-next-line no-console
293
- console.log(` ${colors.cyan}GET /health ${colors.reset} - Health check`);
362
+ console.log(` ${colors.cyan}GET /health ${colors.reset} - Health check`);
294
363
  // eslint-disable-next-line no-console
295
364
  console.log("");
296
365
  // eslint-disable-next-line no-console