fix(bridge): workbench.html inline v3 script injection + both-HTML loop patch #task-264
This commit is contained in:
@@ -194,6 +194,14 @@
|
||||
|
||||
---
|
||||
|
||||
### [2026-03-09] workbench.html inline 스크립트 미삽입 — jetski만 패치한 버그
|
||||
- **증상**: AG 재시작 후 `/deep-inspect` timeout — renderer v3 스크립트 미로딩
|
||||
- **원인**: `setupApprovalObserver()`가 `workbench-jetski-agent.html`에만 inline 삽입하고, `workbench.html`에는 외부 `<script src>`만 추가. `vscode-file://` 프로토콜은 커스텀 .js 파일을 서빙하지 않으므로 사실상 미로딩
|
||||
- **해결**: HTML 패치 로직을 `htmlFiles = ['workbench.html', 'workbench-jetski-agent.html']` 루프로 변경하여 **양쪽 모두 inline** 삽입. 수동 pre-patch 스크립트(`/tmp/patch_workbench.py`)로 즉시 적용 + product.json 체크섬 업데이트
|
||||
- **주의**: **항상 양쪽 HTML을 동일하게 패치**. AG가 어느 HTML을 로드할지 런타임까지 알 수 없음. pre-patch 후 1회 풀 재시작이면 충분 (체크섬 사전 업데이트 완료)
|
||||
|
||||
---
|
||||
|
||||
## 승인 전략 결정 체인 (다음 세션 필독)
|
||||
|
||||
> **이 섹션은 2026-03-08~09 전체 세션의 시행착오를 요약합니다.**
|
||||
@@ -216,7 +224,7 @@
|
||||
|
||||
| # | 접근 | 상태 | 다음 단계 |
|
||||
|---|------|------|-----------|
|
||||
| 1 | **Renderer v3 Deep DOM Traversal** | 구현 완료, **미검증** | AG 재시작 → `/deep-inspect` 결과 확인 |
|
||||
| 1 | **Renderer v3 Deep DOM Traversal** | 구현+배포+pre-patch 완료, **AG 재시작 후 검증 필요** | AG 재시작 → `/deep-inspect` 결과 확인 |
|
||||
| 2 | `<webview>.executeJavaScript()` | v3에 포함, **미검증** | 위 결과에서 `hasExecJS=true`이면 유력 |
|
||||
| 3 | TerminalExecutionPolicy.EAGER (Turbo) | 미시도, **최후 수단** | 통제권 포기이므로 사용자 승인 필요 |
|
||||
|
||||
@@ -225,3 +233,5 @@
|
||||
- 외부 workbench DOM에서 `querySelector`로 접근 **불가** (cross-origin)
|
||||
- `<webview>.executeJavaScript()`가 **유일한 표준 관통 경로** (Electron API)
|
||||
- AG 패치 후 **반드시 풀 프로세스 재시작** 필요 (Reload Window 불충분)
|
||||
- **양쪽 HTML (workbench.html + workbench-jetski-agent.html) 모두 inline 패치** 필수
|
||||
|
||||
|
||||
@@ -6,3 +6,4 @@
|
||||
| 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) + 렌더러 재귀 인스펙터 | `a07d9d3` | 🔧 |
|
||||
| 005 | 18:30~19:28 | workbench.html inline v3 패치 누락 수정 + pre-patch 검증 | `6fdb46b` | 🔧 |
|
||||
|
||||
43
docs/devlog/entries/20260309-005.md
Normal file
43
docs/devlog/entries/20260309-005.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# workbench.html inline v3 패치 누락 수정
|
||||
|
||||
- **시작**: 18:30 KST
|
||||
- **종료**: 19:28 KST
|
||||
- **상태**: 🔧 미완료 (AG 재시작 후 /deep-inspect 검증 필요)
|
||||
|
||||
## 핵심 발견
|
||||
|
||||
### Root cause: workbench.html에 v3 inline 스크립트 미삽입
|
||||
- 이전 세션에서 AG 재시작 후 `/deep-inspect` timeout 발생
|
||||
- Bridge 서버(port 34332)는 정상 (`/ping→pong`)
|
||||
- Extension 코드(v3)도 정상 배포됨 (동일 94986 bytes, 동일 timestamp)
|
||||
- **원인**: `setupApprovalObserver()`가 `workbench-jetski-agent.html`에만 inline 삽입, `workbench.html`에는 외부 `<script src>` 태그만 삽입
|
||||
- `vscode-file://` 프로토콜은 커스텀 .js 파일 서빙 불가 → 사실상 스크립트 미로딩
|
||||
|
||||
### 수정 내용
|
||||
1. **extension.ts**: HTML 패치 로직을 `htmlFiles` 배열 루프로 변경 → 양쪽 HTML 모두 동일한 inline `<script>` 삽입
|
||||
2. **수동 pre-patch**: Python 스크립트로 `workbench.html`에 jetski HTML의 inline 블록 복사 + product.json 체크섬 업데이트
|
||||
3. **검증 스크립트** (`/tmp/verify_patch.py`) 실행 → ALL CHECKS PASSED
|
||||
|
||||
## 검증 결과
|
||||
|
||||
```
|
||||
workbench.html: GB Observer=OK, deepFindButtons=OK, deep-inspect=OK (35422 bytes)
|
||||
workbench-jetski-agent.html: OK (35379 bytes)
|
||||
Checksums: MATCH (both files)
|
||||
Extension: deployed, md5 match, both-HTML loop fix present
|
||||
FINAL VERDICT: ALL CHECKS PASSED
|
||||
```
|
||||
|
||||
## 다음 단계 (다음 세션)
|
||||
|
||||
1. **AG 완전 재시작** (File → Exit → 재실행)
|
||||
2. `curl.exe http://127.0.0.1:34332/deep-inspect` 호출
|
||||
3. webview iframe 접근성 확인 → ACCESSIBLE이면 즉시 E2E 테스트
|
||||
4. BLOCKED이면 `executeJavaScript()` 결과 확인
|
||||
|
||||
## 파일 변경
|
||||
|
||||
| 파일 | 변경 |
|
||||
|------|------|
|
||||
| `extension/src/extension.ts` | HTML 패치 로직을 htmlFiles 루프로 리팩터링 (양쪽 HTML 동일 inline 삽입) |
|
||||
| `.agents/references/known-issues.md` | workbench.html inline 미삽입 이슈 + 승인 전략 상태 업데이트 |
|
||||
@@ -286,17 +286,24 @@ async function setupApprovalObserver() {
|
||||
logToFile(`[OBSERVER] script written → ${scriptPath} (port=${bridgePort})`);
|
||||
if (!integration.isInstalled()) {
|
||||
patcher.install(combinedScript);
|
||||
logToFile('[OBSERVER] workbench.html patched (needs reload)');
|
||||
logToFile('[OBSERVER] patcher.install() called (needs reload)');
|
||||
}
|
||||
// Also patch workbench-jetski-agent.html (Antigravity's actual entry point!)
|
||||
// IMPORTANT: vscode-file:// does NOT serve custom .js files (silent 404),
|
||||
// so we MUST inline the script directly into the HTML.
|
||||
// Patch BOTH HTML files with inline script injection.
|
||||
// CRITICAL: vscode-file:// does NOT serve custom .js files (silent 404),
|
||||
// so we MUST inline the script directly into BOTH HTML files.
|
||||
// workbench.html — loaded by DevTools/standard mode
|
||||
// workbench-jetski-agent.html — loaded by AG agent mode
|
||||
const scriptDir = path.dirname(scriptPath);
|
||||
const jetskiHtml = path.join(scriptDir, 'workbench-jetski-agent.html');
|
||||
try {
|
||||
if (fs.existsSync(jetskiHtml)) {
|
||||
let html = fs.readFileSync(jetskiHtml, 'utf8');
|
||||
// Remove old external script tag if present
|
||||
const htmlFiles = ['workbench.html', 'workbench-jetski-agent.html'];
|
||||
for (const htmlFileName of htmlFiles) {
|
||||
const htmlPath = path.join(scriptDir, htmlFileName);
|
||||
try {
|
||||
if (!fs.existsSync(htmlPath)) {
|
||||
logToFile(`[OBSERVER] ${htmlFileName} not found — skipping`);
|
||||
continue;
|
||||
}
|
||||
let html = fs.readFileSync(htmlPath, 'utf8');
|
||||
// Remove old external script tag if present (legacy, cannot be served)
|
||||
const extMarkerStart = '<!-- AG SDK [variet-gravity-bridge] -->';
|
||||
const extMarkerEnd = '<!-- /AG SDK [variet-gravity-bridge] -->';
|
||||
if (html.includes(extMarkerStart)) {
|
||||
@@ -304,7 +311,7 @@ async function setupApprovalObserver() {
|
||||
'[\\s\\S]*?' +
|
||||
extMarkerEnd.replace(/[[\]]/g, '\\$&') + '\\n?');
|
||||
html = html.replace(extRe, '');
|
||||
logToFile('[OBSERVER] removed external script tag from jetski HTML');
|
||||
logToFile(`[OBSERVER] removed external script tag from ${htmlFileName}`);
|
||||
}
|
||||
// Insert or update inline script
|
||||
const inlineMarkerStart = '<!-- AG SDK INLINE [variet-gravity-bridge] -->';
|
||||
@@ -314,17 +321,17 @@ async function setupApprovalObserver() {
|
||||
'[\\s\\S]*?' +
|
||||
inlineMarkerEnd.replace(/[[\]]/g, '\\$&'));
|
||||
html = html.replace(re, `${inlineMarkerStart}\n<script>\n${combinedScript}\n</script>\n${inlineMarkerEnd}`);
|
||||
logToFile('[OBSERVER] jetski HTML inline script UPDATED');
|
||||
logToFile(`[OBSERVER] ${htmlFileName} inline script UPDATED`);
|
||||
}
|
||||
else {
|
||||
html = html.replace('</html>', `\n${inlineMarkerStart}\n<script>\n${combinedScript}\n</script>\n${inlineMarkerEnd}\n</html>`);
|
||||
logToFile('[OBSERVER] jetski HTML inline script INSERTED');
|
||||
logToFile(`[OBSERVER] ${htmlFileName} inline script INSERTED`);
|
||||
}
|
||||
fs.writeFileSync(jetskiHtml, html, 'utf8');
|
||||
fs.writeFileSync(htmlPath, html, 'utf8');
|
||||
}
|
||||
catch (e) {
|
||||
logToFile(`[OBSERVER] ${htmlFileName} patch error: ${e.message}`);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
logToFile(`[OBSERVER] jetski patch error: ${e.message}`);
|
||||
}
|
||||
}
|
||||
// 4. Update product.json checksums so vscode-file:// serves our patched files
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -246,19 +246,26 @@ async function setupApprovalObserver() {
|
||||
logToFile(`[OBSERVER] script written → ${scriptPath} (port=${bridgePort})`);
|
||||
if (!integration.isInstalled()) {
|
||||
patcher.install(combinedScript);
|
||||
logToFile('[OBSERVER] workbench.html patched (needs reload)');
|
||||
logToFile('[OBSERVER] patcher.install() called (needs reload)');
|
||||
}
|
||||
|
||||
// Also patch workbench-jetski-agent.html (Antigravity's actual entry point!)
|
||||
// IMPORTANT: vscode-file:// does NOT serve custom .js files (silent 404),
|
||||
// so we MUST inline the script directly into the HTML.
|
||||
// Patch BOTH HTML files with inline script injection.
|
||||
// CRITICAL: vscode-file:// does NOT serve custom .js files (silent 404),
|
||||
// so we MUST inline the script directly into BOTH HTML files.
|
||||
// workbench.html — loaded by DevTools/standard mode
|
||||
// workbench-jetski-agent.html — loaded by AG agent mode
|
||||
const scriptDir = path.dirname(scriptPath);
|
||||
const jetskiHtml = path.join(scriptDir, 'workbench-jetski-agent.html');
|
||||
try {
|
||||
if (fs.existsSync(jetskiHtml)) {
|
||||
let html = fs.readFileSync(jetskiHtml, 'utf8');
|
||||
const htmlFiles = ['workbench.html', 'workbench-jetski-agent.html'];
|
||||
for (const htmlFileName of htmlFiles) {
|
||||
const htmlPath = path.join(scriptDir, htmlFileName);
|
||||
try {
|
||||
if (!fs.existsSync(htmlPath)) {
|
||||
logToFile(`[OBSERVER] ${htmlFileName} not found — skipping`);
|
||||
continue;
|
||||
}
|
||||
let html = fs.readFileSync(htmlPath, 'utf8');
|
||||
|
||||
// Remove old external script tag if present
|
||||
// Remove old external script tag if present (legacy, cannot be served)
|
||||
const extMarkerStart = '<!-- AG SDK [variet-gravity-bridge] -->';
|
||||
const extMarkerEnd = '<!-- /AG SDK [variet-gravity-bridge] -->';
|
||||
if (html.includes(extMarkerStart)) {
|
||||
@@ -268,7 +275,7 @@ async function setupApprovalObserver() {
|
||||
extMarkerEnd.replace(/[[\]]/g, '\\$&') + '\\n?'
|
||||
);
|
||||
html = html.replace(extRe, '');
|
||||
logToFile('[OBSERVER] removed external script tag from jetski HTML');
|
||||
logToFile(`[OBSERVER] removed external script tag from ${htmlFileName}`);
|
||||
}
|
||||
|
||||
// Insert or update inline script
|
||||
@@ -283,16 +290,16 @@ async function setupApprovalObserver() {
|
||||
);
|
||||
html = html.replace(re,
|
||||
`${inlineMarkerStart}\n<script>\n${combinedScript}\n</script>\n${inlineMarkerEnd}`);
|
||||
logToFile('[OBSERVER] jetski HTML inline script UPDATED');
|
||||
logToFile(`[OBSERVER] ${htmlFileName} inline script UPDATED`);
|
||||
} else {
|
||||
html = html.replace('</html>',
|
||||
`\n${inlineMarkerStart}\n<script>\n${combinedScript}\n</script>\n${inlineMarkerEnd}\n</html>`);
|
||||
logToFile('[OBSERVER] jetski HTML inline script INSERTED');
|
||||
logToFile(`[OBSERVER] ${htmlFileName} inline script INSERTED`);
|
||||
}
|
||||
fs.writeFileSync(jetskiHtml, html, 'utf8');
|
||||
fs.writeFileSync(htmlPath, html, 'utf8');
|
||||
} catch (e: any) {
|
||||
logToFile(`[OBSERVER] ${htmlFileName} patch error: ${e.message}`);
|
||||
}
|
||||
} catch (e: any) {
|
||||
logToFile(`[OBSERVER] jetski patch error: ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user