From e7631177f8f9c18dba23f90b3d02228b450b6538 Mon Sep 17 00:00:00 2001 From: Variet Worker Date: Wed, 18 Mar 2026 11:08:59 +0900 Subject: [PATCH] =?UTF-8?q?refactor(cleanup):=20v0.5.0=20Collector=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0=20+=20dead=20code=20=EC=A0=95=EB=A6=AC=20+?= =?UTF-8?q?=20HttpBridgeContext=20=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - DELETE collector.py (523줄) - main.py: BOT_MODE=remote 분기 제거 - gateway.py: Collector REST 6개 endpoint 제거 (311→168줄) - bridge.py: RemoteTransport 제거 (480→270줄) - config.py: REMOTE_BRIDGE_URL 제거 - extension.ts: dead code 4개 + stale module vars 제거 - step-probe.ts: getStepProbeContext() 추가, autoApproveEnabled 제거 - FIX: HttpBridgeContext stale primitive (getter 패턴으로 수정) - ADD: extension.log rotation (10MB→2MB tail) - docs: architecture.md, tech-stack.md, known-issues.md 업데이트 --- .agents/references/architecture.md | 5 +- .agents/references/known-issues.md | 1 + .agents/references/tech-stack.md | 6 +- bridge.py | 214 ------------ collector.py | 522 ----------------------------- config.py | 3 +- docs/devlog/2026-03-18.md | 9 +- extension/out/extension.js | 40 +-- extension/out/extension.js.map | 2 +- extension/package.json | 2 +- extension/src/extension.ts | 42 +-- extension/src/step-probe.ts | 13 +- gateway.py | 147 +------- main.py | 41 --- start_bot.bat | 7 +- 15 files changed, 65 insertions(+), 989 deletions(-) delete mode 100644 collector.py diff --git a/.agents/references/architecture.md b/.agents/references/architecture.md index 3c660a6..c231418 100644 --- a/.agents/references/architecture.md +++ b/.agents/references/architecture.md @@ -41,11 +41,10 @@ gravity_control/ ├── bot.py # Discord 봇: 승인 UI, 채널 관리, Hub 핸들러 (1,286줄) ├── hub.py # WebSocket Hub: 연결 관리, 메시지 라우팅 (580줄) ├── auth.py # JWT 토큰 + registration code 인증 (127줄) -├── gateway.py # HTTP REST API + /ws endpoint (310줄) -├── bridge.py # 파일 기반 IPC (레거시 fallback) (479줄) +├── gateway.py # HTTP REST API + /ws endpoint (168줄) +├── bridge.py # 파일 기반 IPC (레거시 fallback) (270줄) ├── watcher.py # Brain 디렉토리 변경 감시 (290줄) ├── parser.py # Markdown → Discord 변환 (245줄) -├── collector.py # 원격 파일→HTTP 릴레이 (⚠️ DEPRECATED — WS Hub 사용) (523줄) │ ├── ── Extension 측 (TypeScript) ── ├── extension/src/ diff --git a/.agents/references/known-issues.md b/.agents/references/known-issues.md index 82d8574..2aa491a 100644 --- a/.agents/references/known-issues.md +++ b/.agents/references/known-issues.md @@ -71,3 +71,4 @@ | 8 | **`processResponseFile` 상태 리셋은 `sawRunningAfterPending=true`만** | processResponseFile 무한 루프 | | 9 | **fs.watch Windows 불안정 — 반드시 polling fallback 병행** | fs.watch silent fail | | 10 | **diff_review는 VS Code 커맨드만 유효** — RPC 3개 전략 모두 실패 확정 | diff_review RPC dead-end | +| 11 | **HttpBridgeContext에 프리미티브 by-value 복사 금지** — 별도 객체 생성 시 getter 사용 | HttpBridgeContext stale primitive | diff --git a/.agents/references/tech-stack.md b/.agents/references/tech-stack.md index e4d5c36..ceab6fe 100644 --- a/.agents/references/tech-stack.md +++ b/.agents/references/tech-stack.md @@ -68,10 +68,7 @@ | 변수명 | 용도 | 기본값 | |--------|------|--------| | BRAIN_PATH | AG 브레인 경로 | `~/.gemini/antigravity/brain` | -| BOT_MODE | `local` / ~~`remote`~~ / `gateway` | `local` | - -> [!WARNING] -> `BOT_MODE=remote` (Collector 모드)는 **deprecated**입니다. `gateway` 모드 + Extension WS를 사용하세요. +| BOT_MODE | `local` / `gateway` | `local` | | DEBOUNCE_SECONDS | Watcher 디바운스 간격 | `5` | | PROJECT_NAME | 프로젝트 이름 | `gravity_control` | @@ -83,7 +80,6 @@ | GATEWAY_API_KEY | REST API 인증 키 | (미설정 시 인증 미사용) | | GRAVITY_HUB_SECRET | WS Hub JWT 서명 시크릿 (64char hex) | (미설정 시 인증 생략) | | GRAVITY_REGISTRATION_CODE | Extension 등록 코드 (32char hex) | (미설정 시 인증 생략) | -| REMOTE_BRIDGE_URL | ~~Collector 원격 URL~~ | ⚠️ deprecated (remote 모드 전용) | ## Extension VS Code 설정 diff --git a/bridge.py b/bridge.py index 2137897..076aef0 100644 --- a/bridge.py +++ b/bridge.py @@ -12,10 +12,6 @@ Protocol: 2. Bot reads pending/ → sends Discord message with ✅/❌ buttons 3. User clicks button → Bot writes JSON to response/ 4. VS Code Extension reads response/ → executes action - -Transport layer: - LocalTransport — file-based (default, single-PC) - RemoteTransport — HTTP-based (future: multi-PC collector mode) """ import json @@ -150,216 +146,6 @@ class LocalTransport(BridgeTransport): (self.bridge_dir / sub).mkdir(parents=True, exist_ok=True) -class RemoteTransport(BridgeTransport): - """HTTP-based transport for Collector → Gateway communication. - - Maps BridgeTransport methods to Gateway API endpoints: - list_json_files("pending") → GET /api/pending (returns list) - write_json("pending", ...) → POST /api/pending - read_json("response", ...) → GET /api/response/{rid} - write_json("commands", ...) → (not used by Collector, Gateway pushes commands) - etc. - """ - - def __init__(self, base_url: str, api_key: str = ""): - self.base_url = base_url.rstrip("/") - self.api_key = api_key - self._headers = {"Content-Type": "application/json"} - if api_key: - self._headers["Authorization"] = f"Bearer {api_key}" - self._session = None # aiohttp.ClientSession — lazy created - - # Connection health - self.connected = False - self._consecutive_failures = 0 - self._max_failures_before_warning = 3 - - # Rate limit backoff - self._rate_limited_until = 0.0 # timestamp until which we should not send requests - self._backoff_seconds = 0.0 # current backoff duration (exponential) - self._BACKOFF_BASE = 2.0 - self._BACKOFF_MAX = 60.0 - self._success_streak = 0 # consecutive successes for gradual backoff reduction - - # Retry queue: list of (method, path, data) tuples - self._retry_queue: list[tuple[str, str, dict | None]] = [] - self._retry_queue_max = 100 - - logger.info(f"RemoteTransport: {self.base_url} (auth={'yes' if api_key else 'no'})") - - async def _get_session(self): - """Lazy-create aiohttp session.""" - if self._session is None or self._session.closed: - import aiohttp - timeout = aiohttp.ClientTimeout(total=10) - self._session = aiohttp.ClientSession( - headers=self._headers, timeout=timeout - ) - return self._session - - async def close(self): - """Close the HTTP session.""" - if self._session and not self._session.closed: - await self._session.close() - - @property - def is_rate_limited(self) -> bool: - """Check if we are currently in a rate-limit backoff period.""" - return time.time() < self._rate_limited_until - - def _apply_backoff(self, retry_after: float = 0): - """Apply exponential backoff for rate limiting.""" - self._success_streak = 0 # Reset success streak on any failure - if retry_after > 0: - self._backoff_seconds = min(retry_after, self._BACKOFF_MAX) - else: - if self._backoff_seconds == 0: - self._backoff_seconds = self._BACKOFF_BASE - else: - self._backoff_seconds = min(self._backoff_seconds * 2, self._BACKOFF_MAX) - self._rate_limited_until = time.time() + self._backoff_seconds - logger.warning(f"RemoteTransport: backing off {self._backoff_seconds:.0f}s (until +{self._backoff_seconds:.0f}s)") - - def _on_request_success(self): - """Gradually reduce backoff after consecutive successes. - - Instead of instantly resetting to 0 (which causes the 1s oscillation loop - when 7 loops share one transport), require sustained success before reducing. - """ - if self._backoff_seconds <= 0: - return # Already at zero, nothing to do - self._success_streak += 1 - if self._success_streak >= 5: - # Halve the backoff (gradual cooldown) - self._backoff_seconds = self._backoff_seconds / 2 - if self._backoff_seconds < 0.5: - self._backoff_seconds = 0 - self._rate_limited_until = 0 - self._success_streak = 0 - - async def _arequest(self, method: str, path: str, data: dict | None = None) -> dict | None: - """Async non-blocking HTTP request to Gateway API.""" - # Skip if in backoff period (except health checks) - if self.is_rate_limited and path != "/health": - return None - - session = await self._get_session() - url = f"{self.base_url}{path}" - try: - kwargs = {} - if data is not None: - kwargs["json"] = data - async with session.request(method, url, **kwargs) as resp: - if resp.status >= 400: - if resp.status == 401: - logger.error("RemoteTransport: 401 Unauthorized — check GATEWAY_API_KEY") - elif resp.status == 429: - retry_after = float(resp.headers.get("Retry-After", 0)) - self._apply_backoff(retry_after) - else: - logger.warning(f"RemoteTransport: {method} {path} → {resp.status}") - return None - result = await resp.json() - if not self.connected: - logger.info("RemoteTransport: ✅ Gateway connected") - self.connected = True - self._consecutive_failures = 0 - self._on_request_success() - return result - except Exception as e: - self._consecutive_failures += 1 - if self._consecutive_failures == self._max_failures_before_warning: - logger.error(f"RemoteTransport: ❌ Gateway unreachable ({self._consecutive_failures} failures): {e}") - elif self._consecutive_failures < self._max_failures_before_warning: - logger.warning(f"RemoteTransport: {method} {path} → {e}") - self.connected = False - # Apply backoff on connection failures too - if self._consecutive_failures >= self._max_failures_before_warning: - self._apply_backoff() - return None - - async def _arequest_retry(self, method: str, path: str, data: dict | None = None) -> dict | None: - """Request with retry queue — failed POSTs are queued for later.""" - result = await self._arequest(method, path, data) - if result is None and method == "POST" and data is not None: - if len(self._retry_queue) < self._retry_queue_max: - self._retry_queue.append((method, path, data)) - return result - - async def flush_retry_queue(self): - """Retry queued failed requests.""" - if not self._retry_queue or not self.connected: - return - queue = self._retry_queue[:] - self._retry_queue.clear() - succeeded = 0 - for method, path, data in queue: - result = await self._arequest(method, path, data) - if result is None: - if len(self._retry_queue) < self._retry_queue_max: - self._retry_queue.append((method, path, data)) - break - succeeded += 1 - if succeeded: - logger.info(f"[RETRY] flushed {succeeded}/{len(queue)} queued requests") - - async def health_check(self) -> bool: - """Check if Gateway is reachable.""" - result = await self._arequest("GET", "/health") - return result is not None and result.get("status") == "ok" - - # ─── Async methods (used by Collector) ─── - - async def awrite_json(self, subdir: str, filename: str, data: dict) -> None: - if subdir == "pending": - await self._arequest_retry("POST", "/api/pending", data) - elif subdir == "response": - rid = data.get("request_id", filename.replace(".json", "")) - await self._arequest_retry("POST", f"/api/response/{rid}", data) - - async def aread_json(self, subdir: str, filename: str) -> dict | None: - rid = filename.replace(".json", "") - if subdir == "response": - return await self._arequest("GET", f"/api/response/{rid}") - return None - - async def apoll_commands(self, project: str) -> list[dict]: - result = await self._arequest("GET", f"/api/commands/{project}") - if result and isinstance(result, dict): - return result.get("commands", []) - return [] - - async def aregister_session(self, conv_id: str, project: str) -> None: - await self._arequest_retry("POST", "/api/register", { - "conversation_id": conv_id, "project_name": project, - }) - - async def asend_chat(self, project: str, content: str, *, attached_files: list[dict] | None = None) -> None: - payload: dict = {"project_name": project, "content": content} - if attached_files: - payload["attached_files"] = attached_files - await self._arequest_retry("POST", "/api/chat", payload) - - async def asend_event(self, event_data: dict) -> None: - await self._arequest_retry("POST", "/api/event", event_data) - - # ─── Sync stubs (ABC compliance, not used in Collector) ─── - - def list_json_files(self, subdir: str) -> list[str]: - return [] - - def read_json(self, subdir: str, filename: str) -> dict | None: - return None - - def write_json(self, subdir: str, filename: str, data: dict) -> None: - pass - - def delete_file(self, subdir: str, filename: str) -> bool: - return True - - def ensure_dirs(self) -> None: - pass - # ─── Bridge Protocol (uses Transport) ─── diff --git a/collector.py b/collector.py deleted file mode 100644 index 54ce823..0000000 --- a/collector.py +++ /dev/null @@ -1,522 +0,0 @@ -"""Collector — local relay between Extension (file-based) and Gateway (HTTP). - -.. deprecated:: - Extension이 WebSocket으로 Hub에 직접 연결하므로 Collector는 더 이상 필요하지 않습니다. - BOT_MODE=gateway + Extension WS 연결을 사용하세요. - 이 모듈은 하위 호환을 위해 유지되며, 향후 제거될 수 있습니다. - -The Collector runs on the local PC alongside the AG IDE. -It bridges the gap between the Extension (which writes to local bridge/ files) -and the remote Gateway (which manages Discord). - -Flow: - Extension → bridge/pending/ → Collector → POST Gateway /api/pending - Gateway /api/response/{rid} → Collector → bridge/response/ → Extension - Gateway /api/commands/{project} → Collector → bridge/commands/ → Extension -""" - -import asyncio -import hashlib -import json -import os -import time -import logging -import uuid -from pathlib import Path - -from bridge import LocalTransport, RemoteTransport -from config import Config -from watcher import BrainEvent, EventType - -logger = logging.getLogger(__name__) - - -class CollectorBridge: - """Bridges local file-based bridge with remote Gateway API. - - Periodically: - 1. Scans local pending/ → forwards new ones to Gateway - 2. Polls Gateway for responses → writes to local response/ - 3. Polls Gateway for commands → writes to local commands/ - """ - - def __init__(self, local: LocalTransport, remote: RemoteTransport, - project_name: str, event_queue: asyncio.Queue | None = None): - self.local = local - self.remote = remote - self.project_name = project_name - self.event_queue = event_queue - self._poll_interval = 5 # seconds (was 3 — reduced I/O frequency) - self._running = False - - # Pre-populate with existing pending files → skip on startup (prevents 만료됨 spam) - self._startup_pending: set[str] = set() - self._forwarded_pending: set[str] = set() - self._forwarded_timestamps: dict[str, float] = {} # rid → when forwarded - self._pending_hashes: dict[str, str] = {} # rid → content hash (for MERGE/status detection) - self._pending_mtimes: dict[str, float] = {} # rid → last known file mtime - self._RESPONSE_POLL_TTL = 300 # 5 min — stop polling responses for old pending - - # Project discovery cache (avoid re-reading register/ every cycle) - self._cached_projects: set[str] | None = None - self._projects_cache_ts: float = 0 - self._PROJECTS_CACHE_TTL = 60.0 # seconds - for fname in self.local.list_json_files("pending"): - rid = fname.replace(".json", "") - self._startup_pending.add(rid) - self._forwarded_pending.add(rid) - # Pre-hash existing files - data = self.local.read_json("pending", fname) - if data: - self._pending_hashes[rid] = hashlib.md5( - json.dumps(data, sort_keys=True).encode() - ).hexdigest() - # Pre-cache mtime - try: - fpath = self.local.bridge_dir / "pending" / fname - self._pending_mtimes[rid] = fpath.stat().st_mtime - except OSError: - pass - if self._startup_pending: - logger.info(f"[COLLECTOR] skipping {len(self._startup_pending)} existing pending files") - - # Startup cleanup: remove stale response files (> 5 min) - self._cleanup_stale_responses() - - def _cleanup_stale_responses(self, max_age: int = 300): - """Remove stale response files (> max_age seconds) on startup.""" - now = time.time() - cleaned = 0 - for fname in self.local.list_json_files("response"): - try: - fpath = self.local.bridge_dir / "response" / fname - if now - fpath.stat().st_mtime > max_age: - self.local.delete_file("response", fname) - cleaned += 1 - except OSError: - pass - if cleaned: - logger.info(f"[COLLECTOR] startup cleanup: removed {cleaned} stale response files") - - async def start(self): - """Start the Collector polling loops with staggered offsets. - - Each loop starts with a different delay to prevent all loops from waking - up at the same time and causing burst requests to Gateway. - """ - self._running = True - logger.info(f"[COLLECTOR] started for project={self.project_name}") - - async def _staggered(coro, offset: float): - await asyncio.sleep(offset) - await coro() - - tasks = [ - _staggered(self._forward_pending_loop, 0.0), - _staggered(self._poll_responses_loop, 0.5), - _staggered(self._poll_commands_loop, 1.0), - _staggered(self._forward_chat_snapshots_loop, 1.5), - _staggered(self._forward_registrations_loop, 2.0), - _staggered(self._health_check_loop, 2.5), - _staggered(self._retry_flush_loop, 3.0), - ] - if self.event_queue: - tasks.append(_staggered(self._forward_events_loop, 3.5)) - await asyncio.gather(*tasks) - - async def stop(self): - """Stop the Collector and close HTTP session.""" - self._running = False - await self.remote.close() - logger.info("[COLLECTOR] stopped") - - # ─── Forward local pending → Gateway ─── - - async def _forward_pending_loop(self): - """Scan local pending/ and forward new + updated requests to Gateway. - - Tracks content hashes to detect: - - New pending files → forward immediately - - MERGE updates (step_probe updates command text) → re-forward - - Status changes (auto_resolved, expired) → re-forward - """ - while self._running: - try: - # Skip cycle if rate-limited - if self.remote.is_rate_limited: - await asyncio.sleep(self._poll_interval) - continue - - current_files = set() - for fname in self.local.list_json_files("pending"): - rid = fname.replace(".json", "") - current_files.add(rid) - - # mtime pre-check: skip read+hash if file hasn't been modified - try: - fpath = self.local.bridge_dir / "pending" / fname - current_mtime = fpath.stat().st_mtime - except OSError: - continue - prev_mtime = self._pending_mtimes.get(rid) - if prev_mtime is not None and current_mtime == prev_mtime: - continue # File untouched since last check — skip read+hash - self._pending_mtimes[rid] = current_mtime - - data = self.local.read_json("pending", fname) - if data is None: - continue - - # Compute content hash to detect changes - content_hash = hashlib.md5( - json.dumps(data, sort_keys=True).encode() - ).hexdigest() - - prev_hash = self._pending_hashes.get(rid) - if prev_hash == content_hash: - continue # No change - - is_new = rid not in self._forwarded_pending - status = data.get("status", "pending") - - if rid in self._startup_pending: - # Startup files: only forward status CHANGES (not re-forward as new pending) - if status == "pending": - continue # Still pending from before startup — skip - # Status changed (auto_resolved/expired) — forward the update - - # Forward to Gateway (new or updated) - await self.remote.awrite_json("pending", fname, data) - self._forwarded_pending.add(rid) - self._forwarded_timestamps[rid] = time.time() - self._pending_hashes[rid] = content_hash - - if is_new: - logger.info(f"[COLLECTOR] → Gateway: pending {rid[:12]}") - else: - logger.info(f"[COLLECTOR] → Gateway: pending UPDATE {rid[:12]} status={status}") - - # ── Phase 1 FIX: delete local auto_resolved/expired after forwarding ── - if status in ("auto_resolved", "expired"): - try: - fpath.unlink() - current_files.discard(rid) - self._forwarded_pending.discard(rid) - self._pending_hashes.pop(rid, None) - self._pending_mtimes.pop(rid, None) - logger.info(f"[COLLECTOR] 🗑 deleted local {status} pending: {rid[:12]}") - except OSError: - pass - - # ── Periodic stale cleanup: delete pending files > 10 min old ── - now_ts = time.time() - for rid in list(current_files): - if rid in self._forwarded_pending: - forwarded_at = self._forwarded_timestamps.get(rid, 0) - if now_ts - forwarded_at > 600: # 10 min since forwarding - try: - stale_path = self.local.bridge_dir / "pending" / f"{rid}.json" - stale_path.unlink() - self._forwarded_pending.discard(rid) - self._pending_hashes.pop(rid, None) - self._pending_mtimes.pop(rid, None) - logger.info(f"[COLLECTOR] 🗑 stale cleanup (>10min): {rid[:12]}") - except OSError: - pass - - # Clean up tracking for deleted files - for rid in list(self._forwarded_pending): - if rid not in current_files and rid not in self._startup_pending: - self._forwarded_pending.discard(rid) - self._pending_hashes.pop(rid, None) - self._pending_mtimes.pop(rid, None) - # Also clean up orphaned hashes/mtimes for files no longer on disk - for rid in list(self._pending_hashes): - if rid not in current_files: - self._pending_hashes.pop(rid, None) - self._pending_mtimes.pop(rid, None) - - except Exception as e: - logger.error(f"[COLLECTOR] forward_pending error: {e}") - - await asyncio.sleep(self._poll_interval) - - # ─── Poll Gateway responses → local ─── - - async def _poll_responses_loop(self): - """Poll Gateway for responses and write them locally for Extension. - - Only polls responses for recently-forwarded pending (within _RESPONSE_POLL_TTL). - Expired entries are removed from tracking to prevent request accumulation. - """ - while self._running: - try: - # Skip cycle if rate-limited - if self.remote.is_rate_limited: - await asyncio.sleep(self._poll_interval) - continue - - now = time.time() - # Clean up expired forwarded pending (stop polling responses for old ones) - expired = [ - rid for rid, ts in self._forwarded_timestamps.items() - if now - ts > self._RESPONSE_POLL_TTL - ] - for rid in expired: - self._forwarded_pending.discard(rid) - self._forwarded_timestamps.pop(rid, None) - # NOTE: intentionally keep _pending_hashes[rid] to prevent - # re-forward cycle (expired pending would be re-detected as - # "new" if hash is cleared). Hash is cleaned up when file - # is actually deleted from disk (see _forward_pending_loop). - if expired: - logger.info(f"[COLLECTOR] expired {len(expired)} stale forwarded pending (>{self._RESPONSE_POLL_TTL}s)") - - # Check each active forwarded pending for a response - active_rids = [ - rid for rid in self._forwarded_pending - if rid not in self._startup_pending - ] - for rid in active_rids: - # Rate-limit guard: stop polling if we got rate-limited mid-cycle - if self.remote.is_rate_limited: - break - data = await self.remote.aread_json("response", f"{rid}.json") - if data is None or data.get("waiting"): - await asyncio.sleep(0.3) # Throttle between individual response polls - continue - - # Write response locally for Extension to pick up - self.local.write_json("response", f"{rid}.json", data) - # Also delete local pending file (Extension expects this) - self.local.delete_file("pending", f"{rid}.json") - self._forwarded_pending.discard(rid) - self._forwarded_timestamps.pop(rid, None) - approved = data.get("approved", "?") - logger.info(f"[COLLECTOR] ← Gateway: response {rid[:12]} approved={approved}") - - except Exception as e: - logger.error(f"[COLLECTOR] poll_responses error: {e}") - - await asyncio.sleep(self._poll_interval) - - # ─── Poll Gateway commands → local ─── - - def _discover_local_projects(self) -> set[str]: - """Discover all project names registered by local Extension instances. - - Reads bridge/register/*.json files, which are written by each AG window's - Extension with {conversation_id, project_name}. Returns unique project names - found, always including self.project_name as a fallback. - - Results are cached for _PROJECTS_CACHE_TTL seconds to avoid re-reading - 22+ register files every polling cycle. - """ - now = time.time() - if self._cached_projects is not None and now - self._projects_cache_ts < self._PROJECTS_CACHE_TTL: - return self._cached_projects - - projects = {self.project_name} - register_dir = self.local.bridge_dir / "register" - if not register_dir.exists(): - self._cached_projects = projects - self._projects_cache_ts = now - return projects - for f in register_dir.glob("*.json"): - try: - data = json.loads(f.read_text(encoding="utf-8-sig")) - p = data.get("project_name", "") - if p: - projects.add(p) - except (json.JSONDecodeError, OSError): - pass - self._cached_projects = projects - self._projects_cache_ts = now - return projects - - def _get_session_project(self, conversation_id: str) -> str | None: - """Look up which project a session belongs to from register/ files. - - Returns project_name if found, None if unknown (allow forwarding). - Uses _discover_local_projects cache timing to avoid redundant I/O. - """ - register_dir = self.local.bridge_dir / "register" - reg_file = register_dir / f"{conversation_id}.json" - if reg_file.exists(): - try: - data = json.loads(reg_file.read_text(encoding="utf-8-sig")) - return data.get("project_name", "") - except (json.JSONDecodeError, OSError): - pass - return None # Unknown → allow (don't block unregistered sessions) - - async def _poll_commands_loop(self): - """Poll Gateway for commands with adaptive per-project intervals. - - When a project returns empty commands repeatedly, its poll interval - increases (3s → 10s → 30s → 60s). On receiving a command, interval - resets to base. This prevents idle projects from wasting requests. - """ - # Per-project adaptive state - project_intervals: dict[str, float] = {} # project → current interval - project_last_poll: dict[str, float] = {} # project → last poll timestamp - _BASE_INTERVAL = 3.0 - _IDLE_STEPS = [10.0, 30.0, 60.0] # progressive idle intervals - project_empty_streak: dict[str, int] = {} # project → consecutive empty polls - - while self._running: - try: - # Skip cycle if rate-limited - if not self.remote.is_rate_limited: - projects = self._discover_local_projects() - now = time.time() - for project in projects: - if self.remote.is_rate_limited: - break - - # Check if this project's interval has elapsed - interval = project_intervals.get(project, _BASE_INTERVAL) - last = project_last_poll.get(project, 0) - if now - last < interval: - continue # Not time yet for this project - - project_last_poll[project] = now - commands = await self.remote.apoll_commands(project) - - if commands: - # Got commands → reset to base interval - project_intervals[project] = _BASE_INTERVAL - project_empty_streak[project] = 0 - for cmd in commands: - cmd_id = cmd.get("id", f"{int(time.time() * 1000)}_{uuid.uuid4().hex[:8]}") - fname = f"{cmd_id}.json" - self.local.write_json("commands", fname, cmd) - logger.info(f"[COLLECTOR] ← Gateway: command [{project}] {cmd.get('text', '?')[:30]}") - else: - # Empty → increase interval progressively - streak = project_empty_streak.get(project, 0) + 1 - project_empty_streak[project] = streak - if streak <= len(_IDLE_STEPS): - project_intervals[project] = _IDLE_STEPS[streak - 1] - # else stays at max (60s) - - await asyncio.sleep(0.3) # Throttle between projects - - except Exception as e: - logger.error(f"[COLLECTOR] poll_commands error: {e}") - - await asyncio.sleep(self._poll_interval) - - # ─── Forward chat snapshots → Gateway ─── - - async def _forward_chat_snapshots_loop(self): - """Forward chat_snapshots/ from Extension to Gateway.""" - while self._running: - try: - snap_dir = self.local.bridge_dir / "chat_snapshots" - if snap_dir.exists(): - for f in snap_dir.glob("*.json"): - try: - data = json.loads(f.read_text(encoding="utf-8-sig")) - project = data.get("project_name", self.project_name) - content = data.get("content", "") - attached_files = data.get("attached_files", []) - if content or attached_files: - await self.remote.asend_chat(project, content, attached_files=attached_files) - af_info = f" +{len(attached_files)} files" if attached_files else "" - logger.info(f"[COLLECTOR] → Gateway: chat snapshot len={len(content)}{af_info}") - f.unlink() # Cleanup after forwarding - except (json.JSONDecodeError, OSError) as e: - logger.warning(f"[COLLECTOR] bad chat snapshot {f.name}: {e}") - except Exception as e: - logger.error(f"[COLLECTOR] forward_chat_snapshots error: {e}") - - await asyncio.sleep(10) # Chat snapshots: less urgent, 10s interval - - # ─── Forward session registrations → Gateway ─── - - async def _forward_registrations_loop(self): - """Forward register/ files from Extension to Gateway.""" - forwarded_regs: set[str] = set() - while self._running: - try: - register_dir = self.local.bridge_dir / "register" - if register_dir.exists(): - for f in register_dir.glob("*.json"): - if f.name in forwarded_regs: - continue - try: - data = json.loads(f.read_text(encoding="utf-8-sig")) - conv_id = data.get("conversation_id", "") - project = data.get("project_name", "") - if conv_id and project: - await self.remote.aregister_session(conv_id, project) - forwarded_regs.add(f.name) - logger.info(f"[COLLECTOR] → Gateway: register {conv_id[:8]} → {project}") - await asyncio.sleep(0.3) # Spread startup burst - except (json.JSONDecodeError, OSError) as e: - logger.warning(f"[COLLECTOR] bad register {f.name}: {e}") - except Exception as e: - logger.error(f"[COLLECTOR] forward_registrations error: {e}") - - await asyncio.sleep(30) # Registration changes rarely — 30s interval - # ─── Forward brain events → Gateway ─── - - async def _forward_events_loop(self): - """Read BrainEvents from Watcher queue and POST to Gateway. - - Phase 2 FIX: Only forward events for sessions belonging to this project. - Uses register/ files to determine session→project mapping. - """ - while self._running: - try: - event: BrainEvent = await asyncio.wait_for( - self.event_queue.get(), timeout=5.0 - ) - - # ── Project filter: only forward events for MY project ── - conv_id = event.conversation_id - session_project = self._get_session_project(conv_id) - if session_project and session_project != self.project_name: - # Skip: this session belongs to another project - continue - - # Serialize event to JSON - event_data = { - "event_type": event.event_type.value, - "conversation_id": event.conversation_id, - "file_name": event.file_name, - "file_path": str(event.file_path) if event.file_path else "", - "content": event.content, - "timestamp": event.timestamp, - } - await self.remote.asend_event(event_data) - logger.info(f"[COLLECTOR] → Gateway: event {event.event_type.value} {event.file_name}") - except asyncio.TimeoutError: - continue - except Exception as e: - logger.error(f"[COLLECTOR] forward_event error: {e}") - - # ─── Health check ─── - - async def _health_check_loop(self): - """Periodically check Gateway connectivity.""" - while self._running: - try: - ok = await self.remote.health_check() - if not ok and self.remote.connected: - logger.warning("[COLLECTOR] ❌ Gateway health check failed") - except Exception: - pass - await asyncio.sleep(30) - - # ─── Retry flush ─── - - async def _retry_flush_loop(self): - """Periodically flush failed request retry queue.""" - while self._running: - try: - await self.remote.flush_retry_queue() - except Exception as e: - logger.error(f"[COLLECTOR] retry flush error: {e}") - await asyncio.sleep(30) # Retry flush: 30s interval (was 10s) diff --git a/config.py b/config.py index b966711..e74f9bc 100644 --- a/config.py +++ b/config.py @@ -43,9 +43,8 @@ class Config: CHANNEL_PREFIX: str = "AG" PROJECT_NAME: str = os.getenv("PROJECT_NAME", "gravity_control") - # Bot mode: 'local' (file-based bridge) or 'remote' (HTTP polling — future) + # Bot mode: 'local' (file-based bridge) or 'gateway' (WS Hub + HTTP API) BOT_MODE: str = os.getenv("BOT_MODE", "local") - REMOTE_BRIDGE_URL: str = os.getenv("REMOTE_BRIDGE_URL", "") GATEWAY_API_KEY: str = os.getenv("GATEWAY_API_KEY", "") # WebSocket Hub diff --git a/docs/devlog/2026-03-18.md b/docs/devlog/2026-03-18.md index 15413bd..4df71b3 100644 --- a/docs/devlog/2026-03-18.md +++ b/docs/devlog/2026-03-18.md @@ -1,10 +1,5 @@ -# Devlog — 2026-03-18 +# 2026-03-18 Devlog | # | 시간 | 작업 | 커밋 | 상태 | |---|------|------|------|------| -| 001 | 06:09~06:26 | known-issues 정리/아카이빙 + Collector 폐기 마킹 + 문서 보강 (architecture, tech-stack, conventions) | `881a424` | ✅ | -| 002 | 06:33~06:50 | Hub/WS 단위 테스트 45개 작성 (연결 관리, pending_owners, 라우팅, 인증) | `ac803d4` | ✅ | -| 003 | 06:50~07:05 | !stop 핸들러 SDK cancelCurrentTask() 교체 + VSIX 재빌드/설치 | `759dab5` | ✅ | -| 004 | 07:03~07:15 | !stop 재수정 — cancelCurrentTask→CancelCascadeInvocation RPC (AG 빨간■ 동일) + VSIX 설치 | `8d5940b` | ✅ | -| 005 | 07:50~08:20 | !stop 최종 수정 — getActiveCascadeId (렌더러 전용 DOM) → activeSessionId (step-probe 폴링) + 데이터 흐름 5경로 검증 | `d55b6b9` | ✅ | -| 006 | 08:25~09:15 | !stop 4차 수정 — activeSessionId 스테일 프리미티브 버그. extension.ts closure가 module-level string(불변) 참조 → step-probe getter export로 교체 + VSIX v0.4.6 | `ab0c116` | ✅ | +| 1 | 11:00 | v0.5.0 Collector 제거 + dead code 정리 + HttpBridgeContext 버그 수정 | `pending` | 🔧 | diff --git a/extension/out/extension.js b/extension/out/extension.js index affba21..0d8d269 100644 --- a/extension/out/extension.js +++ b/extension/out/extension.js @@ -67,6 +67,15 @@ function logToFile(msg) { if (!bridgePath) return; const logFile = path.join(bridgePath, 'extension.log'); + // Log rotation: truncate when >10MB, keep last 2MB + try { + const stat = fs.statSync(logFile); + if (stat.size > 10 * 1024 * 1024) { + const content = fs.readFileSync(logFile, 'utf-8'); + fs.writeFileSync(logFile, content.slice(-2 * 1024 * 1024), 'utf-8'); + } + } + catch { /* file doesn't exist yet */ } fs.appendFileSync(logFile, line + '\n', 'utf-8'); } catch (e) { @@ -84,15 +93,6 @@ let isActive = false; let autoApproveEnabled = false; // toggled via !auto from Discord let watcher = null; let wsBridge = null; // WebSocket Hub connection -const sentPendingIds = new Set(); -// Memory-based dedup: tracks recently created pending step_indexes to prevent -// regeneration after pending file deletion (by Collector/Bot response cycle). -// Map = `${conversationId}:${stepIndex}` → creation timestamp -const recentPendingSteps = new Map(); -const PENDING_MEMORY_TTL_MS = 60_000; // 60 seconds memory retention -// In-memory cache for diff_review metadata (survives pending file deletion by Collector). -// Map -const diffReviewMetadata = new Map(); // ─── Project Detection ─── function detectProjectName() { const config = vscode.workspace.getConfiguration('gravityBridge'); @@ -374,11 +374,6 @@ async function fixLSConnection() { } // ─── Approval Observer + Product.json Checksums extracted to ./html-patcher.ts ─── // ─── HTTP Bridge Server extracted to ./http-bridge.ts ─── -// Shared state for HTTP bridge context (module-level, referenced by BridgeContext too) -let sessionStalled = false; -let lastPendingStepIndex = -1; -let stallProbed = false; -let sawRunningAfterPending = true; // ─── Step Probe, Response Watcher, Approval Strategies → extracted to ./step-probe.ts ─── async function activate(context) { console.log('Gravity Bridge: activating...'); @@ -503,12 +498,11 @@ async function activate(context) { projectName, sdk, wsBridge, - autoApproveEnabled, activeSessionId, - sessionStalled, - lastPendingStepIndex, - stallProbed, - sawRunningAfterPending, + sessionStalled: false, + lastPendingStepIndex: -1, + stallProbed: false, + sawRunningAfterPending: true, setClickTrigger: (action) => { const { setClickTrigger: setTrigger } = require('./http-bridge'); setTrigger(action); @@ -520,10 +514,12 @@ async function activate(context) { writeChatSnapshot, writeChatSnapshotWithFiles, }); - // Start HTTP bridge, then setup observer + // Start HTTP bridge with live step-probe state (prevents stale primitive bug) const httpBridgeCtx = { - bridgePath, projectName, activeSessionId, wsBridge, - sessionStalled, lastPendingStepIndex, logToFile, + bridgePath, projectName, wsBridge, logToFile, + get activeSessionId() { return (0, step_probe_1.getStepProbeContext)().activeSessionId; }, + get sessionStalled() { return (0, step_probe_1.getStepProbeContext)().sessionStalled; }, + get lastPendingStepIndex() { return (0, step_probe_1.getStepProbeContext)().lastPendingStepIndex; }, }; const bridgePort = await (0, http_bridge_1.startHttpBridge)(httpBridgeCtx, sdk); if (bridgePort) { diff --git a/extension/out/extension.js.map b/extension/out/extension.js.map index 8c29d39..eba4da3 100644 --- a/extension/out/extension.js.map +++ b/extension/out/extension.js.map @@ -1 +1 @@ -{"version":3,"file":"extension.js","sourceRoot":"","sources":["../src/extension.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"extension.js","sourceRoot":"","sources":["../src/extension.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/extension/package.json b/extension/package.json index f4796a3..458c3b1 100644 --- a/extension/package.json +++ b/extension/package.json @@ -2,7 +2,7 @@ "name": "gravity-bridge", "displayName": "Gravity Bridge", "description": "Antigravity ↔ Discord 브리지 연동 확장", - "version": "0.4.6", + "version": "0.5.0", "publisher": "variet", "engines": { "vscode": "^1.100.0" diff --git a/extension/src/extension.ts b/extension/src/extension.ts index 953b649..391d5bf 100644 --- a/extension/src/extension.ts +++ b/extension/src/extension.ts @@ -16,7 +16,7 @@ import * as path from 'path'; import * as os from 'os'; import * as cp from 'child_process'; import { WSBridgeClient, WSResponseData, WSCommandData } from './ws-client'; -import { initStepProbe, BridgeContext, writePendingApproval, tryApprovalStrategies, writeRegistration, getApprovalContext, resetPendingState, handleDiffReviewResponse, getActiveSessionId as getStepProbeSessionId } from './step-probe'; +import { initStepProbe, BridgeContext, writePendingApproval, tryApprovalStrategies, writeRegistration, getApprovalContext, resetPendingState, handleDiffReviewResponse, getActiveSessionId as getStepProbeSessionId, getStepProbeContext } from './step-probe'; import { startHttpBridge, getDeterministicPort, HttpBridgeContext } from './http-bridge'; import { setupApprovalObserver } from './html-patcher'; import { watchCommandsDir, handleWSCommand, disposeCommandsWatcher, CommandHandlerContext } from './command-handler'; @@ -31,6 +31,14 @@ function logToFile(msg: string) { try { if (!bridgePath) return; const logFile = path.join(bridgePath, 'extension.log'); + // Log rotation: truncate when >10MB, keep last 2MB + try { + const stat = fs.statSync(logFile); + if (stat.size > 10 * 1024 * 1024) { + const content = fs.readFileSync(logFile, 'utf-8'); + fs.writeFileSync(logFile, content.slice(-2 * 1024 * 1024), 'utf-8'); + } + } catch { /* file doesn't exist yet */ } fs.appendFileSync(logFile, line + '\n', 'utf-8'); } catch (e: any) { console.error(`Gravity Bridge LOG WRITE FAIL: ${e.message}`); @@ -50,15 +58,6 @@ let autoApproveEnabled = false; // toggled via !auto from Discord let watcher: fs.FSWatcher | null = null; let wsBridge: WSBridgeClient | null = null; // WebSocket Hub connection -const sentPendingIds = new Set(); -// Memory-based dedup: tracks recently created pending step_indexes to prevent -// regeneration after pending file deletion (by Collector/Bot response cycle). -// Map = `${conversationId}:${stepIndex}` → creation timestamp -const recentPendingSteps = new Map(); -const PENDING_MEMORY_TTL_MS = 60_000; // 60 seconds memory retention -// In-memory cache for diff_review metadata (survives pending file deletion by Collector). -// Map -const diffReviewMetadata = new Map(); // ─── Project Detection ─── @@ -357,12 +356,6 @@ async function fixLSConnection(): Promise { // ─── HTTP Bridge Server extracted to ./http-bridge.ts ─── -// Shared state for HTTP bridge context (module-level, referenced by BridgeContext too) -let sessionStalled = false; -let lastPendingStepIndex = -1; -let stallProbed = false; -let sawRunningAfterPending = true; - // ─── Step Probe, Response Watcher, Approval Strategies → extracted to ./step-probe.ts ─── @@ -500,12 +493,11 @@ export async function activate(context: vscode.ExtensionContext) { projectName, sdk, wsBridge, - autoApproveEnabled, activeSessionId, - sessionStalled, - lastPendingStepIndex, - stallProbed, - sawRunningAfterPending, + sessionStalled: false, + lastPendingStepIndex: -1, + stallProbed: false, + sawRunningAfterPending: true, setClickTrigger: (action: 'approve' | 'reject') => { const { setClickTrigger: setTrigger } = require('./http-bridge'); setTrigger(action); @@ -517,10 +509,12 @@ export async function activate(context: vscode.ExtensionContext) { writeChatSnapshot, writeChatSnapshotWithFiles, } as BridgeContext); - // Start HTTP bridge, then setup observer + // Start HTTP bridge with live step-probe state (prevents stale primitive bug) const httpBridgeCtx: HttpBridgeContext = { - bridgePath, projectName, activeSessionId, wsBridge, - sessionStalled, lastPendingStepIndex, logToFile, + bridgePath, projectName, wsBridge, logToFile, + get activeSessionId() { return getStepProbeContext().activeSessionId; }, + get sessionStalled() { return getStepProbeContext().sessionStalled; }, + get lastPendingStepIndex() { return getStepProbeContext().lastPendingStepIndex; }, }; const bridgePort = await startHttpBridge(httpBridgeCtx, sdk); if (bridgePort) { diff --git a/extension/src/step-probe.ts b/extension/src/step-probe.ts index e783397..61da069 100644 --- a/extension/src/step-probe.ts +++ b/extension/src/step-probe.ts @@ -14,7 +14,6 @@ export interface BridgeContext { projectName: string; sdk: any; wsBridge: WSBridgeClient | null; - autoApproveEnabled: boolean; activeSessionId: string; sessionStalled: boolean; lastPendingStepIndex: number; @@ -64,6 +63,18 @@ export function getActiveSessionId(): string { return ctx?.activeSessionId || ''; } +/** + * Get step-probe context values for http-bridge. + * Prevents stale primitive copies by reading live ctx values. + */ +export function getStepProbeContext(): { activeSessionId: string; sessionStalled: boolean; lastPendingStepIndex: number } { + return { + activeSessionId: ctx?.activeSessionId || '', + sessionStalled: ctx?.sessionStalled ?? false, + lastPendingStepIndex: ctx?.lastPendingStepIndex ?? -1, + }; +} + /** * Reset pending state after successful approval. * Called after WS response triggers approval in extension.ts. diff --git a/gateway.py b/gateway.py index e2dcd61..a5d6b2e 100644 --- a/gateway.py +++ b/gateway.py @@ -1,20 +1,10 @@ -"""Gateway HTTP API + WebSocket Hub — receives data from Collectors and Extensions. +"""Gateway HTTP API + WebSocket Hub — serves WebSocket Hub and diagnostics. Runs alongside the Discord bot in the server Docker container. -Supports both: - - REST API: for legacy Collectors (HTTP polling) - - WebSocket: for direct Extension connections (real-time) Endpoints: GET /ws — WebSocket endpoint (Extension direct connection) - POST /api/pending — Collector pushes a new approval request GET /api/pending — List all pending requests (for diagnostics) - POST /api/response/{rid} — Collector polls for response (or Gateway pushes) - GET /api/response/{rid} — Get response for a specific request - POST /api/chat — Collector pushes a chat snapshot - POST /api/register — Collector registers session → project mapping - POST /api/command — Gateway pushes command to specific collector - GET /api/commands/{project} — Collector polls for commands GET /health — Health check GET /hub/status — WebSocket Hub diagnostics """ @@ -60,15 +50,9 @@ class GatewayAPI: # WebSocket endpoint (no auth middleware — Hub handles its own auth) self.app.router.add_get("/ws", self._ws_handler) self.app.router.add_get("/hub/status", self._hub_status) - # Legacy REST endpoints (Collector compatibility) + # REST endpoints self.app.router.add_get("/health", self._health) - self.app.router.add_post("/api/pending", self._post_pending) self.app.router.add_get("/api/pending", self._list_pending) - self.app.router.add_get("/api/response/{rid}", self._get_response) - self.app.router.add_post("/api/chat", self._post_chat) - self.app.router.add_post("/api/register", self._post_register) - self.app.router.add_get("/api/commands/{project}", self._get_commands) - self.app.router.add_post("/api/event", self._post_event) # ─── WebSocket Handler ─── @@ -144,25 +128,7 @@ class GatewayAPI: status["hub_connections"] = len(self.hub.connections) return web.json_response(status) - # ─── Pending Approvals (Collector → Gateway → Discord) ─── - - async def _post_pending(self, request: web.Request) -> web.Response: - """Collector pushes a pending approval request.""" - try: - data = await request.json() - rid = data.get("request_id", f"{int(time.time() * 1000)}_{uuid.uuid4().hex[:8]}") - data["request_id"] = rid - data.setdefault("timestamp", time.time()) - data.setdefault("status", "pending") - - # Write to bridge pending dir (bot's scanner will pick it up) - self.bot.bridge.transport.write_json("pending", f"{rid}.json", data) - logger.info(f"[GATEWAY] pending received: {rid[:12]} project={data.get('project_name', '?')}") - - return web.json_response({"ok": True, "request_id": rid}) - except Exception as e: - logger.error(f"[GATEWAY] pending error: {e}") - return web.json_response({"ok": False, "error": str(e)}, status=400) + # ─── Pending List (Diagnostics) ─── async def _list_pending(self, request: web.Request) -> web.Response: """List all pending requests (diagnostics).""" @@ -174,88 +140,14 @@ class GatewayAPI: "status": r.status, } for r in requests]) - # ─── Responses (Discord → Gateway → Collector) ─── - - async def _get_response(self, request: web.Request) -> web.Response: - """Collector polls for a response to a specific pending request.""" - rid = request.match_info["rid"] - data = self.bot.bridge.transport.read_json("response", f"{rid}.json") - if data is None: - return web.json_response({"waiting": True, "request_id": rid}) - - # Serve response and delete both response + pending files (one-time consumption) - self.bot.bridge.transport.delete_file("response", f"{rid}.json") - self.bot.bridge.transport.delete_file("pending", f"{rid}.json") # Bug 2 fix - return web.json_response(data) - - # ─── Chat Snapshots (Collector → Gateway → Discord) ─── - - async def _post_chat(self, request: web.Request) -> web.Response: - """Collector pushes a chat snapshot for relay to Discord.""" - try: - data = await request.json() - project = data.get("project_name", "") - content = data.get("content", "") - attached_files = data.get("attached_files", []) - - if not project or (not content and not attached_files): - return web.json_response({"ok": False, "error": "project_name and content/attached_files required"}, status=400) - - # Write to chat_snapshots dir for bot's scanner - snap_dir = self.bot.bridge.transport.bridge_dir / "chat_snapshots" if hasattr(self.bot.bridge.transport, 'bridge_dir') else None - if snap_dir: - snap_dir.mkdir(parents=True, exist_ok=True) - snap_id = f"{int(time.time() * 1000)}_{uuid.uuid4().hex[:8]}" - snap_data = { - "id": snap_id, - "project_name": project, - "content": content, - "timestamp": time.time(), - } - if attached_files: - snap_data["attached_files"] = attached_files - (snap_dir / f"{snap_id}.json").write_text( - json.dumps(snap_data, ensure_ascii=False, indent=2), - encoding="utf-8", - ) - - af_info = f" +{len(attached_files)} files" if attached_files else "" - logger.info(f"[GATEWAY] chat received: project={project} len={len(content)}{af_info}") - return web.json_response({"ok": True}) - except Exception as e: - logger.error(f"[GATEWAY] chat error: {e}") - return web.json_response({"ok": False, "error": str(e)}, status=400) - - # ─── Registration (Collector → Gateway) ─── - - async def _post_register(self, request: web.Request) -> web.Response: - """Collector registers session → project mapping.""" - try: - data = await request.json() - session_id = data.get("conversation_id", "") - project = data.get("project_name", "") - if session_id and project: - self.bot.conv_to_project[session_id] = project - logger.info(f"[GATEWAY] registered: {session_id[:8]} → {project}") - return web.json_response({"ok": True}) - except Exception as e: - return web.json_response({"ok": False, "error": str(e)}, status=400) - - # ─── Commands (Gateway → Collector) ─── - - async def _get_commands(self, request: web.Request) -> web.Response: - """Collector polls for commands (e.g. !auto, !stop, text messages).""" - project = request.match_info["project"] - commands = self._commands.pop(project, []) - return web.json_response({"commands": commands}) + # ─── Commands (Legacy fallback: Bot → Extension via HTTP when Hub unavailable) ─── def push_command(self, project: str, command: dict): - """Bot pushes a command for a Collector to pick up.""" + """Bot pushes a command for Extension to pick up (Hub fallback).""" if project not in self._commands: self._commands[project] = [] command.setdefault("_ts", time.time()) # TTL tracking self._commands[project].append(command) - # Auto-cleanup stale commands (Security 3: memory leak prevention) self._cleanup_stale_commands() def _cleanup_stale_commands(self): @@ -269,35 +161,6 @@ class GatewayAPI: if not self._commands[project]: del self._commands[project] - # ─── Brain Events (Collector → Gateway → Discord) ─── - - async def _post_event(self, request: web.Request) -> web.Response: - """Collector pushes a brain event (file change) for relay to Discord.""" - try: - data = await request.json() - from watcher import BrainEvent, EventType - - event_type_str = data.get("event_type", "file_changed") - event_type = EventType(event_type_str) - - event = BrainEvent( - event_type=event_type, - conversation_id=data.get("conversation_id", ""), - file_name=data.get("file_name", ""), - file_path=Path(data["file_path"]) if data.get("file_path") else None, - content=data.get("content", ""), - timestamp=data.get("timestamp", time.time()), - ) - - # Inject into bot's event queue (same path as local mode) - await self.bot.event_queue.put(event) - logger.info(f"[GATEWAY] event received: {event_type_str} {event.file_name} conv={event.conversation_id[:8]}") - - return web.json_response({"ok": True}) - except Exception as e: - logger.error(f"[GATEWAY] event error: {e}") - return web.json_response({"ok": False, "error": str(e)}, status=400) - # ─── Run ─── async def start(self): diff --git a/main.py b/main.py index 63a5421..ed61ab2 100644 --- a/main.py +++ b/main.py @@ -51,47 +51,6 @@ async def main(): # Get the running loop loop = asyncio.get_running_loop() - # ── Collector mode (DEPRECATED): no Discord bot, just relay local ↔ Gateway ── - if Config.BOT_MODE == "remote": - logger.warning("=" * 50) - logger.warning("⚠️ Collector mode (BOT_MODE=remote) is DEPRECATED") - logger.warning("Extension이 WebSocket으로 Hub에 직접 연결합니다.") - logger.warning("BOT_MODE=gateway로 전환하세요.") - logger.warning("=" * 50) - - from bridge import LocalTransport, RemoteTransport - from collector import CollectorBridge - - if not Config.REMOTE_BRIDGE_URL: - logger.error("REMOTE_BRIDGE_URL is required for remote (Collector) mode") - sys.exit(1) - - bridge_dir = Config.BRAIN_PATH.parent / "bridge" - local = LocalTransport(bridge_dir) - local.ensure_dirs() - remote = RemoteTransport(Config.REMOTE_BRIDGE_URL, api_key=Config.GATEWAY_API_KEY) - - collector = CollectorBridge(local, remote, project_name=Config.PROJECT_NAME, - event_queue=event_queue) - logger.info(f"Collector mode: {Config.REMOTE_BRIDGE_URL}") - - # Optionally start watcher for brain events (local display only) - watcher = BrainWatcher(event_queue, loop) - - try: - watcher.start() - logger.info(f"Watcher started, {len(watcher.known_sessions)} existing sessions") - await collector.start() - except KeyboardInterrupt: - logger.info("Received keyboard interrupt") - except Exception as e: - logger.error(f"Fatal error: {e}", exc_info=True) - finally: - await collector.stop() - watcher.stop() - logger.info("Collector shutdown complete") - return - # ── Local / Gateway mode ── # Create components diff --git a/start_bot.bat b/start_bot.bat index a2685d8..b22dba0 100644 --- a/start_bot.bat +++ b/start_bot.bat @@ -7,11 +7,10 @@ echo ║ Gravity Bridge Bot Launcher ║ echo ╚══════════════════════════════════════╝ echo. -echo [WARN] 로컬 Collector (BOT_MODE=remote)는 더 이상 필요하지 않습니다. -echo [WARN] Extension이 WebSocket으로 Hub에 직접 연결합니다. -echo [WARN] 서버에서 Docker로 실행하세요: docker compose up -d +echo [INFO] 로컬 테스트 (BOT_MODE=local)를 시작합니다. +echo [INFO] 서버 배포는 BOT_MODE=gateway로 실행하세요. echo. -echo 로컬 테스트 (BOT_MODE=local)를 원하시면 아무 키나 누르세요... +echo 시작하려면 아무 키나 누르세요... pause >nul REM — Find Python (conda first, then system)