fix(bridge): renderer script debugging — async fetch, install path fix, product.json checksums
- Replace sync XHR tryPing() with async fetch tryPingAsync() for port discovery - Add ag-sdk JS file to product.json checksums in updateProductChecksums() - Revert to inline script approach for jetski HTML (vscode-file:// blocks custom .js) - Remove old external script tag cleanup, add inline markers - Update known-issues with 3 new findings - Add devlog entry #16
This commit is contained in:
@@ -99,3 +99,21 @@
|
|||||||
- **원인**: 렌더러 스크립트가 `./ag-bridge-ports.json`을 동기 XHR로 읽으려 하나, `vscode-file://` 프로토콜이 `.json` 파일 서빙 거부
|
- **원인**: 렌더러 스크립트가 `./ag-bridge-ports.json`을 동기 XHR로 읽으려 하나, `vscode-file://` 프로토콜이 `.json` 파일 서빙 거부
|
||||||
- **해결**: (1) 프로젝트명 해시 기반 **결정론적 포트** 사용 (`gravity_control→34332`), (2) 스크립트 생성 시 포트를 `HARDCODED_PORT=${port}`로 직접 삽입
|
- **해결**: (1) 프로젝트명 해시 기반 **결정론적 포트** 사용 (`gravity_control→34332`), (2) 스크립트 생성 시 포트를 `HARDCODED_PORT=${port}`로 직접 삽입
|
||||||
- **주의**: `server.listen(0)` 랜덤 포트 → 매 재시작마다 변경되어 렌더러와 불일치. 결정론적 포트는 `EADDRINUSE` 시 랜덤 폴백 필요
|
- **주의**: `server.listen(0)` 랜덤 포트 → 매 재시작마다 변경되어 렌더러와 불일치. 결정론적 포트는 `EADDRINUSE` 시 랜덤 폴백 필요
|
||||||
|
|
||||||
|
### [2026-03-08] Extension 컴파일 경로 != 설치 경로
|
||||||
|
- **증상**: `npm run compile` 후 Extension 동작이 변하지 않음
|
||||||
|
- **원인**: `npm run compile`은 `extension/out/extension.js`에만 빌드. Antigravity는 `~/.antigravity/extensions/variet.gravity-bridge-X.X.X/out/extension.js`에서 로드
|
||||||
|
- **해결**: 컴파일 후 반드시 설치 경로로 수동 복사하거나, `vsce package` → VSIX 재설치
|
||||||
|
- **주의**: `extension/out/` ≠ 실행 경로. 항상 설치 경로 확인 필요
|
||||||
|
|
||||||
|
### [2026-03-08] Electron 메인 프로세스 체크섬 캐시 — Reload Window 불충분
|
||||||
|
- **증상**: product.json 체크섬 업데이트 + HTML 패치 후 `Reload Window` → 패치 미적용
|
||||||
|
- **원인**: Electron 메인 프로세스가 시작 시 product.json 체크섬을 메모리에 캐시. `Reload Window`는 렌더러만 재시작하므로 캐시된 구 체크섬 사용
|
||||||
|
- **해결**: Antigravity를 **완전 종료 후 재시작** 필요 (File → Exit 후 재실행)
|
||||||
|
- **주의**: `Reload Window` ≠ 앱 재시작. 체크섬 변경 시 항상 풀 재시작 필요
|
||||||
|
|
||||||
|
### [2026-03-08] Renderer 동기 XHR — Electron 보안 정책 차단
|
||||||
|
- **증상**: `tryPing()` 함수가 동기 `XMLHttpRequest`로 HTTP bridge에 연결 시도 → 타임아웃
|
||||||
|
- **원인**: Electron 렌더러 프로세스에서 동기 XHR이 보안 정책에 의해 차단됨
|
||||||
|
- **해결**: `fetch()` + `AbortSignal.timeout(2000)` 비동기 방식으로 교체 (`tryPingAsync`)
|
||||||
|
- **주의**: `async/await` 사용 불가 (ES5 환경). `.then()` 체이닝으로 구현
|
||||||
|
|||||||
@@ -17,3 +17,4 @@
|
|||||||
| 13 | 15:00~16:52 | Multi-window 격리 (v0.3.1→0.3.4): 세션 필터, per-project 포트, 등록 경쟁 조건 수정, DOM Observer 렌더러 디버깅 | - | 🔧 |
|
| 13 | 15:00~16:52 | Multi-window 격리 (v0.3.1→0.3.4): 세션 필터, per-project 포트, 등록 경쟁 조건 수정, DOM Observer 렌더러 디버깅 | - | 🔧 |
|
||||||
| 14 | 17:01~17:38 | **근본 원인 발견**: product.json 체크섬 불일치 → vscode-file:// 원본 캐시 서빙. 체크섬 수동 업데이트로 수정 | - | 🔧 |
|
| 14 | 17:01~17:38 | **근본 원인 발견**: product.json 체크섬 불일치 → vscode-file:// 원본 캐시 서빙. 체크섬 수동 업데이트로 수정 | - | 🔧 |
|
||||||
| 15 | 17:50~18:30 | **v0.3.5**: 포트 디스커버리 수정 (결정론적 포트 + 하드코딩), 인라인 스크립트 전환 (`<script src>` → `<script>inline</script>`), product.json 자동 체크섬 업데이트 | - | 🔧 |
|
| 15 | 17:50~18:30 | **v0.3.5**: 포트 디스커버리 수정 (결정론적 포트 + 하드코딩), 인라인 스크립트 전환 (`<script src>` → `<script>inline</script>`), product.json 자동 체크섬 업데이트 | - | 🔧 |
|
||||||
|
| 16 | 19:00~19:48 | 렌더러 스크립트 로딩 디버깅: sync XHR→async fetch 변환, 설치경로 불일치 발견, vscode-file:// 커스텀 파일 서빙 불가 확인, Electron 풀 재시작 필요 발견 | - | 🔧 |
|
||||||
|
|||||||
19
docs/devlog/entries/20260308-016.md
Normal file
19
docs/devlog/entries/20260308-016.md
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# 렌더러 스크립트 로딩 디버깅 — sync XHR→fetch, 설치경로, Electron 캐시
|
||||||
|
|
||||||
|
- **시간**: 2026-03-08 19:00~19:48
|
||||||
|
- **Commit**: (미커밋)
|
||||||
|
- **Vikunja**: 미매핑
|
||||||
|
|
||||||
|
## 결정 사항
|
||||||
|
- `tryPing()` (동기 XHR) → `tryPingAsync()` (async fetch + AbortSignal.timeout) 전환
|
||||||
|
- Electron 렌더러에서 동기 XHR이 보안 정책에 의해 차단됨
|
||||||
|
- `vscode-file://` 프로토콜은 원본 번들 외 커스텀 `.js` 파일 서빙 불가 확인
|
||||||
|
- product.json checksums에 추가해도 무효
|
||||||
|
- 인라인 `<script>` 방식이 유일한 해법
|
||||||
|
- `npm run compile`은 `extension/out/`에만 빌드 — 설치 경로(`~/.antigravity/extensions/`)에 별도 복사 필요
|
||||||
|
|
||||||
|
## 미완료
|
||||||
|
- **Electron 풀 재시작 후 인라인 스크립트 동작 확인** 필요 (Reload Window ≠ 앱 재시작)
|
||||||
|
- Electron 메인 프로세스가 product.json 체크섬을 시작 시 캐시
|
||||||
|
- `Reload Window`는 렌더러만 재시작 → 캐시된 구 체크섬 사용 → 원본 HTML 서빙
|
||||||
|
- 다음 세션: 앱 완전 종료→재시작 후 `[INLINE-TEST]` 로그 확인으로 검증
|
||||||
@@ -289,24 +289,27 @@ async function setupApprovalObserver() {
|
|||||||
logToFile('[OBSERVER] workbench.html patched (needs reload)');
|
logToFile('[OBSERVER] workbench.html patched (needs reload)');
|
||||||
}
|
}
|
||||||
// Also patch workbench-jetski-agent.html (Antigravity's actual entry point!)
|
// Also patch workbench-jetski-agent.html (Antigravity's actual entry point!)
|
||||||
// IMPORTANT: vscode-file:// protocol does NOT serve custom .js files,
|
// IMPORTANT: vscode-file:// does NOT serve custom .js files (silent 404),
|
||||||
// so we INLINE the script content directly into the HTML.
|
// so we MUST inline the script directly into the HTML.
|
||||||
const scriptDir = path.dirname(scriptPath);
|
const scriptDir = path.dirname(scriptPath);
|
||||||
const jetskiHtml = path.join(scriptDir, 'workbench-jetski-agent.html');
|
const jetskiHtml = path.join(scriptDir, 'workbench-jetski-agent.html');
|
||||||
try {
|
try {
|
||||||
if (fs.existsSync(jetskiHtml)) {
|
if (fs.existsSync(jetskiHtml)) {
|
||||||
let html = fs.readFileSync(jetskiHtml, 'utf8');
|
let html = fs.readFileSync(jetskiHtml, 'utf8');
|
||||||
// Remove old external script tag if present (from previous versions)
|
// Remove old external script tag if present
|
||||||
const oldScriptBasename = path.basename(scriptPath);
|
const extMarkerStart = '<!-- AG SDK [variet-gravity-bridge] -->';
|
||||||
if (html.includes(`src="./${oldScriptBasename}"`)) {
|
const extMarkerEnd = '<!-- /AG SDK [variet-gravity-bridge] -->';
|
||||||
html = html.replace(/\n?<!-- AG SDK \[variet-gravity-bridge\] -->\n?<script src="[^"]*"><\/script>\n?<!-- \/AG SDK \[variet-gravity-bridge\] -->\n?/, '');
|
if (html.includes(extMarkerStart)) {
|
||||||
logToFile('[OBSERVER] removed old external <script src> tag from jetski HTML');
|
const extRe = new RegExp('\\n?' + extMarkerStart.replace(/[[\]]/g, '\\$&') +
|
||||||
|
'[\\s\\S]*?' +
|
||||||
|
extMarkerEnd.replace(/[[\]]/g, '\\$&') + '\\n?');
|
||||||
|
html = html.replace(extRe, '');
|
||||||
|
logToFile('[OBSERVER] removed external script tag from jetski HTML');
|
||||||
}
|
}
|
||||||
// Insert or update inline script
|
// Insert or update inline script
|
||||||
const inlineMarkerStart = '<!-- AG SDK INLINE [variet-gravity-bridge] -->';
|
const inlineMarkerStart = '<!-- AG SDK INLINE [variet-gravity-bridge] -->';
|
||||||
const inlineMarkerEnd = '<!-- /AG SDK INLINE [variet-gravity-bridge] -->';
|
const inlineMarkerEnd = '<!-- /AG SDK INLINE [variet-gravity-bridge] -->';
|
||||||
if (html.includes(inlineMarkerStart)) {
|
if (html.includes(inlineMarkerStart)) {
|
||||||
// Replace existing inline script with updated content
|
|
||||||
const re = new RegExp(inlineMarkerStart.replace(/[[\]]/g, '\\$&') +
|
const re = new RegExp(inlineMarkerStart.replace(/[[\]]/g, '\\$&') +
|
||||||
'[\\s\\S]*?' +
|
'[\\s\\S]*?' +
|
||||||
inlineMarkerEnd.replace(/[[\]]/g, '\\$&'));
|
inlineMarkerEnd.replace(/[[\]]/g, '\\$&'));
|
||||||
@@ -314,7 +317,6 @@ async function setupApprovalObserver() {
|
|||||||
logToFile('[OBSERVER] jetski HTML inline script UPDATED');
|
logToFile('[OBSERVER] jetski HTML inline script UPDATED');
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// First time: insert before </html>
|
|
||||||
html = html.replace('</html>', `\n${inlineMarkerStart}\n<script>\n${combinedScript}\n</script>\n${inlineMarkerEnd}\n</html>`);
|
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] jetski HTML inline script INSERTED');
|
||||||
}
|
}
|
||||||
@@ -372,15 +374,16 @@ function updateProductChecksums() {
|
|||||||
logToFile('[CHECKSUM] no checksums section in product.json');
|
logToFile('[CHECKSUM] no checksums section in product.json');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Files we may modify (relative key in product.json → absolute path)
|
// Files we may modify or create (relative key in product.json → absolute path)
|
||||||
|
// CRITICAL: vscode-file:// only serves files with valid checksums in product.json.
|
||||||
|
// Custom JS files MUST be added here or they'll silently 404.
|
||||||
const filesToCheck = {
|
const filesToCheck = {
|
||||||
'vs/code/electron-browser/workbench/workbench.html': path.join(workbenchDir, 'workbench.html'),
|
'vs/code/electron-browser/workbench/workbench.html': path.join(workbenchDir, 'workbench.html'),
|
||||||
'vs/code/electron-browser/workbench/workbench-jetski-agent.html': path.join(workbenchDir, 'workbench-jetski-agent.html'),
|
'vs/code/electron-browser/workbench/workbench-jetski-agent.html': path.join(workbenchDir, 'workbench-jetski-agent.html'),
|
||||||
|
'vs/code/electron-browser/workbench/ag-sdk-variet-gravity-bridge.js': path.join(workbenchDir, 'ag-sdk-variet-gravity-bridge.js'),
|
||||||
};
|
};
|
||||||
let updated = false;
|
let updated = false;
|
||||||
for (const [key, filePath] of Object.entries(filesToCheck)) {
|
for (const [key, filePath] of Object.entries(filesToCheck)) {
|
||||||
if (!product.checksums[key])
|
|
||||||
continue; // not in checksums, skip
|
|
||||||
if (!fs.existsSync(filePath))
|
if (!fs.existsSync(filePath))
|
||||||
continue;
|
continue;
|
||||||
const fileBytes = fs.readFileSync(filePath);
|
const fileBytes = fs.readFileSync(filePath);
|
||||||
@@ -564,65 +567,29 @@ function generateApprovalObserverScript(_port) {
|
|||||||
function log(m){console.log('[GB Observer] '+m);}
|
function log(m){console.log('[GB Observer] '+m);}
|
||||||
log('v2 Script loaded — discovering bridge port...');
|
log('v2 Script loaded — discovering bridge port...');
|
||||||
|
|
||||||
// ── Port Discovery: try hardcoded port FIRST, then JSON fallback ──
|
// ── Port Discovery: async fetch()-based (sync XHR blocked in Electron renderer) ──
|
||||||
// vscode-file:// protocol blocks .json files, so XHR to ag-bridge-ports.json
|
|
||||||
// returns 404. The extension embeds the known port at script generation time.
|
|
||||||
var HARDCODED_PORT=${_port};
|
var HARDCODED_PORT=${_port};
|
||||||
|
|
||||||
function tryPing(port,cb){
|
function tryPingAsync(port){
|
||||||
try{
|
return fetch('http://127.0.0.1:'+port+'/ping?t='+Date.now(),{signal:AbortSignal.timeout(2000)})
|
||||||
var xhr=new XMLHttpRequest();
|
.then(function(r){return r.text();})
|
||||||
xhr.open('GET','http://127.0.0.1:'+port+'/ping?t='+Date.now(),false);
|
.then(function(t){return t==='pong';})
|
||||||
xhr.timeout=2000;
|
.catch(function(){return false;});
|
||||||
xhr.send();
|
|
||||||
if(xhr.status===200&&xhr.responseText==='pong'){cb(true);return;}
|
|
||||||
}catch(e){}
|
|
||||||
cb(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function discoverPort(cb){
|
function discoverPort(cb){
|
||||||
// Strategy 1: Try hardcoded port immediately
|
|
||||||
log('Trying hardcoded port '+HARDCODED_PORT+'...');
|
log('Trying hardcoded port '+HARDCODED_PORT+'...');
|
||||||
tryPing(HARDCODED_PORT,function(ok){
|
tryPingAsync(HARDCODED_PORT).then(function(ok){
|
||||||
if(ok){log('Port discovered (hardcoded): '+HARDCODED_PORT);cb(HARDCODED_PORT);return;}
|
if(ok){log('Port discovered (hardcoded): '+HARDCODED_PORT);cb(HARDCODED_PORT);return;}
|
||||||
log('Hardcoded port failed, falling back to JSON + port scan...');
|
log('Hardcoded port failed, retrying with backoff...');
|
||||||
|
|
||||||
// Strategy 2: JSON file fallback + port scan
|
|
||||||
var attempts=0;
|
var attempts=0;
|
||||||
var timer=setInterval(function(){
|
var timer=setInterval(function(){
|
||||||
attempts++;
|
attempts++;
|
||||||
if(attempts>60){clearInterval(timer);log('Port discovery timeout after 2min');return;}
|
if(attempts>60){clearInterval(timer);log('Port discovery timeout after 2min');return;}
|
||||||
// Try hardcoded port again (server may start late)
|
tryPingAsync(HARDCODED_PORT).then(function(ok2){
|
||||||
tryPing(HARDCODED_PORT,function(ok2){
|
if(ok2){clearInterval(timer);log('Port discovered (retry #'+attempts+'): '+HARDCODED_PORT);cb(HARDCODED_PORT);}
|
||||||
if(ok2){clearInterval(timer);log('Port discovered (hardcoded retry): '+HARDCODED_PORT);cb(HARDCODED_PORT);return;}
|
|
||||||
});
|
});
|
||||||
// Try JSON file (may work in future Electron versions)
|
|
||||||
try{
|
|
||||||
var xhr=new XMLHttpRequest();
|
|
||||||
xhr.open('GET','./ag-bridge-ports.json?t='+Date.now(),false);
|
|
||||||
xhr.send();
|
|
||||||
if(xhr.status===200){
|
|
||||||
var ports=JSON.parse(xhr.responseText);
|
|
||||||
var keys=Object.keys(ports);
|
|
||||||
for(var i=0;i<keys.length;i++){
|
|
||||||
var port=ports[keys[i]];
|
|
||||||
if(port>0&&port<65536){
|
|
||||||
try{
|
|
||||||
var xhr2=new XMLHttpRequest();
|
|
||||||
xhr2.open('GET','http://127.0.0.1:'+port+'/ping?t='+Date.now(),false);
|
|
||||||
xhr2.timeout=1000;
|
|
||||||
xhr2.send();
|
|
||||||
if(xhr2.status===200&&xhr2.responseText==='pong'){
|
|
||||||
clearInterval(timer);
|
|
||||||
log('Port discovered (JSON): '+port+' (project='+keys[i]+')');
|
|
||||||
cb(port);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}catch(e2){}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}catch(e){}
|
|
||||||
},2000);
|
},2000);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -250,22 +250,25 @@ async function setupApprovalObserver() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Also patch workbench-jetski-agent.html (Antigravity's actual entry point!)
|
// Also patch workbench-jetski-agent.html (Antigravity's actual entry point!)
|
||||||
// IMPORTANT: vscode-file:// protocol does NOT serve custom .js files,
|
// IMPORTANT: vscode-file:// does NOT serve custom .js files (silent 404),
|
||||||
// so we INLINE the script content directly into the HTML.
|
// so we MUST inline the script directly into the HTML.
|
||||||
const scriptDir = path.dirname(scriptPath);
|
const scriptDir = path.dirname(scriptPath);
|
||||||
const jetskiHtml = path.join(scriptDir, 'workbench-jetski-agent.html');
|
const jetskiHtml = path.join(scriptDir, 'workbench-jetski-agent.html');
|
||||||
try {
|
try {
|
||||||
if (fs.existsSync(jetskiHtml)) {
|
if (fs.existsSync(jetskiHtml)) {
|
||||||
let html = fs.readFileSync(jetskiHtml, 'utf8');
|
let html = fs.readFileSync(jetskiHtml, 'utf8');
|
||||||
|
|
||||||
// Remove old external script tag if present (from previous versions)
|
// Remove old external script tag if present
|
||||||
const oldScriptBasename = path.basename(scriptPath);
|
const extMarkerStart = '<!-- AG SDK [variet-gravity-bridge] -->';
|
||||||
if (html.includes(`src="./${oldScriptBasename}"`)) {
|
const extMarkerEnd = '<!-- /AG SDK [variet-gravity-bridge] -->';
|
||||||
html = html.replace(
|
if (html.includes(extMarkerStart)) {
|
||||||
/\n?<!-- AG SDK \[variet-gravity-bridge\] -->\n?<script src="[^"]*"><\/script>\n?<!-- \/AG SDK \[variet-gravity-bridge\] -->\n?/,
|
const extRe = new RegExp(
|
||||||
''
|
'\\n?' + extMarkerStart.replace(/[[\]]/g, '\\$&') +
|
||||||
|
'[\\s\\S]*?' +
|
||||||
|
extMarkerEnd.replace(/[[\]]/g, '\\$&') + '\\n?'
|
||||||
);
|
);
|
||||||
logToFile('[OBSERVER] removed old external <script src> tag from jetski HTML');
|
html = html.replace(extRe, '');
|
||||||
|
logToFile('[OBSERVER] removed external script tag from jetski HTML');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert or update inline script
|
// Insert or update inline script
|
||||||
@@ -273,7 +276,6 @@ async function setupApprovalObserver() {
|
|||||||
const inlineMarkerEnd = '<!-- /AG SDK INLINE [variet-gravity-bridge] -->';
|
const inlineMarkerEnd = '<!-- /AG SDK INLINE [variet-gravity-bridge] -->';
|
||||||
|
|
||||||
if (html.includes(inlineMarkerStart)) {
|
if (html.includes(inlineMarkerStart)) {
|
||||||
// Replace existing inline script with updated content
|
|
||||||
const re = new RegExp(
|
const re = new RegExp(
|
||||||
inlineMarkerStart.replace(/[[\]]/g, '\\$&') +
|
inlineMarkerStart.replace(/[[\]]/g, '\\$&') +
|
||||||
'[\\s\\S]*?' +
|
'[\\s\\S]*?' +
|
||||||
@@ -283,7 +285,6 @@ async function setupApprovalObserver() {
|
|||||||
`${inlineMarkerStart}\n<script>\n${combinedScript}\n</script>\n${inlineMarkerEnd}`);
|
`${inlineMarkerStart}\n<script>\n${combinedScript}\n</script>\n${inlineMarkerEnd}`);
|
||||||
logToFile('[OBSERVER] jetski HTML inline script UPDATED');
|
logToFile('[OBSERVER] jetski HTML inline script UPDATED');
|
||||||
} else {
|
} else {
|
||||||
// First time: insert before </html>
|
|
||||||
html = html.replace('</html>',
|
html = html.replace('</html>',
|
||||||
`\n${inlineMarkerStart}\n<script>\n${combinedScript}\n</script>\n${inlineMarkerEnd}\n</html>`);
|
`\n${inlineMarkerStart}\n<script>\n${combinedScript}\n</script>\n${inlineMarkerEnd}\n</html>`);
|
||||||
logToFile('[OBSERVER] jetski HTML inline script INSERTED');
|
logToFile('[OBSERVER] jetski HTML inline script INSERTED');
|
||||||
@@ -342,15 +343,17 @@ function updateProductChecksums() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Files we may modify (relative key in product.json → absolute path)
|
// Files we may modify or create (relative key in product.json → absolute path)
|
||||||
|
// CRITICAL: vscode-file:// only serves files with valid checksums in product.json.
|
||||||
|
// Custom JS files MUST be added here or they'll silently 404.
|
||||||
const filesToCheck: Record<string, string> = {
|
const filesToCheck: Record<string, string> = {
|
||||||
'vs/code/electron-browser/workbench/workbench.html': path.join(workbenchDir, 'workbench.html'),
|
'vs/code/electron-browser/workbench/workbench.html': path.join(workbenchDir, 'workbench.html'),
|
||||||
'vs/code/electron-browser/workbench/workbench-jetski-agent.html': path.join(workbenchDir, 'workbench-jetski-agent.html'),
|
'vs/code/electron-browser/workbench/workbench-jetski-agent.html': path.join(workbenchDir, 'workbench-jetski-agent.html'),
|
||||||
|
'vs/code/electron-browser/workbench/ag-sdk-variet-gravity-bridge.js': path.join(workbenchDir, 'ag-sdk-variet-gravity-bridge.js'),
|
||||||
};
|
};
|
||||||
|
|
||||||
let updated = false;
|
let updated = false;
|
||||||
for (const [key, filePath] of Object.entries(filesToCheck)) {
|
for (const [key, filePath] of Object.entries(filesToCheck)) {
|
||||||
if (!product.checksums[key]) continue; // not in checksums, skip
|
|
||||||
if (!fs.existsSync(filePath)) continue;
|
if (!fs.existsSync(filePath)) continue;
|
||||||
|
|
||||||
const fileBytes = fs.readFileSync(filePath);
|
const fileBytes = fs.readFileSync(filePath);
|
||||||
@@ -535,65 +538,29 @@ function generateApprovalObserverScript(_port: number): string {
|
|||||||
function log(m){console.log('[GB Observer] '+m);}
|
function log(m){console.log('[GB Observer] '+m);}
|
||||||
log('v2 Script loaded — discovering bridge port...');
|
log('v2 Script loaded — discovering bridge port...');
|
||||||
|
|
||||||
// ── Port Discovery: try hardcoded port FIRST, then JSON fallback ──
|
// ── Port Discovery: async fetch()-based (sync XHR blocked in Electron renderer) ──
|
||||||
// vscode-file:// protocol blocks .json files, so XHR to ag-bridge-ports.json
|
|
||||||
// returns 404. The extension embeds the known port at script generation time.
|
|
||||||
var HARDCODED_PORT=${_port};
|
var HARDCODED_PORT=${_port};
|
||||||
|
|
||||||
function tryPing(port,cb){
|
function tryPingAsync(port){
|
||||||
try{
|
return fetch('http://127.0.0.1:'+port+'/ping?t='+Date.now(),{signal:AbortSignal.timeout(2000)})
|
||||||
var xhr=new XMLHttpRequest();
|
.then(function(r){return r.text();})
|
||||||
xhr.open('GET','http://127.0.0.1:'+port+'/ping?t='+Date.now(),false);
|
.then(function(t){return t==='pong';})
|
||||||
xhr.timeout=2000;
|
.catch(function(){return false;});
|
||||||
xhr.send();
|
|
||||||
if(xhr.status===200&&xhr.responseText==='pong'){cb(true);return;}
|
|
||||||
}catch(e){}
|
|
||||||
cb(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function discoverPort(cb){
|
function discoverPort(cb){
|
||||||
// Strategy 1: Try hardcoded port immediately
|
|
||||||
log('Trying hardcoded port '+HARDCODED_PORT+'...');
|
log('Trying hardcoded port '+HARDCODED_PORT+'...');
|
||||||
tryPing(HARDCODED_PORT,function(ok){
|
tryPingAsync(HARDCODED_PORT).then(function(ok){
|
||||||
if(ok){log('Port discovered (hardcoded): '+HARDCODED_PORT);cb(HARDCODED_PORT);return;}
|
if(ok){log('Port discovered (hardcoded): '+HARDCODED_PORT);cb(HARDCODED_PORT);return;}
|
||||||
log('Hardcoded port failed, falling back to JSON + port scan...');
|
log('Hardcoded port failed, retrying with backoff...');
|
||||||
|
|
||||||
// Strategy 2: JSON file fallback + port scan
|
|
||||||
var attempts=0;
|
var attempts=0;
|
||||||
var timer=setInterval(function(){
|
var timer=setInterval(function(){
|
||||||
attempts++;
|
attempts++;
|
||||||
if(attempts>60){clearInterval(timer);log('Port discovery timeout after 2min');return;}
|
if(attempts>60){clearInterval(timer);log('Port discovery timeout after 2min');return;}
|
||||||
// Try hardcoded port again (server may start late)
|
tryPingAsync(HARDCODED_PORT).then(function(ok2){
|
||||||
tryPing(HARDCODED_PORT,function(ok2){
|
if(ok2){clearInterval(timer);log('Port discovered (retry #'+attempts+'): '+HARDCODED_PORT);cb(HARDCODED_PORT);}
|
||||||
if(ok2){clearInterval(timer);log('Port discovered (hardcoded retry): '+HARDCODED_PORT);cb(HARDCODED_PORT);return;}
|
|
||||||
});
|
});
|
||||||
// Try JSON file (may work in future Electron versions)
|
|
||||||
try{
|
|
||||||
var xhr=new XMLHttpRequest();
|
|
||||||
xhr.open('GET','./ag-bridge-ports.json?t='+Date.now(),false);
|
|
||||||
xhr.send();
|
|
||||||
if(xhr.status===200){
|
|
||||||
var ports=JSON.parse(xhr.responseText);
|
|
||||||
var keys=Object.keys(ports);
|
|
||||||
for(var i=0;i<keys.length;i++){
|
|
||||||
var port=ports[keys[i]];
|
|
||||||
if(port>0&&port<65536){
|
|
||||||
try{
|
|
||||||
var xhr2=new XMLHttpRequest();
|
|
||||||
xhr2.open('GET','http://127.0.0.1:'+port+'/ping?t='+Date.now(),false);
|
|
||||||
xhr2.timeout=1000;
|
|
||||||
xhr2.send();
|
|
||||||
if(xhr2.status===200&&xhr2.responseText==='pong'){
|
|
||||||
clearInterval(timer);
|
|
||||||
log('Port discovered (JSON): '+port+' (project='+keys[i]+')');
|
|
||||||
cb(port);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}catch(e2){}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}catch(e){}
|
|
||||||
},2000);
|
},2000);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user