Merge pull request #1 from cobra91/windows-support

feat: Windows native support — tkinter GUI + cross-platform proxy
This commit is contained in:
cobra91
2026-05-25 14:59:27 +02:00
committed by GitHub
Unverified
5 changed files with 5329 additions and 215 deletions

101
src/cleanup-codex-stale.py Normal file
View File

@@ -0,0 +1,101 @@
#!/usr/bin/env python3
"""Cleanup stale Codex Launcher processes and artifacts — cross-platform.
Kills registered process groups and removes stale PID/socket files left
by previous Codex Launcher sessions.
Windows: uses taskkill /F /T /PID
Linux: uses kill -TERM -- -PGID
"""
import json, os, sys, subprocess, time
from pathlib import Path
IS_WINDOWS = sys.platform == "win32"
if IS_WINDOWS:
_local = os.environ.get("LOCALAPPDATA", str(Path.home() / "AppData" / "Local"))
PID_REGISTRY = Path(_local) / "codex-launcher" / "pids.json"
CODEX_DIR = Path.home() / ".codex"
_local_share = Path(_local)
_cache = Path(_local)
else:
PID_REGISTRY = Path.home() / ".cache" / "codex-launcher" / "pids.json"
CODEX_DIR = Path.home() / ".codex"
_local_share = Path.home() / ".local" / "share"
_cache = Path.home() / ".cache"
def kill_group(pid):
if IS_WINDOWS:
subprocess.run(["taskkill", "/F", "/T", "/PID", str(pid)],
capture_output=True, timeout=10)
else:
import signal
try:
pgid = os.getpgid(pid)
os.killpg(pgid, signal.SIGTERM)
time.sleep(0.5)
try:
os.killpg(pgid, signal.SIGKILL)
except OSError:
pass
except OSError:
pass
def main():
print("[cleanup] Cleaning up stale Codex Launcher processes...", file=sys.stderr)
if PID_REGISTRY.exists():
try:
with open(PID_REGISTRY) as f:
registry = json.load(f)
except Exception as e:
print(f"[cleanup] Failed to read PID registry: {e}", file=sys.stderr)
registry = {}
for kind, info in registry.items():
pid = info.get("pid") if isinstance(info, dict) else info
if pid and isinstance(pid, int):
print(f"[cleanup] Killing {kind} (PID {pid})", file=sys.stderr)
kill_group(pid)
try:
PID_REGISTRY.unlink()
except OSError:
pass
else:
print("[cleanup] No PID registry found — nothing to stop", file=sys.stderr)
stale_files = []
if IS_WINDOWS:
stale_files = [
_cache / "codex-desktop" / ".codex-desktop-pid",
_cache / "codex-desktop" / ".webview-pid",
]
else:
stale_files = [
CODEX_DIR / ".launch-action-socket",
CODEX_DIR / ".codex-desktop-launch-action",
CODEX_DIR / ".codex-desktop-pid",
CODEX_DIR / ".webview-pid",
_local_share / "codex-desktop" / ".codex-desktop-pid",
_local_share / "codex-desktop" / ".webview-pid",
_cache / "codex-desktop" / ".codex-desktop-pid",
_cache / "codex-desktop" / ".webview-pid",
]
for fp in stale_files:
try:
if fp.exists():
fp.unlink()
print(f"[cleanup] Removed {fp}", file=sys.stderr)
except OSError:
pass
print("[cleanup] Done", file=sys.stderr)
if __name__ == "__main__":
main()

2700
src/codex-launcher-gui.py Normal file

File diff suppressed because it is too large Load Diff

