Files
gravity_web/server/auto-discover.js

130 lines
4.2 KiB
JavaScript

/**
* Antigravity 자동 발견 모듈
*
* localhost의 CDP 포트 범위를 스캔하여 실행 중인 Antigravity 인스턴스를 감지.
* 발견된 인스턴스는 자동으로 세션 생성하여 웹 UI에 표시.
*/
const http = require('http');
// 스캔할 포트 범위
const SCAN_PORTS = [9000, 9222, 9229, 9333, 9001, 9002, 9003, 9004, 9005];
const SCAN_INTERVAL_MS = 15000; // 15초마다 스캔
/**
* 특정 포트의 CDP 엔드포인트에 요청하여 Antigravity인지 확인
* @returns {Promise<{port, title, url}|null>}
*/
function probeCDPPort(host, port, timeoutMs = 2000) {
return new Promise((resolve) => {
const req = http.get(`http://${host}:${port}/json`, { timeout: timeoutMs }, (res) => {
let data = '';
res.on('data', chunk => data += chunk);
res.on('end', () => {
try {
const targets = JSON.parse(data);
// Antigravity 워크벤치 타겟 찾기
const mainTarget = targets.find(t =>
t.type === 'page' &&
t.url.includes('workbench.html') &&
!t.url.includes('jetski')
);
if (mainTarget) {
resolve({
port,
title: mainTarget.title || 'Antigravity',
url: mainTarget.url,
});
} else {
resolve(null);
}
} catch {
resolve(null);
}
});
});
req.on('error', () => resolve(null));
req.on('timeout', () => { req.destroy(); resolve(null); });
});
}
/**
* 모든 포트를 병렬 스캔하여 Antigravity 인스턴스 목록 반환
* @returns {Promise<Array<{port, title, url}>>}
*/
async function scanForAntigravity(host = 'localhost') {
const probes = SCAN_PORTS.map(port => probeCDPPort(host, port));
const results = await Promise.all(probes);
return results.filter(Boolean);
}
class AutoDiscovery {
constructor(options = {}) {
this.host = options.host || 'localhost';
this.intervalMs = options.intervalMs || SCAN_INTERVAL_MS;
this.onDiscovered = options.onDiscovered || null; // callback({port, title, url})
this.onLost = options.onLost || null; // callback(port)
this.knownPorts = new Set();
this.timer = null;
}
/**
* 자동 발견 시작
*/
async start() {
console.log(`[AutoDiscovery] 시작 — ${this.host}에서 Antigravity 포트 스캔`);
// 즉시 한 번 스캔
await this._scan();
// 주기적 스캔
this.timer = setInterval(() => this._scan(), this.intervalMs);
}
/**
* 자동 발견 중지
*/
stop() {
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
}
/**
* 내부 스캔 로직
*/
async _scan() {
try {
const found = await scanForAntigravity(this.host);
const foundPorts = new Set(found.map(f => f.port));
// 새로 발견된 인스턴스
for (const instance of found) {
if (!this.knownPorts.has(instance.port)) {
this.knownPorts.add(instance.port);
console.log(`[AutoDiscovery] 발견: ${this.host}:${instance.port}${instance.title}`);
if (this.onDiscovered) {
this.onDiscovered(instance);
}
}
}
// 사라진 인스턴스
for (const port of this.knownPorts) {
if (!foundPorts.has(port)) {
this.knownPorts.delete(port);
console.log(`[AutoDiscovery] 소실: ${this.host}:${port}`);
if (this.onLost) {
this.onLost(port);
}
}
}
} catch (err) {
console.error('[AutoDiscovery] 스캔 오류:', err.message);
}
}
}
module.exports = { AutoDiscovery, scanForAntigravity, probeCDPPort };