feat: Windows native support — tkinter GUI, cross-platform proxy

Based on v3.10.4 upstream.

New files:
- src/codex-launcher-gui.py: full tkinter GUI (10 classes) replacing GTK on Windows
- src/codex_launcher_lib.py: shared cross-platform module (~1870 lines)
- src/cleanup-codex-stale.py: cross-platform process cleanup

translate-proxy.py Windows adaptations:
- Platform-aware paths: %LOCALAPPDATA% for cache, %APPDATA% for config
- Platform-aware credentials, signals, memory reporting, shell commands
- UTF-8 encoding on all file writes (Windows cp1252 default)
- FORCE_MODEL: proxy overrides Codex Desktop model selection
- DeepSeek reasoning_content round-trip for multi-turn tool sessions
- Cleanup orphan .tmp files at startup
- requests.log rotation (2000 lines cap)
- User-Agent: codex-launcher/1.0 in outbound HTTP requests
This commit is contained in:
cobra91
2026-05-25 12:45:05 +02:00
Unverified
parent db2b33befc
commit 84aadb8f0e
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 http.client
import selectors
import tempfile
_IS_WINDOWS = sys.platform == "win32"
# ═══════════════════════════════════════════════════════════════════
# Config
@@ -241,13 +244,28 @@ MODELS = []
CC_VERSION = ""
REASONING_ENABLED = True
REASONING_EFFORT = "medium"
FORCE_MODEL = ""
BGP_ROUTES = []
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)
_REQUESTS_DIR = os.path.join(_LOG_DIR, "requests")
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")
_provider_caps_path = os.path.join(_LOG_DIR, "provider-caps.json")
_stats_lock = threading.Lock()
@@ -257,7 +275,7 @@ _STATS_FLUSH_INTERVAL = 5.0
_STATS = {}
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:
_LOG_FILE = None
@@ -273,6 +291,9 @@ _deepseek_reasoning_store = {}
_deepseek_reasoning_lock = threading.Lock()
_MAX_DS_STORED = 100
_last_reasoning_store = {}
_last_reasoning_lock = threading.Lock()
_crof_lock = threading.Lock()
_provider_caps_lock = threading.Lock()
_provider_caps = None
@@ -302,7 +323,10 @@ _CODEBUFF_AGENT_MAP = {
"moonshotai/kimi-k2.6": "base2-free-kimi",
"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_session_cache = {"instance_id": None, "expires": 0, "model": None}
_codebuff_token_lock = threading.Lock()
@@ -634,7 +658,7 @@ def _refresh_google_token(token_data, token_path):
new_tokens = json.loads(resp.read())
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)
with open(token_path, "w") as f:
with open(token_path, "w", encoding="utf-8") as f:
json.dump(token_data, f, indent=2)
print("[oauth] token refreshed OK", file=sys.stderr)
return token_data["access_token"]
@@ -727,7 +751,7 @@ def _fetch_antigravity_version():
version = m.group(0)
try:
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)
except Exception:
pass
@@ -762,6 +786,7 @@ def _init_runtime():
CC_VERSION = CONFIG.get("cc_version", "")
REASONING_ENABLED = CONFIG.get("reasoning_enabled", True)
REASONING_EFFORT = CONFIG.get("reasoning_effort", "medium")
FORCE_MODEL = (CONFIG.get("force_model") or "").strip()
BGP_ROUTES = CONFIG.get("bgp_routes", [])
_api_key_pool = None
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():
try:
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)
except Exception as e:
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())
tokens["access_token"] = new_tokens.get("access_token", tokens.get("access_token"))
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)
print("[oauth] token refreshed OK", file=sys.stderr)
return tokens["access_token"]
@@ -983,7 +1008,7 @@ def _load_stats():
def _atomic_write_json(path, obj):
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)
os.replace(tmp, path)
@@ -1297,7 +1322,7 @@ def _load_bgp_stats():
def _save_bgp_stats(stats):
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)
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")
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)
os.replace(tmp, path)
_rotate_snapshots()
@@ -1813,7 +1838,7 @@ def update_snapshot_response(request_id, status, duration_s=None, error=None):
meta["error"] = str(error)[:200]
snapshot["_meta"] = meta
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)
os.replace(tmp, path)
except Exception:
@@ -1865,6 +1890,27 @@ def _bucket_for_route(route):
# 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):
msgs = []
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}})
_DEFAULT_CC_CONFIG = {
"workingDir": "/tmp",
"workingDir": tempfile.gettempdir(),
"date": "",
"environment": "linux",
"shell": "bash",
"environment": "windows" if _IS_WINDOWS else "linux",
"shell": "powershell" if _IS_WINDOWS else "bash",
"files": [],
"structure": [],
"isGitRepo": False,
@@ -2462,13 +2508,24 @@ def _build_explore_cmd(text_for_url):
api_base = repo_url.replace("/admin/", "/api/v1/repos/")
else:
api_base = repo_url
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"
)
if _IS_WINDOWS:
cmd = (
f"cd $env:TEMP; "
f"$r = Invoke-WebRequest -Uri '{api_base}/contents/README.md' -UseBasicParsing -TimeoutSec 15 2>$null; "
f"if ($r) {{ $j = $r.Content | ConvertFrom-Json; [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($j.content)) | Select-Object -First 600 }}; "
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."
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)
if _url_in_text:
_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"
# 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)
if _file_m:
_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})"
# 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:
_intent_text = _intent_m.group(1).strip()
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]}"
if _synth_cmd:
@@ -3891,11 +3957,13 @@ def _extract_text(content):
# HTTP Server
# ═══════════════════════════════════════════════════════════════════
_MAX_REQLOG_LINES = 2000
def _log_resp(resp_id, status, output):
try:
import datetime as _dt
_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")
if output:
for o in output:
@@ -3908,6 +3976,11 @@ def _log_resp(resp_id, status, output):
_f.write(f" -> {ot}\n")
_f.write(f"{'='*60}\n")
_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:
pass
@@ -4064,10 +4137,26 @@ class Handler(http.server.BaseHTTPRequestHandler):
info["total"] = 0
self.send_json(200, info)
elif self.path in ("/health", "/v1/health"):
import resource as _res
_mem_mb = 0
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:
pass
_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"
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" prev_id={prev_id}\n")
_lf.write(f" raw_input_types={raw_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")
if isinstance(input_data, list):
for i, item in enumerate(input_data):
@@ -4143,6 +4232,9 @@ class Handler(http.server.BaseHTTPRequestHandler):
_lf.flush()
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)
_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", "")
@@ -4211,6 +4303,7 @@ class Handler(http.server.BaseHTTPRequestHandler):
body["input"] = input_data
messages = oa_input_to_messages(input_data)
messages = _inject_stored_reasoning(messages)
instructions = body.get("instructions", "").strip()
if instructions:
messages.insert(0, {"role": "system", "content": instructions})
@@ -4612,7 +4705,7 @@ class Handler(http.server.BaseHTTPRequestHandler):
if n_contents > 10:
debug_path = os.path.join(_LOG_DIR, f"gemini-long-ctx-{self._session_id}.json")
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)
except Exception:
pass
@@ -4628,7 +4721,7 @@ class Handler(http.server.BaseHTTPRequestHandler):
if e.code == 400 and OAUTH_PROVIDER.startswith("google"):
try:
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)
print(f"[{self._session_id}] saved 400 debug request to {debug_path}", file=sys.stderr)
except Exception:
@@ -4940,7 +5033,8 @@ class Handler(http.server.BaseHTTPRequestHandler):
pass
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():
print("[translate-proxy] stream cancelled", file=sys.stderr)
break
@@ -4958,6 +5052,16 @@ class Handler(http.server.BaseHTTPRequestHandler):
_log_resp(last_resp_id, last_status, last_output)
if last_resp_id and input_data is not None:
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)
# Auto-learn provider quirks before flushing the bad response to Codex.
@@ -5924,8 +6028,14 @@ def main():
global SERVER, _START_TIME
_START_TIME = time.time()
_init_runtime()
signal.signal(signal.SIGTERM, _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:
from http.server import ThreadingHTTPServer as _BaseSrv
except ImportError:
@@ -6132,7 +6242,7 @@ Postamble text."""
_check("FIX23 explore nested JSON: parsed", len(_calls_m) == 1, f"got {len(_calls_m)} calls")
if _calls_m:
_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")
# Pattern N: require_escalation block (FIX 24)
@@ -6142,7 +6252,7 @@ Postamble text."""
if _calls_n:
_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: 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)
_esc_bare = 'I want to proceed.\n<request_escalation_permission />\nPlease let me continue.'
@@ -6154,13 +6264,13 @@ Postamble text."""
# 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")
_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}")
# 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"}]')
_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",
file=sys.stderr)