diff --git a/codex-launcher_3.9.7_all.deb b/codex-launcher_3.9.7_all.deb index 1ff8129..fce6636 100644 Binary files a/codex-launcher_3.9.7_all.deb and b/codex-launcher_3.9.7_all.deb differ diff --git a/src/codex-launcher-gui b/src/codex-launcher-gui index b04a767..b719339 100755 --- a/src/codex-launcher-gui +++ b/src/codex-launcher-gui @@ -27,13 +27,13 @@ model_catalog_json = "" CHANGELOG = [ ("3.9.7", "2026-05-25", [ - "Forward real FreeBuff error messages to user (not generic 429)", + "Forward real Codebuff error messages to user (not generic 429)", "Return HTTP 200 with Responses API format for rate limits so Codex displays message", - "Extract retryAfterMs from FreeBuff 429 responses for accurate cooldown", + "Extract retryAfterMs from Codebuff 429 responses for accurate cooldown", "RateLimitError carries upstream message through session + chat error paths", "BrokenPipeError crash fix on 'all accounts exhausted' response", "Fix 3 SyntaxWarnings for invalid escape sequences in docstrings", - "_freebuff_start_run returns actual error body instead of None", + "_codebuff_start_run returns actual error body instead of None", ]), ("3.9.6", "2026-05-25", [ "Fix Gemini follow-up turns returning text-only instead of tool calls", @@ -45,37 +45,37 @@ CHANGELOG = [ "Smart tool output compaction: old=3000, recent=20000 chars", "Follow-through guardrail system instruction for autonomous agent behavior", "Stream hang fix for function-call-only responses", - "Multi-account rotation for freebuff, Google OAuth, API keys", + "Multi-account rotation for codebuff, Google OAuth, API keys", "/v1/accounts endpoint for account pool status", ]), ("3.9.0", "2026-05-24", [ - "Multi-account rotation for OAuth providers (freebuff, Google, API keys)", + "Multi-account rotation for OAuth providers (codebuff, Google, API keys)", "Automatic failover: when one account hits rate limit, next is used", - "Freebuff: supports accounts[] array in credentials.json", + "Codebuff: supports accounts[] array in credentials.json", "Google OAuth: supports multiple token files (google-*-oauth-token-N.json)", "API keys: comma-separated keys rotate on 429 errors", "New /v1/accounts endpoint shows account pool status", - "Added x-freebuff-model and x-freebuff-instance-id headers", + "Added x-codebuff-model and x-codebuff-instance-id headers", ]), ("3.8.4", "2026-05-24", [ - "FIXED: Freebuff streaming — SSE events now reach Codex client", - "Root cause: stream_buffered_events was never called for freebuff", - "Freebuff stream uses buffered flushing (30ms / 4KB / urgent)", - "Freebuff OAuth — built-in login flow (no external CLI needed)", - "Freebuff API: reverse-engineered www.codebuff.com endpoints", - "Freebuff session management with instance ID (waiting room)", - "Freebuff agent run lifecycle (start/finish) with model routing", + "FIXED: Codebuff streaming — SSE events now reach Codex client", + "Root cause: stream_buffered_events was never called for codebuff", + "Codebuff stream uses buffered flushing (30ms / 4KB / urgent)", + "Codebuff OAuth — built-in login flow (no external CLI needed)", + "Codebuff API: reverse-engineered www.codebuff.com endpoints", + "Codebuff session management with instance ID (waiting room)", + "Codebuff agent run lifecycle (start/finish) with model routing", "Free DeepSeek V4 Pro, V4 Flash, Kimi K2.6, MiniMax M2.7", - "Reasoning mode works with freebuff (thinking tokens supported)", + "Reasoning mode works with codebuff (thinking tokens supported)", "GUI: Sandbox mode selector (Read-only / Workspace / Full Access)", "GUI: Approval mode selector (Untrusted / On Request / Full Auto)", - "GUI: Freebuff Login button in endpoint editor", + "GUI: Codebuff Login button in endpoint editor", "Fixed _STATS undefined error in /health endpoint", - "Fixed freebuff credential path (reads default account)", + "Fixed codebuff credential path (reads default account)", ]), ("3.8.1", "2026-05-24", [ - "Freebuff integration — free DeepSeek V4 Pro, V4 Flash, Kimi K2.6, MiniMax M2.7", - "Freebuff backend: auto agent-run lifecycle, credential detection, model routing", + "Codebuff integration — free DeepSeek V4 Pro, V4 Flash, Kimi K2.6, MiniMax M2.7", + "Codebuff backend: auto agent-run lifecycle, credential detection, model routing", "Restored all provider presets (Command Code, Crof, OpenAdapter, OpenRouter, etc.)", "AI Monitoring — self-healing watchdog with 3-tier response system", "HealthWatcher: monitors proxy health every 5s, auto-restarts on crash", @@ -365,7 +365,7 @@ PROVIDER_PRESETS = { "GLM-4-Flash", "GLM-4-FlashX", "GLM-Z1-Flash", ], }, - "Freebuff (Free DeepSeek/Kimi)": { + "Codebuff (Free DeepSeek/Kimi)": { "backend_type": "freebuff", "base_url": "https://freebuff.com", "oauth_provider": "freebuff", @@ -386,7 +386,7 @@ def label_for_backend(backend_type): "openai-compat": "OpenAI-compatible", "anthropic": "Anthropic", "command-code": "Command Code", - "freebuff": "Freebuff (Free AI)", + "freebuff": "Codebuff (Free AI)", "native": "Native", }.get(backend_type, backend_type) @@ -3010,7 +3010,7 @@ class EditEndpointDialog(Gtk.Dialog): for val, lab in [("openai-compat", "OpenAI-compatible (needs proxy)"), ("anthropic", "Anthropic (needs proxy)"), ("command-code", "Command Code (needs proxy)"), - ("freebuff", "Freebuff - Free DeepSeek/Kimi (needs proxy)"), + ("freebuff", "Codebuff - Free DeepSeek/Kimi (needs proxy)"), ("gemini-oauth-cli", "Gemini CLI OAuth (needs proxy)"), ("gemini-oauth-antigravity", "Antigravity OAuth (needs proxy)"), ("native", "Native OpenAI (no proxy)")]: @@ -3176,8 +3176,8 @@ class EditEndpointDialog(Gtk.Dialog): is_oauth = bool(oauth_provider) self._oauth_btn.set_visible(is_oauth) if oauth_provider == "freebuff": - self._oauth_btn.set_label("Freebuff Login") - self._entry_key.set_placeholder_text("Auto-filled by freebuff login") + self._oauth_btn.set_label("Codebuff Login") + self._entry_key.set_placeholder_text("Auto-filled by codebuff login") elif is_oauth: self._oauth_btn.set_label("OAuth Login") self._entry_key.set_placeholder_text("Auto-filled by OAuth") @@ -3489,7 +3489,7 @@ class EditEndpointDialog(Gtk.Dialog): dlg.run() def _freebuff_oauth_flow(self): - dlg = Gtk.Dialog(title="Freebuff Login", parent=self, modal=True) + dlg = Gtk.Dialog(title="Codebuff Login", parent=self, modal=True) dlg.add_button("Cancel", Gtk.ResponseType.CANCEL) dlg.set_default_size(500, 240) area = dlg.get_content_area() @@ -3499,7 +3499,7 @@ class EditEndpointDialog(Gtk.Dialog): area.set_margin_bottom(12) area.set_spacing(8) - area.pack_start(Gtk.Label(label="Sign in with GitHub via Freebuff", use_markup=True, xalign=0), False, False, 0) + area.pack_start(Gtk.Label(label="Sign in with GitHub via Codebuff", use_markup=True, xalign=0), False, False, 0) self._oauth_status = Gtk.Label(label="Requesting login URL…", xalign=0) self._oauth_status.set_line_wrap(True) @@ -3520,10 +3520,10 @@ class EditEndpointDialog(Gtk.Dialog): self._fb_oauth_result = {"success": False, "user": None, "error": None} - def _freebuff_auth_thread(): + def _codebuff_auth_thread(): try: fingerprint_id = str(uuid.uuid4()) - auth_url = "https://freebuff.com/api/auth/cli/code" + auth_url = "https://codebuff.com/api/auth/cli/code" body = json.dumps({"fingerprintId": fingerprint_id}).encode() req = urllib.request.Request(auth_url, data=body, headers={"Content-Type": "application/json", "User-Agent": "codex-launcher/3.9.7"}) @@ -3534,7 +3534,7 @@ class EditEndpointDialog(Gtk.Dialog): expires_at = data.get("expiresAt", 0) or data.get("expires_at", 0) if not login_url: self._fb_oauth_result["error"] = "Server returned no login URL" - GLib.idle_add(self._freebuff_oauth_done, dlg, spinner) + GLib.idle_add(self._codebuff_oauth_done, dlg, spinner) return def _set_link(): @@ -3545,7 +3545,7 @@ class EditEndpointDialog(Gtk.Dialog): webbrowser.open(login_url) - poll_url = f"https://freebuff.com/api/auth/cli/status?fingerprintId={urllib.parse.quote(fingerprint_id)}&fingerprintHash={urllib.parse.quote(fingerprint_hash)}&expiresAt={expires_at}" + poll_url = f"https://codebuff.com/api/auth/cli/status?fingerprintId={urllib.parse.quote(fingerprint_id)}&fingerprintHash={urllib.parse.quote(fingerprint_hash)}&expiresAt={expires_at}" deadline = time.time() + 300 while time.time() < deadline: time.sleep(2) @@ -3558,23 +3558,23 @@ class EditEndpointDialog(Gtk.Dialog): if user and user.get("authToken"): self._fb_oauth_result["success"] = True self._fb_oauth_result["user"] = user - GLib.idle_add(self._freebuff_oauth_done, dlg, spinner) + GLib.idle_add(self._codebuff_oauth_done, dlg, spinner) return except urllib.error.HTTPError: pass except Exception: pass self._fb_oauth_result["error"] = "Login timed out after 5 minutes." - GLib.idle_add(self._freebuff_oauth_done, dlg, spinner) + GLib.idle_add(self._codebuff_oauth_done, dlg, spinner) except Exception as e: self._fb_oauth_result["error"] = str(e)[:200] - GLib.idle_add(self._freebuff_oauth_done, dlg, spinner) + GLib.idle_add(self._codebuff_oauth_done, dlg, spinner) - threading.Thread(target=_freebuff_auth_thread, daemon=True).start() + threading.Thread(target=_codebuff_auth_thread, daemon=True).start() dlg.connect("response", lambda d, r: d.destroy()) dlg.run() - def _freebuff_oauth_done(self, dlg, spinner): + def _codebuff_oauth_done(self, dlg, spinner): spinner.stop() if self._fb_oauth_result["success"] and self._fb_oauth_result["user"]: user = self._fb_oauth_result["user"] @@ -3593,7 +3593,7 @@ class EditEndpointDialog(Gtk.Dialog): os.chmod(creds_path, 0o600) self._entry_key.set_text(user.get("authToken", "")) self._oauth_status.set_markup('Authorization successful! Credentials saved.') - dlg.set_title("Freebuff Login – Success") + dlg.set_title("Codebuff Login – Success") GLib.timeout_add(1500, lambda: dlg.response(Gtk.ResponseType.OK)) else: self._oauth_status.set_markup(f'{self._fb_oauth_result["error"] or "Login failed."}') diff --git a/src/translate-proxy.py b/src/translate-proxy.py index c916422..ca7dd5d 100755 --- a/src/translate-proxy.py +++ b/src/translate-proxy.py @@ -172,7 +172,7 @@ DEFAULT_MODELS = { "anthropic": [ {"id": "claude-sonnet-4-20250514", "object": "model", "created": 1700000000, "owned_by": "anthropic"}, ], - "freebuff": [ + "codebuff": [ {"id": "deepseek/deepseek-v4-pro", "object": "model", "created": 1700000000, "owned_by": "freebuff"}, {"id": "deepseek/deepseek-v4-flash", "object": "model", "created": 1700000000, "owned_by": "freebuff"}, {"id": "moonshotai/kimi-k2.6", "object": "model", "created": 1700000000, "owned_by": "freebuff"}, @@ -187,7 +187,7 @@ def load_config(): p = argparse.ArgumentParser(description="Responses API translation proxy") p.add_argument("--config", help="JSON config file path") p.add_argument("--port", type=int, default=None) - p.add_argument("--backend", default=None, choices=["openai-compat", "anthropic", "command-code", "freebuff", "auto"]) + p.add_argument("--backend", default=None, choices=["openai-compat", "anthropic", "command-code", "codebuff", "auto"]) p.add_argument("--target-url", default=None) p.add_argument("--api-key", default=None) p.add_argument("--models-file", default=None, help="JSON file with model list array") @@ -294,43 +294,43 @@ _conn_pool = {} _STREAM_IDLE_TIMEOUT = 300 -_FREEBUFF_AUTH_URL = "https://freebuff.com" -_FREEBUFF_API_URL = "https://www.codebuff.com" -_FREEBUFF_AGENT_MAP = { +_CODEBUFF_AUTH_URL = "https://freebuff.com" +_CODEBUFF_API_URL = "https://www.codebuff.com" +_CODEBUFF_AGENT_MAP = { "deepseek/deepseek-v4-pro": "base2-free-deepseek", "deepseek/deepseek-v4-flash": "base2-free-deepseek-flash", "moonshotai/kimi-k2.6": "base2-free-kimi", "minimax/minimax-m2.7": "base2-free", } -_FREEBUFF_CREDS_PATH = os.path.join(os.path.expanduser("~"), ".config", "manicode", "credentials.json") -_freebuff_token_cache = {"token": None, "checked": 0} -_freebuff_session_cache = {"instance_id": None, "expires": 0, "model": None} -_freebuff_token_lock = threading.Lock() +_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() -def _get_freebuff_token(): - with _freebuff_token_lock: - if _freebuff_token_cache["token"] and _freebuff_token_cache["checked"] > time.time() - 300: - return _freebuff_token_cache["token"] +def _get_codebuff_token(): + with _codebuff_token_lock: + if _codebuff_token_cache["token"] and _codebuff_token_cache["checked"] > time.time() - 300: + return _codebuff_token_cache["token"] try: - with open(_FREEBUFF_CREDS_PATH) as f: + with open(_CODEBUFF_CREDS_PATH) as f: creds = json.load(f) default_account = creds.get("default", {}) token = default_account.get("authToken") or creds.get("apiKey") or "" - with _freebuff_token_lock: - _freebuff_token_cache["token"] = token - _freebuff_token_cache["checked"] = time.time() + with _codebuff_token_lock: + _codebuff_token_cache["token"] = token + _codebuff_token_cache["checked"] = time.time() return token except Exception as e: - print(f"[freebuff] no credentials at {_FREEBUFF_CREDS_PATH}: {e}", file=sys.stderr) + print(f"[codebuff] no credentials at {_CODEBUFF_CREDS_PATH}: {e}", file=sys.stderr) return "" -def _freebuff_get_session(token, model): - with _freebuff_token_lock: - sc = _freebuff_session_cache +def _codebuff_get_session(token, model): + with _codebuff_token_lock: + sc = _codebuff_session_cache if sc["instance_id"] and sc["expires"] > time.time() + 60 and sc["model"] == model: return sc["instance_id"] try: - url = f"{_FREEBUFF_API_URL}/api/v1/freebuff/session" + url = f"{_CODEBUFF_API_URL}/api/v1/freebuff/session" body = json.dumps({"model": model}).encode() req = urllib.request.Request(url, data=body, headers={ "Content-Type": "application/json", @@ -358,27 +358,27 @@ def _freebuff_get_session(token, model): if not user_msg: user_msg = _sanitize_err_body(err_body) raise RateLimitError(retry_s, user_msg) - print(f"[freebuff] session HTTP {e.code}: {err_body[:200]}", file=sys.stderr) + print(f"[codebuff] session HTTP {e.code}: {err_body[:200]}", file=sys.stderr) return None data = json.loads(resp.read()) instance_id = data.get("instanceId", data.get("data", {}).get("instance_id", "")) expires_at = data.get("remainingMs", 0) if instance_id: - with _freebuff_token_lock: - _freebuff_session_cache["instance_id"] = instance_id - _freebuff_session_cache["expires"] = time.time() + min(expires_at / 1000, 3600) - _freebuff_session_cache["model"] = model - print(f"[freebuff] session active, instance={instance_id[:8]}...", file=sys.stderr) + with _codebuff_token_lock: + _codebuff_session_cache["instance_id"] = instance_id + _codebuff_session_cache["expires"] = time.time() + min(expires_at / 1000, 3600) + _codebuff_session_cache["model"] = model + print(f"[codebuff] session active, instance={instance_id[:8]}...", file=sys.stderr) return instance_id return None except RateLimitError: raise except Exception as e: - print(f"[freebuff] session failed: {e}", file=sys.stderr) + print(f"[codebuff] session failed: {e}", file=sys.stderr) return None -def _freebuff_start_run(token, agent_id): - url = f"{_FREEBUFF_API_URL}/api/v1/agent-runs" +def _codebuff_start_run(token, agent_id): + url = f"{_CODEBUFF_API_URL}/api/v1/agent-runs" body = json.dumps({"action": "START", "agentId": agent_id, "ancestorRunIds": []}).encode() req = urllib.request.Request(url, data=body, headers={ "Content-Type": "application/json", @@ -389,11 +389,11 @@ def _freebuff_start_run(token, agent_id): resp = urllib.request.urlopen(req, timeout=15) data = json.loads(resp.read()) run_id = data.get("runId") - print(f"[freebuff] started run {run_id} for agent {agent_id}", file=sys.stderr) + print(f"[codebuff] started run {run_id} for agent {agent_id}", file=sys.stderr) return run_id, None except urllib.error.HTTPError as e: err = e.read().decode()[:500] - print(f"[freebuff] start run failed: HTTP {e.code}: {err}", file=sys.stderr) + print(f"[codebuff] start run failed: HTTP {e.code}: {err}", file=sys.stderr) if e.code == 429: retry_s = 120 try: @@ -406,11 +406,11 @@ def _freebuff_start_run(token, agent_id): return None, ("rate_limit_error", 429, retry_s, _sanitize_err_body(err)) return None, ("upstream_error", e.code, 0, _sanitize_err_body(err)) except Exception as e: - print(f"[freebuff] start run error: {e}", file=sys.stderr) + print(f"[codebuff] start run error: {e}", file=sys.stderr) return None, ("proxy_error", 502, 0, str(e)) -def _freebuff_finish_run(token, run_id, status="completed"): - url = f"{_FREEBUFF_API_URL}/api/v1/agent-runs" +def _codebuff_finish_run(token, run_id, status="completed"): + url = f"{_CODEBUFF_API_URL}/api/v1/agent-runs" body = json.dumps({"action": "FINISH", "runId": run_id, "status": status, "totalSteps": 1, "directCredits": 0, "totalCredits": 0}).encode() req = urllib.request.Request(url, data=body, headers={ @@ -421,7 +421,7 @@ def _freebuff_finish_run(token, run_id, status="completed"): try: urllib.request.urlopen(req, timeout=10) except Exception as e: - print(f"[freebuff] finish run {run_id} error: {e}", file=sys.stderr) + print(f"[codebuff] finish run {run_id} error: {e}", file=sys.stderr) # ═══════════════════════════════════════════════════════════════════ # Multi-account rotation system @@ -514,12 +514,12 @@ class AccountPool: result.append(info) return result -class FreebuffAccountPool(AccountPool): +class CodebuffAccountPool(AccountPool): def _do_load(self): - if not os.path.exists(_FREEBUFF_CREDS_PATH): + if not os.path.exists(_CODEBUFF_CREDS_PATH): return None try: - with open(_FREEBUFF_CREDS_PATH) as f: + with open(_CODEBUFF_CREDS_PATH) as f: creds = json.load(f) except Exception: return None @@ -588,14 +588,14 @@ class APIKeyPool(AccountPool): def load_accounts(self, force=False): return len(self._accounts) -_fb_pool = FreebuffAccountPool("freebuff") +_cb_pool = CodebuffAccountPool("codebuff") _google_antigravity_pool = GoogleAccountPool("antigravity") _google_cli_pool = GoogleAccountPool("cli") -def _get_freebuff_account(): - """Return (token, account_dict) for best available freebuff account.""" - _fb_pool.load_accounts() - acct = _fb_pool.get() +def _get_codebuff_account(): + """Return (token, account_dict) for best available codebuff account.""" + _cb_pool.load_accounts() + acct = _cb_pool.get() if not acct: return "", None return acct["token"], acct @@ -764,7 +764,7 @@ def _init_runtime(): REASONING_EFFORT = CONFIG.get("reasoning_effort", "medium") 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 ("freebuff",): + if API_KEY and "," in API_KEY and not OAUTH_PROVIDER.startswith("google") and BACKEND not in ("codebuff",): _api_key_pool = APIKeyPool(BACKEND, API_KEY) print(f"[multi-account] API key pool: {len(_api_key_pool._accounts)} keys for {BACKEND}", file=sys.stderr) if OAUTH_PROVIDER == "google-antigravity": @@ -1087,9 +1087,9 @@ def _fb_get_any_reasoning(): return _fb_reasoning_store[k]["reasoning"] return "" -def _freebuff_hard_disable_reasoning(messages): +def _codebuff_hard_disable_reasoning(messages): """Strip all reasoning/thinking fields from every message. - FreeBuff rejects mixed reasoning_content histories. + Codebuff rejects mixed reasoning_content histories. The final chat body must be clean before POST.""" for msg in messages: if not isinstance(msg, dict): @@ -1150,7 +1150,7 @@ def _ds_rebuild_tool_history(messages): rebuilt.append(msg) return rebuilt -def _fb_input_to_messages(input_data, instructions=""): +def _cb_input_to_messages(input_data, instructions=""): msgs = [] tool_name_by_id = {} pending_tool_calls = [] @@ -4037,9 +4037,9 @@ class Handler(http.server.BaseHTTPRequestHandler): self.send_json(200, {"object": "list", "data": MODELS}) elif self.path in ("/v1/accounts", "/accounts"): info = {"provider": BACKEND, "oauth_provider": OAUTH_PROVIDER} - if BACKEND == "freebuff": - info["accounts"] = _fb_pool.status() - info["total"] = len(_fb_pool._accounts) + if BACKEND == "codebuff": + info["accounts"] = _cb_pool.status() + info["total"] = len(_cb_pool._accounts) elif OAUTH_PROVIDER and OAUTH_PROVIDER.startswith("google"): pool = _google_antigravity_pool if OAUTH_PROVIDER == "google-antigravity" else _google_cli_pool info["accounts"] = pool.status() @@ -4149,8 +4149,8 @@ class Handler(http.server.BaseHTTPRequestHandler): self._handle_anthropic(body, model, stream, tracker) elif BACKEND == "command-code": self._handle_command_code(body, model, stream, tracker) - elif BACKEND == "freebuff": - self._handle_freebuff(body, model, stream, tracker) + elif BACKEND == "codebuff": + self._handle_codebuff(body, model, stream, tracker) elif (BACKEND or "").startswith("gemini-oauth"): self._handle_gemini_oauth(body, model, stream, tracker) else: @@ -5213,69 +5213,69 @@ class Handler(http.server.BaseHTTPRequestHandler): if rid: store_response(rid, body.get("input", ""), result.get("output", [])) - def _handle_freebuff(self, body, model, stream, tracker=None): - agent_id = _FREEBUFF_AGENT_MAP.get(model) + def _handle_codebuff(self, body, model, stream, tracker=None): + agent_id = _CODEBUFF_AGENT_MAP.get(model) if not agent_id: matched = None - for m in _FREEBUFF_AGENT_MAP: + for m in _CODEBUFF_AGENT_MAP: if model.lower().replace("/", "").replace("-", "") in m.lower().replace("/", "").replace("-", ""): matched = m break if matched: - agent_id = _FREEBUFF_AGENT_MAP[matched] + agent_id = _CODEBUFF_AGENT_MAP[matched] model = matched else: fallback_model = "deepseek/deepseek-v4-flash" - agent_id = _FREEBUFF_AGENT_MAP.get(fallback_model, "base2-free-deepseek-flash") - print(f"[freebuff] unknown model '{model}', falling back to {fallback_model}", file=sys.stderr) + agent_id = _CODEBUFF_AGENT_MAP.get(fallback_model, "base2-free-deepseek-flash") + print(f"[codebuff] unknown model '{model}', falling back to {fallback_model}", file=sys.stderr) model = fallback_model - _fb_pool.load_accounts() - pool_status = _fb_pool.status() + _cb_pool.load_accounts() + pool_status = _cb_pool.status() n_accounts = len(pool_status) if n_accounts == 0: return self.send_json(401, {"error": {"type": "auth_error", - "message": "No freebuff credentials found. Add accounts to ~/.config/manicode/credentials.json"}}) + "message": "No codebuff credentials found. Add accounts to ~/.config/manicode/credentials.json"}}) last_err = None for attempt in range(n_accounts): - token, acct = _get_freebuff_account() + token, acct = _get_codebuff_account() if not token: return self.send_json(401, {"error": {"type": "auth_error", - "message": "No freebuff credentials found. All accounts exhausted."}}) + "message": "No codebuff credentials found. All accounts exhausted."}}) acct_id = acct.get("id", "?") if acct else "?" if attempt > 0: - print(f"[freebuff] rotation attempt {attempt+1}/{n_accounts}, trying account {acct_id}", file=sys.stderr) + print(f"[codebuff] rotation attempt {attempt+1}/{n_accounts}, trying account {acct_id}", file=sys.stderr) - run_id, run_err = _freebuff_start_run(token, agent_id) + run_id, run_err = _codebuff_start_run(token, agent_id) if not run_id: if run_err and run_err[0] == "rate_limit_error": retry_s = run_err[2] - _fb_pool.mark_rate_limited(acct, retry_s) - last_err = ("rate_limit_error", run_err[1], f"Account {acct_id} rate-limited by FreeBuff: {run_err[3]}") + _cb_pool.mark_rate_limited(acct, retry_s) + last_err = ("rate_limit_error", run_err[1], f"Account {acct_id} rate-limited by Codebuff: {run_err[3]}") else: - _fb_pool.mark_rate_limited(acct, 60) + _cb_pool.mark_rate_limited(acct, 60) last_err = ("upstream_error", run_err[1] if run_err else 502, f"Failed to start agent run for {acct_id}: {run_err[3] if run_err else 'unknown error'}") continue try: - instance_id = _freebuff_get_session(token, model) + instance_id = _codebuff_get_session(token, model) except RateLimitError as rle: retry_s = rle.retry_seconds fb_msg = rle.message mins = int(retry_s // 60) user_msg = fb_msg if fb_msg else f"Daily session limit reached. Resets in {mins}m." - print(f"[freebuff] session 429 for {acct_id}, retry after {retry_s:.0f}s", file=sys.stderr) - _fb_pool.mark_rate_limited(acct, retry_s) - _freebuff_finish_run(token, run_id, "completed") + print(f"[codebuff] session 429 for {acct_id}, retry after {retry_s:.0f}s", file=sys.stderr) + _cb_pool.mark_rate_limited(acct, retry_s) + _codebuff_finish_run(token, run_id, "completed") last_err = ("rate_limit_error", 429, user_msg) continue input_data = body.get("input", "") instructions = body.get("instructions", "").strip() - messages = _fb_input_to_messages(input_data, instructions) + messages = _cb_input_to_messages(input_data, instructions) messages = _ds_rebuild_tool_history(messages) metadata = { @@ -5301,7 +5301,7 @@ class Handler(http.server.BaseHTTPRequestHandler): if body.get("tool_choice"): chat_body["tool_choice"] = body["tool_choice"] - target = f"{_FREEBUFF_API_URL}/api/v1/chat/completions" + target = f"{_CODEBUFF_API_URL}/api/v1/chat/completions" headers = { "Content-Type": "application/json", "Authorization": f"Bearer {token}", @@ -5311,7 +5311,7 @@ class Handler(http.server.BaseHTTPRequestHandler): if instance_id: headers["x-freebuff-instance-id"] = instance_id - print(f"[{self._session_id}] [freebuff] POST {target} model={model} stream={stream} run={run_id} acct={acct_id}", file=sys.stderr) + print(f"[{self._session_id}] [codebuff] POST {target} model={model} stream={stream} run={run_id} acct={acct_id}", file=sys.stderr) chat_body_b = json.dumps(chat_body).encode() try: @@ -5319,7 +5319,7 @@ class Handler(http.server.BaseHTTPRequestHandler): upstream = urllib.request.urlopen(req, timeout=_upstream_timeout(body, stream)) except urllib.error.HTTPError as e: err_body = e.read().decode()[:1000] - _freebuff_finish_run(token, run_id, "failed") + _codebuff_finish_run(token, run_id, "failed") if e.code in (429, 426): reset_ms = 0 fb_msg = "" @@ -5336,18 +5336,18 @@ class Handler(http.server.BaseHTTPRequestHandler): if not fb_msg: fb_msg = _sanitize_err_body(err_body) user_msg = f"{fb_msg} (resets in {mins}m)" if fb_msg else f"Rate limited. Resets in {mins}m." - _fb_pool.mark_rate_limited(acct, duration) + _cb_pool.mark_rate_limited(acct, duration) last_err = ("rate_limit_error", e.code, user_msg) - print(f"[freebuff] account {acct_id} got HTTP {e.code}, rotating", file=sys.stderr) + print(f"[codebuff] account {acct_id} got HTTP {e.code}, rotating", file=sys.stderr) continue if _is_reasoning_content_error(err_body): - print(f"[freebuff] reasoning_content error, retrying with thinking disabled", file=sys.stderr) - result = self._fb_retry_thinking_disabled(body, model, token, agent_id, stream, tracker, input_data, instructions, err_body, acct) + print(f"[codebuff] reasoning_content error, retrying with thinking disabled", file=sys.stderr) + result = self._cb_retry_thinking_disabled(body, model, token, agent_id, stream, tracker, input_data, instructions, err_body, acct) return result - print(f"[freebuff] HTTP {e.code}: {err_body[:300]}", file=sys.stderr) + print(f"[codebuff] HTTP {e.code}: {err_body[:300]}", file=sys.stderr) return self.send_json(e.code, {"error": {"type": "upstream_error", "message": _sanitize_err_body(err_body)}}) except Exception as e: - _freebuff_finish_run(token, run_id, "failed") + _codebuff_finish_run(token, run_id, "failed") return self.send_json(502, {"error": {"type": "proxy_error", "message": str(e)}}) t0 = time.time() @@ -5392,11 +5392,11 @@ class Handler(http.server.BaseHTTPRequestHandler): _reasoning_out=reasoning_out), on_event=_on_fb_event) except (ConnectionResetError, BrokenPipeError, ConnectionAbortedError): - print(f"[{self._session_id}] [freebuff] client disconnected", file=sys.stderr) + print(f"[{self._session_id}] [codebuff] client disconnected", file=sys.stderr) return success = finish_reason[0] != "length" - _record_usage("freebuff", model, success, time.time() - t0) + _record_usage("codebuff", model, success, time.time() - t0) if last_resp_id[0] and input_data is not None: store_response(last_resp_id[0], input_data, last_output[0]) if last_resp_id[0] and reasoning_out.get("text") or reasoning_out.get("tool_calls"): @@ -5406,7 +5406,7 @@ class Handler(http.server.BaseHTTPRequestHandler): if reasoning_out.get("text"): asm["reasoning_content"] = reasoning_out["text"] _ds_store_assistant(last_resp_id[0], asm) - print(f"[{self._session_id}] [freebuff] stream done status={last_status[0]} in {time.time()-t0:.1f}s acct={acct_id}", file=sys.stderr) + print(f"[{self._session_id}] [codebuff] stream done status={last_status[0]} in {time.time()-t0:.1f}s acct={acct_id}", file=sys.stderr) else: raw = upstream.read().decode() chat_resp = json.loads(raw) @@ -5415,9 +5415,9 @@ class Handler(http.server.BaseHTTPRequestHandler): rid = result.get("id") if rid: store_response(rid, input_data, result.get("output", [])) - print(f"[{self._session_id}] [freebuff] non-stream done in {time.time()-t0:.1f}s acct={acct_id}", file=sys.stderr) + print(f"[{self._session_id}] [codebuff] non-stream done in {time.time()-t0:.1f}s acct={acct_id}", file=sys.stderr) finally: - _freebuff_finish_run(token, run_id, "completed") + _codebuff_finish_run(token, run_id, "completed") return if last_err: @@ -5447,15 +5447,15 @@ class Handler(http.server.BaseHTTPRequestHandler): except (BrokenPipeError, ConnectionResetError, ConnectionAbortedError): return - def _fb_retry_thinking_disabled(self, body, model, token, agent_id, stream, tracker, input_data, instructions, original_error, acct=None): - run_id, run_err = _freebuff_start_run(token, agent_id) + def _cb_retry_thinking_disabled(self, body, model, token, agent_id, stream, tracker, input_data, instructions, original_error, acct=None): + run_id, run_err = _codebuff_start_run(token, agent_id) if not run_id: msg = run_err[3] if run_err else "unknown error" return self.send_json(run_err[1] if run_err else 502, {"error": {"type": run_err[0] if run_err else "upstream_error", "message": f"Failed to start agent run for retry: {msg}"}}) - instance_id = _freebuff_get_session(token, model) - messages = _fb_input_to_messages(input_data, instructions) - _freebuff_hard_disable_reasoning(messages) + instance_id = _codebuff_get_session(token, model) + messages = _cb_input_to_messages(input_data, instructions) + _codebuff_hard_disable_reasoning(messages) metadata = {"run_id": run_id, "cost_mode": "free"} if instance_id: metadata["freebuff_instance_id"] = instance_id @@ -5473,22 +5473,22 @@ class Handler(http.server.BaseHTTPRequestHandler): chat_body["tools"] = tools if body.get("tool_choice"): chat_body["tool_choice"] = body["tool_choice"] - target = f"{_FREEBUFF_API_URL}/api/v1/chat/completions" + target = f"{_CODEBUFF_API_URL}/api/v1/chat/completions" headers = {"Content-Type": "application/json", "Authorization": f"Bearer {token}", "User-Agent": "codex-launcher/3.9.7", "x-freebuff-model": model} if instance_id: headers["x-freebuff-instance-id"] = instance_id - print(f"[freebuff] retry POST {target} model={model} stream={stream} run={run_id} (thinking disabled via DeepSeek native)", file=sys.stderr) + print(f"[codebuff] retry POST {target} model={model} stream={stream} run={run_id} (thinking disabled via DeepSeek native)", file=sys.stderr) try: req = urllib.request.Request(target, data=json.dumps(chat_body).encode(), headers=headers) upstream = urllib.request.urlopen(req, timeout=_upstream_timeout(body, stream)) except urllib.error.HTTPError as e: err_body = e.read().decode()[:500] - _freebuff_finish_run(token, run_id, "failed") - print(f"[freebuff] thinking-disabled retry failed: HTTP {e.code}: {err_body[:300]}", file=sys.stderr) - return self.send_json(e.code, {"error": {"type": "freebuff_deepseek_thinking_error", - "message": "FreeBuff/DeepSeek V4 requires reasoning_content round-trip for tool-call sessions. Use Command Code provider for this model instead.", "upstream_error": _sanitize_err_body(err_body)}}) + _codebuff_finish_run(token, run_id, "failed") + print(f"[codebuff] thinking-disabled retry failed: HTTP {e.code}: {err_body[:300]}", file=sys.stderr) + return self.send_json(e.code, {"error": {"type": "codebuff_deepseek_thinking_error", + "message": "Codebuff/DeepSeek V4 requires reasoning_content round-trip for tool-call sessions. Use Command Code provider for this model instead.", "upstream_error": _sanitize_err_body(err_body)}}) except Exception as e: - _freebuff_finish_run(token, run_id, "failed") + _codebuff_finish_run(token, run_id, "failed") return self.send_json(502, {"error": {"type": "proxy_error", "message": str(e)}}) t0 = time.time() try: @@ -5531,7 +5531,7 @@ class Handler(http.server.BaseHTTPRequestHandler): except (ConnectionResetError, BrokenPipeError, ConnectionAbortedError): return success = finish_reason[0] != "length" - _record_usage("freebuff", model, success, time.time() - t0) + _record_usage("codebuff", model, success, time.time() - t0) if last_resp_id[0] and input_data is not None: store_response(last_resp_id[0], input_data, last_output[0]) if last_resp_id[0] and reasoning_out.get("text") or reasoning_out.get("tool_calls"): @@ -5541,7 +5541,7 @@ class Handler(http.server.BaseHTTPRequestHandler): if reasoning_out.get("text"): asm["reasoning_content"] = reasoning_out["text"] _ds_store_assistant(last_resp_id[0], asm) - print(f"[{self._session_id}] [freebuff] retry stream done status={last_status[0]} in {time.time()-t0:.1f}s", file=sys.stderr) + print(f"[{self._session_id}] [codebuff] retry stream done status={last_status[0]} in {time.time()-t0:.1f}s", file=sys.stderr) else: raw = upstream.read().decode() chat_resp = json.loads(raw) @@ -5550,9 +5550,9 @@ class Handler(http.server.BaseHTTPRequestHandler): rid = result.get("id") if rid: store_response(rid, input_data, result.get("output", [])) - print(f"[{self._session_id}] [freebuff] retry non-stream done in {time.time()-t0:.1f}s", file=sys.stderr) + print(f"[{self._session_id}] [codebuff] retry non-stream done in {time.time()-t0:.1f}s", file=sys.stderr) finally: - _freebuff_finish_run(token, run_id, "completed") + _codebuff_finish_run(token, run_id, "completed") def _handle_auto(self, body, model, stream, tracker=None): """Auto-sensing backend: probe schema, adapt, retry on errors. @@ -5896,10 +5896,10 @@ def main(): print(f"translate-proxy ({BACKEND}) listening on http://127.0.0.1:{PORT}", flush=True) print(f"Target: {TARGET_URL}", flush=True) print(f"Models: {[m['id'] for m in MODELS]}", flush=True) - if BACKEND == "freebuff": - _fb_pool.load_accounts(force=True) - fb_status = _fb_pool.status() - print(f"[multi-account] freebuff: {len(fb_status)} accounts loaded {[a['id'] for a in fb_status]}", flush=True) + if BACKEND == "codebuff": + _cb_pool.load_accounts(force=True) + fb_status = _cb_pool.status() + print(f"[multi-account] codebuff: {len(fb_status)} accounts loaded {[a['id'] for a in fb_status]}", flush=True) if OAUTH_PROVIDER and OAUTH_PROVIDER.startswith("google"): pool = _google_antigravity_pool if OAUTH_PROVIDER == "google-antigravity" else _google_cli_pool pool.load_accounts(force=True)