1972
src/codex_launcher_lib.py Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -160,6 +160,9 @@ import time, uuid, os, sys, argparse, threading, socket, collections, contextlib
import dataclasses import dataclasses
import http.client import http.client
import selectors import selectors
import tempfile
_IS_WINDOWS = sys.platform == "win32"
# ═══════════════════════════════════════════════════════════════════ # ═══════════════════════════════════════════════════════════════════
# Config # Config
@@ -241,13 +244,28 @@ MODELS = []
CC_VERSION = "" CC_VERSION = ""
REASONING_ENABLED = True REASONING_ENABLED = True
REASONING_EFFORT = "medium" REASONING_EFFORT = "medium"
FORCE_MODEL = ""
BGP_ROUTES = [] BGP_ROUTES = []
SERVER = None SERVER = None
_LOG_DIR = os.path.join(os.path.expanduser("~"), ".cache", "codex-proxy") if _IS_WINDOWS:
_LOG_DIR = os.path.join(os.environ.get("LOCALAPPDATA", os.path.expanduser("~")), "codex-proxy")
else:
_LOG_DIR = os.path.join(os.path.expanduser("~"), ".cache", "codex-proxy")
os.makedirs(_LOG_DIR, exist_ok=True) os.makedirs(_LOG_DIR, exist_ok=True)
_REQUESTS_DIR = os.path.join(_LOG_DIR, "requests") _REQUESTS_DIR = os.path.join(_LOG_DIR, "requests")
os.makedirs(_REQUESTS_DIR, exist_ok=True) os.makedirs(_REQUESTS_DIR, exist_ok=True)
try:
for _f in os.listdir(_REQUESTS_DIR):
if _f.endswith(".tmp"):
os.remove(os.path.join(_REQUESTS_DIR, _f))
for _f in os.listdir(_LOG_DIR):
if _f.startswith("proxy-") and _f.endswith(".json"):
os.remove(os.path.join(_LOG_DIR, _f))
if _f.startswith("models-") and _f.endswith(".json"):
os.remove(os.path.join(_LOG_DIR, _f))
except Exception:
pass
_stats_path = os.path.join(_LOG_DIR, "usage-stats.json") _stats_path = os.path.join(_LOG_DIR, "usage-stats.json")
_provider_caps_path = os.path.join(_LOG_DIR, "provider-caps.json") _provider_caps_path = os.path.join(_LOG_DIR, "provider-caps.json")
_stats_lock = threading.Lock() _stats_lock = threading.Lock()
@@ -257,7 +275,7 @@ _STATS_FLUSH_INTERVAL = 5.0
_STATS = {} _STATS = {}
try: try:
_LOG_FILE = open(os.path.join(_LOG_DIR, "proxy.log"), "a") _LOG_FILE = open(os.path.join(_LOG_DIR, "proxy.log"), "a", encoding="utf-8")
except Exception: except Exception:
_LOG_FILE = None _LOG_FILE = None
@@ -273,6 +291,9 @@ _deepseek_reasoning_store = {}
_deepseek_reasoning_lock = threading.Lock() _deepseek_reasoning_lock = threading.Lock()
_MAX_DS_STORED = 100 _MAX_DS_STORED = 100
_last_reasoning_store = {}
_last_reasoning_lock = threading.Lock()
_crof_lock = threading.Lock() _crof_lock = threading.Lock()
_provider_caps_lock = threading.Lock() _provider_caps_lock = threading.Lock()
_provider_caps = None _provider_caps = None
@@ -302,7 +323,10 @@ _CODEBUFF_AGENT_MAP = {
"moonshotai/kimi-k2.6": "base2-free-kimi", "moonshotai/kimi-k2.6": "base2-free-kimi",
"minimax/minimax-m2.7": "base2-free", "minimax/minimax-m2.7": "base2-free",
} }
_CODEBUFF_CREDS_PATH = os.path.join(os.path.expanduser("~"), ".config", "manicode", "credentials.json") if _IS_WINDOWS:
_CODEBUFF_CREDS_PATH = os.path.join(os.environ.get("APPDATA", os.path.expanduser("~")), "manicode", "credentials.json")
else:
_CODEBUFF_CREDS_PATH = os.path.join(os.path.expanduser("~"), ".config", "manicode", "credentials.json")
_codebuff_token_cache = {"token": None, "checked": 0} _codebuff_token_cache = {"token": None, "checked": 0}
_codebuff_session_cache = {"instance_id": None, "expires": 0, "model": None} _codebuff_session_cache = {"instance_id": None, "expires": 0, "model": None}
_codebuff_token_lock = threading.Lock() _codebuff_token_lock = threading.Lock()
@@ -634,7 +658,7 @@ def _refresh_google_token(token_data, token_path):
new_tokens = json.loads(resp.read()) new_tokens = json.loads(resp.read())
token_data["access_token"] = new_tokens.get("access_token", token_data.get("access_token")) token_data["access_token"] = new_tokens.get("access_token", token_data.get("access_token"))
token_data["expires_at"] = time.time() + new_tokens.get("expires_in", 3600) token_data["expires_at"] = time.time() + new_tokens.get("expires_in", 3600)
with open(token_path, "w") as f: with open(token_path, "w", encoding="utf-8") as f:
json.dump(token_data, f, indent=2) json.dump(token_data, f, indent=2)
print("[oauth] token refreshed OK", file=sys.stderr) print("[oauth] token refreshed OK", file=sys.stderr)
return token_data["access_token"] return token_data["access_token"]
@@ -727,7 +751,7 @@ def _fetch_antigravity_version():
version = m.group(0) version = m.group(0)
try: try:
os.makedirs(os.path.dirname(cache_path), exist_ok=True) os.makedirs(os.path.dirname(cache_path), exist_ok=True)
with open(cache_path, "w") as f: with open(cache_path, "w", encoding="utf-8") as f:
json.dump({"version": version, "checked_at": time.time()}, f) json.dump({"version": version, "checked_at": time.time()}, f)
except Exception: except Exception:
pass pass
@@ -762,6 +786,7 @@ def _init_runtime():
CC_VERSION = CONFIG.get("cc_version", "") CC_VERSION = CONFIG.get("cc_version", "")
REASONING_ENABLED = CONFIG.get("reasoning_enabled", True) REASONING_ENABLED = CONFIG.get("reasoning_enabled", True)
REASONING_EFFORT = CONFIG.get("reasoning_effort", "medium") REASONING_EFFORT = CONFIG.get("reasoning_effort", "medium")
FORCE_MODEL = (CONFIG.get("force_model") or "").strip()
BGP_ROUTES = CONFIG.get("bgp_routes", []) BGP_ROUTES = CONFIG.get("bgp_routes", [])
_api_key_pool = None _api_key_pool = None
if API_KEY and "," in API_KEY and not OAUTH_PROVIDER.startswith("google") and BACKEND not in ("codebuff", "freebuff"): if API_KEY and "," in API_KEY and not OAUTH_PROVIDER.startswith("google") and BACKEND not in ("codebuff", "freebuff"):
@@ -903,7 +928,7 @@ def _load_provider_caps():
def _save_provider_caps(): def _save_provider_caps():
try: try:
os.makedirs(os.path.dirname(_provider_caps_path), exist_ok=True) os.makedirs(os.path.dirname(_provider_caps_path), exist_ok=True)
with open(_provider_caps_path, "w") as f: with open(_provider_caps_path, "w", encoding="utf-8") as f:
json.dump(_provider_caps or {}, f, indent=2) json.dump(_provider_caps or {}, f, indent=2)
except Exception as e: except Exception as e:
print(f"[provider-sensor] failed to save caps: {e}", file=sys.stderr) print(f"[provider-sensor] failed to save caps: {e}", file=sys.stderr)
@@ -959,7 +984,7 @@ def _refresh_oauth_token_for(api_key, oauth_provider):
new_tokens = json.loads(resp.read()) new_tokens = json.loads(resp.read())
tokens["access_token"] = new_tokens.get("access_token", tokens.get("access_token")) tokens["access_token"] = new_tokens.get("access_token", tokens.get("access_token"))
tokens["expires_at"] = time.time() + new_tokens.get("expires_in", 3600) tokens["expires_at"] = time.time() + new_tokens.get("expires_in", 3600)
with open(token_path, "w") as f: with open(token_path, "w", encoding="utf-8") as f:
json.dump(tokens, f, indent=2) json.dump(tokens, f, indent=2)
print("[oauth] token refreshed OK", file=sys.stderr) print("[oauth] token refreshed OK", file=sys.stderr)
return tokens["access_token"] return tokens["access_token"]
@@ -983,7 +1008,7 @@ def _load_stats():
def _atomic_write_json(path, obj): def _atomic_write_json(path, obj):
tmp = path + ".tmp" tmp = path + ".tmp"
with open(tmp, "w") as f: with open(tmp, "w", encoding="utf-8") as f:
json.dump(obj, f, indent=2, ensure_ascii=False) json.dump(obj, f, indent=2, ensure_ascii=False)
os.replace(tmp, path) os.replace(tmp, path)
@@ -1297,7 +1322,7 @@ def _load_bgp_stats():
def _save_bgp_stats(stats): def _save_bgp_stats(stats):
tmp = _BGP_STATS_PATH + ".tmp" tmp = _BGP_STATS_PATH + ".tmp"
with open(tmp, "w") as f: with open(tmp, "w", encoding="utf-8") as f:
json.dump(stats, f, indent=2) json.dump(stats, f, indent=2)
os.replace(tmp, _BGP_STATS_PATH) os.replace(tmp, _BGP_STATS_PATH)
@@ -1790,7 +1815,7 @@ def save_request_snapshot(request_id, body):
} }
path = os.path.join(_REQUESTS_DIR, f"{request_id}.json") path = os.path.join(_REQUESTS_DIR, f"{request_id}.json")
tmp = path + ".tmp" tmp = path + ".tmp"
with open(tmp, "w") as f: with open(tmp, "w", encoding="utf-8") as f:
json.dump(snapshot, f, ensure_ascii=False, indent=2) json.dump(snapshot, f, ensure_ascii=False, indent=2)
os.replace(tmp, path) os.replace(tmp, path)
_rotate_snapshots() _rotate_snapshots()
@@ -1813,7 +1838,7 @@ def update_snapshot_response(request_id, status, duration_s=None, error=None):
meta["error"] = str(error)[:200] meta["error"] = str(error)[:200]
snapshot["_meta"] = meta snapshot["_meta"] = meta
tmp = path + ".tmp" tmp = path + ".tmp"
with open(tmp, "w") as f: with open(tmp, "w", encoding="utf-8") as f:
json.dump(snapshot, f, ensure_ascii=False, indent=2) json.dump(snapshot, f, ensure_ascii=False, indent=2)
os.replace(tmp, path) os.replace(tmp, path)
except Exception: except Exception:
@@ -1865,6 +1890,27 @@ def _bucket_for_route(route):
# OpenAI-compat backend # OpenAI-compat backend
# ═══════════════════════════════════════════════════════════════════ # ═══════════════════════════════════════════════════════════════════
def _inject_stored_reasoning(messages):
with _last_reasoning_lock:
snapshot = dict(_last_reasoning_store)
if not snapshot:
return messages
expired = [k for k, v in snapshot.items() if time.time() - v["ts"] > _RESPONSE_TTL]
for k in expired:
with _last_reasoning_lock:
_last_reasoning_store.pop(k, None)
snapshot.pop(k, None)
if not snapshot:
return messages
latest = max(snapshot.values(), key=lambda v: v["ts"])
reasoning = latest.get("reasoning", "")
if not reasoning:
return messages
for msg in messages:
if msg.get("role") == "assistant" and "reasoning_content" not in msg and msg.get("tool_calls"):
msg["reasoning_content"] = reasoning
return messages
def oa_input_to_messages(input_data): def oa_input_to_messages(input_data):
msgs = [] msgs = []
tool_name_by_id = {} tool_name_by_id = {}
@@ -2384,10 +2430,10 @@ def an_stream_to_sse(stream, model, req_id):
"status": status, "created": int(time.time()), "output": completed}}) "status": status, "created": int(time.time()), "output": completed}})
_DEFAULT_CC_CONFIG = { _DEFAULT_CC_CONFIG = {
"workingDir": "/tmp", "workingDir": tempfile.gettempdir(),
"date": "", "date": "",
"environment": "linux", "environment": "windows" if _IS_WINDOWS else "linux",
"shell": "bash", "shell": "powershell" if _IS_WINDOWS else "bash",
"files": [], "files": [],
"structure": [], "structure": [],
"isGitRepo": False, "isGitRepo": False,
@@ -2462,13 +2508,24 @@ def _build_explore_cmd(text_for_url):
api_base = repo_url.replace("/admin/", "/api/v1/repos/") api_base = repo_url.replace("/admin/", "/api/v1/repos/")
else: else:
api_base = repo_url api_base = repo_url
cmd = ( if _IS_WINDOWS:
f"cd /tmp && " cmd = (
f"curl -sL --max-time 15 '{api_base}/contents/README.md' 2>/dev/null | " f"cd $env:TEMP; "
f"python3 -c \"import sys,json,base64; d=json.load(sys.stdin); print(base64.b64decode(d['content']).decode())\" 2>/dev/null | head -600 && " f"$r = Invoke-WebRequest -Uri '{api_base}/contents/README.md' -UseBasicParsing -TimeoutSec 15 2>$null; "
f"curl -sL --max-time 15 '{api_base}/contents' 2>/dev/null | python3 -c \"import sys,json; d=json.load(sys.stdin); print('\\n'.join(f'{{x.get(\'path\')}} {{x.get(\'type\')}}' for x in d[:50]))\" 2>/dev/null && " f"if ($r) {{ $j = $r.Content | ConvertFrom-Json; [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($j.content)) | Select-Object -First 600 }}; "
f"curl -sL --max-time 15 '{api_base}/releases' 2>/dev/null | python3 -c \"import sys,json; d=json.load(sys.stdin); print(json.dumps(d[:3], indent=2)[:2000])\" 2>/dev/null" f"$r2 = Invoke-WebRequest -Uri '{api_base}/contents' -UseBasicParsing -TimeoutSec 15 2>$null; "
) f"if ($r2) {{ $j2 = $r2.Content | ConvertFrom-Json; $j2 | Select-Object -First 50 | ForEach-Object {{ $_.path + ' ' + $_.type }} }}; "
f"$r3 = Invoke-WebRequest -Uri '{api_base}/releases' -UseBasicParsing -TimeoutSec 15 2>$null; "
f"if ($r3) {{ ($r3.Content | ConvertFrom-Json | Select-Object -First 3 | ConvertTo-Json).Substring(0, [Math]::Min(2000, ($r3.Content | ConvertFrom-Json | Select-Object -First 3 | ConvertTo-Json).Length)) }}"
)
else:
cmd = (
f"cd /tmp && "
f"curl -sL --max-time 15 '{api_base}/contents/README.md' 2>/dev/null | "
f"python3 -c \"import sys,json,base64; d=json.load(sys.stdin); print(base64.b64decode(d['content']).decode())\" 2>/dev/null | head -600 && "
f"curl -sL --max-time 15 '{api_base}/contents' 2>/dev/null | python3 -c \"import sys,json; d=json.load(sys.stdin); print('\\n'.join(f'{{x.get(\'path\')}} {{x.get(\'type\')}}' for x in d[:50]))\" 2>/dev/null && "
f"curl -sL --max-time 15 '{api_base}/releases' 2>/dev/null | python3 -c \"import sys,json; d=json.load(sys.stdin); print(json.dumps(d[:3], indent=2)[:2000])\" 2>/dev/null"
)
return cmd, "Explore repository to understand the app and gather README, root contents, and releases for the landing page." return cmd, "Explore repository to understand the app and gather README, root contents, and releases for the landing page."
def _parse_commandcode_text_tool_calls(text): def _parse_commandcode_text_tool_calls(text):
@@ -3322,7 +3379,10 @@ def cc_stream_to_sse(cc_stream, model, req_id):
_url_in_text = re.search(r"https?://[^\s\]'\\>\",]+", text_buf) _url_in_text = re.search(r"https?://[^\s\]'\\>\",]+", text_buf)
if _url_in_text: if _url_in_text:
_synth_url = _url_in_text.group(0).rstrip(")].,;'\\\"") _synth_url = _url_in_text.group(0).rstrip(")].,;'\\\"")
_synth_cmd = f"curl -sL --max-time 15 '{_synth_url}' 2>/dev/null | head -200" if _IS_WINDOWS:
_synth_cmd = f"Invoke-WebRequest -Uri '{_synth_url}' -UseBasicParsing -TimeoutSec 15 | Select-Object -ExpandProperty Content | Select-Object -First 200"
else:
_synth_cmd = f"curl -sL --max-time 15 '{_synth_url}' 2>/dev/null | head -200"
_synth_just = "Auto-synthesized: URL detected in text, fetching" _synth_just = "Auto-synthesized: URL detected in text, fetching"
# Heuristic 2: File path references → list or read # Heuristic 2: File path references → list or read
@@ -3330,7 +3390,10 @@ def cc_stream_to_sse(cc_stream, model, req_id):
_file_m = re.search(r"(?:read|open|view|check|examine|cat|show)\s+(?:the\s+)?(?:file\s+)?[`'\"]?(/[^\s'\"]+\.\w+)", _tl) _file_m = re.search(r"(?:read|open|view|check|examine|cat|show)\s+(?:the\s+)?(?:file\s+)?[`'\"]?(/[^\s'\"]+\.\w+)", _tl)
if _file_m: if _file_m:
_fpath = _file_m.group(1) _fpath = _file_m.group(1)
_synth_cmd = f"cat '{_fpath}' 2>/dev/null | head -200 || ls -la '{_fpath}'" if _IS_WINDOWS:
_synth_cmd = f"Get-Content '{_fpath}' -ErrorAction SilentlyContinue | Select-Object -First 200; if (-not $?) {{ Get-Item '{_fpath}' | Select-Object Name,Length,LastWriteTime }}"
else:
_synth_cmd = f"cat '{_fpath}' 2>/dev/null | head -200 || ls -la '{_fpath}'"
_synth_just = f"Auto-synthesized: file reference detected ({_fpath})" _synth_just = f"Auto-synthesized: file reference detected ({_fpath})"
# Heuristic 3: Shell command mentioned in backticks or quotes # Heuristic 3: Shell command mentioned in backticks or quotes
@@ -3358,7 +3421,10 @@ def cc_stream_to_sse(cc_stream, model, req_id):
if _intent_m: if _intent_m:
_intent_text = _intent_m.group(1).strip() _intent_text = _intent_m.group(1).strip()
if len(_intent_text) > 10 and len(_intent_text) < 200: if len(_intent_text) > 10 and len(_intent_text) < 200:
_synth_cmd = f"echo 'Stuck recovery: model intent was: {_intent_text[:100]}'" if _IS_WINDOWS:
_synth_cmd = f"Write-Output 'Stuck recovery: model intent was: {_intent_text[:100]}'"
else:
_synth_cmd = f"echo 'Stuck recovery: model intent was: {_intent_text[:100]}'"
_synth_just = f"Auto-synthesized from intent text: {_intent_text[:80]}" _synth_just = f"Auto-synthesized from intent text: {_intent_text[:80]}"
if _synth_cmd: if _synth_cmd:
@@ -3891,11 +3957,13 @@ def _extract_text(content):
# HTTP Server # HTTP Server
# ═══════════════════════════════════════════════════════════════════ # ═══════════════════════════════════════════════════════════════════
_MAX_REQLOG_LINES = 2000
def _log_resp(resp_id, status, output): def _log_resp(resp_id, status, output):
try: try:
import datetime as _dt import datetime as _dt
_lp = os.path.join(_LOG_DIR, "requests.log") _lp = os.path.join(_LOG_DIR, "requests.log")
with open(_lp, "a") as _f: with open(_lp, "a", encoding="utf-8") as _f:
_f.write(f" RESPONSE id={resp_id} status={status}\n") _f.write(f" RESPONSE id={resp_id} status={status}\n")
if output: if output:
for o in output: for o in output:
@@ -3908,6 +3976,11 @@ def _log_resp(resp_id, status, output):
_f.write(f" -> {ot}\n") _f.write(f" -> {ot}\n")
_f.write(f"{'='*60}\n") _f.write(f"{'='*60}\n")
_f.flush() _f.flush()
_f.seek(0)
lines = _f.readlines()
if len(lines) > _MAX_REQLOG_LINES:
with open(_lp, "w", encoding="utf-8") as _f2:
_f2.writelines(lines[-_MAX_REQLOG_LINES:])
except Exception: except Exception:
pass pass
@@ -4064,10 +4137,26 @@ class Handler(http.server.BaseHTTPRequestHandler):
info["total"] = 0 info["total"] = 0
self.send_json(200, info) self.send_json(200, info)
elif self.path in ("/health", "/v1/health"): elif self.path in ("/health", "/v1/health"):
import resource as _res
_mem_mb = 0 _mem_mb = 0
try: try:
_mem_mb = _res.getrusage(_res.RUSAGE_SELF).ru_maxrss / 1024 if _IS_WINDOWS:
import ctypes
class _PMI(ctypes.Structure):
_fields_ = [("cb", ctypes.c_ulong), ("PageFaultCount", ctypes.c_ulong),
("PeakWorkingSetSize", ctypes.c_size_t), ("WorkingSetSize", ctypes.c_size_t),
("QuotaPeakPagedPoolUsage", ctypes.c_size_t), ("QuotaPagedPoolUsage", ctypes.c_size_t),
("QuotaPeakNonPagedPoolUsage", ctypes.c_size_t), ("QuotaNonPagedPoolUsage", ctypes.c_size_t),
("PagefileUsage", ctypes.c_size_t), ("PeakPagefileUsage", ctypes.c_size_t)]
_pmi = _PMI()
_pmi.cb = ctypes.sizeof(_PMI)
ctypes.windll.psapi.GetProcessMemoryInfo.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_ulong]
ctypes.windll.psapi.GetProcessMemoryInfo.restype = ctypes.c_int
ctypes.windll.psapi.GetProcessMemoryInfo(
ctypes.windll.kernel32.GetCurrentProcess(), ctypes.byref(_pmi), _pmi.cb)
_mem_mb = _pmi.PeakWorkingSetSize / (1024 * 1024)
else:
import resource as _res
_mem_mb = _res.getrusage(_res.RUSAGE_SELF).ru_maxrss / 1024
except Exception: except Exception:
pass pass
_uptime = time.time() - _START_TIME if '_START_TIME' in dir() else 0 _uptime = time.time() - _START_TIME if '_START_TIME' in dir() else 0
@@ -4122,12 +4211,12 @@ class Handler(http.server.BaseHTTPRequestHandler):
resolved_types = [i.get("type") for i in input_data] if isinstance(input_data, list) else "str" resolved_types = [i.get("type") for i in input_data] if isinstance(input_data, list) else "str"
print(f"[{_sid}] prev_id={prev_id} raw={raw_types} resolved={resolved_types}", file=sys.stderr) print(f"[{_sid}] prev_id={prev_id} raw={raw_types} resolved={resolved_types}", file=sys.stderr)
with open(_log_path, "a") as _lf: with open(_log_path, "a", encoding="utf-8") as _lf:
_lf.write(f"\n{'='*60}\n{_ts} [session={_sid}] REQUEST {self.path}\n") _lf.write(f"\n{'='*60}\n{_ts} [session={_sid}] REQUEST {self.path}\n")
_lf.write(f" prev_id={prev_id}\n") _lf.write(f" prev_id={prev_id}\n")
_lf.write(f" raw_input_types={raw_types}\n") _lf.write(f" raw_input_types={raw_types}\n")
_lf.write(f" resolved_input_types={resolved_types}\n") _lf.write(f" resolved_input_types={resolved_types}\n")
_lf.write(f" stream={body.get('stream')} model={body.get('model')}\n") _lf.write(f" stream={body.get('stream')} model={body.get('model')} force_model={FORCE_MODEL}\n")
_lf.write(f" store_keys={list(_response_store.keys())}\n") _lf.write(f" store_keys={list(_response_store.keys())}\n")
if isinstance(input_data, list): if isinstance(input_data, list):
for i, item in enumerate(input_data): for i, item in enumerate(input_data):
@@ -4143,6 +4232,9 @@ class Handler(http.server.BaseHTTPRequestHandler):
_lf.flush() _lf.flush()
model = body.get("model", MODELS[0]["id"] if MODELS else "unknown") model = body.get("model", MODELS[0]["id"] if MODELS else "unknown")
if FORCE_MODEL:
model = FORCE_MODEL
body["model"] = FORCE_MODEL
stream = body.get("stream", False) stream = body.get("stream", False)
_desktop_forced_models = {"gpt-5.4-mini", "gpt-5.4", "gpt-5.5", "gpt-5-codex", "gpt-5.3-codex"} _desktop_forced_models = {"gpt-5.4-mini", "gpt-5.4", "gpt-5.5", "gpt-5-codex", "gpt-5.3-codex"}
_launcher_model = os.environ.get("CODEX_LAUNCHER_MODEL", "") _launcher_model = os.environ.get("CODEX_LAUNCHER_MODEL", "")
@@ -4211,6 +4303,7 @@ class Handler(http.server.BaseHTTPRequestHandler):
body["input"] = input_data body["input"] = input_data
messages = oa_input_to_messages(input_data) messages = oa_input_to_messages(input_data)
messages = _inject_stored_reasoning(messages)
instructions = body.get("instructions", "").strip() instructions = body.get("instructions", "").strip()
if instructions: if instructions:
messages.insert(0, {"role": "system", "content": instructions}) messages.insert(0, {"role": "system", "content": instructions})
@@ -4612,7 +4705,7 @@ class Handler(http.server.BaseHTTPRequestHandler):
if n_contents > 10: if n_contents > 10:
debug_path = os.path.join(_LOG_DIR, f"gemini-long-ctx-{self._session_id}.json") debug_path = os.path.join(_LOG_DIR, f"gemini-long-ctx-{self._session_id}.json")
try: try:
with open(debug_path, "w") as dbg: with open(debug_path, "w", encoding="utf-8") as dbg:
json.dump({"contents_count": n_contents, "contents_roles": [c.get("role") for c in contents], "has_tools": has_tools, "model": model, "wrapped_size": len(body_b)}, dbg, indent=2) json.dump({"contents_count": n_contents, "contents_roles": [c.get("role") for c in contents], "has_tools": has_tools, "model": model, "wrapped_size": len(body_b)}, dbg, indent=2)
except Exception: except Exception:
pass pass
@@ -4628,7 +4721,7 @@ class Handler(http.server.BaseHTTPRequestHandler):
if e.code == 400 and OAUTH_PROVIDER.startswith("google"): if e.code == 400 and OAUTH_PROVIDER.startswith("google"):
try: try:
debug_path = os.path.join(_LOG_DIR, "gemini-last-400-request.json") debug_path = os.path.join(_LOG_DIR, "gemini-last-400-request.json")
with open(debug_path, "w") as dbg: with open(debug_path, "w", encoding="utf-8") as dbg:
json.dump({"endpoint": ep, "model": model, "wrapped": wrapped, "error": err_body}, dbg, indent=2) json.dump({"endpoint": ep, "model": model, "wrapped": wrapped, "error": err_body}, dbg, indent=2)
print(f"[{self._session_id}] saved 400 debug request to {debug_path}", file=sys.stderr) print(f"[{self._session_id}] saved 400 debug request to {debug_path}", file=sys.stderr)
except Exception: except Exception:
@@ -4940,7 +5033,8 @@ class Handler(http.server.BaseHTTPRequestHandler):
pass pass
try: try:
for event in oa_stream_to_sse(upstream, model, body.get("request_id") or body.get("id")): reasoning_out = {}
for event in oa_stream_to_sse(upstream, model, body.get("request_id") or body.get("id"), _reasoning_out=reasoning_out):
if tracker and tracker.cancelled.is_set(): if tracker and tracker.cancelled.is_set():
print("[translate-proxy] stream cancelled", file=sys.stderr) print("[translate-proxy] stream cancelled", file=sys.stderr)
break break
@@ -4958,6 +5052,16 @@ class Handler(http.server.BaseHTTPRequestHandler):
_log_resp(last_resp_id, last_status, last_output) _log_resp(last_resp_id, last_status, last_output)
if last_resp_id and input_data is not None: if last_resp_id and input_data is not None:
store_response(last_resp_id, input_data, last_output) store_response(last_resp_id, input_data, last_output)
if reasoning_out.get("text"):
with _last_reasoning_lock:
_last_reasoning_store[last_resp_id or ""] = {
"reasoning": reasoning_out["text"],
"tool_calls": reasoning_out.get("tool_calls", []),
"ts": time.time(),
}
while len(_last_reasoning_store) > _MAX_STORED:
oldest = next(iter(_last_reasoning_store))
del _last_reasoning_store[oldest]
_record_usage(provider, model, success, time.time() - t0, error_type="length" if not success else None) _record_usage(provider, model, success, time.time() - t0, error_type="length" if not success else None)
# Auto-learn provider quirks before flushing the bad response to Codex. # Auto-learn provider quirks before flushing the bad response to Codex.
@@ -5925,8 +6029,14 @@ def main():
global SERVER, _START_TIME global SERVER, _START_TIME
_START_TIME = time.time() _START_TIME = time.time()
_init_runtime() _init_runtime()
signal.signal(signal.SIGTERM, _handle_shutdown_signal)
signal.signal(signal.SIGINT, _handle_shutdown_signal) signal.signal(signal.SIGINT, _handle_shutdown_signal)
if _IS_WINDOWS:
if hasattr(signal, "SIGBREAK"):
signal.signal(signal.SIGBREAK, _handle_shutdown_signal)
import atexit
atexit.register(lambda: setattr(sys.modules[__name__], '_SHUTDOWN_REQUESTED', True))
else:
signal.signal(signal.SIGTERM, _handle_shutdown_signal)
try: try:
from http.server import ThreadingHTTPServer as _BaseSrv from http.server import ThreadingHTTPServer as _BaseSrv
except ImportError: except ImportError:
@@ -6133,7 +6243,7 @@ Postamble text."""
_check("FIX23 explore nested JSON: parsed", len(_calls_m) == 1, f"got {len(_calls_m)} calls") _check("FIX23 explore nested JSON: parsed", len(_calls_m) == 1, f"got {len(_calls_m)} calls")
if _calls_m: if _calls_m:
_args_m = json.loads(_calls_m[0].get("arguments", "{}")) _args_m = json.loads(_calls_m[0].get("arguments", "{}"))
_check("FIX23 explore nested JSON: cmd has curl", "curl" in _args_m.get("cmd", ""), f"got {_args_m.get('cmd')!r}") _check("FIX23 explore nested JSON: cmd has fetch cmd", "curl" in _args_m.get("cmd", "") or "Invoke-WebRequest" in _args_m.get("cmd", ""), f"got {_args_m.get('cmd')!r}")
_check("FIX23 explore nested JSON: URL in cmd", "github.rommark.dev" in _args_m.get("cmd", ""), f"missing URL in cmd") _check("FIX23 explore nested JSON: URL in cmd", "github.rommark.dev" in _args_m.get("cmd", ""), f"missing URL in cmd")
# Pattern N: require_escalation block (FIX 24) # Pattern N: require_escalation block (FIX 24)
@@ -6143,7 +6253,7 @@ Postamble text."""
if _calls_n: if _calls_n:
_args_n = json.loads(_calls_n[0].get("arguments", "{}")) _args_n = json.loads(_calls_n[0].get("arguments", "{}"))
_check("FIX24 require_escalation: name is exec_command", _calls_n[0].get("name") == "exec_command", f"got {_calls_n[0].get('name')}") _check("FIX24 require_escalation: name is exec_command", _calls_n[0].get("name") == "exec_command", f"got {_calls_n[0].get('name')}")
_check("FIX24 require_escalation: cmd has curl or echo", "curl" in _args_n.get("cmd", "") or "echo" in _args_n.get("cmd", ""), f"got {_args_n.get('cmd')!r}") _check("FIX24 require_escalation: cmd has fetch or echo", "curl" in _args_n.get("cmd", "") or "echo" in _args_n.get("cmd", "") or "Invoke-WebRequest" in _args_n.get("cmd", "") or "Write-Output" in _args_n.get("cmd", ""), f"got {_args_n.get('cmd')!r}")
# Pattern N2: bare request_escalation_permission tag (FIX 24b) # Pattern N2: bare request_escalation_permission tag (FIX 24b)
_esc_bare = 'I want to proceed.\n<request_escalation_permission />\nPlease let me continue.' _esc_bare = 'I want to proceed.\n<request_escalation_permission />\nPlease let me continue.'
@@ -6155,13 +6265,13 @@ Postamble text."""
# Pattern O: _build_explore_cmd module-level function (FIX 23/25) # Pattern O: _build_explore_cmd module-level function (FIX 23/25)
_cmd_o, _just_o = _build_explore_cmd("https://github.rommark.dev/admin/Z.AI-Chat-for-Android") _cmd_o, _just_o = _build_explore_cmd("https://github.rommark.dev/admin/Z.AI-Chat-for-Android")
_check("FIX23/25 _build_explore_cmd: returns cmd", _cmd_o is not None, "returned None") _check("FIX23/25 _build_explore_cmd: returns cmd", _cmd_o is not None, "returned None")
_check("FIX23/25 _build_explore_cmd: has curl", _cmd_o and "curl" in _cmd_o, f"no curl in {_cmd_o!r}") _check("FIX23/25 _build_explore_cmd: has fetch cmd", _cmd_o and ("curl" in _cmd_o or "Invoke-WebRequest" in _cmd_o), f"no fetch cmd in {_cmd_o!r}")
_check("FIX23/25 _build_explore_cmd: has api path", _cmd_o and "/api/v1/repos/" in _cmd_o, f"no api path in {_cmd_o!r}") _check("FIX23/25 _build_explore_cmd: has api path", _cmd_o and "/api/v1/repos/" in _cmd_o, f"no api path in {_cmd_o!r}")
# Pattern O2: _build_explore_cmd with JSON array containing URL # Pattern O2: _build_explore_cmd with JSON array containing URL
_cmd_o2, _ = _build_explore_cmd('[{"content": "https://github.rommark.dev/admin/Z.AI-Chat-for-Android"}]') _cmd_o2, _ = _build_explore_cmd('[{"content": "https://github.rommark.dev/admin/Z.AI-Chat-for-Android"}]')
_check("FIX23/25 _build_explore_cmd from JSON array: returns cmd", _cmd_o2 is not None, "returned None") _check("FIX23/25 _build_explore_cmd from JSON array: returns cmd", _cmd_o2 is not None, "returned None")
_check("FIX23/25 _build_explore_cmd from JSON array: has curl", _cmd_o2 and "curl" in _cmd_o2, f"no curl in {_cmd_o2!r}") _check("FIX23/25 _build_explore_cmd from JSON array: has fetch cmd", _cmd_o2 and ("curl" in _cmd_o2 or "Invoke-WebRequest" in _cmd_o2), f"no fetch cmd in {_cmd_o2!r}")
print(f"[CC-SELF-TEST] Results: {_counts[0]} passed, {_counts[1]} failed", print(f"[CC-SELF-TEST] Results: {_counts[0]} passed, {_counts[1]} failed",
file=sys.stderr) file=sys.stderr)

File diff suppressed because it is too large Load Diff