diff --git a/docs/devlog/2026-03-09.md b/docs/devlog/2026-03-09.md index fdccfeb..bc984d7 100644 --- a/docs/devlog/2026-03-09.md +++ b/docs/devlog/2026-03-09.md @@ -5,3 +5,4 @@ | 001 | 08:00~09:17 | 승인 실행 메커니즘 연구 + step-type별 VS Code 명령 분기 구현 | included in 002 | 🔧 | | 002 | 09:21~15:07 | SDK 승인 명령 미등록 확정 + Renderer DOM Click 구현 | `4497e96` | 🔧 | | 003 | 15:32~17:59 | Renderer v3 deep DOM traversal (iframe/webview/shadow 관통) | `32bf5ae` | 🔧 | +| 004 | 18:08~18:23 | Deep inspect HTTP endpoint (/deep-inspect) + 렌더러 재귀 인스펙터 | | 🔧 | diff --git a/extension/out/extension.js b/extension/out/extension.js index ae08693..15ecf8c 100644 --- a/extension/out/extension.js +++ b/extension/out/extension.js @@ -411,6 +411,10 @@ let observerHttpServer = null; const pendingResponses = new Map(); // Click trigger: extension sets this, renderer polls and clicks button let clickTrigger = null; +// Deep inspect trigger: curl sets this, renderer picks it up and POSTs results back +let deepInspectRequested = false; +let deepInspectResult = null; +let deepInspectWaiters = []; /** Derive a deterministic port from project name (range 10000-60000) */ function getDeterministicPort(name) { let hash = 0; @@ -507,6 +511,65 @@ function startObserverHttpBridge() { } return; } + // GET /deep-inspect — trigger deep DOM inspection from renderer + if (req.method === 'GET' && url.pathname === '/deep-inspect') { + deepInspectRequested = true; + logToFile('[HTTP] deep-inspect triggered — waiting for renderer result...'); + // Wait up to 10s for renderer to POST result + const timeout = setTimeout(() => { + deepInspectWaiters = deepInspectWaiters.filter(w => w !== waiter); + if (deepInspectResult) { + res.writeHead(200, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify(deepInspectResult)); + } + else { + res.writeHead(200, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({ status: 'timeout', message: 'Renderer did not respond in 10s. Is the v3 script loaded?' })); + } + }, 10000); + const waiter = (data) => { + clearTimeout(timeout); + res.writeHead(200, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify(data)); + }; + deepInspectWaiters.push(waiter); + return; + } + // GET /deep-inspect-trigger — renderer polls this + if (req.method === 'GET' && url.pathname === '/deep-inspect-trigger') { + const requested = deepInspectRequested; + deepInspectRequested = false; + res.writeHead(200, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({ inspect: requested })); + return; + } + // POST /deep-inspect-result — renderer posts inspection results here + if (req.method === 'POST' && url.pathname === '/deep-inspect-result') { + let body = ''; + req.on('data', (c) => body += c); + req.on('end', () => { + try { + const data = JSON.parse(body); + deepInspectResult = data; + logToFile(`[HTTP] deep-inspect result received (${body.length} bytes)`); + // Write to file for reference + const inspectFile = path.join(bridgePath, 'deep-inspect-result.json'); + fs.writeFileSync(inspectFile, JSON.stringify(data, null, 2)); + // Notify waiters + const waiters = [...deepInspectWaiters]; + deepInspectWaiters = []; + waiters.forEach(w => w(data)); + res.writeHead(200, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({ ok: true })); + } + catch (e) { + logToFile(`[HTTP] deep-inspect-result parse error: ${e.message}`); + res.writeHead(400); + res.end(JSON.stringify({ error: e.message })); + } + }); + return; + } // GET /ping — health check if (url.pathname === '/ping') { res.writeHead(200); @@ -642,55 +705,133 @@ function generateApprovalObserverScript(_port) { }catch(e){} } - // ── DOM Structure Dump (one-time on startup) ── - function dumpDOMStructure(){ - if(_domDumped)return; - _domDumped=true; - try{ - // Count iframes - var iframes=document.querySelectorAll('iframe'); - log('DOM-DUMP: '+iframes.length+' iframes found'); - for(var i=0;i0){node.csp=[];for(var c=0;c