#!/usr/bin/env bun // @bun var __create = Object.create; var __getProtoOf = Object.getPrototypeOf; var __defProp = Object.defineProperty; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; function __accessProp(key) { return this[key]; } var __toESMCache_node; var __toESMCache_esm; var __toESM = (mod, isNodeMode, target) => { var canCache = mod != null && typeof mod === "object"; if (canCache) { var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap; var cached = cache.get(mod); if (cached) return cached; } target = mod != null ? __create(__getProtoOf(mod)) : {}; const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target; for (let key of __getOwnPropNames(mod)) if (!__hasOwnProp.call(to, key)) __defProp(to, key, { get: __accessProp.bind(mod, key), enumerable: true }); if (canCache) cache.set(mod, to); return to; }; var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports); var __returnValue = (v) => v; function __exportSetter(name, newValue) { this[name] = __returnValue.bind(null, newValue); } var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true, configurable: true, set: __exportSetter.bind(all, name) }); }; // node_modules/sisteransi/src/index.js var require_src = __commonJS((exports, module) => { var ESC = "\x1B"; var CSI = `${ESC}[`; var beep = "\x07"; var cursor = { to(x, y) { if (!y) return `${CSI}${x + 1}G`; return `${CSI}${y + 1};${x + 1}H`; }, move(x, y) { let ret = ""; if (x < 0) ret += `${CSI}${-x}D`; else if (x > 0) ret += `${CSI}${x}C`; if (y < 0) ret += `${CSI}${-y}A`; else if (y > 0) ret += `${CSI}${y}B`; return ret; }, up: (count = 1) => `${CSI}${count}A`, down: (count = 1) => `${CSI}${count}B`, forward: (count = 1) => `${CSI}${count}C`, backward: (count = 1) => `${CSI}${count}D`, nextLine: (count = 1) => `${CSI}E`.repeat(count), prevLine: (count = 1) => `${CSI}F`.repeat(count), left: `${CSI}G`, hide: `${CSI}?25l`, show: `${CSI}?25h`, save: `${ESC}7`, restore: `${ESC}8` }; var scroll = { up: (count = 1) => `${CSI}S`.repeat(count), down: (count = 1) => `${CSI}T`.repeat(count) }; var erase = { screen: `${CSI}2J`, up: (count = 1) => `${CSI}1J`.repeat(count), down: (count = 1) => `${CSI}J`.repeat(count), line: `${CSI}2K`, lineEnd: `${CSI}K`, lineStart: `${CSI}1K`, lines(count) { let clear = ""; for (let i = 0;i < count; i++) clear += this.line + (i < count - 1 ? cursor.up() : ""); if (count) clear += cursor.left; return clear; } }; module.exports = { cursor, scroll, erase, beep }; }); // node_modules/picocolors/picocolors.js var require_picocolors = __commonJS((exports, module) => { var p = process || {}; var argv = p.argv || []; var env = p.env || {}; var isColorSupported = !(!!env.NO_COLOR || argv.includes("--no-color")) && (!!env.FORCE_COLOR || argv.includes("--color") || p.platform === "win32" || (p.stdout || {}).isTTY && env.TERM !== "dumb" || !!env.CI); var formatter = (open, close, replace = open) => (input) => { let string = "" + input, index = string.indexOf(close, open.length); return ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close; }; var replaceClose = (string, close, replace, index) => { let result = "", cursor = 0; do { result += string.substring(cursor, index) + replace; cursor = index + close.length; index = string.indexOf(close, cursor); } while (~index); return result + string.substring(cursor); }; var createColors = (enabled = isColorSupported) => { let f = enabled ? formatter : () => String; return { isColorSupported: enabled, reset: f("\x1B[0m", "\x1B[0m"), bold: f("\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"), dim: f("\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"), italic: f("\x1B[3m", "\x1B[23m"), underline: f("\x1B[4m", "\x1B[24m"), inverse: f("\x1B[7m", "\x1B[27m"), hidden: f("\x1B[8m", "\x1B[28m"), strikethrough: f("\x1B[9m", "\x1B[29m"), black: f("\x1B[30m", "\x1B[39m"), red: f("\x1B[31m", "\x1B[39m"), green: f("\x1B[32m", "\x1B[39m"), yellow: f("\x1B[33m", "\x1B[39m"), blue: f("\x1B[34m", "\x1B[39m"), magenta: f("\x1B[35m", "\x1B[39m"), cyan: f("\x1B[36m", "\x1B[39m"), white: f("\x1B[37m", "\x1B[39m"), gray: f("\x1B[90m", "\x1B[39m"), bgBlack: f("\x1B[40m", "\x1B[49m"), bgRed: f("\x1B[41m", "\x1B[49m"), bgGreen: f("\x1B[42m", "\x1B[49m"), bgYellow: f("\x1B[43m", "\x1B[49m"), bgBlue: f("\x1B[44m", "\x1B[49m"), bgMagenta: f("\x1B[45m", "\x1B[49m"), bgCyan: f("\x1B[46m", "\x1B[49m"), bgWhite: f("\x1B[47m", "\x1B[49m"), blackBright: f("\x1B[90m", "\x1B[39m"), redBright: f("\x1B[91m", "\x1B[39m"), greenBright: f("\x1B[92m", "\x1B[39m"), yellowBright: f("\x1B[93m", "\x1B[39m"), blueBright: f("\x1B[94m", "\x1B[39m"), magentaBright: f("\x1B[95m", "\x1B[39m"), cyanBright: f("\x1B[96m", "\x1B[39m"), whiteBright: f("\x1B[97m", "\x1B[39m"), bgBlackBright: f("\x1B[100m", "\x1B[49m"), bgRedBright: f("\x1B[101m", "\x1B[49m"), bgGreenBright: f("\x1B[102m", "\x1B[49m"), bgYellowBright: f("\x1B[103m", "\x1B[49m"), bgBlueBright: f("\x1B[104m", "\x1B[49m"), bgMagentaBright: f("\x1B[105m", "\x1B[49m"), bgCyanBright: f("\x1B[106m", "\x1B[49m"), bgWhiteBright: f("\x1B[107m", "\x1B[49m") }; }; module.exports = createColors(); module.exports.createColors = createColors; }); // src/cli.ts import { spawn as spawn2 } from "child_process"; import { open } from "fs/promises"; // node_modules/@clack/core/dist/index.mjs var import_sisteransi = __toESM(require_src(), 1); var import_picocolors = __toESM(require_picocolors(), 1); import { stdin as $, stdout as k } from "process"; import * as f from "readline"; import _ from "readline"; import { WriteStream as U } from "tty"; function q({ onlyFirst: e = false } = {}) { const F = ["[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?(?:\\u0007|\\u001B\\u005C|\\u009C))", "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))"].join("|"); return new RegExp(F, e ? undefined : "g"); } var J = q(); function S(e) { if (typeof e != "string") throw new TypeError(`Expected a \`string\`, got \`${typeof e}\``); return e.replace(J, ""); } function T(e) { return e && e.__esModule && Object.prototype.hasOwnProperty.call(e, "default") ? e.default : e; } var j = { exports: {} }; (function(e) { var u = {}; e.exports = u, u.eastAsianWidth = function(t) { var s = t.charCodeAt(0), C = t.length == 2 ? t.charCodeAt(1) : 0, D = s; return 55296 <= s && s <= 56319 && 56320 <= C && C <= 57343 && (s &= 1023, C &= 1023, D = s << 10 | C, D += 65536), D == 12288 || 65281 <= D && D <= 65376 || 65504 <= D && D <= 65510 ? "F" : D == 8361 || 65377 <= D && D <= 65470 || 65474 <= D && D <= 65479 || 65482 <= D && D <= 65487 || 65490 <= D && D <= 65495 || 65498 <= D && D <= 65500 || 65512 <= D && D <= 65518 ? "H" : 4352 <= D && D <= 4447 || 4515 <= D && D <= 4519 || 4602 <= D && D <= 4607 || 9001 <= D && D <= 9002 || 11904 <= D && D <= 11929 || 11931 <= D && D <= 12019 || 12032 <= D && D <= 12245 || 12272 <= D && D <= 12283 || 12289 <= D && D <= 12350 || 12353 <= D && D <= 12438 || 12441 <= D && D <= 12543 || 12549 <= D && D <= 12589 || 12593 <= D && D <= 12686 || 12688 <= D && D <= 12730 || 12736 <= D && D <= 12771 || 12784 <= D && D <= 12830 || 12832 <= D && D <= 12871 || 12880 <= D && D <= 13054 || 13056 <= D && D <= 19903 || 19968 <= D && D <= 42124 || 42128 <= D && D <= 42182 || 43360 <= D && D <= 43388 || 44032 <= D && D <= 55203 || 55216 <= D && D <= 55238 || 55243 <= D && D <= 55291 || 63744 <= D && D <= 64255 || 65040 <= D && D <= 65049 || 65072 <= D && D <= 65106 || 65108 <= D && D <= 65126 || 65128 <= D && D <= 65131 || 110592 <= D && D <= 110593 || 127488 <= D && D <= 127490 || 127504 <= D && D <= 127546 || 127552 <= D && D <= 127560 || 127568 <= D && D <= 127569 || 131072 <= D && D <= 194367 || 177984 <= D && D <= 196605 || 196608 <= D && D <= 262141 ? "W" : 32 <= D && D <= 126 || 162 <= D && D <= 163 || 165 <= D && D <= 166 || D == 172 || D == 175 || 10214 <= D && D <= 10221 || 10629 <= D && D <= 10630 ? "Na" : D == 161 || D == 164 || 167 <= D && D <= 168 || D == 170 || 173 <= D && D <= 174 || 176 <= D && D <= 180 || 182 <= D && D <= 186 || 188 <= D && D <= 191 || D == 198 || D == 208 || 215 <= D && D <= 216 || 222 <= D && D <= 225 || D == 230 || 232 <= D && D <= 234 || 236 <= D && D <= 237 || D == 240 || 242 <= D && D <= 243 || 247 <= D && D <= 250 || D == 252 || D == 254 || D == 257 || D == 273 || D == 275 || D == 283 || 294 <= D && D <= 295 || D == 299 || 305 <= D && D <= 307 || D == 312 || 319 <= D && D <= 322 || D == 324 || 328 <= D && D <= 331 || D == 333 || 338 <= D && D <= 339 || 358 <= D && D <= 359 || D == 363 || D == 462 || D == 464 || D == 466 || D == 468 || D == 470 || D == 472 || D == 474 || D == 476 || D == 593 || D == 609 || D == 708 || D == 711 || 713 <= D && D <= 715 || D == 717 || D == 720 || 728 <= D && D <= 731 || D == 733 || D == 735 || 768 <= D && D <= 879 || 913 <= D && D <= 929 || 931 <= D && D <= 937 || 945 <= D && D <= 961 || 963 <= D && D <= 969 || D == 1025 || 1040 <= D && D <= 1103 || D == 1105 || D == 8208 || 8211 <= D && D <= 8214 || 8216 <= D && D <= 8217 || 8220 <= D && D <= 8221 || 8224 <= D && D <= 8226 || 8228 <= D && D <= 8231 || D == 8240 || 8242 <= D && D <= 8243 || D == 8245 || D == 8251 || D == 8254 || D == 8308 || D == 8319 || 8321 <= D && D <= 8324 || D == 8364 || D == 8451 || D == 8453 || D == 8457 || D == 8467 || D == 8470 || 8481 <= D && D <= 8482 || D == 8486 || D == 8491 || 8531 <= D && D <= 8532 || 8539 <= D && D <= 8542 || 8544 <= D && D <= 8555 || 8560 <= D && D <= 8569 || D == 8585 || 8592 <= D && D <= 8601 || 8632 <= D && D <= 8633 || D == 8658 || D == 8660 || D == 8679 || D == 8704 || 8706 <= D && D <= 8707 || 8711 <= D && D <= 8712 || D == 8715 || D == 8719 || D == 8721 || D == 8725 || D == 8730 || 8733 <= D && D <= 8736 || D == 8739 || D == 8741 || 8743 <= D && D <= 8748 || D == 8750 || 8756 <= D && D <= 8759 || 8764 <= D && D <= 8765 || D == 8776 || D == 8780 || D == 8786 || 8800 <= D && D <= 8801 || 8804 <= D && D <= 8807 || 8810 <= D && D <= 8811 || 8814 <= D && D <= 8815 || 8834 <= D && D <= 8835 || 8838 <= D && D <= 8839 || D == 8853 || D == 8857 || D == 8869 || D == 8895 || D == 8978 || 9312 <= D && D <= 9449 || 9451 <= D && D <= 9547 || 9552 <= D && D <= 9587 || 9600 <= D && D <= 9615 || 9618 <= D && D <= 9621 || 9632 <= D && D <= 9633 || 9635 <= D && D <= 9641 || 9650 <= D && D <= 9651 || 9654 <= D && D <= 9655 || 9660 <= D && D <= 9661 || 9664 <= D && D <= 9665 || 9670 <= D && D <= 9672 || D == 9675 || 9678 <= D && D <= 9681 || 9698 <= D && D <= 9701 || D == 9711 || 9733 <= D && D <= 9734 || D == 9737 || 9742 <= D && D <= 9743 || 9748 <= D && D <= 9749 || D == 9756 || D == 9758 || D == 9792 || D == 9794 || 9824 <= D && D <= 9825 || 9827 <= D && D <= 9829 || 9831 <= D && D <= 9834 || 9836 <= D && D <= 9837 || D == 9839 || 9886 <= D && D <= 9887 || 9918 <= D && D <= 9919 || 9924 <= D && D <= 9933 || 9935 <= D && D <= 9953 || D == 9955 || 9960 <= D && D <= 9983 || D == 10045 || D == 10071 || 10102 <= D && D <= 10111 || 11093 <= D && D <= 11097 || 12872 <= D && D <= 12879 || 57344 <= D && D <= 63743 || 65024 <= D && D <= 65039 || D == 65533 || 127232 <= D && D <= 127242 || 127248 <= D && D <= 127277 || 127280 <= D && D <= 127337 || 127344 <= D && D <= 127386 || 917760 <= D && D <= 917999 || 983040 <= D && D <= 1048573 || 1048576 <= D && D <= 1114109 ? "A" : "N"; }, u.characterLength = function(t) { var s = this.eastAsianWidth(t); return s == "F" || s == "W" || s == "A" ? 2 : 1; }; function F(t) { return t.match(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[^\uD800-\uDFFF]/g) || []; } u.length = function(t) { for (var s = F(t), C = 0, D = 0;D < s.length; D++) C = C + this.characterLength(s[D]); return C; }, u.slice = function(t, s, C) { textLen = u.length(t), s = s || 0, C = C || 1, s < 0 && (s = textLen + s), C < 0 && (C = textLen + C); for (var D = "", i = 0, n = F(t), E = 0;E < n.length; E++) { var h = n[E], o = u.length(h); if (i >= s - (o == 2 ? 1 : 0)) if (i + o <= C) D += h; else break; i += o; } return D; }; })(j); var Q = j.exports; var X = T(Q); var DD = function() { return /\uD83C\uDFF4\uDB40\uDC67\uDB40\uDC62(?:\uDB40\uDC77\uDB40\uDC6C\uDB40\uDC73|\uDB40\uDC73\uDB40\uDC63\uDB40\uDC74|\uDB40\uDC65\uDB40\uDC6E\uDB40\uDC67)\uDB40\uDC7F|(?:\uD83E\uDDD1\uD83C\uDFFF\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFF\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69]))(?:\uD83C[\uDFFB-\uDFFE])|(?:\uD83E\uDDD1\uD83C\uDFFE\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFE\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69]))(?:\uD83C[\uDFFB-\uDFFD\uDFFF])|(?:\uD83E\uDDD1\uD83C\uDFFD\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFD\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69]))(?:\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])|(?:\uD83E\uDDD1\uD83C\uDFFC\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFC\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69]))(?:\uD83C[\uDFFB\uDFFD-\uDFFF])|(?:\uD83E\uDDD1\uD83C\uDFFB\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFB\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69]))(?:\uD83C[\uDFFC-\uDFFF])|\uD83D\uDC68(?:\uD83C\uDFFB(?:\u200D(?:\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFF])|\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFF]))|\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFC-\uDFFF])|[\u2695\u2696\u2708]\uFE0F|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD]))?|(?:\uD83C[\uDFFC-\uDFFF])\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFF])|\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFF]))|\u200D(?:\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83D\uDC68|(?:\uD83D[\uDC68\uDC69])\u200D(?:\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67]))|\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFF\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFE])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFE\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFD\uDFFF])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFD\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFC\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFD-\uDFFF])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|(?:\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708]|\u200D[\u2695\u2696\u2708])\uFE0F|\u200D(?:(?:\uD83D[\uDC68\uDC69])\u200D(?:\uD83D[\uDC66\uDC67])|\uD83D[\uDC66\uDC67])|\uD83C\uDFFF|\uD83C\uDFFE|\uD83C\uDFFD|\uD83C\uDFFC)?|(?:\uD83D\uDC69(?:\uD83C\uDFFB\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D(?:\uD83D[\uDC68\uDC69])|\uD83D[\uDC68\uDC69])|(?:\uD83C[\uDFFC-\uDFFF])\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D(?:\uD83D[\uDC68\uDC69])|\uD83D[\uDC68\uDC69]))|\uD83E\uDDD1(?:\uD83C[\uDFFB-\uDFFF])\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1)(?:\uD83C[\uDFFB-\uDFFF])|\uD83D\uDC69\u200D\uD83D\uDC69\u200D(?:\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67]))|\uD83D\uDC69(?:\u200D(?:\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D(?:\uD83D[\uDC68\uDC69])|\uD83D[\uDC68\uDC69])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFF\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFE\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFD\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFC\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFB\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD]))|\uD83E\uDDD1(?:\u200D(?:\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFF\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFE\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFD\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFC\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFB\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD]))|\uD83D\uDC69\u200D\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC69\u200D\uD83D\uDC69\u200D(?:\uD83D[\uDC66\uDC67])|\uD83D\uDC69\u200D\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67])|(?:\uD83D\uDC41\uFE0F\u200D\uD83D\uDDE8|\uD83E\uDDD1(?:\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708]|\uD83C\uDFFB\u200D[\u2695\u2696\u2708]|\u200D[\u2695\u2696\u2708])|\uD83D\uDC69(?:\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708]|\uD83C\uDFFB\u200D[\u2695\u2696\u2708]|\u200D[\u2695\u2696\u2708])|\uD83D\uDE36\u200D\uD83C\uDF2B|\uD83C\uDFF3\uFE0F\u200D\u26A7|\uD83D\uDC3B\u200D\u2744|(?:(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC70\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD35\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD4\uDDD6-\uDDDD])(?:\uD83C[\uDFFB-\uDFFF])|\uD83D\uDC6F|\uD83E[\uDD3C\uDDDE\uDDDF])\u200D[\u2640\u2642]|(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)(?:\uFE0F|\uD83C[\uDFFB-\uDFFF])\u200D[\u2640\u2642]|\uD83C\uDFF4\u200D\u2620|(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC70\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD35\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD4\uDDD6-\uDDDD])\u200D[\u2640\u2642]|[\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u2328\u23CF\u23ED-\u23EF\u23F1\u23F2\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB\u25FC\u2600-\u2604\u260E\u2611\u2618\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u2692\u2694-\u2697\u2699\u269B\u269C\u26A0\u26A7\u26B0\u26B1\u26C8\u26CF\u26D1\u26D3\u26E9\u26F0\u26F1\u26F4\u26F7\u26F8\u2702\u2708\u2709\u270F\u2712\u2714\u2716\u271D\u2721\u2733\u2734\u2744\u2747\u2763\u27A1\u2934\u2935\u2B05-\u2B07\u3030\u303D\u3297\u3299]|\uD83C[\uDD70\uDD71\uDD7E\uDD7F\uDE02\uDE37\uDF21\uDF24-\uDF2C\uDF36\uDF7D\uDF96\uDF97\uDF99-\uDF9B\uDF9E\uDF9F\uDFCD\uDFCE\uDFD4-\uDFDF\uDFF5\uDFF7]|\uD83D[\uDC3F\uDCFD\uDD49\uDD4A\uDD6F\uDD70\uDD73\uDD76-\uDD79\uDD87\uDD8A-\uDD8D\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA\uDECB\uDECD-\uDECF\uDEE0-\uDEE5\uDEE9\uDEF0\uDEF3])\uFE0F|\uD83C\uDFF3\uFE0F\u200D\uD83C\uDF08|\uD83D\uDC69\u200D\uD83D\uDC67|\uD83D\uDC69\u200D\uD83D\uDC66|\uD83D\uDE35\u200D\uD83D\uDCAB|\uD83D\uDE2E\u200D\uD83D\uDCA8|\uD83D\uDC15\u200D\uD83E\uDDBA|\uD83E\uDDD1(?:\uD83C\uDFFF|\uD83C\uDFFE|\uD83C\uDFFD|\uD83C\uDFFC|\uD83C\uDFFB)?|\uD83D\uDC69(?:\uD83C\uDFFF|\uD83C\uDFFE|\uD83C\uDFFD|\uD83C\uDFFC|\uD83C\uDFFB)?|\uD83C\uDDFD\uD83C\uDDF0|\uD83C\uDDF6\uD83C\uDDE6|\uD83C\uDDF4\uD83C\uDDF2|\uD83D\uDC08\u200D\u2B1B|\u2764\uFE0F\u200D(?:\uD83D\uDD25|\uD83E\uDE79)|\uD83D\uDC41\uFE0F|\uD83C\uDFF3\uFE0F|\uD83C\uDDFF(?:\uD83C[\uDDE6\uDDF2\uDDFC])|\uD83C\uDDFE(?:\uD83C[\uDDEA\uDDF9])|\uD83C\uDDFC(?:\uD83C[\uDDEB\uDDF8])|\uD83C\uDDFB(?:\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDEE\uDDF3\uDDFA])|\uD83C\uDDFA(?:\uD83C[\uDDE6\uDDEC\uDDF2\uDDF3\uDDF8\uDDFE\uDDFF])|\uD83C\uDDF9(?:\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDED\uDDEF-\uDDF4\uDDF7\uDDF9\uDDFB\uDDFC\uDDFF])|\uD83C\uDDF8(?:\uD83C[\uDDE6-\uDDEA\uDDEC-\uDDF4\uDDF7-\uDDF9\uDDFB\uDDFD-\uDDFF])|\uD83C\uDDF7(?:\uD83C[\uDDEA\uDDF4\uDDF8\uDDFA\uDDFC])|\uD83C\uDDF5(?:\uD83C[\uDDE6\uDDEA-\uDDED\uDDF0-\uDDF3\uDDF7-\uDDF9\uDDFC\uDDFE])|\uD83C\uDDF3(?:\uD83C[\uDDE6\uDDE8\uDDEA-\uDDEC\uDDEE\uDDF1\uDDF4\uDDF5\uDDF7\uDDFA\uDDFF])|\uD83C\uDDF2(?:\uD83C[\uDDE6\uDDE8-\uDDED\uDDF0-\uDDFF])|\uD83C\uDDF1(?:\uD83C[\uDDE6-\uDDE8\uDDEE\uDDF0\uDDF7-\uDDFB\uDDFE])|\uD83C\uDDF0(?:\uD83C[\uDDEA\uDDEC-\uDDEE\uDDF2\uDDF3\uDDF5\uDDF7\uDDFC\uDDFE\uDDFF])|\uD83C\uDDEF(?:\uD83C[\uDDEA\uDDF2\uDDF4\uDDF5])|\uD83C\uDDEE(?:\uD83C[\uDDE8-\uDDEA\uDDF1-\uDDF4\uDDF6-\uDDF9])|\uD83C\uDDED(?:\uD83C[\uDDF0\uDDF2\uDDF3\uDDF7\uDDF9\uDDFA])|\uD83C\uDDEC(?:\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEE\uDDF1-\uDDF3\uDDF5-\uDDFA\uDDFC\uDDFE])|\uD83C\uDDEB(?:\uD83C[\uDDEE-\uDDF0\uDDF2\uDDF4\uDDF7])|\uD83C\uDDEA(?:\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDED\uDDF7-\uDDFA])|\uD83C\uDDE9(?:\uD83C[\uDDEA\uDDEC\uDDEF\uDDF0\uDDF2\uDDF4\uDDFF])|\uD83C\uDDE8(?:\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDEE\uDDF0-\uDDF5\uDDF7\uDDFA-\uDDFF])|\uD83C\uDDE7(?:\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEF\uDDF1-\uDDF4\uDDF6-\uDDF9\uDDFB\uDDFC\uDDFE\uDDFF])|\uD83C\uDDE6(?:\uD83C[\uDDE8-\uDDEC\uDDEE\uDDF1\uDDF2\uDDF4\uDDF6-\uDDFA\uDDFC\uDDFD\uDDFF])|[#\*0-9]\uFE0F\u20E3|\u2764\uFE0F|(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC70\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD35\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD4\uDDD6-\uDDDD])(?:\uD83C[\uDFFB-\uDFFF])|(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)(?:\uFE0F|\uD83C[\uDFFB-\uDFFF])|\uD83C\uDFF4|(?:[\u270A\u270B]|\uD83C[\uDF85\uDFC2\uDFC7]|\uD83D[\uDC42\uDC43\uDC46-\uDC50\uDC66\uDC67\uDC6B-\uDC6D\uDC72\uDC74-\uDC76\uDC78\uDC7C\uDC83\uDC85\uDC8F\uDC91\uDCAA\uDD7A\uDD95\uDD96\uDE4C\uDE4F\uDEC0\uDECC]|\uD83E[\uDD0C\uDD0F\uDD18-\uDD1C\uDD1E\uDD1F\uDD30-\uDD34\uDD36\uDD77\uDDB5\uDDB6\uDDBB\uDDD2\uDDD3\uDDD5])(?:\uD83C[\uDFFB-\uDFFF])|(?:[\u261D\u270C\u270D]|\uD83D[\uDD74\uDD90])(?:\uFE0F|\uD83C[\uDFFB-\uDFFF])|[\u270A\u270B]|\uD83C[\uDF85\uDFC2\uDFC7]|\uD83D[\uDC08\uDC15\uDC3B\uDC42\uDC43\uDC46-\uDC50\uDC66\uDC67\uDC6B-\uDC6D\uDC72\uDC74-\uDC76\uDC78\uDC7C\uDC83\uDC85\uDC8F\uDC91\uDCAA\uDD7A\uDD95\uDD96\uDE2E\uDE35\uDE36\uDE4C\uDE4F\uDEC0\uDECC]|\uD83E[\uDD0C\uDD0F\uDD18-\uDD1C\uDD1E\uDD1F\uDD30-\uDD34\uDD36\uDD77\uDDB5\uDDB6\uDDBB\uDDD2\uDDD3\uDDD5]|\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC70\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD35\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD4\uDDD6-\uDDDD]|\uD83D\uDC6F|\uD83E[\uDD3C\uDDDE\uDDDF]|[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55]|\uD83C[\uDC04\uDCCF\uDD8E\uDD91-\uDD9A\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF84\uDF86-\uDF93\uDFA0-\uDFC1\uDFC5\uDFC6\uDFC8\uDFC9\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF8-\uDFFF]|\uD83D[\uDC00-\uDC07\uDC09-\uDC14\uDC16-\uDC3A\uDC3C-\uDC3E\uDC40\uDC44\uDC45\uDC51-\uDC65\uDC6A\uDC79-\uDC7B\uDC7D-\uDC80\uDC84\uDC88-\uDC8E\uDC90\uDC92-\uDCA9\uDCAB-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDDA4\uDDFB-\uDE2D\uDE2F-\uDE34\uDE37-\uDE44\uDE48-\uDE4A\uDE80-\uDEA2\uDEA4-\uDEB3\uDEB7-\uDEBF\uDEC1-\uDEC5\uDED0-\uDED2\uDED5-\uDED7\uDEEB\uDEEC\uDEF4-\uDEFC\uDFE0-\uDFEB]|\uD83E[\uDD0D\uDD0E\uDD10-\uDD17\uDD1D\uDD20-\uDD25\uDD27-\uDD2F\uDD3A\uDD3F-\uDD45\uDD47-\uDD76\uDD78\uDD7A-\uDDB4\uDDB7\uDDBA\uDDBC-\uDDCB\uDDD0\uDDE0-\uDDFF\uDE70-\uDE74\uDE78-\uDE7A\uDE80-\uDE86\uDE90-\uDEA8\uDEB0-\uDEB6\uDEC0-\uDEC2\uDED0-\uDED6]|(?:[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u270A\u270B\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55]|\uD83C[\uDC04\uDCCF\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF93\uDFA0-\uDFCA\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF4\uDFF8-\uDFFF]|\uD83D[\uDC00-\uDC3E\uDC40\uDC42-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDD7A\uDD95\uDD96\uDDA4\uDDFB-\uDE4F\uDE80-\uDEC5\uDECC\uDED0-\uDED2\uDED5-\uDED7\uDEEB\uDEEC\uDEF4-\uDEFC\uDFE0-\uDFEB]|\uD83E[\uDD0C-\uDD3A\uDD3C-\uDD45\uDD47-\uDD78\uDD7A-\uDDCB\uDDCD-\uDDFF\uDE70-\uDE74\uDE78-\uDE7A\uDE80-\uDE86\uDE90-\uDEA8\uDEB0-\uDEB6\uDEC0-\uDEC2\uDED0-\uDED6])|(?:[#\*0-9\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u261D\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692-\u2697\u2699\u269B\u269C\u26A0\u26A1\u26A7\u26AA\u26AB\u26B0\u26B1\u26BD\u26BE\u26C4\u26C5\u26C8\u26CE\u26CF\u26D1\u26D3\u26D4\u26E9\u26EA\u26F0-\u26F5\u26F7-\u26FA\u26FD\u2702\u2705\u2708-\u270D\u270F\u2712\u2714\u2716\u271D\u2721\u2728\u2733\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763\u2764\u2795-\u2797\u27A1\u27B0\u27BF\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299]|\uD83C[\uDC04\uDCCF\uDD70\uDD71\uDD7E\uDD7F\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE02\uDE1A\uDE2F\uDE32-\uDE3A\uDE50\uDE51\uDF00-\uDF21\uDF24-\uDF93\uDF96\uDF97\uDF99-\uDF9B\uDF9E-\uDFF0\uDFF3-\uDFF5\uDFF7-\uDFFF]|\uD83D[\uDC00-\uDCFD\uDCFF-\uDD3D\uDD49-\uDD4E\uDD50-\uDD67\uDD6F\uDD70\uDD73-\uDD7A\uDD87\uDD8A-\uDD8D\uDD90\uDD95\uDD96\uDDA4\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA-\uDE4F\uDE80-\uDEC5\uDECB-\uDED2\uDED5-\uDED7\uDEE0-\uDEE5\uDEE9\uDEEB\uDEEC\uDEF0\uDEF3-\uDEFC\uDFE0-\uDFEB]|\uD83E[\uDD0C-\uDD3A\uDD3C-\uDD45\uDD47-\uDD78\uDD7A-\uDDCB\uDDCD-\uDDFF\uDE70-\uDE74\uDE78-\uDE7A\uDE80-\uDE86\uDE90-\uDEA8\uDEB0-\uDEB6\uDEC0-\uDEC2\uDED0-\uDED6])\uFE0F|(?:[\u261D\u26F9\u270A-\u270D]|\uD83C[\uDF85\uDFC2-\uDFC4\uDFC7\uDFCA-\uDFCC]|\uD83D[\uDC42\uDC43\uDC46-\uDC50\uDC66-\uDC78\uDC7C\uDC81-\uDC83\uDC85-\uDC87\uDC8F\uDC91\uDCAA\uDD74\uDD75\uDD7A\uDD90\uDD95\uDD96\uDE45-\uDE47\uDE4B-\uDE4F\uDEA3\uDEB4-\uDEB6\uDEC0\uDECC]|\uD83E[\uDD0C\uDD0F\uDD18-\uDD1F\uDD26\uDD30-\uDD39\uDD3C-\uDD3E\uDD77\uDDB5\uDDB6\uDDB8\uDDB9\uDDBB\uDDCD-\uDDCF\uDDD1-\uDDDD])/g; }; var uD = T(DD); function A(e, u = {}) { if (typeof e != "string" || e.length === 0 || (u = { ambiguousIsNarrow: true, ...u }, e = S(e), e.length === 0)) return 0; e = e.replace(uD(), " "); const F = u.ambiguousIsNarrow ? 1 : 2; let t = 0; for (const s of e) { const C = s.codePointAt(0); if (C <= 31 || C >= 127 && C <= 159 || C >= 768 && C <= 879) continue; switch (X.eastAsianWidth(s)) { case "F": case "W": t += 2; break; case "A": t += F; break; default: t += 1; } } return t; } var d = 10; var M = (e = 0) => (u) => `\x1B[${u + e}m`; var P = (e = 0) => (u) => `\x1B[${38 + e};5;${u}m`; var W = (e = 0) => (u, F, t) => `\x1B[${38 + e};2;${u};${F};${t}m`; var r = { modifier: { reset: [0, 0], bold: [1, 22], dim: [2, 22], italic: [3, 23], underline: [4, 24], overline: [53, 55], inverse: [7, 27], hidden: [8, 28], strikethrough: [9, 29] }, color: { black: [30, 39], red: [31, 39], green: [32, 39], yellow: [33, 39], blue: [34, 39], magenta: [35, 39], cyan: [36, 39], white: [37, 39], blackBright: [90, 39], gray: [90, 39], grey: [90, 39], redBright: [91, 39], greenBright: [92, 39], yellowBright: [93, 39], blueBright: [94, 39], magentaBright: [95, 39], cyanBright: [96, 39], whiteBright: [97, 39] }, bgColor: { bgBlack: [40, 49], bgRed: [41, 49], bgGreen: [42, 49], bgYellow: [43, 49], bgBlue: [44, 49], bgMagenta: [45, 49], bgCyan: [46, 49], bgWhite: [47, 49], bgBlackBright: [100, 49], bgGray: [100, 49], bgGrey: [100, 49], bgRedBright: [101, 49], bgGreenBright: [102, 49], bgYellowBright: [103, 49], bgBlueBright: [104, 49], bgMagentaBright: [105, 49], bgCyanBright: [106, 49], bgWhiteBright: [107, 49] } }; Object.keys(r.modifier); var FD = Object.keys(r.color); var eD = Object.keys(r.bgColor); [...FD, ...eD]; function tD() { const e = new Map; for (const [u, F] of Object.entries(r)) { for (const [t, s] of Object.entries(F)) r[t] = { open: `\x1B[${s[0]}m`, close: `\x1B[${s[1]}m` }, F[t] = r[t], e.set(s[0], s[1]); Object.defineProperty(r, u, { value: F, enumerable: false }); } return Object.defineProperty(r, "codes", { value: e, enumerable: false }), r.color.close = "\x1B[39m", r.bgColor.close = "\x1B[49m", r.color.ansi = M(), r.color.ansi256 = P(), r.color.ansi16m = W(), r.bgColor.ansi = M(d), r.bgColor.ansi256 = P(d), r.bgColor.ansi16m = W(d), Object.defineProperties(r, { rgbToAnsi256: { value: (u, F, t) => u === F && F === t ? u < 8 ? 16 : u > 248 ? 231 : Math.round((u - 8) / 247 * 24) + 232 : 16 + 36 * Math.round(u / 255 * 5) + 6 * Math.round(F / 255 * 5) + Math.round(t / 255 * 5), enumerable: false }, hexToRgb: { value: (u) => { const F = /[a-f\d]{6}|[a-f\d]{3}/i.exec(u.toString(16)); if (!F) return [0, 0, 0]; let [t] = F; t.length === 3 && (t = [...t].map((C) => C + C).join("")); const s = Number.parseInt(t, 16); return [s >> 16 & 255, s >> 8 & 255, s & 255]; }, enumerable: false }, hexToAnsi256: { value: (u) => r.rgbToAnsi256(...r.hexToRgb(u)), enumerable: false }, ansi256ToAnsi: { value: (u) => { if (u < 8) return 30 + u; if (u < 16) return 90 + (u - 8); let F, t, s; if (u >= 232) F = ((u - 232) * 10 + 8) / 255, t = F, s = F; else { u -= 16; const i = u % 36; F = Math.floor(u / 36) / 5, t = Math.floor(i / 6) / 5, s = i % 6 / 5; } const C = Math.max(F, t, s) * 2; if (C === 0) return 30; let D = 30 + (Math.round(s) << 2 | Math.round(t) << 1 | Math.round(F)); return C === 2 && (D += 60), D; }, enumerable: false }, rgbToAnsi: { value: (u, F, t) => r.ansi256ToAnsi(r.rgbToAnsi256(u, F, t)), enumerable: false }, hexToAnsi: { value: (u) => r.ansi256ToAnsi(r.hexToAnsi256(u)), enumerable: false } }), r; } var sD = tD(); var g = new Set(["\x1B", "\x9B"]); var CD = 39; var b = "\x07"; var O = "["; var iD = "]"; var I = "m"; var w = `${iD}8;;`; var N = (e) => `${g.values().next().value}${O}${e}${I}`; var L = (e) => `${g.values().next().value}${w}${e}${b}`; var rD = (e) => e.split(" ").map((u) => A(u)); var y = (e, u, F) => { const t = [...u]; let s = false, C = false, D = A(S(e[e.length - 1])); for (const [i, n] of t.entries()) { const E = A(n); if (D + E <= F ? e[e.length - 1] += n : (e.push(n), D = 0), g.has(n) && (s = true, C = t.slice(i + 1).join("").startsWith(w)), s) { C ? n === b && (s = false, C = false) : n === I && (s = false); continue; } D += E, D === F && i < t.length - 1 && (e.push(""), D = 0); } !D && e[e.length - 1].length > 0 && e.length > 1 && (e[e.length - 2] += e.pop()); }; var ED = (e) => { const u = e.split(" "); let F = u.length; for (;F > 0 && !(A(u[F - 1]) > 0); ) F--; return F === u.length ? e : u.slice(0, F).join(" ") + u.slice(F).join(""); }; var oD = (e, u, F = {}) => { if (F.trim !== false && e.trim() === "") return ""; let t = "", s, C; const D = rD(e); let i = [""]; for (const [E, h] of e.split(" ").entries()) { F.trim !== false && (i[i.length - 1] = i[i.length - 1].trimStart()); let o = A(i[i.length - 1]); if (E !== 0 && (o >= u && (F.wordWrap === false || F.trim === false) && (i.push(""), o = 0), (o > 0 || F.trim === false) && (i[i.length - 1] += " ", o++)), F.hard && D[E] > u) { const B = u - o, p = 1 + Math.floor((D[E] - B - 1) / u); Math.floor((D[E] - 1) / u) < p && i.push(""), y(i, h, u); continue; } if (o + D[E] > u && o > 0 && D[E] > 0) { if (F.wordWrap === false && o < u) { y(i, h, u); continue; } i.push(""); } if (o + D[E] > u && F.wordWrap === false) { y(i, h, u); continue; } i[i.length - 1] += h; } F.trim !== false && (i = i.map((E) => ED(E))); const n = [...i.join(` `)]; for (const [E, h] of n.entries()) { if (t += h, g.has(h)) { const { groups: B } = new RegExp(`(?:\\${O}(?\\d+)m|\\${w}(?.*)${b})`).exec(n.slice(E).join("")) || { groups: {} }; if (B.code !== undefined) { const p = Number.parseFloat(B.code); s = p === CD ? undefined : p; } else B.uri !== undefined && (C = B.uri.length === 0 ? undefined : B.uri); } const o = sD.codes.get(Number(s)); n[E + 1] === ` ` ? (C && (t += L("")), s && o && (t += N(o))) : h === ` ` && (s && o && (t += N(s)), C && (t += L(C))); } return t; }; function R(e, u, F) { return String(e).normalize().replace(/\r\n/g, ` `).split(` `).map((t) => oD(t, u, F)).join(` `); } var nD = Object.defineProperty; var aD = (e, u, F) => (u in e) ? nD(e, u, { enumerable: true, configurable: true, writable: true, value: F }) : e[u] = F; var a = (e, u, F) => (aD(e, typeof u != "symbol" ? u + "" : u, F), F); function hD(e, u) { if (e === u) return; const F = e.split(` `), t = u.split(` `), s = []; for (let C = 0;C < Math.max(F.length, t.length); C++) F[C] !== t[C] && s.push(C); return s; } var V = Symbol("clack:cancel"); function lD(e) { return e === V; } function v(e, u) { e.isTTY && e.setRawMode(u); } var z = new Map([["k", "up"], ["j", "down"], ["h", "left"], ["l", "right"]]); var xD = new Set(["up", "down", "left", "right", "space", "enter"]); class x { constructor({ render: u, input: F = $, output: t = k, ...s }, C = true) { a(this, "input"), a(this, "output"), a(this, "rl"), a(this, "opts"), a(this, "_track", false), a(this, "_render"), a(this, "_cursor", 0), a(this, "state", "initial"), a(this, "value"), a(this, "error", ""), a(this, "subscribers", new Map), a(this, "_prevFrame", ""), this.opts = s, this.onKeypress = this.onKeypress.bind(this), this.close = this.close.bind(this), this.render = this.render.bind(this), this._render = u.bind(this), this._track = C, this.input = F, this.output = t; } prompt() { const u = new U(0); return u._write = (F, t, s) => { this._track && (this.value = this.rl.line.replace(/\t/g, ""), this._cursor = this.rl.cursor, this.emit("value", this.value)), s(); }, this.input.pipe(u), this.rl = _.createInterface({ input: this.input, output: u, tabSize: 2, prompt: "", escapeCodeTimeout: 50 }), _.emitKeypressEvents(this.input, this.rl), this.rl.prompt(), this.opts.initialValue !== undefined && this._track && this.rl.write(this.opts.initialValue), this.input.on("keypress", this.onKeypress), v(this.input, true), this.output.on("resize", this.render), this.render(), new Promise((F, t) => { this.once("submit", () => { this.output.write(import_sisteransi.cursor.show), this.output.off("resize", this.render), v(this.input, false), F(this.value); }), this.once("cancel", () => { this.output.write(import_sisteransi.cursor.show), this.output.off("resize", this.render), v(this.input, false), F(V); }); }); } on(u, F) { const t = this.subscribers.get(u) ?? []; t.push({ cb: F }), this.subscribers.set(u, t); } once(u, F) { const t = this.subscribers.get(u) ?? []; t.push({ cb: F, once: true }), this.subscribers.set(u, t); } emit(u, ...F) { const t = this.subscribers.get(u) ?? [], s = []; for (const C of t) C.cb(...F), C.once && s.push(() => t.splice(t.indexOf(C), 1)); for (const C of s) C(); } unsubscribe() { this.subscribers.clear(); } onKeypress(u, F) { if (this.state === "error" && (this.state = "active"), F?.name && !this._track && z.has(F.name) && this.emit("cursor", z.get(F.name)), F?.name && xD.has(F.name) && this.emit("cursor", F.name), u && (u.toLowerCase() === "y" || u.toLowerCase() === "n") && this.emit("confirm", u.toLowerCase() === "y"), u === "\t" && this.opts.placeholder && (this.value || (this.rl.write(this.opts.placeholder), this.emit("value", this.opts.placeholder))), u && this.emit("key", u.toLowerCase()), F?.name === "return") { if (this.opts.validate) { const t = this.opts.validate(this.value); t && (this.error = t, this.state = "error", this.rl.write(this.value)); } this.state !== "error" && (this.state = "submit"); } u === "\x03" && (this.state = "cancel"), (this.state === "submit" || this.state === "cancel") && this.emit("finalize"), this.render(), (this.state === "submit" || this.state === "cancel") && this.close(); } close() { this.input.unpipe(), this.input.removeListener("keypress", this.onKeypress), this.output.write(` `), v(this.input, false), this.rl.close(), this.emit(`${this.state}`, this.value), this.unsubscribe(); } restoreCursor() { const u = R(this._prevFrame, process.stdout.columns, { hard: true }).split(` `).length - 1; this.output.write(import_sisteransi.cursor.move(-999, u * -1)); } render() { const u = R(this._render(this) ?? "", process.stdout.columns, { hard: true }); if (u !== this._prevFrame) { if (this.state === "initial") this.output.write(import_sisteransi.cursor.hide); else { const F = hD(this._prevFrame, u); if (this.restoreCursor(), F && F?.length === 1) { const t = F[0]; this.output.write(import_sisteransi.cursor.move(0, t)), this.output.write(import_sisteransi.erase.lines(1)); const s = u.split(` `); this.output.write(s[t]), this._prevFrame = u, this.output.write(import_sisteransi.cursor.move(0, s.length - t - 1)); return; } else if (F && F?.length > 1) { const t = F[0]; this.output.write(import_sisteransi.cursor.move(0, t)), this.output.write(import_sisteransi.erase.down()); const s = u.split(` `).slice(t); this.output.write(s.join(` `)), this._prevFrame = u; return; } this.output.write(import_sisteransi.erase.down()); } this.output.write(u), this.state === "initial" && (this.state = "active"), this._prevFrame = u; } } } class BD extends x { get cursor() { return this.value ? 0 : 1; } get _value() { return this.cursor === 0; } constructor(u) { super(u, false), this.value = !!u.initialValue, this.on("value", () => { this.value = this._value; }), this.on("confirm", (F) => { this.output.write(import_sisteransi.cursor.move(0, -1)), this.value = F, this.state = "submit", this.close(); }), this.on("cursor", () => { this.value = !this.value; }); } } var wD = Object.defineProperty; var yD = (e, u, F) => (u in e) ? wD(e, u, { enumerable: true, configurable: true, writable: true, value: F }) : e[u] = F; var Z = (e, u, F) => (yD(e, typeof u != "symbol" ? u + "" : u, F), F); var $D = class extends x { constructor(u) { super(u, false), Z(this, "options"), Z(this, "cursor", 0), this.options = u.options, this.cursor = this.options.findIndex(({ value: F }) => F === u.initialValue), this.cursor === -1 && (this.cursor = 0), this.changeValue(), this.on("cursor", (F) => { switch (F) { case "left": case "up": this.cursor = this.cursor === 0 ? this.options.length - 1 : this.cursor - 1; break; case "down": case "right": this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1; break; } this.changeValue(); }); } get _value() { return this.options[this.cursor]; } changeValue() { this.value = this._value.value; } }; var TD = Object.defineProperty; var jD = (e, u, F) => (u in e) ? TD(e, u, { enumerable: true, configurable: true, writable: true, value: F }) : e[u] = F; var MD = (e, u, F) => (jD(e, typeof u != "symbol" ? u + "" : u, F), F); class PD extends x { constructor(u) { super(u), MD(this, "valueWithCursor", ""), this.on("finalize", () => { this.value || (this.value = u.defaultValue), this.valueWithCursor = this.value; }), this.on("value", () => { if (this.cursor >= this.value.length) this.valueWithCursor = `${this.value}${import_picocolors.default.inverse(import_picocolors.default.hidden("_"))}`; else { const F = this.value.slice(0, this.cursor), t = this.value.slice(this.cursor); this.valueWithCursor = `${F}${import_picocolors.default.inverse(t[0])}${t.slice(1)}`; } }); } get cursor() { return this._cursor; } } var WD = globalThis.process.platform.startsWith("win"); function OD({ input: e = $, output: u = k, overwrite: F = true, hideCursor: t = true } = {}) { const s = f.createInterface({ input: e, output: u, prompt: "", tabSize: 1 }); f.emitKeypressEvents(e, s), e.isTTY && e.setRawMode(true); const C = (D, { name: i }) => { if (String(D) === "\x03") { t && u.write(import_sisteransi.cursor.show), process.exit(0); return; } if (!F) return; let n = i === "return" ? 0 : -1, E = i === "return" ? -1 : 0; f.moveCursor(u, n, E, () => { f.clearLine(u, 1, () => { e.once("keypress", C); }); }); }; return t && u.write(import_sisteransi.cursor.hide), e.once("keypress", C), () => { e.off("keypress", C), t && u.write(import_sisteransi.cursor.show), e.isTTY && !WD && e.setRawMode(false), s.terminal = false, s.close(); }; } // node_modules/@clack/prompts/dist/index.mjs var import_picocolors2 = __toESM(require_picocolors(), 1); var import_sisteransi2 = __toESM(require_src(), 1); import h from "process"; function q2() { return h.platform !== "win32" ? h.env.TERM !== "linux" : Boolean(h.env.CI) || Boolean(h.env.WT_SESSION) || Boolean(h.env.TERMINUS_SUBLIME) || h.env.ConEmuTask === "{cmd::Cmder}" || h.env.TERM_PROGRAM === "Terminus-Sublime" || h.env.TERM_PROGRAM === "vscode" || h.env.TERM === "xterm-256color" || h.env.TERM === "alacritty" || h.env.TERMINAL_EMULATOR === "JetBrains-JediTerm"; } var _2 = q2(); var o = (r2, n) => _2 ? r2 : n; var H = o("\u25C6", "*"); var I2 = o("\u25A0", "x"); var x2 = o("\u25B2", "x"); var S2 = o("\u25C7", "o"); var K = o("\u250C", "T"); var a2 = o("\u2502", "|"); var d2 = o("\u2514", "\u2014"); var b2 = o("\u25CF", ">"); var E = o("\u25CB", " "); var C = o("\u25FB", "[\u2022]"); var w2 = o("\u25FC", "[+]"); var M2 = o("\u25FB", "[ ]"); var U2 = o("\u25AA", "\u2022"); var B = o("\u2500", "-"); var Z2 = o("\u256E", "+"); var z2 = o("\u251C", "+"); var X2 = o("\u256F", "+"); var J2 = o("\u25CF", "\u2022"); var Y = o("\u25C6", "*"); var Q2 = o("\u25B2", "!"); var ee = o("\u25A0", "x"); var y2 = (r2) => { switch (r2) { case "initial": case "active": return import_picocolors2.default.cyan(H); case "cancel": return import_picocolors2.default.red(I2); case "error": return import_picocolors2.default.yellow(x2); case "submit": return import_picocolors2.default.green(S2); } }; var te = (r2) => new PD({ validate: r2.validate, placeholder: r2.placeholder, defaultValue: r2.defaultValue, initialValue: r2.initialValue, render() { const n = `${import_picocolors2.default.gray(a2)} ${y2(this.state)} ${r2.message} `, i = r2.placeholder ? import_picocolors2.default.inverse(r2.placeholder[0]) + import_picocolors2.default.dim(r2.placeholder.slice(1)) : import_picocolors2.default.inverse(import_picocolors2.default.hidden("_")), t = this.value ? this.valueWithCursor : i; switch (this.state) { case "error": return `${n.trim()} ${import_picocolors2.default.yellow(a2)} ${t} ${import_picocolors2.default.yellow(d2)} ${import_picocolors2.default.yellow(this.error)} `; case "submit": return `${n}${import_picocolors2.default.gray(a2)} ${import_picocolors2.default.dim(this.value || r2.placeholder)}`; case "cancel": return `${n}${import_picocolors2.default.gray(a2)} ${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(this.value ?? ""))}${this.value?.trim() ? ` ` + import_picocolors2.default.gray(a2) : ""}`; default: return `${n}${import_picocolors2.default.cyan(a2)} ${t} ${import_picocolors2.default.cyan(d2)} `; } } }).prompt(); var se = (r2) => { const n = r2.active ?? "Yes", i = r2.inactive ?? "No"; return new BD({ active: n, inactive: i, initialValue: r2.initialValue ?? true, render() { const t = `${import_picocolors2.default.gray(a2)} ${y2(this.state)} ${r2.message} `, s = this.value ? n : i; switch (this.state) { case "submit": return `${t}${import_picocolors2.default.gray(a2)} ${import_picocolors2.default.dim(s)}`; case "cancel": return `${t}${import_picocolors2.default.gray(a2)} ${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(s))} ${import_picocolors2.default.gray(a2)}`; default: return `${t}${import_picocolors2.default.cyan(a2)} ${this.value ? `${import_picocolors2.default.green(b2)} ${n}` : `${import_picocolors2.default.dim(E)} ${import_picocolors2.default.dim(n)}`} ${import_picocolors2.default.dim("/")} ${this.value ? `${import_picocolors2.default.dim(E)} ${import_picocolors2.default.dim(i)}` : `${import_picocolors2.default.green(b2)} ${i}`} ${import_picocolors2.default.cyan(d2)} `; } } }).prompt(); }; var ie = (r2) => { const n = (t, s) => { const c2 = t.label ?? String(t.value); return s === "active" ? `${import_picocolors2.default.green(b2)} ${c2} ${t.hint ? import_picocolors2.default.dim(`(${t.hint})`) : ""}` : s === "selected" ? `${import_picocolors2.default.dim(c2)}` : s === "cancelled" ? `${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(c2))}` : `${import_picocolors2.default.dim(E)} ${import_picocolors2.default.dim(c2)}`; }; let i = 0; return new $D({ options: r2.options, initialValue: r2.initialValue, render() { const t = `${import_picocolors2.default.gray(a2)} ${y2(this.state)} ${r2.message} `; switch (this.state) { case "submit": return `${t}${import_picocolors2.default.gray(a2)} ${n(this.options[this.cursor], "selected")}`; case "cancel": return `${t}${import_picocolors2.default.gray(a2)} ${n(this.options[this.cursor], "cancelled")} ${import_picocolors2.default.gray(a2)}`; default: { const s = r2.maxItems === undefined ? 1 / 0 : Math.max(r2.maxItems, 5); this.cursor >= i + s - 3 ? i = Math.max(Math.min(this.cursor - s + 3, this.options.length - s), 0) : this.cursor < i + 2 && (i = Math.max(this.cursor - 2, 0)); const c2 = s < this.options.length && i > 0, l2 = s < this.options.length && i + s < this.options.length; return `${t}${import_picocolors2.default.cyan(a2)} ${this.options.slice(i, i + s).map((u, m2, $2) => m2 === 0 && c2 ? import_picocolors2.default.dim("...") : m2 === $2.length - 1 && l2 ? import_picocolors2.default.dim("...") : n(u, m2 + i === this.cursor ? "active" : "inactive")).join(` ${import_picocolors2.default.cyan(a2)} `)} ${import_picocolors2.default.cyan(d2)} `; } } } }).prompt(); }; var ue = (r2 = "") => { process.stdout.write(`${import_picocolors2.default.gray(d2)} ${import_picocolors2.default.red(r2)} `); }; var oe = (r2 = "") => { process.stdout.write(`${import_picocolors2.default.gray(K)} ${r2} `); }; var f2 = { message: (r2 = "", { symbol: n = import_picocolors2.default.gray(a2) } = {}) => { const i = [`${import_picocolors2.default.gray(a2)}`]; if (r2) { const [t, ...s] = r2.split(` `); i.push(`${n} ${t}`, ...s.map((c2) => `${import_picocolors2.default.gray(a2)} ${c2}`)); } process.stdout.write(`${i.join(` `)} `); }, info: (r2) => { f2.message(r2, { symbol: import_picocolors2.default.blue(J2) }); }, success: (r2) => { f2.message(r2, { symbol: import_picocolors2.default.green(Y) }); }, step: (r2) => { f2.message(r2, { symbol: import_picocolors2.default.green(S2) }); }, warn: (r2) => { f2.message(r2, { symbol: import_picocolors2.default.yellow(Q2) }); }, warning: (r2) => { f2.warn(r2); }, error: (r2) => { f2.message(r2, { symbol: import_picocolors2.default.red(ee) }); } }; var de = () => { const r2 = _2 ? ["\u25D2", "\u25D0", "\u25D3", "\u25D1"] : ["\u2022", "o", "O", "0"], n = _2 ? 80 : 120; let i, t, s = false, c2 = ""; const l2 = (v2 = "") => { s = true, i = OD(), c2 = v2.replace(/\.+$/, ""), process.stdout.write(`${import_picocolors2.default.gray(a2)} `); let g2 = 0, p = 0; t = setInterval(() => { const O2 = import_picocolors2.default.magenta(r2[g2]), P2 = ".".repeat(Math.floor(p)).slice(0, 3); process.stdout.write(import_sisteransi2.cursor.move(-999, 0)), process.stdout.write(import_sisteransi2.erase.down(1)), process.stdout.write(`${O2} ${c2}${P2}`), g2 = g2 + 1 < r2.length ? g2 + 1 : 0, p = p < r2.length ? p + 0.125 : 0; }, n); }, u = (v2 = "", g2 = 0) => { c2 = v2 ?? c2, s = false, clearInterval(t); const p = g2 === 0 ? import_picocolors2.default.green(S2) : g2 === 1 ? import_picocolors2.default.red(I2) : import_picocolors2.default.red(x2); process.stdout.write(import_sisteransi2.cursor.move(-999, 0)), process.stdout.write(import_sisteransi2.erase.down(1)), process.stdout.write(`${p} ${c2} `), i(); }, m2 = (v2 = "") => { c2 = v2 ?? c2; }, $2 = (v2) => { const g2 = v2 > 1 ? "Something went wrong" : "Canceled"; s && u(g2, v2); }; return process.on("uncaughtExceptionMonitor", () => $2(2)), process.on("unhandledRejection", () => $2(2)), process.on("SIGINT", () => $2(1)), process.on("SIGTERM", () => $2(1)), process.on("exit", $2), { start: l2, stop: u, message: m2 }; }; // src/constants.ts import { homedir } from "os"; import { join } from "path"; var PROVIDERS = ["opencode", "openai", "qwen", "zai"]; var PERMISSION_MODES = ["default", "acceptEdits", "auto", "bypassPermissions"]; var MODELS = [ { id: "minimax-m2.7", name: "MiniMax M2.7", description: "High performance coding model" }, { id: "minimax-m2.5", name: "MiniMax M2.5", description: "Balanced speed and quality" }, { id: "kimi-k2.6", name: "Kimi K2.6", description: "Latest Kimi reasoning model" }, { id: "kimi-k2.5", name: "Kimi K2.5", description: "Strong reasoning for complex tasks" }, { id: "glm-5.1", name: "GLM-5.1", description: "Enhanced reasoning from Zhipu AI" }, { id: "glm-5", name: "GLM-5", description: "Latest generation from Zhipu AI" }, { id: "deepseek-v4-pro", name: "DeepSeek V4 Pro", description: "High-performance DeepSeek coding model" }, { id: "deepseek-v4-flash", name: "DeepSeek V4 Flash", description: "Fast DeepSeek coding model" }, { id: "qwen3.6-plus", name: "Qwen3.6 Plus", description: "Latest Qwen3 flagship" }, { id: "qwen3.5-plus", name: "Qwen3.5 Plus", description: "Qwen3 flagship" }, { id: "mimo-v2-pro", name: "MiMo V2 Pro", description: "MiMo coding model" }, { id: "mimo-v2-omni", name: "MiMo V2 Omni", description: "MiMo omni model" }, { id: "mimo-v2.5-pro", name: "MiMo V2.5 Pro", description: "MiMo V2.5 coding model" }, { id: "mimo-v2.5", name: "MiMo V2.5", description: "MiMo V2.5 model" } ]; var OPENCODE_GO_ENDPOINT = "https://opencode.ai/zen/go/v1/chat/completions"; var OPENCODE_MODELS_ENDPOINT = "https://opencode.ai/zen/go/v1/models"; var OPENCODE_MODELS_CACHE_TTL_MS = 60 * 60 * 1000; var OPENCODE_MODELS_FETCH_TIMEOUT_MS = 5000; var OPENAI_MODELS = [ { id: "gpt-5.2", name: "GPT-5.2", description: "Latest GPT-5 model" }, { id: "gpt-5.3", name: "GPT-5.3", description: "High performance GPT-5" }, { id: "gpt-5.4", name: "GPT-5.4", description: "Balanced GPT-5" }, { id: "gpt-5.1-codex", name: "GPT-5.1 Codex", description: "Code-optimized GPT-5.1" }, { id: "gpt-5.2-codex", name: "GPT-5.2 Codex", description: "Code-optimized GPT-5.2" }, { id: "gpt-5.3-codex", name: "GPT-5.3 Codex", description: "Code-optimized GPT-5.3" } ]; var CODEX_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann"; var CODEX_AUTH_URL = "https://auth.openai.com/oauth/authorize"; var CODEX_TOKEN_URL = "https://auth.openai.com/oauth/token"; var CODEX_REDIRECT_URI = "http://localhost:1455/auth/callback"; var CODEX_SCOPE = "openid profile email offline_access"; var CODEX_API_URL = "https://chatgpt.com/backend-api/codex/responses"; var CODEX_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage"; var CODEX_MODELS_ENDPOINT = "https://chatgpt.com/backend-api/codex/models"; var CODEX_RELEASES_LATEST_URL = "https://api.github.com/repos/openai/codex/releases/latest"; var CODEX_MODELS_CLIENT_VERSION = "0.128.0"; var CODEX_MODELS_CACHE_TTL_MS = 60 * 60 * 1000; var CODEX_MODELS_FETCH_TIMEOUT_MS = 15000; var QWEN_MODELS = [ { id: "qwen3-coder-plus", name: "Qwen3 Coder Plus", description: "High-performance Qwen coding model" }, { id: "qwen3-coder-flash", name: "Qwen3 Coder Flash", description: "Fast, cost-efficient Qwen coding model" } ]; var QWEN_CLIENT_ID = "f0304373b74a44d2b584a3fb70ca9e56"; var QWEN_DEVICE_CODE_URL = "https://chat.qwen.ai/api/v1/oauth2/device/code"; var QWEN_TOKEN_URL = "https://chat.qwen.ai/api/v1/oauth2/token"; var QWEN_SCOPE = "openid profile email model.completion"; var QWEN_DEFAULT_API_BASE = "https://portal.qwen.ai/v1"; var QWEN_CODE_VERSION = "0.13.2"; var QWEN_TOKEN_EXPIRY_BUFFER_MS = 5 * 60 * 1000; var QWEN_COOLDOWN_UNAUTHORIZED_MS = 5 * 60 * 1000; var QWEN_COOLDOWN_PAYMENT_MS = 60 * 60 * 1000; var QWEN_COOLDOWN_TRANSIENT_MS = 5000; var QWEN_COOLDOWN_NOT_FOUND_MS = 15 * 60 * 1000; var QWEN_RATE_LIMIT_BACKOFF_BASE_MS = 1000; var QWEN_RATE_LIMIT_BACKOFF_MAX_MS = 2 * 60 * 1000; var QWEN_RATE_LIMIT_BACKOFF_MAX_LEVEL = 15; var QWEN_MAX_ROTATION_ATTEMPTS = 5; var QWEN_DEFAULT_STRATEGY = "fill-first"; var QWEN_DEFAULT_STICKY_LIMIT = 3; function buildQwenApiBase(resourceUrl) { if (!resourceUrl) return QWEN_DEFAULT_API_BASE; const raw = resourceUrl.trim(); if (raw.startsWith("http")) return raw.replace(/\/$/, ""); return `https://${raw.replace(/\/$/, "")}/v1`; } function buildQwenChatCompletionsUrl(resourceUrl) { return `${buildQwenApiBase(resourceUrl)}/chat/completions`; } function buildQwenHeaders(accessToken, stream) { const userAgent = `QwenCode/${QWEN_CODE_VERSION} (${process.platform}; ${process.arch})`; const stainlessOs = process.platform === "darwin" ? "MacOS" : process.platform === "win32" ? "Windows" : "Linux"; return { "Content-Type": "application/json", Authorization: `Bearer ${accessToken}`, "User-Agent": userAgent, "X-DashScope-UserAgent": userAgent, "X-DashScope-AuthType": "qwen-oauth", "X-DashScope-CacheControl": "enable", "X-Stainless-Runtime": "node", "X-Stainless-Runtime-Version": process.version, "X-Stainless-Lang": "js", "X-Stainless-Arch": process.arch, "X-Stainless-Os": stainlessOs, "X-Stainless-Package-Version": "5.11.0", "X-Stainless-Retry-Count": "0", Accept: stream ? "text/event-stream" : "application/json" }; } var ZAI_MODELS = [ { id: "glm-4.7", name: "GLM-4.7", description: "Fast and reliable \u2014 stable availability" }, { id: "glm-5-turbo", name: "GLM-5 Turbo", description: "Fast GLM-5 variant" }, { id: "glm-5.1", name: "GLM-5.1", description: "Latest GLM \u2014 best reasoning (may be overloaded)" }, { id: "glm-5", name: "GLM-5", description: "Base GLM-5 model (may be overloaded)" } ]; var ZAI_BASE_URL = "https://chat.z.ai"; var ZAI_API_V2 = `${ZAI_BASE_URL}/api/v2/chat/completions`; var ZAI_AUTH_URL = `${ZAI_BASE_URL}/api/v1/auths/`; var ZAI_CHAT_NEW_URL = `${ZAI_BASE_URL}/api/v1/chats/new`; var ZAI_FE_VERSION = "prod-fe-1.1.11"; var ZAI_HMAC_KEY = "key-@@@@)))()((9))-xxxx&&&%%%%%"; var ZAI_TIME_BUCKET_MS = 5 * 60 * 1000; var CONFIG_DIR = join(homedir(), ".opencode-go-cli"); var CONFIG_FILE = join(CONFIG_DIR, "config.json"); var QWEN_DB_FILE = join(CONFIG_DIR, "qwen.db"); var OPENCODE_MODELS_CACHE_FILE = join(CONFIG_DIR, "opencode-models.json"); var CODEX_MODELS_CACHE_FILE = join(CONFIG_DIR, "openai-models.json"); var INSTALLATIONS_DIR = join(CONFIG_DIR, "installations"); var DEFAULT_INSTALLATION_ID = "default"; var DEFAULT_PROXY_PORT = 8080; var PROXY_PORT_FALLBACK_ATTEMPTS = 20; var PRESERVED_CLAUDE_CODE_VARS = new Set([ "CLAUDE_CODE_GIT_BASH_PATH", "CLAUDE_CODE_NO_FLICKER", "CLAUDE_CODE_SHELL", "CLAUDE_CODE_TMPDIR" ]); // src/providers/opencode-models.ts import { existsSync, mkdirSync as mkdirSync2, readFileSync, writeFileSync, unlinkSync } from "fs"; import { dirname } from "path"; // src/logger.ts import { appendFileSync, mkdirSync } from "fs"; import { join as join2 } from "path"; import { homedir as homedir2 } from "os"; var LEVEL_ORDER = { DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3 }; function isDebugEnabled() { const v2 = process.env["DEBUG"]; return v2 === "1" || v2 === "true"; } var silenced = false; var logFilePath = null; function getLogFilePath() { if (!logFilePath) { const dir = join2(homedir2(), ".opencode-go-cli"); mkdirSync(dir, { recursive: true }); logFilePath = join2(dir, "proxy.log"); } return logFilePath; } class Logger { level; prefix; constructor(options) { if (options?.level) { if (!(options.level in LEVEL_ORDER)) { throw new Error(`[logger] Invalid log level: "${options.level}"`); } this.level = options.level; } else { this.level = isDebugEnabled() ? "DEBUG" : "INFO"; } this.prefix = options?.prefix ?? ""; } log(level, ...args) { if (LEVEL_ORDER[level] < LEVEL_ORDER[this.level]) return; const levelTag = `[${level}]`; const prefix = this.prefix ? `${this.prefix} ` : ""; const parts = args.map((a3) => typeof a3 === "string" ? a3 : String(a3)); for (const part of parts) { for (const line of part.split(` `)) { const msg = `${prefix}${levelTag} ${line}`; if (silenced) { if (isDebugEnabled()) { const ts = new Date().toISOString(); appendFileSync(getLogFilePath(), `${ts} ${msg} `); } } else { console.log(msg); } } } } debug(...args) { this.log("DEBUG", ...args); } info(...args) { this.log("INFO", ...args); } warn(...args) { this.log("WARN", ...args); } error(...args) { this.log("ERROR", ...args); } } function silenceLogger() { silenced = true; } function createLogger(prefix) { return new Logger({ prefix }); } // src/providers/opencode-models.ts function resolveCacheFile() { return process.env["OPENCODE_MODELS_CACHE_FILE_OVERRIDE"] || OPENCODE_MODELS_CACHE_FILE; } var log = createLogger("[opencode-models]"); var memoryCache = null; var SEGMENT_MAP = { gpt: "GPT", glm: "GLM", claude: "Claude", gemini: "Gemini", kimi: "Kimi", deepseek: "DeepSeek", mimo: "MiMo", qwen: "Qwen", ling: "Ling", minimax: "MiniMax", nemotron: "Nemotron", trinity: "Trinity", opus: "Opus", sonnet: "Sonnet", haiku: "Haiku", pro: "Pro", omni: "Omni", flash: "Flash", mini: "Mini", nano: "Nano", max: "Max", turbo: "Turbo", plus: "Plus", codex: "Codex", coder: "Coder", spark: "Spark", large: "Large", preview: "Preview" }; var NUMERIC_SEGMENT = /^\d+(?:\.\d+)*$/; var MODEL_VERSION_SEGMENT = /^[kmv]\d+(?:\.\d+)*$/i; var QWEN_VERSION_SEGMENT = /^qwen\d+(?:\.\d+)*$/i; function prettySegment(segment) { const lower = segment.toLowerCase(); const mapped = SEGMENT_MAP[lower]; if (mapped) return mapped; if (MODEL_VERSION_SEGMENT.test(segment)) return segment.toUpperCase(); if (QWEN_VERSION_SEGMENT.test(segment)) return `Qwen${segment.slice(4)}`; return segment; } function humanizeModelId(id) { const segments = id.split("-"); const isFree = segments.at(-1) === "free"; const core = isFree ? segments.slice(0, -1) : segments; const pretty = core.map(prettySegment); let out = ""; for (let i = 0;i < pretty.length; i++) { const seg = pretty[i]; if (i === 0) { out = seg; continue; } const joinWithDot = NUMERIC_SEGMENT.test(seg) && NUMERIC_SEGMENT.test(core[i - 1]); out += joinWithDot ? `.${seg}` : ` ${seg}`; } out = out.trim(); return isFree ? `${out} (Free)` : out; } function buildDescription(raw) { if (raw.id.endsWith("-free")) return "Free tier"; if (raw.owned_by && raw.owned_by !== "opencode") return `Provided by ${raw.owned_by}`; return ""; } function normalize(raw) { if (!raw || !Array.isArray(raw.data)) return []; const out = []; const seen = new Set; for (const entry of raw.data) { if (!entry || typeof entry.id !== "string" || entry.id.length === 0) continue; if (seen.has(entry.id)) continue; seen.add(entry.id); const owned = typeof entry.owned_by === "string" ? entry.owned_by : undefined; out.push({ id: entry.id, name: humanizeModelId(entry.id), description: buildDescription({ id: entry.id, owned_by: owned }) }); } return out; } function readCacheFile() { try { if (!existsSync(resolveCacheFile())) return null; const raw = JSON.parse(readFileSync(resolveCacheFile(), "utf-8")); if (!raw || raw.version !== 2 || raw.endpoint !== OPENCODE_MODELS_ENDPOINT) return null; if (typeof raw.fetchedAt !== "number") return null; if (!Array.isArray(raw.models)) return null; return raw; } catch (err) { log.debug(`Cache read failed: ${err.message}`); return null; } } function writeCacheFile(models) { try { mkdirSync2(dirname(resolveCacheFile()), { recursive: true }); const payload = { version: 2, endpoint: OPENCODE_MODELS_ENDPOINT, fetchedAt: Date.now(), models }; writeFileSync(resolveCacheFile(), JSON.stringify(payload, null, 2)); } catch (err) { log.debug(`Cache write failed: ${err.message}`); } } async function fetchFromApi() { const controller = new AbortController; const timer = setTimeout(() => controller.abort(), OPENCODE_MODELS_FETCH_TIMEOUT_MS); try { const res = await fetch(OPENCODE_MODELS_ENDPOINT, { signal: controller.signal, headers: { Accept: "application/json" } }); if (!res.ok) { log.debug(`Fetch returned HTTP ${res.status}`); return null; } const json = await res.json(); const models = normalize(json); if (models.length === 0) { log.debug("Fetch returned empty model list"); return null; } return models; } catch (err) { log.debug(`Fetch failed: ${err.message}`); return null; } finally { clearTimeout(timer); } } async function getOpenCodeModels(options = {}) { const { refresh = false, offline = false } = options; const now = Date.now(); if (!refresh && memoryCache && now - memoryCache.fetchedAt < OPENCODE_MODELS_CACHE_TTL_MS) { return { models: memoryCache.models, source: "cache", fetchedAt: memoryCache.fetchedAt }; } if (!refresh) { const disk2 = readCacheFile(); if (disk2 && now - disk2.fetchedAt < OPENCODE_MODELS_CACHE_TTL_MS) { memoryCache = { fetchedAt: disk2.fetchedAt, models: disk2.models }; return { models: disk2.models, source: "cache", fetchedAt: disk2.fetchedAt }; } } if (offline) { const disk2 = readCacheFile(); if (disk2) { memoryCache = { fetchedAt: disk2.fetchedAt, models: disk2.models }; return { models: disk2.models, source: "cache", fetchedAt: disk2.fetchedAt }; } return { models: MODELS, source: "fallback" }; } const fresh = await fetchFromApi(); if (fresh) { memoryCache = { fetchedAt: Date.now(), models: fresh }; writeCacheFile(fresh); return { models: fresh, source: "network", fetchedAt: memoryCache.fetchedAt }; } const disk = readCacheFile(); if (disk) { memoryCache = { fetchedAt: disk.fetchedAt, models: disk.models }; return { models: disk.models, source: "cache", fetchedAt: disk.fetchedAt }; } return { models: MODELS, source: "fallback" }; } function clearOpenCodeModelsCache() { memoryCache = null; try { if (existsSync(resolveCacheFile())) unlinkSync(resolveCacheFile()); } catch {} } // src/providers/openai-models.ts import { existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "fs"; import { dirname as dirname2 } from "path"; var log2 = createLogger("[openai-models]"); var CLIENT_VERSION_CACHE_TTL_MS = 60 * 60 * 1000; var VERSION_RE = /^\d+\.\d+\.\d+$/; var memoryCache2 = null; var clientVersionCache = null; function resolveCacheFile2() { return process.env["OPENCODE_OPENAI_MODELS_CACHE_FILE_OVERRIDE"] || CODEX_MODELS_CACHE_FILE; } function numberValue(value) { return typeof value === "number" && Number.isFinite(value) ? value : Number.MAX_SAFE_INTEGER; } function shouldListModel(entry) { if (entry.supported_in_api === false) return false; if (entry.visibility === "hide") return false; return true; } function normalize2(raw) { if (!raw || !Array.isArray(raw.models)) return []; const models = []; const seen = new Set; for (const entry of raw.models) { const id = typeof entry.slug === "string" ? entry.slug.trim() : ""; if (!id || seen.has(id) || !shouldListModel(entry)) continue; seen.add(id); const displayName = typeof entry.display_name === "string" && entry.display_name.trim() ? entry.display_name.trim() : humanizeModelId(id); const description = typeof entry.description === "string" ? entry.description.trim() : ""; models.push({ id, name: displayName, description, priority: numberValue(entry.priority) }); } return models.sort((a3, b3) => a3.priority - b3.priority || a3.id.localeCompare(b3.id)).map(({ priority: _priority, ...model }) => model); } function isUsableCache(cache, now, clientVersion) { if (!cache || now - cache.fetchedAt >= CODEX_MODELS_CACHE_TTL_MS) return false; return !clientVersion || cache.clientVersion === clientVersion; } function readCacheFile2() { try { const file = resolveCacheFile2(); if (!existsSync2(file)) return null; const raw = JSON.parse(readFileSync2(file, "utf-8")); if (!raw || raw.version !== 2 || typeof raw.fetchedAt !== "number") return null; if (typeof raw.clientVersion !== "string" || !raw.clientVersion) return null; if (!Array.isArray(raw.models)) return null; return raw; } catch (err) { log2.debug(`Cache read failed: ${err.message}`); return null; } } function writeCacheFile2(models, clientVersion) { try { const file = resolveCacheFile2(); mkdirSync3(dirname2(file), { recursive: true }); const payload = { version: 2, fetchedAt: Date.now(), clientVersion, models }; writeFileSync2(file, JSON.stringify(payload, null, 2)); } catch (err) { log2.debug(`Cache write failed: ${err.message}`); } } async function fetchFromApi2(args) { const headers = { Accept: "application/json", Authorization: `Bearer ${args.accessToken}` }; if (args.accountId && !args.accountId.startsWith("email_") && !args.accountId.startsWith("local_")) { headers["chatgpt-account-id"] = args.accountId; } const url = new URL(CODEX_MODELS_ENDPOINT); url.searchParams.set("client_version", args.clientVersion); const controller = new AbortController; const timer = setTimeout(() => controller.abort(), CODEX_MODELS_FETCH_TIMEOUT_MS); try { const response = await fetch(url, { headers, signal: controller.signal }); if (!response.ok) { log2.debug(`Fetch returned HTTP ${response.status}`); return null; } const json = await response.json(); const models = normalize2(json); if (models.length === 0) { log2.debug("Fetch returned empty model list"); return null; } return models; } catch (err) { log2.debug(`Fetch failed: ${err.message}`); return null; } finally { clearTimeout(timer); } } async function getLatestCodexClientVersion() { const envVersion = process.env["OPENCODE_CODEX_CLIENT_VERSION"]; if (envVersion && VERSION_RE.test(envVersion)) return envVersion; const now = Date.now(); if (clientVersionCache && now - clientVersionCache.fetchedAt < CLIENT_VERSION_CACHE_TTL_MS) { return clientVersionCache.version; } try { const response = await fetch(CODEX_RELEASES_LATEST_URL, { headers: { Accept: "application/vnd.github+json" }, signal: AbortSignal.timeout(1e4) }); if (!response.ok) { log2.debug(`Codex release fetch returned HTTP ${response.status}`); return CODEX_MODELS_CLIENT_VERSION; } const json = await response.json(); const version = typeof json.name === "string" && VERSION_RE.test(json.name) ? json.name : typeof json.tag_name === "string" ? json.tag_name.replace(/^rust-v/, "") : ""; if (!VERSION_RE.test(version)) { log2.debug("Codex release fetch returned an unexpected version"); return CODEX_MODELS_CLIENT_VERSION; } clientVersionCache = { fetchedAt: now, version }; return version; } catch (err) { log2.debug(`Codex release fetch failed: ${err.message}`); return CODEX_MODELS_CLIENT_VERSION; } } async function getOpenAIModels(options = {}) { const { accessToken, accountId, refresh = false, offline = false } = options; const now = Date.now(); const clientVersion = !offline && accessToken ? options.clientVersion ?? await getLatestCodexClientVersion() : null; if (!refresh && isUsableCache(memoryCache2, now, clientVersion)) { return { models: memoryCache2.models, source: "cache", fetchedAt: memoryCache2.fetchedAt }; } if (!refresh) { const disk2 = readCacheFile2(); if (isUsableCache(disk2, now, clientVersion)) { memoryCache2 = { fetchedAt: disk2.fetchedAt, clientVersion: disk2.clientVersion, models: disk2.models }; return { models: disk2.models, source: "cache", fetchedAt: disk2.fetchedAt }; } } if (!accessToken || offline) { const disk2 = readCacheFile2(); if (disk2) { memoryCache2 = { fetchedAt: disk2.fetchedAt, clientVersion: disk2.clientVersion, models: disk2.models }; return { models: disk2.models, source: "cache", fetchedAt: disk2.fetchedAt }; } return { models: OPENAI_MODELS, source: "fallback" }; } const fresh = await fetchFromApi2({ accessToken, accountId, clientVersion }); if (fresh) { memoryCache2 = { fetchedAt: Date.now(), clientVersion, models: fresh }; writeCacheFile2(fresh, clientVersion); return { models: fresh, source: "network", fetchedAt: memoryCache2.fetchedAt }; } const disk = readCacheFile2(); if (disk) { memoryCache2 = { fetchedAt: disk.fetchedAt, clientVersion: disk.clientVersion, models: disk.models }; return { models: disk.models, source: "cache", fetchedAt: disk.fetchedAt }; } return { models: OPENAI_MODELS, source: "fallback" }; } function clearOpenAIModelsCache() { memoryCache2 = null; try { const file = resolveCacheFile2(); if (existsSync2(file)) unlinkSync2(file); } catch {} } // src/statusline/state.ts import { existsSync as existsSync3, mkdirSync as mkdirSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs"; import { dirname as dirname3, join as join3 } from "path"; function getStatuslineStateFile() { return process.env["OPENCODE_STATUSLINE_STATE_FILE_OVERRIDE"] ?? join3(CONFIG_DIR, "statusline-state.json"); } function isStatuslineState(value) { return value?.version === 1 && PROVIDERS.includes(value.provider) && typeof value.model === "string" && PERMISSION_MODES.includes(value.permissionMode) && typeof value.proxyUrl === "string" && typeof value.startedAt === "string" && typeof value.cliVersion === "string" && typeof value.updatedAt === "string"; } function numberOrZero(value) { return typeof value === "number" && Number.isFinite(value) ? value : 0; } function readStatuslineState() { const file = getStatuslineStateFile(); try { if (!existsSync3(file)) return null; const parsed = JSON.parse(readFileSync3(file, "utf-8")); return isStatuslineState(parsed) ? parsed : null; } catch { return null; } } function writeStatuslineState(state) { const file = getStatuslineStateFile(); mkdirSync4(dirname3(file), { recursive: true }); writeFileSync3(file, JSON.stringify(state, null, 2)); } function updateStatuslineUsage(usage) { const state = readStatuslineState(); if (!state) return; const inputTokens = numberOrZero(usage.input_tokens); const outputTokens = numberOrZero(usage.output_tokens); const cacheCreationInputTokens = numberOrZero(usage.cache_creation_input_tokens); const cacheReadInputTokens = numberOrZero(usage.cache_read_input_tokens); if (inputTokens + outputTokens + cacheCreationInputTokens + cacheReadInputTokens === 0) { return; } const updatedAt = new Date().toISOString(); const contextTokens = inputTokens + cacheCreationInputTokens + cacheReadInputTokens; writeStatuslineState({ ...state, updatedAt, lastUsage: { inputTokens, outputTokens, cacheCreationInputTokens, cacheReadInputTokens, contextTokens, totalTokens: inputTokens + outputTokens + cacheCreationInputTokens + cacheReadInputTokens, updatedAt } }); } function updateStatuslineRateLimits(rateLimits) { const state = readStatuslineState(); if (!state) return; writeStatuslineState({ ...state, updatedAt: new Date().toISOString(), rateLimits }); } function buildStatuslineState(args) { const now = args.now ?? new Date; const timestamp = now.toISOString(); return { version: 1, provider: args.provider, model: args.model, permissionMode: args.permissionMode, proxyUrl: args.proxyUrl, startedAt: timestamp, cliVersion: args.cliVersion, updatedAt: timestamp }; } // src/providers/openai-usage.ts function numberValue2(value) { return typeof value === "number" && Number.isFinite(value) ? value : undefined; } function mapResetAt(window, nowSeconds) { const resetAt = numberValue2(window.reset_at); if (resetAt !== undefined) return resetAt; const resetAfter = numberValue2(window.reset_after_seconds); return resetAfter !== undefined ? nowSeconds + Math.max(0, resetAfter) : undefined; } function mapWindow(window, nowSeconds) { if (!window || typeof window.used_percent !== "number") return; return { used_percentage: window.used_percent, resets_at: mapResetAt(window, nowSeconds), limit_window_seconds: numberValue2(window.limit_window_seconds) }; } function mapCodexUsageToStatuslineRateLimits(payload, now = new Date) { const rateLimit = payload.rate_limit; if (!rateLimit) return null; const nowSeconds = Math.floor(now.getTime() / 1000); const primary = rateLimit.primary_window ?? null; const secondary = rateLimit.secondary_window ?? null; const limits = { source: "openai-wham", updatedAt: now.toISOString() }; if (primary?.limit_window_seconds === 604800) { limits.seven_day = mapWindow(primary, nowSeconds); } else { limits.five_hour = mapWindow(primary, nowSeconds); } const secondaryWindow = mapWindow(secondary, nowSeconds); if (secondaryWindow) { limits.seven_day = secondaryWindow; } return limits.five_hour || limits.seven_day ? limits : null; } async function fetchCodexUsageRateLimits(args) { const headers = { Accept: "application/json", Authorization: `Bearer ${args.accessToken}` }; if (args.accountId && !args.accountId.startsWith("email_") && !args.accountId.startsWith("local_")) { headers["chatgpt-account-id"] = args.accountId; } const response = await fetch(CODEX_USAGE_URL, { method: "GET", headers, signal: args.signal }); if (!response.ok) { throw new Error(`Codex usage fetch failed: HTTP ${response.status}`); } const payload = await response.json(); return mapCodexUsageToStatuslineRateLimits(payload); } async function refreshStatuslineCodexUsage(args) { try { const limits = await fetchCodexUsageRateLimits({ accessToken: args.accessToken, accountId: args.accountId, signal: AbortSignal.timeout(1e4) }); if (!limits) return false; updateStatuslineRateLimits(limits); return true; } catch { return false; } } // src/config.ts import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync6, unlinkSync as unlinkSync3, rmSync } from "fs"; // src/db/index.ts import { Database } from "bun:sqlite"; import { mkdirSync as mkdirSync5, existsSync as existsSync4 } from "fs"; var _db = null; function ensureConfigDir() { if (!existsSync4(CONFIG_DIR)) { mkdirSync5(CONFIG_DIR, { recursive: true }); } } function db() { if (_db) return _db; const override = process.env.QWEN_DB_PATH; const path = override && override.length > 0 ? override : QWEN_DB_FILE; if (!override) ensureConfigDir(); _db = new Database(path); _db.exec("PRAGMA journal_mode = WAL"); _db.exec("PRAGMA foreign_keys = ON"); _db.exec(` CREATE TABLE IF NOT EXISTS accounts ( id TEXT PRIMARY KEY, email TEXT, display_name TEXT, access_token TEXT NOT NULL, refresh_token TEXT NOT NULL, expires_at TEXT NOT NULL, resource_url TEXT, priority INTEGER NOT NULL DEFAULT 1, is_active INTEGER NOT NULL DEFAULT 1, test_status TEXT NOT NULL DEFAULT 'unknown', last_error TEXT, error_code INTEGER, last_error_at TEXT, backoff_level INTEGER NOT NULL DEFAULT 0, consecutive_use_count INTEGER NOT NULL DEFAULT 0, last_used_at TEXT, created_at TEXT NOT NULL, updated_at TEXT NOT NULL ) `); _db.exec(` CREATE TABLE IF NOT EXISTS model_locks ( id INTEGER PRIMARY KEY AUTOINCREMENT, account_id TEXT NOT NULL REFERENCES accounts(id) ON DELETE CASCADE, model TEXT NOT NULL, locked_until TEXT NOT NULL ) `); _db.exec(` CREATE TABLE IF NOT EXISTS settings ( key TEXT PRIMARY KEY, value TEXT NOT NULL ) `); _db.query("INSERT OR IGNORE INTO settings (key, value) VALUES (?, ?)").run("strategy", QWEN_DEFAULT_STRATEGY); _db.query("INSERT OR IGNORE INTO settings (key, value) VALUES (?, ?)").run("sticky_limit", String(QWEN_DEFAULT_STICKY_LIMIT)); return _db; } function resetDb() { if (_db) { _db.close(); _db = null; } } function getSetting(key) { const row = db().query("SELECT value FROM settings WHERE key = ?").get(key); return row?.value ?? null; } function getStrategy() { const v2 = getSetting("strategy") ?? QWEN_DEFAULT_STRATEGY; return v2 === "round-robin" ? "round-robin" : "fill-first"; } function getStickyLimit() { const raw = getSetting("sticky_limit") ?? String(QWEN_DEFAULT_STICKY_LIMIT); const n = parseInt(raw, 10); return Number.isFinite(n) && n > 0 ? n : QWEN_DEFAULT_STICKY_LIMIT; } // src/config.ts function getConfig() { try { if (existsSync5(CONFIG_FILE)) { return JSON.parse(readFileSync4(CONFIG_FILE, "utf-8")); } } catch {} return {}; } function saveConfig(config) { mkdirSync6(CONFIG_DIR, { recursive: true }); writeFileSync4(CONFIG_FILE, JSON.stringify(config, null, 2)); } function resetAll() { resetDb(); try { rmSync(CONFIG_DIR, { recursive: true, force: true }); } catch {} } // src/path.ts import { execSync } from "child_process"; import { join as join4 } from "path"; function getInstallationPath(id = DEFAULT_INSTALLATION_ID) { return join4(INSTALLATIONS_DIR, id); } function resolveClaudePath() { try { if (process.platform === "win32") { return execSync("where claude", { encoding: "utf-8" }).trim().split(/\r?\n/)[0] ?? "claude"; } return execSync("which claude", { encoding: "utf-8" }).trim(); } catch { return "claude"; } } // src/env.ts function cleanupClaudeCodeVars(env) { for (const key of Object.keys(env)) { if (PRESERVED_CLAUDE_CODE_VARS.has(key)) continue; if (key.startsWith("CLAUDECODE") || key.startsWith("CLAUDE_CODE")) { delete env[key]; } } } function buildClaudeEnv(apiKey, model, baseUrl, installationId) { const env = {}; for (const [key, value] of Object.entries(process.env)) { if (value !== undefined) { env[key] = value; } } cleanupClaudeCodeVars(env); delete env["ANTHROPIC_API_KEY"]; delete env["ANTHROPIC_ACCOUNT_ID"]; delete env["CLAUDE_ACCOUNT_ID"]; env["ANTHROPIC_BASE_URL"] = baseUrl; env["ANTHROPIC_AUTH_TOKEN"] = apiKey; env["ANTHROPIC_MODEL"] = model; env["CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC"] = "1"; env["CLAUDE_CODE_NO_FLICKER"] = "1"; env["CLAUDE_CODE_SUBAGENT_MODEL"] = model; env["ANTHROPIC_DEFAULT_SONNET_MODEL"] = model; env["ANTHROPIC_DEFAULT_OPUS_MODEL"] = model; env["ANTHROPIC_DEFAULT_HAIKU_MODEL"] = model; if (installationId && installationId !== DEFAULT_INSTALLATION_ID) { env["CLAUDE_CONFIG_DIR"] = getInstallationPath(installationId); } return env; } // src/auth/oauth.ts import { randomBytes } from "crypto"; // node_modules/jose/dist/browser/util/base64url.js var exports_base64url = {}; __export(exports_base64url, { encode: () => encode2, decode: () => decode2 }); // node_modules/jose/dist/browser/lib/buffer_utils.js var encoder = new TextEncoder; var decoder = new TextDecoder; var MAX_INT32 = 2 ** 32; // node_modules/jose/dist/browser/runtime/base64url.js var encodeBase64 = (input) => { let unencoded = input; if (typeof unencoded === "string") { unencoded = encoder.encode(unencoded); } const CHUNK_SIZE = 32768; const arr = []; for (let i = 0;i < unencoded.length; i += CHUNK_SIZE) { arr.push(String.fromCharCode.apply(null, unencoded.subarray(i, i + CHUNK_SIZE))); } return btoa(arr.join("")); }; var encode = (input) => { return encodeBase64(input).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_"); }; var decodeBase64 = (encoded) => { const binary = atob(encoded); const bytes = new Uint8Array(binary.length); for (let i = 0;i < binary.length; i++) { bytes[i] = binary.charCodeAt(i); } return bytes; }; var decode = (input) => { let encoded = input; if (encoded instanceof Uint8Array) { encoded = decoder.decode(encoded); } encoded = encoded.replace(/-/g, "+").replace(/_/g, "/").replace(/\s/g, ""); try { return decodeBase64(encoded); } catch { throw new TypeError("The input to be decoded is not correctly encoded."); } }; // node_modules/jose/dist/browser/util/base64url.js var encode2 = encode; var decode2 = decode; // node_modules/@openauthjs/openauth/dist/esm/pkce.js function generateVerifier(length) { const buffer = new Uint8Array(length); crypto.getRandomValues(buffer); return exports_base64url.encode(buffer); } async function generateChallenge(verifier, method) { if (method === "plain") return verifier; const encoder2 = new TextEncoder; const data = encoder2.encode(verifier); const hash = await crypto.subtle.digest("SHA-256", data); return exports_base64url.encode(new Uint8Array(hash)); } async function generatePKCE(length = 64) { if (length < 43 || length > 128) { throw new Error("Code verifier length must be between 43 and 128 characters"); } const verifier = generateVerifier(length); const challenge = await generateChallenge(verifier, "S256"); return { verifier, challenge, method: "S256" }; } // src/auth/oauth.ts function createState() { return randomBytes(16).toString("hex"); } async function exchangeAuthorizationCode(code, verifier, redirectUri = CODEX_REDIRECT_URI) { const res = await fetch(CODEX_TOKEN_URL, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: new URLSearchParams({ grant_type: "authorization_code", client_id: CODEX_CLIENT_ID, code, code_verifier: verifier, redirect_uri: redirectUri }) }); if (!res.ok) { const text = await res.text().catch(() => ""); console.error(`[oauth] Token exchange failed: ${res.status} ${text}`); return { type: "failed" }; } const json = await res.json(); if (!json?.access_token || !json?.refresh_token || typeof json?.expires_in !== "number") { console.error("[oauth] Token response missing fields:", json); return { type: "failed" }; } return { type: "success", access: json.access_token, refresh: json.refresh_token, expires: Date.now() + json.expires_in * 1000, ...extractOpenAIClaims(json.id_token) }; } async function refreshAccessToken(refreshToken) { try { const response = await fetch(CODEX_TOKEN_URL, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: new URLSearchParams({ grant_type: "refresh_token", refresh_token: refreshToken, client_id: CODEX_CLIENT_ID }) }); if (!response.ok) { const text = await response.text().catch(() => ""); console.error(`[oauth] Token refresh failed: ${response.status} ${text}`); return { type: "failed" }; } const json = await response.json(); if (!json?.access_token || !json?.refresh_token || typeof json?.expires_in !== "number") { console.error("[oauth] Token refresh response missing fields:", json); return { type: "failed" }; } return { type: "success", access: json.access_token, refresh: json.refresh_token, expires: Date.now() + json.expires_in * 1000, ...extractOpenAIClaims(json.id_token) }; } catch (error) { const err = error; console.error(`[oauth] Token refresh error: ${err.message}`); return { type: "failed" }; } } function decodeJWT(token) { try { const parts = token.split("."); if (parts.length !== 3) return null; const payload = parts[1]; const decoded = Buffer.from(payload, "base64").toString("utf-8"); return JSON.parse(decoded); } catch { return null; } } function extractOpenAIClaims(idToken) { if (!idToken) return {}; const claims = decodeJWT(idToken); const auth = claims?.["https://api.openai.com/auth"]; const authClaims = auth && typeof auth === "object" ? auth : {}; const accountId = typeof authClaims.chatgpt_account_id === "string" ? authClaims.chatgpt_account_id : typeof claims?.chatgpt_account_id === "string" ? claims.chatgpt_account_id : undefined; const planType = typeof authClaims.chatgpt_plan_type === "string" ? authClaims.chatgpt_plan_type : typeof claims?.chatgpt_plan_type === "string" ? claims.chatgpt_plan_type : undefined; return { accountId, planType }; } async function createAuthorizationFlow() { const pkce = await generatePKCE(); const state = createState(); const url = new URL(CODEX_AUTH_URL); url.searchParams.set("response_type", "code"); url.searchParams.set("client_id", CODEX_CLIENT_ID); url.searchParams.set("redirect_uri", CODEX_REDIRECT_URI); url.searchParams.set("scope", CODEX_SCOPE); url.searchParams.set("code_challenge", pkce.challenge); url.searchParams.set("code_challenge_method", "S256"); url.searchParams.set("state", state); url.searchParams.set("id_token_add_organizations", "true"); url.searchParams.set("codex_cli_simplified_flow", "true"); url.searchParams.set("originator", "opencode-go-cli"); return { pkce, state, url: url.toString() }; } // src/proxy/helpers.ts function generateMsgId() { return "msg_" + Math.random().toString(36).substring(2, 26); } function mapStopReason(finishReason) { switch (finishReason) { case "stop": return "end_turn"; case "tool_calls": return "tool_use"; case "length": return "max_tokens"; default: return "end_turn"; } } function makeSSE(event, data) { return `event: ${event} data: ${JSON.stringify(data)} `; } function convertImageSource(source) { if (source?.type === "base64") { return `data:${source.media_type};base64,${source.data}`; } return source?.url ?? ""; } // src/proxy/request-conversion.ts function convertAnthropicRequestToOpenAI(body) { const openaiMessages = []; if (body.system) { let systemText; if (typeof body.system === "string") { systemText = body.system; } else if (Array.isArray(body.system)) { systemText = body.system.filter((b3) => b3.type === "text").map((b3) => b3.text).join(` `); } else { systemText = String(body.system); } openaiMessages.push({ role: "system", content: systemText }); } for (const msg of body.messages ?? []) { const role = msg.role; const content = msg.content; if (role === "user") { if (typeof content === "string") { openaiMessages.push({ role: "user", content }); continue; } if (Array.isArray(content)) { const hasToolResult = content.some((b3) => b3.type === "tool_result"); if (hasToolResult) { const textParts = []; for (const block of content) { if (block.type === "tool_result") { let toolContent = ""; if (typeof block.content === "string") { toolContent = block.content; } else if (Array.isArray(block.content)) { toolContent = block.content.filter((c2) => c2.type === "text").map((c2) => c2.text).join(" "); } openaiMessages.push({ role: "tool", tool_call_id: block.tool_use_id, content: toolContent || "Success" }); } else if (block.type === "text" && block.text) { textParts.push(block.text); } } if (textParts.length > 0) { openaiMessages.push({ role: "user", content: textParts.join(` `) }); } } else { const openaiContent = []; for (const block of content) { if (block.type === "text") { openaiContent.push({ type: "text", text: block.text }); } else if (block.type === "image") { openaiContent.push({ type: "image_url", image_url: { url: convertImageSource(block.source) } }); } } openaiMessages.push({ role: "user", content: openaiContent }); } } } else if (role === "assistant") { const openaiMsg = { role: "assistant" }; if (typeof content === "string") { openaiMsg.content = content; } else if (Array.isArray(content)) { const textParts = []; const reasoningParts = []; const toolCalls = []; for (const block of content) { if (block.type === "text") { textParts.push(block.text); } else if (block.type === "thinking") { if (typeof block.thinking === "string") { reasoningParts.push(block.thinking); } else if (typeof block.text === "string") { reasoningParts.push(block.text); } } else if (block.type === "redacted_thinking") { reasoningParts.push("[redacted]"); } else if (block.type === "tool_use") { toolCalls.push({ id: block.id, type: "function", function: { name: block.name, arguments: JSON.stringify(block.input) } }); } } if (textParts.length > 0) { openaiMsg.content = textParts.join(` `); } if (toolCalls.length > 0) { openaiMsg.tool_calls = toolCalls; } if (reasoningParts.length > 0) { openaiMsg.reasoning_content = reasoningParts.join(` `); } else if (toolCalls.length > 0) { openaiMsg.reasoning_content = "[no reasoning provided]"; } } openaiMessages.push(openaiMsg); } } const tools = []; if (body.tools) { for (const tool of body.tools) { tools.push({ type: "function", function: { name: tool.name, description: tool.description ?? "", parameters: tool.input_schema } }); } } const openaiBody = { model: body.model, messages: openaiMessages, stream: body.stream ?? false }; if (body.stream === true) { openaiBody.stream_options = { include_usage: true }; } if (body.max_tokens != null) openaiBody.max_tokens = body.max_tokens; if (body.temperature != null) openaiBody.temperature = body.temperature; if (body.top_p != null) openaiBody.top_p = body.top_p; if (body.stop_sequences) openaiBody.stop = body.stop_sequences; if (tools.length > 0) { openaiBody.tools = tools; if (body.tool_choice) { const tc = body.tool_choice; if (tc.type === "any") openaiBody.tool_choice = "required"; else if (tc.type === "auto") openaiBody.tool_choice = "auto"; else if (tc.type === "tool") { openaiBody.tool_choice = { type: "function", function: { name: tc.name } }; } } } return openaiBody; } // src/proxy/usage.ts function numberOrZero2(value) { return typeof value === "number" && Number.isFinite(value) ? value : 0; } function mapChatUsageToAnthropic(usage) { const promptDetails = usage?.prompt_tokens_details ?? {}; return { input_tokens: numberOrZero2(usage?.prompt_tokens ?? usage?.input_tokens), output_tokens: numberOrZero2(usage?.completion_tokens ?? usage?.output_tokens), cache_creation_input_tokens: numberOrZero2(usage?.cache_creation_input_tokens ?? promptDetails.cache_creation_tokens), cache_read_input_tokens: numberOrZero2(usage?.cache_read_input_tokens ?? promptDetails.cached_tokens) }; } function mapResponsesUsageToAnthropic(usage) { const inputDetails = usage?.input_tokens_details ?? {}; return { input_tokens: numberOrZero2(usage?.input_tokens), output_tokens: numberOrZero2(usage?.output_tokens), cache_creation_input_tokens: numberOrZero2(usage?.cache_creation_input_tokens ?? inputDetails.cache_creation_tokens), cache_read_input_tokens: numberOrZero2(usage?.cache_read_input_tokens ?? inputDetails.cached_tokens) }; } function recordStatuslineUsage(usage) { try { updateStatuslineUsage(usage); } catch {} } // src/proxy/response-conversion.ts function convertOpenAIResponseToAnthropic(openaiResp) { const choice = openaiResp.choices?.[0]; const message = choice?.message; const contentBlocks = []; if (message?.content) { contentBlocks.push({ type: "text", text: message.content }); } if (message?.tool_calls) { for (const tc of message.tool_calls) { let input = {}; try { input = JSON.parse(tc.function.arguments); } catch {} contentBlocks.push({ type: "tool_use", id: tc.id, name: tc.function.name, input }); } } const usage = mapChatUsageToAnthropic(openaiResp.usage); recordStatuslineUsage(usage); return { id: openaiResp.id ? openaiResp.id.replace("chatcmpl", "msg") : generateMsgId(), type: "message", role: "assistant", content: contentBlocks, model: openaiResp.model ?? "", stop_reason: mapStopReason(choice?.finish_reason), stop_sequence: null, usage }; } // src/proxy/stream-conversion.ts async function* streamOpenAIToAnthropic(response) { const msgId = generateMsgId(); const logger = createLogger("[proxy]"); yield makeSSE("message_start", { type: "message_start", message: { id: msgId, type: "message", role: "assistant", content: [], model: "proxy", stop_reason: null, stop_sequence: null, usage: { input_tokens: 0, output_tokens: 0 } } }); yield makeSSE("ping", { type: "ping" }); let textBlockStarted = false; let nextBlockIndex = 0; const toolCallAccumulators = {}; const openaiToolIndexToBlockIndex = {}; let encounteredToolCall = false; let usage = null; if (!response.body) { logger.warn("response.body is null!"); return; } const reader = response.body.getReader(); const decoder2 = new TextDecoder("utf-8"); let buffer = ""; let rawChunkCount = 0; while (true) { const { value, done } = await reader.read(); if (done) { logger.debug(`OpenAI stream ended after ${rawChunkCount} raw chunks`); break; } const rawText = decoder2.decode(value, { stream: true }); rawChunkCount++; if (rawChunkCount <= 3) { logger.debug(`Raw OpenAI chunk #${rawChunkCount}: ${rawText.slice(0, 300).replace(/\n/g, "\\n")}`); } buffer += rawText; const lines = buffer.split(` `); buffer = lines.pop() ?? ""; for (const line of lines) { const trimmed = line.trim(); if (!trimmed || !trimmed.startsWith("data:")) continue; const dataStr = trimmed.replace(/^data:\s*/, ""); if (dataStr === "[DONE]") { if (textBlockStarted && Object.keys(toolCallAccumulators).length === 0) { yield makeSSE("content_block_stop", { type: "content_block_stop", index: 0 }); } if (encounteredToolCall) { for (const idx in toolCallAccumulators) { yield makeSSE("content_block_stop", { type: "content_block_stop", index: parseInt(idx, 10) }); } } const finalUsage2 = mapChatUsageToAnthropic(usage); recordStatuslineUsage(finalUsage2); yield makeSSE("message_delta", { type: "message_delta", delta: { stop_reason: encounteredToolCall ? "tool_use" : "end_turn", stop_sequence: null }, usage: finalUsage2 }); yield makeSSE("message_stop", { type: "message_stop" }); return; } let parsed; try { parsed = JSON.parse(dataStr); } catch { continue; } if (parsed.usage) { usage = parsed.usage; } const choice = parsed.choices?.[0]; if (!choice) continue; const delta = choice.delta; if (!delta) continue; if (delta.content) { if (!textBlockStarted) { textBlockStarted = true; yield makeSSE("content_block_start", { type: "content_block_start", index: nextBlockIndex, content_block: { type: "text", text: "" } }); } yield makeSSE("content_block_delta", { type: "content_block_delta", index: nextBlockIndex, delta: { type: "text_delta", text: delta.content } }); } if (delta.tool_calls) { for (const tc of delta.tool_calls) { const openaiIdx = tc.index; if (openaiToolIndexToBlockIndex[openaiIdx] === undefined) { encounteredToolCall = true; if (textBlockStarted && Object.keys(toolCallAccumulators).length === 0) { yield makeSSE("content_block_stop", { type: "content_block_stop", index: nextBlockIndex }); nextBlockIndex++; } const blockIndex2 = nextBlockIndex; openaiToolIndexToBlockIndex[openaiIdx] = blockIndex2; toolCallAccumulators[blockIndex2] = ""; yield makeSSE("content_block_start", { type: "content_block_start", index: blockIndex2, content_block: { type: "tool_use", id: tc.id ?? `tool_${blockIndex2}`, name: tc.function?.name ?? "", input: {} } }); nextBlockIndex = blockIndex2 + 1; } const blockIndex = openaiToolIndexToBlockIndex[openaiIdx]; const newArgs = tc.function?.arguments ?? ""; if (newArgs) { yield makeSSE("content_block_delta", { type: "content_block_delta", index: blockIndex, delta: { type: "input_json_delta", partial_json: newArgs } }); toolCallAccumulators[blockIndex] += newArgs; } } } if (choice.finish_reason) { if (textBlockStarted && Object.keys(toolCallAccumulators).length === 0) { yield makeSSE("content_block_stop", { type: "content_block_stop", index: 0 }); } if (encounteredToolCall) { for (const idx in toolCallAccumulators) { yield makeSSE("content_block_stop", { type: "content_block_stop", index: parseInt(idx, 10) }); } } const finalUsage2 = mapChatUsageToAnthropic(usage); recordStatuslineUsage(finalUsage2); yield makeSSE("message_delta", { type: "message_delta", delta: { stop_reason: mapStopReason(choice.finish_reason), stop_sequence: null }, usage: finalUsage2 }); yield makeSSE("message_stop", { type: "message_stop" }); return; } } } const finalUsage = mapChatUsageToAnthropic(usage); recordStatuslineUsage(finalUsage); yield makeSSE("message_delta", { type: "message_delta", delta: { stop_reason: "end_turn", stop_sequence: null }, usage: finalUsage }); yield makeSSE("message_stop", { type: "message_stop" }); } // src/proxy/request-conversion-responses.ts function convertAnthropicRequestToResponses(body) { let instructions; if (body.system) { if (typeof body.system === "string") { instructions = body.system; } else if (Array.isArray(body.system)) { instructions = body.system.filter((b3) => b3.type === "text").map((b3) => b3.text).join(` `); } else { instructions = String(body.system); } } const input = []; for (const msg of body.messages ?? []) { const role = msg.role; const content = msg.content; if (role === "user") { if (typeof content === "string") { input.push({ type: "message", role: "user", content: [{ type: "input_text", text: content }] }); continue; } if (Array.isArray(content)) { const hasToolResult = content.some((b3) => b3.type === "tool_result"); if (hasToolResult) { const textParts = []; for (const block of content) { if (block.type === "tool_result") { let output = ""; if (typeof block.content === "string") { output = block.content; } else if (Array.isArray(block.content)) { output = block.content.filter((c2) => c2.type === "text").map((c2) => c2.text).join(" "); } input.push({ type: "function_call_output", call_id: block.tool_use_id, output: output || "Success" }); } else if (block.type === "text" && block.text) { textParts.push({ type: "input_text", text: block.text }); } } if (textParts.length > 0) { input.push({ type: "message", role: "user", content: textParts }); } } else { const contentItems = []; for (const block of content) { if (block.type === "text") { contentItems.push({ type: "input_text", text: block.text }); } else if (block.type === "image") { contentItems.push({ type: "input_image", image_url: convertImageSource(block.source) }); } } input.push({ type: "message", role: "user", content: contentItems }); } } } else if (role === "assistant") { if (typeof content === "string") { input.push({ type: "message", role: "assistant", content: [{ type: "output_text", text: content }] }); continue; } if (Array.isArray(content)) { const outputContent = []; for (const block of content) { if (block.type === "text") { outputContent.push({ type: "output_text", text: block.text }); } else if (block.type === "tool_use") { if (outputContent.length > 0) { input.push({ type: "message", role: "assistant", content: [...outputContent] }); outputContent.length = 0; } input.push({ type: "function_call", call_id: block.id, name: block.name, arguments: JSON.stringify(block.input) }); } } if (outputContent.length > 0) { input.push({ type: "message", role: "assistant", content: outputContent }); } } } } const tools = []; if (body.tools) { for (const tool of body.tools) { let params = tool.input_schema; if (!params || !params.properties || Object.keys(params.properties).length === 0) { params = { type: "object", properties: { query: { type: "string", description: "The search query or input" } }, required: ["query"] }; } tools.push({ type: "function", name: tool.name, description: tool.description ?? "", parameters: params, strict: false }); } } const responsesBody = { model: body.model, input, stream: true, store: false }; if (instructions) { responsesBody.instructions = instructions; } if (tools.length > 0) { responsesBody.tools = tools; if (body.tool_choice) { const tc = body.tool_choice; if (tc.type === "any") responsesBody.tool_choice = "required"; else if (tc.type === "auto") responsesBody.tool_choice = "auto"; else if (tc.type === "tool") { responsesBody.tool_choice = { type: "function", name: tc.name }; } } } return responsesBody; } // src/proxy/stream-conversion-responses.ts async function* streamResponsesToAnthropic(response) { const msgId = generateMsgId(); const logger = createLogger("[proxy]"); yield makeSSE("message_start", { type: "message_start", message: { id: msgId, type: "message", role: "assistant", content: [], model: "proxy", stop_reason: null, stop_sequence: null, usage: { input_tokens: 0, output_tokens: 0 } } }); yield makeSSE("ping", { type: "ping" }); let nextBlockIndex = 0; let textBlockStarted = false; let textBlockIndex = -1; const toolCallBlocks = {}; let hasToolCalls = false; let usage = null; if (!response.body) { logger.warn("response.body is null!"); return; } const reader = response.body.getReader(); const decoder2 = new TextDecoder("utf-8"); let buffer = ""; let rawChunkCount = 0; while (true) { const { value, done } = await reader.read(); if (done) { logger.debug(`Responses API stream ended after ${rawChunkCount} raw chunks`); break; } const rawText = decoder2.decode(value, { stream: true }); rawChunkCount++; if (rawChunkCount <= 3) { logger.debug(`Raw Responses chunk #${rawChunkCount}: ${rawText.slice(0, 300).replace(/\n/g, "\\n")}`); } buffer += rawText; const lines = buffer.split(` `); buffer = lines.pop() ?? ""; let currentEventType = ""; for (const line of lines) { const trimmed = line.trim(); if (!trimmed) continue; if (trimmed.startsWith("event:")) { currentEventType = trimmed.replace(/^event:\s*/, ""); continue; } if (!trimmed.startsWith("data:")) continue; const dataStr = trimmed.replace(/^data:\s*/, ""); let parsed; try { parsed = JSON.parse(dataStr); } catch { continue; } if (currentEventType === "response.output_text.delta") { if (!textBlockStarted) { textBlockStarted = true; textBlockIndex = nextBlockIndex; nextBlockIndex++; yield makeSSE("content_block_start", { type: "content_block_start", index: textBlockIndex, content_block: { type: "text", text: "" } }); } yield makeSSE("content_block_delta", { type: "content_block_delta", index: textBlockIndex, delta: { type: "text_delta", text: parsed.delta ?? "" } }); } if (currentEventType === "response.output_text.done") { if (textBlockStarted) { yield makeSSE("content_block_stop", { type: "content_block_stop", index: textBlockIndex }); textBlockStarted = false; } } if (currentEventType === "response.output_item.added" && parsed.item?.type === "function_call") { hasToolCalls = true; if (textBlockStarted) { yield makeSSE("content_block_stop", { type: "content_block_stop", index: textBlockIndex }); textBlockStarted = false; } const blockIndex = nextBlockIndex; nextBlockIndex++; const itemId = parsed.item.id ?? parsed.item.call_id; toolCallBlocks[itemId] = blockIndex; yield makeSSE("content_block_start", { type: "content_block_start", index: blockIndex, content_block: { type: "tool_use", id: parsed.item.call_id ?? itemId, name: parsed.item.name ?? "", input: {} } }); } if (currentEventType === "response.function_call_arguments.delta") { const itemId = parsed.item_id; const blockIndex = toolCallBlocks[itemId]; if (blockIndex !== undefined) { yield makeSSE("content_block_delta", { type: "content_block_delta", index: blockIndex, delta: { type: "input_json_delta", partial_json: parsed.delta ?? "" } }); } } if (currentEventType === "response.output_item.done" && parsed.item?.type === "function_call") { const itemId = parsed.item.id ?? parsed.item.call_id; const blockIndex = toolCallBlocks[itemId]; if (blockIndex !== undefined) { yield makeSSE("content_block_stop", { type: "content_block_stop", index: blockIndex }); } } if (currentEventType === "response.output_item.done" && parsed.item?.type === "message") { if (textBlockStarted) { yield makeSSE("content_block_stop", { type: "content_block_stop", index: textBlockIndex }); textBlockStarted = false; } } if (currentEventType === "response.completed") { usage = parsed.response?.usage; if (textBlockStarted) { yield makeSSE("content_block_stop", { type: "content_block_stop", index: textBlockIndex }); } const finalUsage2 = mapResponsesUsageToAnthropic(usage); recordStatuslineUsage(finalUsage2); yield makeSSE("message_delta", { type: "message_delta", delta: { stop_reason: hasToolCalls ? "tool_use" : "end_turn", stop_sequence: null }, usage: finalUsage2 }); yield makeSSE("message_stop", { type: "message_stop" }); return; } currentEventType = ""; } } if (textBlockStarted) { yield makeSSE("content_block_stop", { type: "content_block_stop", index: textBlockIndex }); } const finalUsage = mapResponsesUsageToAnthropic(usage); recordStatuslineUsage(finalUsage); yield makeSSE("message_delta", { type: "message_delta", delta: { stop_reason: "end_turn", stop_sequence: null }, usage: finalUsage }); yield makeSSE("message_stop", { type: "message_stop" }); } // src/search/searxng.ts import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync5, existsSync as existsSync6 } from "fs"; import { join as join5 } from "path"; import { homedir as homedir3 } from "os"; var logger = createLogger("[searxng]"); var CONTAINER_NAME = "opencode-searxng"; var SEARXNG_PORT = 8888; var SEARXNG_IMAGE = "searxng/searxng"; var SEARXNG_URL = `http://localhost:${SEARXNG_PORT}`; var SETTINGS_DIR = join5(homedir3(), ".opencode-go-cli", "searxng"); function ensureSettings() { const settingsFile = join5(SETTINGS_DIR, "settings.yml"); if (existsSync6(settingsFile)) return; mkdirSync7(SETTINGS_DIR, { recursive: true }); const secret = Math.random().toString(36).substring(2) + Math.random().toString(36).substring(2); writeFileSync5(settingsFile, `use_default_settings: true search: formats: - html - json server: secret_key: "${secret}" limiter: false image_proxy: false `); logger.info("Created SearXNG settings with JSON format enabled"); } async function isDockerAvailable() { try { const proc = Bun.spawn(["docker", "info"], { stdout: "ignore", stderr: "ignore" }); const code = await proc.exited; return code === 0; } catch { return false; } } async function isContainerRunning() { try { const proc = Bun.spawn(["docker", "inspect", "-f", "{{.State.Running}}", CONTAINER_NAME], { stdout: "pipe", stderr: "ignore" }); const output = await new Response(proc.stdout).text(); await proc.exited; return output.trim() === "true"; } catch { return false; } } async function startContainer() { logger.info("Starting SearXNG container..."); const startProc = Bun.spawn(["docker", "start", CONTAINER_NAME], { stdout: "ignore", stderr: "ignore" }); if (await startProc.exited === 0) { logger.info("SearXNG container started (existing)"); return true; } ensureSettings(); const settingsPath = SETTINGS_DIR.replace(/\\/g, "/"); const runProc = Bun.spawn([ "docker", "run", "-d", "--name", CONTAINER_NAME, "-p", `${SEARXNG_PORT}:8080`, "-v", `${settingsPath}/settings.yml:/etc/searxng/settings.yml:ro`, "--restart", "unless-stopped", SEARXNG_IMAGE ], { stdout: "pipe", stderr: "pipe" }); const code = await runProc.exited; if (code === 0) { logger.info("SearXNG container created and started"); return true; } const stderr = await new Response(runProc.stderr).text(); logger.error(`Failed to start SearXNG: ${stderr.slice(0, 300)}`); return false; } async function waitForReady(maxWaitMs = 15000) { const start = Date.now(); while (Date.now() - start < maxWaitMs) { try { const res = await fetch(`${SEARXNG_URL}/healthz`, { signal: AbortSignal.timeout(2000) }); if (res.ok) return true; } catch {} await new Promise((r2) => setTimeout(r2, 500)); } return false; } async function ensureSearXNG() { if (await isContainerRunning()) { logger.debug("SearXNG already running"); return true; } if (!await isDockerAvailable()) { logger.warn("Docker not available \u2014 WebSearch interception disabled"); return false; } if (!await startContainer()) { return false; } logger.info("Waiting for SearXNG to be ready..."); const ready = await waitForReady(); if (!ready) { logger.error("SearXNG failed to become ready in time"); return false; } logger.info("SearXNG ready"); return true; } async function search(query, maxResults = 5) { try { const params = new URLSearchParams({ q: query, format: "json", categories: "general" }); const res = await fetch(`${SEARXNG_URL}/search?${params}`, { signal: AbortSignal.timeout(1e4) }); if (!res.ok) { logger.error(`SearXNG search failed: ${res.status}`); return []; } const data = await res.json(); const results = []; for (const r2 of (data.results ?? []).slice(0, maxResults)) { results.push({ title: r2.title ?? "", url: r2.url ?? "", content: r2.content ?? "", publishedDate: r2.publishedDate ?? undefined }); } logger.debug(`Search "${query.slice(0, 50)}": ${results.length} results`); return results; } catch (e2) { logger.error(`Search error: ${e2.message}`); return []; } } // src/proxy/websearch-interceptor.ts var logger2 = createLogger("[websearch]"); function hasWebSearchTool(body) { if (!body.tools || !Array.isArray(body.tools)) return false; return body.tools.some((t) => typeof t.type === "string" && t.type.startsWith("web_search_") || t.name === "web_search" && !t.input_schema); } function extractQuery(body) { const messages = body.messages ?? []; for (const msg of messages) { if (msg.role !== "user") continue; const content = msg.content; let text = ""; if (typeof content === "string") { text = content; } else if (Array.isArray(content)) { text = content.filter((b3) => b3.type === "text").map((b3) => b3.text).join(" "); } const match = text.match(/for the query:\s*(.+)/i); if (match) return match[1].trim(); if (text.trim()) return text.trim(); } return ""; } function generateSrvToolId() { const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; let id = "srvtoolu_"; for (let i = 0;i < 20; i++) { id += chars.charAt(Math.floor(Math.random() * chars.length)); } return id; } function buildSearchResultBlocks(toolId, query, results) { const searchResults = results.map((r2) => ({ type: "web_search_result", url: r2.url, title: r2.title, encrypted_content: "", page_age: r2.publishedDate ?? "" })); return [ { type: "server_tool_use", id: toolId, name: "web_search", input: { query } }, { type: "web_search_tool_result", tool_use_id: toolId, content: searchResults.length > 0 ? searchResults : [{ type: "web_search_tool_result_error", error_code: "unavailable" }] } ]; } async function handleWebSearch(body) { const query = extractQuery(body); const isStreaming = body.stream === true; const msgId = generateMsgId(); const toolId = generateSrvToolId(); logger2.info(`WebSearch intercepted: "${query.slice(0, 80)}"`); const results = await search(query); logger2.info(`WebSearch results: ${results.length}`); const contentBlocks = buildSearchResultBlocks(toolId, query, results); if (results.length > 0) { const summaryText = results.map((r2, i) => `[${i + 1}] ${r2.title} URL: ${r2.url} ${r2.content}`).join(` `); contentBlocks.push({ type: "text", text: summaryText }); } if (!isStreaming) { return new Response(JSON.stringify({ id: msgId, type: "message", role: "assistant", content: contentBlocks, model: body.model ?? "proxy", stop_reason: "end_turn", stop_sequence: null, usage: { input_tokens: 0, output_tokens: 0, server_tool_use: { web_search_requests: 1 } } }), { status: 200, headers: { "Content-Type": "application/json" } }); } const chunks = []; chunks.push(makeSSE("message_start", { type: "message_start", message: { id: msgId, type: "message", role: "assistant", content: [], model: body.model ?? "proxy", stop_reason: null, stop_sequence: null, usage: { input_tokens: 0, output_tokens: 0 } } })); chunks.push(makeSSE("ping", { type: "ping" })); chunks.push(makeSSE("content_block_start", { type: "content_block_start", index: 0, content_block: { type: "server_tool_use", id: toolId, name: "web_search" } })); chunks.push(makeSSE("content_block_delta", { type: "content_block_delta", index: 0, delta: { type: "input_json_delta", partial_json: JSON.stringify({ query }) } })); chunks.push(makeSSE("content_block_stop", { type: "content_block_stop", index: 0 })); const resultContent = results.length > 0 ? results.map((r2) => ({ type: "web_search_result", url: r2.url, title: r2.title, encrypted_content: "", page_age: r2.publishedDate ?? "" })) : [{ type: "web_search_tool_result_error", error_code: "unavailable" }]; chunks.push(makeSSE("content_block_start", { type: "content_block_start", index: 1, content_block: { type: "web_search_tool_result", tool_use_id: toolId, content: resultContent } })); chunks.push(makeSSE("content_block_stop", { type: "content_block_stop", index: 1 })); if (results.length > 0) { const summaryText = results.map((r2, i) => `[${i + 1}] ${r2.title} URL: ${r2.url} ${r2.content}`).join(` `); chunks.push(makeSSE("content_block_start", { type: "content_block_start", index: 2, content_block: { type: "text", text: "" } })); chunks.push(makeSSE("content_block_delta", { type: "content_block_delta", index: 2, delta: { type: "text_delta", text: summaryText } })); chunks.push(makeSSE("content_block_stop", { type: "content_block_stop", index: 2 })); } chunks.push(makeSSE("message_delta", { type: "message_delta", delta: { stop_reason: "end_turn", stop_sequence: null }, usage: { output_tokens: 0, server_tool_use: { web_search_requests: 1 } } })); chunks.push(makeSSE("message_stop", { type: "message_stop" })); const encoder2 = new TextEncoder; const stream = new ReadableStream({ start(controller) { for (const chunk of chunks) { controller.enqueue(encoder2.encode(chunk)); } controller.close(); } }); return new Response(stream, { status: 200, headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", Connection: "keep-alive" } }); } // src/db/accounts.ts import { randomUUID } from "crypto"; function listAccounts() { return db().query("SELECT * FROM accounts ORDER BY priority ASC").all(); } function countAccounts() { const row = db().query("SELECT COUNT(*) AS c FROM accounts").get(); return row?.c ?? 0; } function getAccountById(id) { const exact = db().query("SELECT * FROM accounts WHERE id = ?").get(id); if (exact) return exact; const prefix = db().query("SELECT * FROM accounts WHERE id LIKE ? || '%' LIMIT 1").get(id); return prefix ?? null; } function getAccountByEmail(email) { return db().query("SELECT * FROM accounts WHERE email LIKE '%' || ? || '%' LIMIT 1").get(email) ?? null; } function addAccount(data) { const now = new Date().toISOString(); if (data.email) { const existing = db().query("SELECT * FROM accounts WHERE email = ?").get(data.email); if (existing) { db().query(`UPDATE accounts SET access_token = ?, refresh_token = ?, expires_at = ?, resource_url = ?, test_status = 'unknown', last_error = NULL, error_code = NULL, backoff_level = 0, is_active = 1, updated_at = ? WHERE id = ?`).run(data.access_token, data.refresh_token, data.expires_at, data.resource_url, now, existing.id); return getAccountById(existing.id); } } const id = randomUUID(); const row = db().query("SELECT COALESCE(MAX(priority), 0) AS m FROM accounts").get(); const maxPri = row?.m ?? 0; db().query(`INSERT INTO accounts (id, email, display_name, access_token, refresh_token, expires_at, resource_url, priority, is_active, test_status, backoff_level, consecutive_use_count, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 1, 'unknown', 0, 0, ?, ?)`).run(id, data.email, data.display_name, data.access_token, data.refresh_token, data.expires_at, data.resource_url, maxPri + 1, now, now); return getAccountById(id); } function updateAccount(id, patch) { const entries = Object.entries(patch); if (entries.length === 0) return; const sets = entries.map(([k2]) => `${k2} = ?`).join(", "); const values = entries.map(([, v2]) => v2); const now = new Date().toISOString(); db().query(`UPDATE accounts SET ${sets}, updated_at = ? WHERE id = ?`).run(...values, now, id); } function removeAccount(id) { const { changes } = db().query("DELETE FROM accounts WHERE id = ?").run(id); if (changes > 0) reorderPriorities(); return changes > 0; } function reorderPriorities() { const rows = db().query("SELECT id FROM accounts ORDER BY priority ASC").all(); const stmt = db().query("UPDATE accounts SET priority = ? WHERE id = ?"); rows.forEach((a3, i) => stmt.run(i + 1, a3.id)); } // src/auth/qwen/refresh.ts async function refreshQwenToken(refreshToken) { try { const resp = await fetch(QWEN_TOKEN_URL, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", Accept: "application/json" }, body: new URLSearchParams({ grant_type: "refresh_token", refresh_token: refreshToken, client_id: QWEN_CLIENT_ID }) }); if (!resp.ok) return null; const data = await resp.json(); if (!data.access_token) return null; return { accessToken: data.access_token, refreshToken: data.refresh_token ?? refreshToken, expiresIn: data.expires_in ?? 3600, resourceUrl: data.resource_url }; } catch { return null; } } async function checkAndRefreshAccount(account) { const expiresAt = new Date(account.expires_at).getTime(); if (expiresAt - Date.now() > QWEN_TOKEN_EXPIRY_BUFFER_MS) { return account; } const refreshed = await refreshQwenToken(account.refresh_token); if (!refreshed) return account; const patch = { access_token: refreshed.accessToken, refresh_token: refreshed.refreshToken, expires_at: new Date(Date.now() + refreshed.expiresIn * 1000).toISOString() }; if (refreshed.resourceUrl) { patch.resource_url = refreshed.resourceUrl; } updateAccount(account.id, patch); return { ...account, ...patch }; } // src/db/locks.ts var ALL_MODELS = "__all"; function isModelLockActive(accountId, model) { const now = new Date().toISOString(); const key = model ?? ALL_MODELS; const lock = db().query(`SELECT id FROM model_locks WHERE account_id = ? AND (model = ? OR model = ?) AND locked_until > ? LIMIT 1`).get(accountId, key, ALL_MODELS, now); return !!lock; } function setModelLock(accountId, model, cooldownMs) { const key = model ?? ALL_MODELS; const until = new Date(Date.now() + cooldownMs).toISOString(); const existing = db().query("SELECT id FROM model_locks WHERE account_id = ? AND model = ?").get(accountId, key); if (existing) { db().query("UPDATE model_locks SET locked_until = ? WHERE id = ?").run(until, existing.id); } else { db().query("INSERT INTO model_locks (account_id, model, locked_until) VALUES (?, ?, ?)").run(accountId, key, until); } } function clearModelLock(accountId, model) { const key = model ?? ALL_MODELS; db().query("DELETE FROM model_locks WHERE account_id = ? AND model = ?").run(accountId, key); } function getActiveModelLocks(accountId) { const now = new Date().toISOString(); return db().query("SELECT model, locked_until FROM model_locks WHERE account_id = ? AND locked_until > ?").all(accountId, now); } function getEarliestLockUntil(model) { const now = new Date().toISOString(); const key = model ?? ALL_MODELS; const row = db().query(`SELECT MIN(locked_until) AS until FROM model_locks WHERE (model = ? OR model = ?) AND locked_until > ?`).get(key, ALL_MODELS, now); return row?.until ?? null; } // src/rotator/fallback.ts function exponentialCooldown(level) { return Math.min(QWEN_RATE_LIMIT_BACKOFF_BASE_MS * Math.pow(2, level), QWEN_RATE_LIMIT_BACKOFF_MAX_MS); } function checkFallbackError(status, errorText, backoffLevel = 0) { const lower = errorText.toLowerCase(); if (status === 401) { return { shouldFallback: true, cooldownMs: QWEN_COOLDOWN_UNAUTHORIZED_MS }; } if (status === 402 || status === 403) { return { shouldFallback: true, cooldownMs: QWEN_COOLDOWN_PAYMENT_MS }; } if (status === 404) { return { shouldFallback: false, cooldownMs: QWEN_COOLDOWN_NOT_FOUND_MS }; } if (status === 429 || lower.includes("rate limit") || lower.includes("quota exceeded") || lower.includes("too many requests")) { const newLevel = Math.min(backoffLevel + 1, QWEN_RATE_LIMIT_BACKOFF_MAX_LEVEL); return { shouldFallback: true, cooldownMs: exponentialCooldown(backoffLevel), newBackoffLevel: newLevel }; } if (status >= 500 || lower.includes("timeout")) { return { shouldFallback: true, cooldownMs: QWEN_COOLDOWN_TRANSIENT_MS }; } if (lower.includes("request not allowed")) { return { shouldFallback: true, cooldownMs: QWEN_COOLDOWN_UNAUTHORIZED_MS }; } return { shouldFallback: false, cooldownMs: 0 }; } function formatDuration(ms) { if (ms <= 0) return "expired"; if (ms < 60000) return `${Math.ceil(ms / 1000)}s`; if (ms < 3600000) return `${Math.ceil(ms / 60000)}m`; return `${Math.ceil(ms / 3600000)}h`; } // src/rotator/index.ts function isRateLimitedResult(v2) { return typeof v2 === "object" && v2 !== null && "allRateLimited" in v2 && v2.allRateLimited === true; } function selectAccount(model, excludeIds) { const strategy = getStrategy(); const stickyLimit = getStickyLimit(); const all = db().query("SELECT * FROM accounts WHERE is_active = 1 ORDER BY priority ASC").all(); const candidates = all.filter((a3) => { if (excludeIds?.has(a3.id)) return false; if (isModelLockActive(a3.id, model)) return false; return true; }); if (candidates.length === 0) { const anyLocked = all.some((a3) => !excludeIds?.has(a3.id) && isModelLockActive(a3.id, model)); if (anyLocked) { const earliest = getEarliestLockUntil(model); const retryAfter = earliest ?? new Date(Date.now() + 60000).toISOString(); const diff = earliest ? new Date(earliest).getTime() - Date.now() : 60000; return { allRateLimited: true, retryAfter, retryAfterHuman: `reset after ${formatDuration(diff)}` }; } return null; } let selected; if (strategy === "round-robin") { const withUsage = candidates.filter((a3) => a3.last_used_at); const withoutUsage = candidates.filter((a3) => !a3.last_used_at); if (withoutUsage.length > 0) { selected = withoutUsage[0]; } else { const oldestFirst = [...withUsage].sort((a3, b3) => new Date(a3.last_used_at).getTime() - new Date(b3.last_used_at).getTime()); const mostRecent = [...withUsage].sort((a3, b3) => new Date(b3.last_used_at).getTime() - new Date(a3.last_used_at).getTime())[0]; selected = mostRecent.consecutive_use_count < stickyLimit ? mostRecent : oldestFirst[0] ?? candidates[0]; } } else { selected = candidates[0]; } const isSticky = strategy === "round-robin" && selected.last_used_at !== null && selected.consecutive_use_count < stickyLimit; updateAccount(selected.id, { last_used_at: new Date().toISOString(), consecutive_use_count: isSticky ? selected.consecutive_use_count + 1 : 1 }); return selected; } function markAccountUnavailable(accountId, status, errorText, model) { const row = db().query("SELECT backoff_level FROM accounts WHERE id = ?").get(accountId); const decision = checkFallbackError(status, errorText, row?.backoff_level ?? 0); const patch = { last_error: errorText.slice(0, 500), error_code: status, last_error_at: new Date().toISOString() }; if (decision.cooldownMs > 0) { patch.test_status = "unavailable"; setModelLock(accountId, model, decision.cooldownMs); } if (decision.newBackoffLevel !== undefined) { patch.backoff_level = decision.newBackoffLevel; } updateAccount(accountId, patch); return { shouldFallback: decision.shouldFallback, cooldownMs: decision.cooldownMs }; } function clearAccountError(accountId, model = null) { updateAccount(accountId, { test_status: "active", last_error: null, error_code: null, backoff_level: 0 }); if (model) clearModelLock(accountId, model); } // src/proxy/qwen-handler.ts var logger3 = createLogger("[qwen]"); async function handleQwenRequest(anthropicBody) { const isStreaming = anthropicBody.stream === true; const model = anthropicBody.model ?? null; const openAIBody = convertAnthropicRequestToOpenAI(anthropicBody); const excludeIds = new Set; let lastError = null; let lastRateLimited = null; for (let attempt = 0;attempt < QWEN_MAX_ROTATION_ATTEMPTS; attempt++) { const picked = selectAccount(model, excludeIds); if (picked === null) { return errorResponse(503, "no_accounts", "No Qwen accounts available. Run `opencode-go --qwen-login` to add one."); } if (isRateLimitedResult(picked)) { lastRateLimited = picked; break; } const account = picked; let refreshed; try { refreshed = await checkAndRefreshAccount(account); } catch (err) { logger3.warn(`Refresh threw for ${shortId(account.id)}: ${errMsg(err)}`); refreshed = account; } const url = buildQwenChatCompletionsUrl(refreshed.resource_url); const headers = buildQwenHeaders(refreshed.access_token, isStreaming); const body = JSON.stringify({ ...openAIBody, stream: isStreaming }); logger3.debug(`attempt ${attempt + 1}/${QWEN_MAX_ROTATION_ATTEMPTS} \u2192 account=${shortId(account.id)} model=${model} stream=${isStreaming}`); let upstream; try { upstream = await fetch(url, { method: "POST", headers, body }); } catch (err) { const msg = errMsg(err); logger3.warn(`Network error on ${shortId(account.id)}: ${msg}`); const decision = markAccountUnavailable(account.id, 599, msg, model); if (decision.shouldFallback) { excludeIds.add(account.id); lastError = { status: 599, text: msg }; continue; } return errorResponse(502, "upstream_network_error", msg); } if (!upstream.ok) { const errText = await upstream.text().catch(() => ""); logger3.warn(`Upstream ${upstream.status} on ${shortId(account.id)}: ${errText.slice(0, 120)}`); const decision = markAccountUnavailable(account.id, upstream.status, errText, model); lastError = { status: upstream.status, text: errText }; if (decision.shouldFallback) { excludeIds.add(account.id); continue; } return errorResponse(upstream.status, "api_error", errText); } clearAccountError(account.id, model); logger3.info(`ok \u2190 ${shortId(account.id)} (${upstream.status}) stream=${isStreaming}`); if (isStreaming) { return wrapStream(upstream); } const data = await upstream.json(); const anthropicResponse = convertOpenAIResponseToAnthropic(data); return new Response(JSON.stringify(anthropicResponse), { status: 200, headers: { "Content-Type": "application/json" } }); } if (lastRateLimited) { return errorResponse(429, "rate_limit", `All Qwen accounts are cooling down. ${lastRateLimited.retryAfterHuman}`, { "Retry-After": secondsUntil(lastRateLimited.retryAfter) }); } if (lastError) { return errorResponse(lastError.status >= 400 && lastError.status < 600 ? lastError.status : 502, "all_accounts_failed", `Tried ${QWEN_MAX_ROTATION_ATTEMPTS} accounts, last error: ${lastError.text.slice(0, 300)}`); } return errorResponse(503, "no_accounts", "No Qwen accounts could serve the request."); } function wrapStream(upstream) { const generator = streamOpenAIToAnthropic(upstream); const stream = new ReadableStream({ async start(controller) { const encoder2 = new TextEncoder; try { for await (const chunk of generator) { controller.enqueue(encoder2.encode(chunk)); } } catch (e2) { logger3.error(`stream error: ${errMsg(e2)}`); } finally { controller.close(); } } }); return new Response(stream, { status: 200, headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", Connection: "keep-alive" } }); } function errorResponse(status, type, message, extraHeaders = {}) { return new Response(JSON.stringify({ type: "error", error: { type, message } }), { status, headers: { "Content-Type": "application/json", ...extraHeaders } }); } function shortId(id) { return id.slice(0, 8); } function errMsg(e2) { return e2 instanceof Error ? e2.message : String(e2); } function secondsUntil(iso) { const diff = new Date(iso).getTime() - Date.now(); return String(Math.max(1, Math.ceil(diff / 1000))); } // src/proxy/zai-signature.ts import { createHmac } from "crypto"; function generateZaiSignature(params) { const { requestId, timestamp, userId, prompt } = params; const sortedPayload = `requestId,${requestId},timestamp,${timestamp},user_id,${userId}`; const b64Prompt = Buffer.from(prompt, "utf-8").toString("base64"); const dataToSign = `${sortedPayload}|${b64Prompt}|${timestamp}`; const bucket = Math.floor(Number(timestamp) / ZAI_TIME_BUCKET_MS); const k1 = createHmac("sha256", ZAI_HMAC_KEY).update(String(bucket)).digest("hex"); const signature = createHmac("sha256", k1).update(dataToSign).digest("hex"); return signature; } function buildZaiUrlParams(params) { const { timestamp, requestId, userId, token } = params; return [ `timestamp=${timestamp}`, `requestId=${requestId}`, `user_id=${userId}`, `version=0.0.1`, `platform=web`, `token=${encodeURIComponent(token)}`, `language=en-US`, `timezone=America%2FSao_Paulo`, `cookie_enabled=true`, `screen_width=1280`, `screen_height=720`, `screen_resolution=1280x720`, `color_depth=24`, `pixel_ratio=1`, `pathname=%2F`, `host=chat.z.ai`, `hostname=chat.z.ai`, `protocol=https%3A`, `referrer=`, `title=Z.ai`, `timezone_offset=180`, `is_mobile=false`, `is_touch=false`, `max_touch_points=0`, `browser_name=Chrome`, `os_name=Linux`, `signature_timestamp=${timestamp}` ].join("&"); } // src/proxy/zai-stream.ts async function* streamZaiToAnthropic(response, model) { const msgId = generateMsgId(); const logger4 = createLogger("[zai]"); yield makeSSE("message_start", { type: "message_start", message: { id: msgId, type: "message", role: "assistant", content: [], model, stop_reason: null, stop_sequence: null, usage: { input_tokens: 0, output_tokens: 0 } } }); yield makeSSE("ping", { type: "ping" }); let textBlockStarted = false; let blockIndex = 0; let outputTokens = 0; if (!response.body) { logger4.warn("response.body is null!"); return; } const reader = response.body.getReader(); const decoder2 = new TextDecoder("utf-8"); let buffer = ""; while (true) { const { value, done } = await reader.read(); if (done) break; buffer += decoder2.decode(value, { stream: true }); const lines = buffer.split(` `); buffer = lines.pop() ?? ""; for (const line of lines) { const trimmed = line.trim(); if (!trimmed || !trimmed.startsWith("data:")) continue; const dataStr = trimmed.replace(/^data:\s*/, ""); if (dataStr === "[DONE]" || dataStr.includes('"[DONE]"')) { if (textBlockStarted) { yield makeSSE("content_block_stop", { type: "content_block_stop", index: blockIndex }); } yield makeSSE("message_delta", { type: "message_delta", delta: { stop_reason: "end_turn", stop_sequence: null }, usage: { output_tokens: outputTokens } }); yield makeSSE("message_stop", { type: "message_stop" }); return; } let parsed; try { parsed = JSON.parse(dataStr); } catch { continue; } const eventData = parsed?.data; const phase = eventData?.phase; const deltaContent = eventData?.delta_content; if (phase !== "answer" || !deltaContent) continue; outputTokens++; if (!textBlockStarted) { textBlockStarted = true; yield makeSSE("content_block_start", { type: "content_block_start", index: blockIndex, content_block: { type: "text", text: "" } }); } yield makeSSE("content_block_delta", { type: "content_block_delta", index: blockIndex, delta: { type: "text_delta", text: deltaContent } }); } } if (textBlockStarted) { yield makeSSE("content_block_stop", { type: "content_block_stop", index: blockIndex }); } yield makeSSE("message_delta", { type: "message_delta", delta: { stop_reason: "end_turn", stop_sequence: null }, usage: { output_tokens: outputTokens } }); yield makeSSE("message_stop", { type: "message_stop" }); } // src/proxy/zai-handler.ts var logger4 = createLogger("[zai]"); var cachedChatId = null; var cachedChatModel = null; async function validateZaiToken(token) { try { const resp = await fetch(ZAI_AUTH_URL, { headers: { Authorization: `Bearer ${token}` } }); if (!resp.ok) return null; const data = await resp.json(); return data?.id ?? null; } catch { return null; } } async function createChat(token, model) { const resp = await fetch(ZAI_CHAT_NEW_URL, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` }, body: JSON.stringify({ chat: { title: "OpenCode Go", models: [model] } }) }); if (!resp.ok) { throw new Error(`Failed to create Z.ai chat: ${resp.status} ${await resp.text()}`); } const data = await resp.json(); return data.id; } async function ensureChatId(token, model) { cachedChatId = await createChat(token, model); cachedChatModel = model; logger4.debug(`Created chat ${cachedChatId?.slice(0, 8)} for model ${model}`); return cachedChatId; } async function handleZaiRequest(anthropicBody, zaiToken) { const isStreaming = anthropicBody.stream === true; const model = anthropicBody.model ?? "glm-4.7"; const prompt = extractPrompt(anthropicBody); const userId = await validateZaiToken(zaiToken); if (!userId) { return errorResponse2(401, "auth_error", "Invalid Z.ai token. Run 'opencode-go --zai-login' to update."); } const chatId = await ensureChatId(zaiToken, model); const timestamp = String(Date.now()); const requestId = crypto.randomUUID(); const signature = generateZaiSignature({ requestId, timestamp, userId, prompt }); const urlParams = buildZaiUrlParams({ timestamp, requestId, userId, token: zaiToken }); const messages = convertMessages(anthropicBody); const zaiBody = { stream: true, model, messages, signature_prompt: prompt, params: {}, extra: {}, features: { image_generation: false, web_search: false, auto_web_search: false, preview_mode: true, flags: [], enable_thinking: false }, variables: buildVariables(), chat_id: chatId, id: crypto.randomUUID(), current_user_message_id: crypto.randomUUID(), current_user_message_parent_id: null, background_tasks: { tags_generation: true, title_generation: true } }; const url = `${ZAI_API_V2}?${urlParams}`; logger4.debug(`\u2192 Z.ai model=${model} chat=${chatId.slice(0, 8)} stream=${isStreaming}`); let upstream; try { upstream = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${zaiToken}`, "Accept-Language": "en-US", "X-FE-Version": ZAI_FE_VERSION, "X-Signature": signature }, body: JSON.stringify(zaiBody) }); } catch (err) { const msg = err instanceof Error ? err.message : String(err); logger4.error(`Network error: ${msg}`); return errorResponse2(502, "network_error", msg); } if (!upstream.ok) { const errText = await upstream.text(); logger4.error(`Z.ai ${upstream.status}: ${errText.slice(0, 200)}`); if (upstream.status === 500) { return errorResponse2(503, "model_overloaded", `Z.ai model "${model}" returned 500 \u2014 likely overloaded. Try a different model with --model (e.g. glm-4.7).`); } return errorResponse2(upstream.status, "api_error", errText); } logger4.info(`\u2190 Z.ai ${upstream.status} stream=${isStreaming}`); if (isStreaming) { return wrapStream2(upstream, model); } let fullText = ""; for await (const chunk of streamZaiToAnthropic(upstream, model)) { const lines = chunk.split(` `); for (const line of lines) { if (!line.startsWith("data: ")) continue; try { const evt = JSON.parse(line.slice(6)); if (evt.type === "content_block_delta" && evt.delta?.type === "text_delta") { fullText += evt.delta.text; } } catch {} } } const anthropicResponse = { id: generateMsgId(), type: "message", role: "assistant", content: [{ type: "text", text: fullText }], model, stop_reason: "end_turn", stop_sequence: null, usage: { input_tokens: 0, output_tokens: 0, cache_creation_input_tokens: 0, cache_read_input_tokens: 0 } }; return new Response(JSON.stringify(anthropicResponse), { status: 200, headers: { "Content-Type": "application/json" } }); } function wrapStream2(upstream, model) { const generator = streamZaiToAnthropic(upstream, model); const stream = new ReadableStream({ async start(controller) { const encoder2 = new TextEncoder; try { for await (const chunk of generator) { controller.enqueue(encoder2.encode(chunk)); } } catch (e2) { logger4.error(`stream error: ${e2 instanceof Error ? e2.message : String(e2)}`); } finally { controller.close(); } } }); return new Response(stream, { status: 200, headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", Connection: "keep-alive" } }); } function extractPrompt(body) { const messages = body?.messages ?? []; for (let i = messages.length - 1;i >= 0; i--) { const msg = messages[i]; if (msg?.role !== "user") continue; const content = msg.content; if (typeof content === "string") return content; if (Array.isArray(content)) { for (const block of content) { if (block?.type === "text") return block.text ?? ""; } } } return ""; } function convertMessages(body) { const messages = []; for (const msg of body?.messages ?? []) { if (msg.role === "user") { if (typeof msg.content === "string") { messages.push({ role: "user", content: msg.content }); } else if (Array.isArray(msg.content)) { const textParts = msg.content.filter((b3) => b3.type === "text").map((b3) => b3.text).join(` `); if (textParts) messages.push({ role: "user", content: textParts }); } } else if (msg.role === "assistant") { if (typeof msg.content === "string") { messages.push({ role: "assistant", content: msg.content }); } else if (Array.isArray(msg.content)) { const textParts = msg.content.filter((b3) => b3.type === "text").map((b3) => b3.text).join(` `); if (textParts) messages.push({ role: "assistant", content: textParts }); } } } return messages; } function buildVariables() { const now = new Date; const pad = (n) => String(n).padStart(2, "0"); const dateStr = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`; const timeStr = `${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`; const days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]; return { "{{CURRENT_DATETIME}}": `${dateStr} ${timeStr}`, "{{CURRENT_DATE}}": dateStr, "{{CURRENT_TIMEZONE}}": Intl.DateTimeFormat().resolvedOptions().timeZone, "{{CURRENT_TIME}}": timeStr, "{{CURRENT_WEEKDAY}}": days[now.getDay()], "{{USER_LANGUAGE}}": "en-US", "{{USER_LOCATION}}": "Unknown", "{{USER_NAME}}": "User" }; } function errorResponse2(status, type, message) { return new Response(JSON.stringify({ type: "error", error: { type, message } }), { status, headers: { "Content-Type": "application/json" } }); } // src/proxy/server.ts var logger5 = createLogger("[proxy]"); function isPortInUseError(err) { return err?.code === "EADDRINUSE" || err?.message?.includes("address already in use"); } function formatPortRange(startPort, attempts) { const endPort = startPort + attempts - 1; return attempts === 1 ? `${startPort}` : `${startPort}-${endPort}`; } async function startProxy(port, provider, attempts = 1) { const config = getConfig(); const endpoint = provider === "openai" ? CODEX_API_URL : provider === "qwen" ? "qwen-dynamic" : provider === "zai" ? "zai-dynamic" : OPENCODE_GO_ENDPOINT; let accessToken = ""; let openaiAccountId; if (provider === "openai") { if (!config.openaiTokens) { throw new Error("OpenAI tokens not found. Run 'opencode-go --oauth-login' first."); } openaiAccountId = config.openaiTokens.accountId; if (Date.now() > config.openaiTokens.expiresAt - 60000) { logger5.info("OpenAI token expired, refreshing..."); const refreshed = await refreshAccessToken(config.openaiTokens.refresh); if (refreshed.type === "success") { config.openaiTokens = { access: refreshed.access, refresh: refreshed.refresh, expiresAt: refreshed.expires, accountId: refreshed.accountId ?? config.openaiTokens.accountId, planType: refreshed.planType ?? config.openaiTokens.planType }; openaiAccountId = config.openaiTokens.accountId; saveConfig(config); } } accessToken = config.openaiTokens.access; logger5.info(`Starting on port ${port} (OpenAI)`); logger5.debug(`Endpoint: ${endpoint}`); } else if (provider === "qwen") { if (countAccounts() === 0) { throw new Error("No Qwen accounts found. Run 'opencode-go --qwen-login' first."); } logger5.info(`Starting on port ${port} (Qwen)`); } else if (provider === "zai") { const cfg = getConfig(); if (!cfg.zaiToken) { throw new Error("No Z.ai token found. Run 'opencode-go --zai-login' first."); } logger5.info(`Starting on port ${port} (Z.ai)`); } else { if (!config.apiKey) { throw new Error("API key not found. Run 'opencode-go --setup' first."); } accessToken = config.apiKey; logger5.info(`Starting on port ${port} (OpenCode Go)`); logger5.debug(`API Key: ${accessToken.slice(0, 10)}...`); logger5.debug(`Endpoint: ${endpoint}`); } function refreshOpenAIUsageState() { if (provider !== "openai" || !accessToken) return; refreshStatuslineCodexUsage({ accessToken, accountId: openaiAccountId }); } let server; let boundPort; for (let attempt = 0;attempt < attempts; attempt++) { const candidatePort = port + attempt; try { server = Bun.serve({ port: candidatePort, idleTimeout: 255, async fetch(req) { const url = new URL(req.url); logger5.debug(`${req.method} ${url.pathname}`); if (req.method === "HEAD" || req.method === "GET") { return new Response("OK", { status: 200 }); } if (req.method !== "POST") { return new Response("Method not allowed", { status: 405 }); } if (url.pathname === "/v1/messages") { try { const anthropicBody = await req.json(); const isStreaming = anthropicBody.stream === true; logger5.debug(`Model: ${anthropicBody.model} | stream: ${isStreaming} | tools: ${anthropicBody.tools?.length ?? 0}`); if (hasWebSearchTool(anthropicBody)) { logger5.info("Intercepting WebSearch request"); return await handleWebSearch(anthropicBody); } if (provider === "qwen") { return await handleQwenRequest(anthropicBody); } if (provider === "zai") { const cfg = getConfig(); return await handleZaiRequest(anthropicBody, cfg.zaiToken); } const isResponses = provider === "openai"; const outBody = isResponses ? convertAnthropicRequestToResponses(anthropicBody) : convertAnthropicRequestToOpenAI(anthropicBody); logger5.debug(`\u2192 ${endpoint} (${isResponses ? "Responses API" : "Chat Completions"})`); logger5.debug(`Request body: ${JSON.stringify(outBody).slice(0, 2000)}`); const response = await fetch(endpoint, { method: "POST", headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json" }, body: JSON.stringify(outBody) }); logger5.info(`\u2190 status ${response.status}`); if (!response.ok) { const errorText = await response.text(); logger5.error(`API error: ${errorText.slice(0, 500)}`); return new Response(JSON.stringify({ type: "error", error: { type: "api_error", message: errorText } }), { status: response.status, headers: { "Content-Type": "application/json" } }); } const upstreamIsStreaming = isResponses ? true : isStreaming; if (upstreamIsStreaming) { const streamGenerator = isResponses ? streamResponsesToAnthropic(response) : streamOpenAIToAnthropic(response); if (isStreaming) { const stream = new ReadableStream({ async start(controller) { let chunkCount = 0; try { for await (const chunk of streamGenerator) { chunkCount++; if (chunkCount <= 5) { logger5.debug(`SSE chunk #${chunkCount}: ${chunk.slice(0, 200).replace(/\n/g, "\\n")}`); } controller.enqueue(new TextEncoder().encode(chunk)); } logger5.debug(`Stream complete: ${chunkCount} chunks sent`); } catch (e2) { logger5.error(`Stream error: ${e2}`); } finally { refreshOpenAIUsageState(); controller.close(); } } }); return new Response(stream, { status: 200, headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", Connection: "keep-alive" } }); } else { let fullText = ""; const toolUses = []; const toolArgs = {}; let stopReason = "end_turn"; let finalUsage = { input_tokens: 0, output_tokens: 0, cache_creation_input_tokens: 0, cache_read_input_tokens: 0 }; for await (const chunk of streamGenerator) { const lines = chunk.split(` `); for (const line of lines) { if (!line.startsWith("data: ")) continue; const dataStr = line.slice(6); try { const evt = JSON.parse(dataStr); if (evt.type === "content_block_start" && evt.content_block?.type === "text") {} else if (evt.type === "content_block_delta" && evt.delta?.type === "text_delta") { fullText += evt.delta.text; } else if (evt.type === "content_block_start" && evt.content_block?.type === "tool_use") { toolUses.push({ type: "tool_use", id: evt.content_block.id, name: evt.content_block.name, input: {} }); toolArgs[evt.index] = ""; } else if (evt.type === "content_block_delta" && evt.delta?.type === "input_json_delta") { toolArgs[evt.index] = (toolArgs[evt.index] ?? "") + evt.delta.partial_json; } else if (evt.type === "message_delta" && evt.delta?.stop_reason) { stopReason = evt.delta.stop_reason; if (evt.usage) finalUsage = evt.usage; } } catch {} } } refreshOpenAIUsageState(); for (const tu of toolUses) { const idx = toolUses.indexOf(tu); const rawArgs = Object.values(toolArgs).find((_3, i) => i === idx); if (rawArgs) { try { tu.input = JSON.parse(rawArgs); } catch {} } } const content = []; if (fullText) content.push({ type: "text", text: fullText }); content.push(...toolUses); const anthropicResponse2 = { id: `msg_${Math.random().toString(36).substring(2, 15)}`, type: "message", role: "assistant", content, model: anthropicBody.model ?? "", stop_reason: stopReason, stop_sequence: null, usage: finalUsage }; logger5.debug(`Response (collected): stop_reason=${stopReason}, blocks=${content.length}`); return new Response(JSON.stringify(anthropicResponse2), { status: 200, headers: { "Content-Type": "application/json" } }); } } const data = await response.json(); const anthropicResponse = convertOpenAIResponseToAnthropic(data); refreshOpenAIUsageState(); logger5.debug(`Response: stop_reason=${anthropicResponse.stop_reason}, blocks=${anthropicResponse.content.length}`); return new Response(JSON.stringify(anthropicResponse), { status: 200, headers: { "Content-Type": "application/json" } }); } catch (e2) { logger5.error(`Internal error: ${e2.message}`); return new Response(JSON.stringify({ type: "error", error: { type: "internal_server_error", message: e2.message } }), { status: 500, headers: { "Content-Type": "application/json" } }); } } return new Response("Not found", { status: 404 }); } }); boundPort = server.port; break; } catch (err) { if (isPortInUseError(err) && attempt < attempts - 1) { logger5.warn(`Port ${candidatePort} is already in use. Trying ${candidatePort + 1}...`); continue; } if (isPortInUseError(err)) { throw new Error(`No available proxy port in range ${formatPortRange(port, attempts)}.`); } throw err; } } if (boundPort === undefined) { throw new Error(`Failed to start proxy on port range ${formatPortRange(port, attempts)}.`); } if (boundPort !== port) { logger5.info(`Preferred port ${port} unavailable, using ${boundPort}`); } logger5.info(`Proxy ready at http://localhost:${boundPort}`); logger5.info("Waiting for Claude Code..."); ensureSearXNG().then((ok) => { if (ok) logger5.info("WebSearch interception enabled (SearXNG)"); else logger5.warn("WebSearch interception disabled (SearXNG unavailable)"); }); return boundPort; } // src/auth/server.ts import http from "http"; function startLocalOAuthServer(state) { const server = http.createServer((req, res) => { try { const url = new URL(req.url || "", "http://localhost"); if (url.pathname !== "/auth/callback") { res.statusCode = 404; res.end("Not found"); return; } const receivedState = url.searchParams.get("state"); if (receivedState !== state) { res.statusCode = 400; res.end("State mismatch"); return; } const code = url.searchParams.get("code"); if (!code) { res.statusCode = 400; res.end("Missing authorization code"); return; } res.statusCode = 200; res.setHeader("Content-Type", "text/html; charset=utf-8"); res.end(successHtml); server._lastCode = code; } catch { res.statusCode = 500; res.end("Internal error"); } }); return new Promise((resolve) => { server.on("error", (err) => { console.error(`[oauth] Failed to bind port 1455 (${err?.code}). Falling back to manual.`); resolve({ port: 1455, ready: false, close: () => { try { server.close(); } catch {} }, waitForCode: async () => null }); }).listen(1455, "127.0.0.1", () => { resolve({ port: 1455, ready: true, close: () => server.close(), waitForCode: async () => { for (let i = 0;i < 600; i++) { const lastCode = server._lastCode; if (lastCode) return { code: lastCode }; await new Promise((r2) => setTimeout(r2, 100)); } return null; } }); }); }); } var successHtml = ` Authorization Successful
\u2713

Authorized!

You can close this window and return to the terminal.

`; // src/auth/qwen/pkce.ts import { createHash, randomBytes as randomBytes2 } from "crypto"; function generatePKCE2() { const codeVerifier = randomBytes2(32).toString("base64url"); const codeChallenge = createHash("sha256").update(codeVerifier).digest("base64url"); return { codeVerifier, codeChallenge }; } function parseIdTokenEmail(idToken) { try { const parts = idToken.split("."); const part = parts[1]; if (!part) return null; const payload = JSON.parse(Buffer.from(part, "base64url").toString("utf-8")); return typeof payload.email === "string" ? payload.email : null; } catch { return null; } } // src/auth/qwen/device-flow.ts var POLL_MAX_ATTEMPTS = 60; async function requestDeviceCode(codeChallenge) { const resp = await fetch(QWEN_DEVICE_CODE_URL, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: new URLSearchParams({ client_id: QWEN_CLIENT_ID, scope: QWEN_SCOPE, code_challenge: codeChallenge, code_challenge_method: "S256" }) }); if (!resp.ok) { throw new Error(`Device code request failed (${resp.status}): ${await resp.text()}`); } return await resp.json(); } async function pollForToken(deviceCode, codeVerifier, intervalSecs, expiresIn, onTick) { const body = new URLSearchParams({ grant_type: "urn:ietf:params:oauth:grant-type:device_code", device_code: deviceCode, client_id: QWEN_CLIENT_ID, code_verifier: codeVerifier }); const deadline = Date.now() + expiresIn * 1000; let interval = intervalSecs; for (let i = 0;i < POLL_MAX_ATTEMPTS; i++) { onTick?.(Math.max(0, Math.ceil((deadline - Date.now()) / 1000))); await Bun.sleep(interval * 1000); if (Date.now() >= deadline) { throw new Error("Device code expired. Please try again."); } const resp = await fetch(QWEN_TOKEN_URL, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", Accept: "application/json" }, body }); if (resp.ok) { return await resp.json(); } const data = await resp.json().catch(() => ({})); const error = data.error; if (error === "authorization_pending") continue; if (error === "slow_down") { interval = Math.min(interval + 5, 30); continue; } if (error === "expired_token") { throw new Error("Device code expired. Please try again."); } if (error === "access_denied") { throw new Error("Authorization denied by user."); } throw new Error(`Token poll failed (${resp.status}): ${data.error_description ?? error ?? "unknown"}`); } throw new Error("Timed out waiting for authorization."); } async function qwenLogin(callbacks = {}) { const { codeVerifier, codeChallenge } = generatePKCE2(); const device = await requestDeviceCode(codeChallenge); await callbacks.onDeviceCode?.(device); const tokens = await pollForToken(device.device_code, codeVerifier, device.interval ?? 5, device.expires_in ?? 300, callbacks.onPollTick); const email = tokens.id_token ? parseIdTokenEmail(tokens.id_token) : null; const expiresAt = new Date(Date.now() + tokens.expires_in * 1000).toISOString(); return addAccount({ email, display_name: email, access_token: tokens.access_token, refresh_token: tokens.refresh_token, expires_at: expiresAt, resource_url: tokens.resource_url ?? null }); } // src/auth/zai/browser-login.ts import { mkdir, stat } from "fs/promises"; import { spawn } from "child_process"; import { join as join6 } from "path"; var DEBUG_HOST = "127.0.0.1"; var DEBUG_PORT = 9222; var STARTUP_TIMEOUT_MS = 20000; var LOGIN_TIMEOUT_MS = 5 * 60 * 1000; var POLL_INTERVAL_MS = 1500; var IS_WSL = process.platform === "linux" && (process.env.WSL_DISTRO_NAME !== undefined || process.env.WSL_INTEROP !== undefined); function getWindowsBrowserCandidates() { const programFiles = process.env["ProgramFiles"] ?? "C:\\Program Files"; const programFilesX86 = process.env["ProgramFiles(x86)"] ?? "C:\\Program Files (x86)"; const localAppData = process.env["LOCALAPPDATA"] ?? join6(process.env["USERPROFILE"] ?? "C:\\Users\\Default", "AppData", "Local"); return [ join6(programFiles, "Google", "Chrome", "Application", "chrome.exe"), join6(programFilesX86, "Google", "Chrome", "Application", "chrome.exe"), join6(localAppData, "Google", "Chrome", "Application", "chrome.exe"), join6(programFiles, "Microsoft", "Edge", "Application", "msedge.exe"), join6(programFilesX86, "Microsoft", "Edge", "Application", "msedge.exe"), join6(programFiles, "BraveSoftware", "Brave-Browser", "Application", "brave.exe"), join6(programFilesX86, "BraveSoftware", "Brave-Browser", "Application", "brave.exe"), join6(localAppData, "BraveSoftware", "Brave-Browser", "Application", "brave.exe") ]; } var WSL_WINDOWS_BROWSER_CANDIDATES = [ "/mnt/c/Program Files/Google/Chrome/Application/chrome.exe", "/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe", "/mnt/c/Program Files/Microsoft/Edge/Application/msedge.exe", "/mnt/c/Program Files (x86)/Microsoft/Edge/Application/msedge.exe", "/mnt/c/Program Files/BraveSoftware/Brave-Browser/Application/brave.exe", "/mnt/c/Program Files (x86)/BraveSoftware/Brave-Browser/Application/brave.exe" ]; var MACOS_BROWSER_CANDIDATES = [ "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", "/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary", "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge", "/Applications/Brave Browser.app/Contents/MacOS/Brave Browser", "/Applications/Arc.app/Contents/MacOS/Arc", "/Applications/Chromium.app/Contents/MacOS/Chromium" ]; var LINUX_BROWSER_COMMANDS = [ "google-chrome", "google-chrome-stable", "chromium", "chromium-browser", "microsoft-edge", "microsoft-edge-stable", "brave-browser" ]; async function getZaiTokenViaBrowser(validateToken, onStatus) { const browserPath = await resolveBrowserPath(); if (!browserPath) { throw new Error("Nenhum navegador compat\xEDvel encontrado. Instale Chrome/Edge/Chromium ou defina a vari\xE1vel BROWSER."); } const profileDir = getBrowserProfileDir(browserPath); await mkdir(profileDir, { recursive: true }); const existingToken = await tryGetExistingToken(validateToken); if (existingToken) { return { ...existingToken, browserPath, reusedSession: true }; } onStatus?.("Abrindo navegador para login no Z.ai..."); const child = launchBrowser(browserPath, profileDir); try { await waitForDevtools(onStatus); const token = await waitForToken(onStatus); const userId = await validateToken(token); if (!userId) { throw new Error("Token capturado, mas inv\xE1lido na valida\xE7\xE3o do Z.ai."); } return { token, userId, browserPath, reusedSession: false }; } finally { try { child.kill(); } catch {} } } async function tryGetExistingToken(validateToken) { try { const resp = await fetch(`http://${DEBUG_HOST}:${DEBUG_PORT}/json/version`); if (!resp.ok) return null; const token = await fetchTokenFromTargets(); if (!token) return null; const userId = await validateToken(token); if (!userId) return null; return { token, userId }; } catch { return null; } } function launchBrowser(browserPath, profileDir) { const args = [ `--remote-debugging-port=${DEBUG_PORT}`, `--user-data-dir=${profileDir}`, "--new-window", "https://chat.z.ai" ]; return spawn(browserPath, args, { detached: true, stdio: "ignore" }); } function getBrowserProfileDir(browserPath) { if (IS_WSL && browserPath.toLowerCase().endsWith(".exe")) { return "/mnt/c/Users/Public/opencode-go-cli/zai-browser-profile"; } return join6(CONFIG_DIR, "zai-browser-profile"); } async function resolveBrowserPath() { const envBrowser = process.env.BROWSER?.trim(); if (envBrowser) { const resolved = await resolveExecutable(envBrowser); if (resolved) return resolved; } if (process.platform === "win32") { for (const candidate of getWindowsBrowserCandidates()) { if (await fileExists(candidate)) return candidate; } return null; } if (process.platform === "darwin") { for (const candidate of MACOS_BROWSER_CANDIDATES) { if (await fileExists(candidate)) return candidate; } return null; } for (const cmd of LINUX_BROWSER_COMMANDS) { const resolved = await which(cmd); if (resolved) return resolved; } if (IS_WSL) { for (const candidate of WSL_WINDOWS_BROWSER_CANDIDATES) { if (await fileExists(candidate)) return candidate; } } return null; } async function resolveExecutable(input) { if (input.includes("/") || input.includes("\\")) { return await fileExists(input) ? input : null; } return which(input); } async function fileExists(path) { try { const info = await stat(path); return info.isFile(); } catch { return false; } } async function which(command) { if (process.platform === "win32") { return whichWindows(command); } return whichUnix(command); } async function whichWindows(command) { const proc = spawn("where.exe", [command], { stdio: ["ignore", "pipe", "ignore"], windowsHide: true }); let output = ""; for await (const chunk of proc.stdout) { output += chunk.toString(); } const code = await new Promise((resolve) => { proc.on("error", () => resolve(1)); proc.on("close", (value) => resolve(value ?? 1)); }); if (code !== 0) return null; const first = output.split(/\r?\n/).find((line) => line.trim().length > 0); return first ? first.trim() : null; } async function whichUnix(command) { const proc = spawn("/bin/sh", ["-c", `command -v ${shellQuote(command)} 2>/dev/null || true`], { stdio: ["ignore", "pipe", "ignore"] }); let output = ""; for await (const chunk of proc.stdout) { output += chunk.toString(); } const code = await new Promise((resolve) => { proc.on("error", () => resolve(1)); proc.on("close", (value) => resolve(value ?? 1)); }); if (code !== 0) return null; const result = output.trim(); return result || null; } async function waitForDevtools(onStatus) { const start = Date.now(); while (Date.now() - start < STARTUP_TIMEOUT_MS) { try { const resp = await fetch(`http://${DEBUG_HOST}:${DEBUG_PORT}/json/version`); if (resp.ok) return; } catch {} onStatus?.("Esperando o navegador iniciar..."); await sleep(500); } throw new Error("N\xE3o consegui conectar ao navegador automatizado. Veja se o Edge/Chrome abriu direito."); } async function waitForToken(onStatus) { const start = Date.now(); while (Date.now() - start < LOGIN_TIMEOUT_MS) { const token = await fetchTokenFromTargets(); if (token) return token; onStatus?.("Fa\xE7a login com Google na janela aberta... estou esperando o token aparecer."); await sleep(POLL_INTERVAL_MS); } throw new Error("Tempo esgotado esperando login no Z.ai."); } async function fetchTokenFromTargets() { const targets = await listTargets(); const pageTargets = targets.filter((target) => target.type === "page" && target.webSocketDebuggerUrl && (target.url.startsWith("https://chat.z.ai") || target.url === "about:blank")); for (const target of pageTargets) { const token = await evaluateOnTarget(target.webSocketDebuggerUrl, `(() => { try { return window.localStorage.getItem("token"); } catch { return null; } })()`); if (typeof token === "string" && token.length > 50) { return token; } } return null; } async function listTargets() { const resp = await fetch(`http://${DEBUG_HOST}:${DEBUG_PORT}/json/list`); if (!resp.ok) throw new Error(`DevTools list falhou: ${resp.status}`); return await resp.json(); } async function evaluateOnTarget(webSocketUrl, expression) { const ws = new WebSocket(webSocketUrl); await new Promise((resolve, reject) => { ws.addEventListener("open", () => resolve(), { once: true }); ws.addEventListener("error", () => reject(new Error("Falha ao conectar no DevTools WebSocket")), { once: true }); }); try { const result = await new Promise((resolve, reject) => { const id = 1; const timeout = setTimeout(() => reject(new Error("Timeout avaliando express\xE3o no navegador")), 5000); ws.addEventListener("message", (event) => { try { const msg = JSON.parse(String(event.data)); if (msg.id !== id) return; clearTimeout(timeout); if (msg.error) { reject(new Error(msg.error.message ?? "Erro desconhecido no Runtime.evaluate")); return; } resolve(msg.result?.result?.value ?? null); } catch (err) { clearTimeout(timeout); reject(err); } }); ws.send(JSON.stringify({ id, method: "Runtime.evaluate", params: { expression, returnByValue: true, awaitPromise: true } })); }); return result; } finally { try { ws.close(); } catch {} } } function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } function shellQuote(value) { return `'${value.replace(/'/g, `'\\''`)}'`; } // src/statusline/install.ts import { chmodSync, copyFileSync, existsSync as existsSync7, mkdirSync as mkdirSync8, readFileSync as readFileSync5, writeFileSync as writeFileSync6 } from "fs"; import { homedir as homedir4 } from "os"; import { dirname as dirname4, join as join7 } from "path"; import { fileURLToPath } from "url"; function normalizePath(path) { return path.replace(/\\/g, "/"); } function quotePath(path) { return `"${normalizePath(path).replace(/"/g, "\\\"")}"`; } function getClaudeSettingsFile() { return process.env["OPENCODE_CLAUDE_SETTINGS_FILE_OVERRIDE"] ?? join7(homedir4(), ".claude", "settings.json"); } function getInstalledStatuslineScriptFile(configDir = CONFIG_DIR) { return join7(configDir, "statusline.js"); } function buildStatuslineCommand(scriptPath, runtimeCommand = "bun") { return `${runtimeCommand} ${quotePath(scriptPath)}`; } function buildStatuslineConfig(command) { return { type: "command", command, padding: 1, refreshInterval: 5 }; } function buildStatuslineSnippet(scriptPath = getInstalledStatuslineScriptFile(), runtimeCommand = "bun") { return JSON.stringify({ statusLine: buildStatuslineConfig(buildStatuslineCommand(scriptPath, runtimeCommand)) }, null, 2); } function findBundledStatuslineScript() { const here = dirname4(fileURLToPath(import.meta.url)); const candidates = [ join7(here, "statusline.js"), join7(process.cwd(), "dist", "statusline.js"), join7(here, "..", "..", "dist", "statusline.js") ]; return candidates.find((candidate) => existsSync7(candidate)) ?? null; } function readSettings(settingsPath) { if (!existsSync7(settingsPath)) return {}; try { return JSON.parse(readFileSync5(settingsPath, "utf-8")); } catch { throw new Error(`Invalid Claude Code settings JSON: ${settingsPath}`); } } function writeSettings(settingsPath, settings) { mkdirSync8(dirname4(settingsPath), { recursive: true }); writeFileSync6(settingsPath, `${JSON.stringify(settings, null, 2)} `); } function copyScript(source, target) { mkdirSync8(dirname4(target), { recursive: true }); copyFileSync(source, target); try { chmodSync(target, 493); } catch {} } function isOwnedStatusline(statusLine, scriptPath) { const command = typeof statusLine?.command === "string" ? statusLine.command : ""; const normalizedCommand = normalizePath(command); const normalizedScript = normalizePath(scriptPath); return normalizedCommand.includes(normalizedScript) || normalizedCommand.includes(".opencode-go-cli/statusline.js"); } function installStatusline(options = {}) { const configDir = options.configDir ?? CONFIG_DIR; const scriptPath = getInstalledStatuslineScriptFile(configDir); const settingsPath = options.claudeSettingsFile ?? getClaudeSettingsFile(); const command = buildStatuslineCommand(scriptPath, options.runtimeCommand); const manualSnippet = buildStatuslineSnippet(scriptPath, options.runtimeCommand); const settings = readSettings(settingsPath); const existing = settings.statusLine; const source = options.sourceScriptFile ?? findBundledStatuslineScript(); if (!source) { throw new Error("Bundled statusline script not found. Run `bun run build` first."); } if (existing && !options.force && !isOwnedStatusline(existing, scriptPath)) { copyScript(source, scriptPath); return { status: "conflict", scriptPath, settingsPath, manualSnippet }; } copyScript(source, scriptPath); const nextStatusLine = buildStatuslineConfig(command); const alreadyInstalled = JSON.stringify(existing) === JSON.stringify(nextStatusLine); settings.statusLine = nextStatusLine; writeSettings(settingsPath, settings); return { status: alreadyInstalled ? "already-installed" : "installed", scriptPath, settingsPath, manualSnippet }; } // src/statusline/debug.ts import { existsSync as existsSync8, mkdirSync as mkdirSync9, readFileSync as readFileSync6, rmSync as rmSync2, unlinkSync as unlinkSync4, writeFileSync as writeFileSync7 } from "fs"; import { dirname as dirname5, join as join8 } from "path"; function getStatuslineDebugFlagFile() { return process.env["OPENCODE_STATUSLINE_DEBUG_FLAG_FILE_OVERRIDE"] ?? join8(CONFIG_DIR, "statusline-debug.enabled"); } function getStatuslineDebugLatestFile() { return process.env["OPENCODE_STATUSLINE_DEBUG_LATEST_FILE_OVERRIDE"] ?? join8(CONFIG_DIR, "statusline-debug-latest.json"); } function getStatuslineDebugLogFile() { return process.env["OPENCODE_STATUSLINE_DEBUG_LOG_FILE_OVERRIDE"] ?? join8(CONFIG_DIR, "statusline-debug.jsonl"); } function isStatuslineDebugEnabled() { const env = process.env["OPENCODE_STATUSLINE_DEBUG"]; if (env === "1" || env === "true" || env === "yes") return true; return existsSync8(getStatuslineDebugFlagFile()); } function enableStatuslineDebug() { const flagFile = getStatuslineDebugFlagFile(); mkdirSync9(dirname5(flagFile), { recursive: true }); writeFileSync7(flagFile, new Date().toISOString()); } function disableStatuslineDebug() { try { unlinkSync4(getStatuslineDebugFlagFile()); } catch {} } function clearStatuslineDebugFiles() { for (const file of [getStatuslineDebugLatestFile(), getStatuslineDebugLogFile()]) { try { rmSync2(file, { force: true }); } catch {} } } function readLatestStatuslineDebugCapture() { try { return JSON.parse(readFileSync6(getStatuslineDebugLatestFile(), "utf-8")); } catch { return null; } } // src/cli.ts async function getPackageVersion() { const packageJson = await Bun.file(new URL("../package.json", import.meta.url)).json(); return packageJson.version ?? "0.0.0"; } function printStatuslineDebugCapture() { const capture = readLatestStatuslineDebugCapture(); if (!capture) { console.log("No statusline debug capture found yet."); console.log(`Latest file: ${getStatuslineDebugLatestFile()}`); return; } console.log(`Statusline debug: ${isStatuslineDebugEnabled() ? "enabled" : "disabled"}`); console.log(`Captured at: ${capture.capturedAt}`); console.log(`Parse OK: ${capture.parseOk}`); console.log(`Top-level keys: ${capture.topLevelKeys.join(", ") || "(none)"}`); console.log(`Has context_window: ${capture.contextWindow ? "yes" : "no"}`); console.log(`Has rate_limits: ${capture.rateLimits ? "yes" : "no"}`); if (capture.state?.lastUsage) { console.log(`Local usage fallback: ${capture.state.lastUsage.contextTokens} context tokens`); } else { console.log("Local usage fallback: none"); } if (capture.state?.rateLimits) { const fiveHour = capture.state.rateLimits.five_hour?.used_percentage; const sevenDay = capture.state.rateLimits.seven_day?.used_percentage; console.log(`Local Codex usage: 5h ${fiveHour ?? "?"}% | 7d ${sevenDay ?? "?"}%`); } else { console.log("Local Codex usage: none"); } console.log(`Latest file: ${getStatuslineDebugLatestFile()}`); console.log(""); console.log(JSON.stringify(capture.input, null, 2)); } async function setupApiKey() { const apiKey = await te({ message: "Enter your OpenCode Go API key:", placeholder: "sk-opencode-...", validate: (value) => { if (!value || value.length < 10) return "Please enter a valid API key"; } }); if (lD(apiKey)) { ue("Cancelled"); process.exit(0); } const config = getConfig(); config.apiKey = apiKey; saveConfig(config); f2.success("API key saved!"); return apiKey; } async function setupOpenAIOAuth() { const spinner = de(); spinner.start("Starting authorization flow..."); const flow = await createAuthorizationFlow(); const server = await startLocalOAuthServer(flow.state); spinner.stop("Authorization server ready"); try { await open(flow.url); } catch {} if (!server.ready) { server.close(); f2.error("OAuth server failed to start. Port 1455 may be in use."); f2.info(`Visit this URL manually: ${flow.url}`); return false; } f2.info("Waiting for authorization..."); f2.info(`Or visit: ${flow.url}`); const result = await server.waitForCode(); server.close(); if (!result) { f2.error("Authorization timeout. Please try again."); return false; } const tokens = await exchangeAuthorizationCode(result.code, flow.pkce.verifier); if (tokens.type === "success") { const config = getConfig(); config.openaiTokens = { access: tokens.access, refresh: tokens.refresh, expiresAt: tokens.expires, accountId: tokens.accountId, planType: tokens.planType }; saveConfig(config); f2.success("OpenAI authenticated!"); return true; } f2.error("Authorization failed. Please try again."); return false; } async function setupQwenOAuth() { const spinner = de(); spinner.start("Requesting device code from Qwen..."); try { const account = await qwenLogin({ onDeviceCode: async (device) => { spinner.stop("Device code received"); f2.info(`Open in your browser: ${device.verification_uri_complete ?? device.verification_uri}`); f2.info(`User code: ${device.user_code}`); try { await open(device.verification_uri_complete ?? device.verification_uri); } catch {} spinner.start("Waiting for authorization\u2026"); }, onPollTick: (secsLeft) => { const m2 = Math.floor(secsLeft / 60); const s = secsLeft % 60; spinner.message(`Waiting for authorization\u2026 ${m2 > 0 ? `${m2}m ${s}s` : `${s}s`} remaining`); } }); spinner.stop(`Qwen account saved: ${account.email ?? account.id.slice(0, 8)} (priority ${account.priority})`); return true; } catch (err) { spinner.stop("Qwen authorization failed"); f2.error(err instanceof Error ? err.message : String(err)); return false; } } function formatExpiry(iso) { const diff = new Date(iso).getTime() - Date.now(); if (diff < 0) return "expired"; if (diff < 60000) return `${Math.ceil(diff / 1000)}s`; if (diff < 3600000) return `${Math.ceil(diff / 60000)}m`; if (diff < 86400000) return `${Math.ceil(diff / 3600000)}h`; return `${Math.ceil(diff / 86400000)}d`; } async function setupZaiToken() { const spinner = de(); spinner.start("Preparando login automatizado do Z.ai..."); try { const result = await getZaiTokenViaBrowser(validateZaiToken, (message) => { spinner.message(message); }); const config = getConfig(); config.zaiToken = result.token; saveConfig(config); spinner.stop(result.reusedSession ? `Z.ai j\xE1 estava logado! Token validado e salvo (${result.userId.slice(0, 8)}...)` : `Login do Z.ai conclu\xEDdo! Token capturado e salvo (${result.userId.slice(0, 8)}...)`); f2.info(`Navegador usado: ${result.browserPath}`); return true; } catch (err) { spinner.stop("Falha no login automatizado do Z.ai"); f2.error(err instanceof Error ? err.message : String(err)); f2.info("Se der ruim com o navegador autom\xE1tico, a\xED sim a gente parte pro plano B manual."); return false; } } function printQwenAccountTable(accounts) { if (accounts.length === 0) { console.log("\nNo Qwen accounts found. Run `opencode-go --qwen-login` to add one.\n"); return; } console.log(""); console.log(` ${"#".padEnd(3)} ${"ID".padEnd(10)} ${"Email".padEnd(32)} ${"Status".padEnd(13)} ${"Pri".padEnd(4)} Expires`); console.log(" " + "\u2500".repeat(78)); accounts.forEach((acc, i) => { const id = acc.id.slice(0, 8); const email = (acc.email ?? acc.display_name ?? "(no email)").slice(0, 30).padEnd(32); const status = !acc.is_active ? "disabled".padEnd(13) : acc.test_status.padEnd(13); console.log(` ${String(i + 1).padEnd(3)} ${id.padEnd(10)} ${email} ${status} ${String(acc.priority).padEnd(4)} ${formatExpiry(acc.expires_at)}`); for (const lock of getActiveModelLocks(acc.id)) { const m2 = lock.model === "__all" ? "ALL models" : lock.model; console.log(` \u26A0 locked: ${m2} until ${formatExpiry(lock.locked_until)}`); } if (acc.last_error) { console.log(` \u2717 last error (${acc.error_code}): ${acc.last_error.slice(0, 60)}`); } }); console.log(""); } async function testQwenAccount(acc) { const label = acc.email ?? acc.id.slice(0, 8); const spinner = de(); spinner.start(`Testing ${label}\u2026`); try { const refreshed = await checkAndRefreshAccount(acc); const start = Date.now(); const resp = await fetch(buildQwenChatCompletionsUrl(refreshed.resource_url), { method: "POST", headers: buildQwenHeaders(refreshed.access_token, false), body: JSON.stringify({ model: "qwen3-coder-flash", messages: [{ role: "user", content: "Hi" }], max_tokens: 5, stream: false }), signal: AbortSignal.timeout(15000) }); const latency = Date.now() - start; if (resp.ok) { updateAccount(acc.id, { test_status: "active", last_error: null, error_code: null }); spinner.stop(`${label} OK (${latency}ms)`); } else { const errText = await resp.text(); updateAccount(acc.id, { test_status: "unavailable", last_error: errText.slice(0, 300), error_code: resp.status, last_error_at: new Date().toISOString() }); spinner.stop(`${label} FAIL ${resp.status}: ${errText.slice(0, 60)}`); } } catch (err) { const msg = err instanceof Error ? err.message : String(err); updateAccount(acc.id, { test_status: "unavailable", last_error: msg.slice(0, 300), error_code: 0, last_error_at: new Date().toISOString() }); spinner.stop(`${label} ERROR: ${msg.slice(0, 60)}`); } } function getProviderStatus() { const config = getConfig(); const opencode = config.apiKey ? `\u2713 ${config.apiKey.slice(0, 12)}...` : "not configured"; const openai = config.openaiTokens ? "\u2713 logged in" : "not logged in"; const qwenCount = countAccounts(); const qwen = qwenCount === 0 ? "no accounts" : `${qwenCount} account${qwenCount === 1 ? "" : "s"}`; const zai = config.zaiToken ? "\u2713 token set" : "not configured"; return { opencode, openai, qwen, zai }; } async function interactiveMain() { const status = getProviderStatus(); oe("OpenCode Go CLI"); const action = await ie({ message: "What do you want to do?", options: [ { value: "start", label: "Start Claude Code", hint: "launch with a model" }, { value: "settings", label: "Settings", hint: "providers, keys, login" } ] }); if (lD(action)) { ue("Bye!"); process.exit(0); } if (action === "settings") { await settingsMenu(); return; } await startFlow(); } async function settingsMenu() { const status = getProviderStatus(); const config = getConfig(); const setting = await ie({ message: "Settings:", options: [ { value: "opencode-key", label: `OpenCode Go \u2014 API key`, hint: status.opencode }, { value: "openai-login", label: `OpenAI \u2014 Login with OAuth`, hint: status.openai }, ...config.openaiTokens ? [{ value: "openai-logout", label: "OpenAI \u2014 Logout", hint: "remove saved tokens" }] : [], { value: "qwen-login", label: "Qwen \u2014 Add account (device flow)", hint: status.qwen }, { value: "zai-login", label: "Z.ai \u2014 Login no navegador", hint: status.zai }, ...countAccounts() > 0 ? [ { value: "qwen-list", label: "Qwen \u2014 List accounts", hint: "status, locks, last error" }, { value: "qwen-test", label: "Qwen \u2014 Test all accounts", hint: "validate tokens" } ] : [], { value: "reset", label: "Reset all", hint: "delete all configuration" }, { value: "back", label: "\u2190 Back" } ] }); if (lD(setting)) { ue("Bye!"); process.exit(0); } if (setting === "opencode-key") { await setupApiKey(); f2.info("Run opencode-go again to start Claude Code."); process.exit(0); } if (setting === "openai-login") { await setupOpenAIOAuth(); f2.info("Run opencode-go again to start Claude Code."); process.exit(0); } if (setting === "openai-logout") { const config2 = getConfig(); delete config2.openaiTokens; if (config2.provider === "openai") config2.provider = "opencode"; saveConfig(config2); f2.success("OpenAI tokens removed."); process.exit(0); } if (setting === "qwen-login") { await setupQwenOAuth(); f2.info("Run opencode-go again to start Claude Code."); process.exit(0); } if (setting === "zai-login") { await setupZaiToken(); f2.info("Run opencode-go again to start Claude Code."); process.exit(0); } if (setting === "qwen-list") { printQwenAccountTable(listAccounts()); process.exit(0); } if (setting === "qwen-test") { for (const acc of listAccounts()) { await testQwenAccount(acc); } process.exit(0); } if (setting === "reset") { const confirm = await se({ message: "Delete all configuration? This cannot be undone.", initialValue: false }); if (lD(confirm) || !confirm) { f2.info("Cancelled."); process.exit(0); } resetAll(); f2.success("All configuration deleted (config, Qwen DB, Z.ai profile)."); process.exit(0); } await interactiveMain(); } async function selectProvider() { const status = getProviderStatus(); const provider = await ie({ message: "Select provider:", options: [ { value: "opencode", label: "OpenCode Go", hint: status.opencode }, { value: "openai", label: "OpenAI (ChatGPT Plus/Pro)", hint: status.openai }, { value: "qwen", label: "Qwen (OAuth device flow)", hint: status.qwen }, { value: "zai", label: "Z.ai (free GLM models)", hint: status.zai } ] }); if (lD(provider)) { ue("Bye!"); process.exit(0); } return provider; } async function resolveModelsForProvider(provider, options = {}) { if (provider === "openai") { const config = getConfig(); const spinner2 = de(); spinner2.start(options.refresh ? "Refreshing OpenAI models..." : "Loading OpenAI models..."); const result2 = await getOpenAIModels({ accessToken: config.openaiTokens?.access, accountId: config.openaiTokens?.accountId, refresh: options.refresh }); if (result2.source === "network") { spinner2.stop(`Loaded ${result2.models.length} models from ChatGPT`); } else if (result2.source === "cache") { spinner2.stop(`Loaded ${result2.models.length} cached OpenAI models`); } else { spinner2.stop(`Using built-in OpenAI fallback (${result2.models.length} models)`); } return result2.models; } if (provider === "qwen") return QWEN_MODELS; if (provider === "zai") return ZAI_MODELS; const spinner = de(); spinner.start(options.refresh ? "Refreshing OpenCode models..." : "Loading OpenCode models..."); const result = await getOpenCodeModels({ refresh: options.refresh }); if (result.source === "network") { spinner.stop(`Loaded ${result.models.length} models from opencode.ai`); } else if (result.source === "cache") { spinner.stop(`Loaded ${result.models.length} cached models`); } else { spinner.stop(`Using built-in fallback (${result.models.length} models)`); } return result.models; } async function selectModel(provider) { const config = getConfig(); const models = await resolveModelsForProvider(provider); const model = await ie({ message: "Select model:", options: models.map((m2) => ({ value: m2.id, label: m2.name, hint: m2.description })), initialValue: config.lastModel }); if (lD(model)) { ue("Bye!"); process.exit(0); } return model; } async function selectPermissionMode() { const mode = await ie({ message: "Permission mode:", options: [ { value: "default", label: "Default", hint: "asks permission for everything" }, { value: "acceptEdits", label: "Accept edits", hint: "auto-approve file edits, ask for commands" }, { value: "auto", label: "Auto mode", hint: "classifier reviews actions (experimental)" }, { value: "bypassPermissions", label: "Bypass permissions", hint: "skip all checks \u2014 use with caution" } ] }); if (lD(mode)) { ue("Bye!"); process.exit(0); } return mode; } function buildPermissionArgs(mode) { switch (mode) { case "default": return []; case "acceptEdits": return ["--permission-mode", "acceptEdits"]; case "auto": return ["--permission-mode", "auto", "--enable-auto-mode"]; case "bypassPermissions": return ["--dangerously-skip-permissions"]; } } async function ensureProviderAuth(provider) { const config = getConfig(); if (provider === "openai") { if (config.openaiTokens) return true; f2.warn("Not logged in to OpenAI."); const shouldAuth = await se({ message: "Login with OpenAI now?", initialValue: true }); if (lD(shouldAuth) || !shouldAuth) return false; return await setupOpenAIOAuth(); } if (provider === "qwen") { if (countAccounts() > 0) return true; f2.warn("No Qwen accounts configured."); const shouldAuth = await se({ message: "Login with Qwen now?", initialValue: true }); if (lD(shouldAuth) || !shouldAuth) return false; return await setupQwenOAuth(); } if (provider === "zai") { if (config.zaiToken) return true; f2.warn("No Z.ai token configured."); const shouldAuth = await se({ message: "Fazer login no Z.ai agora?", initialValue: true }); if (lD(shouldAuth) || !shouldAuth) return false; return await setupZaiToken(); } if (config.apiKey) return true; f2.warn("No OpenCode Go API key configured."); const shouldSetup = await se({ message: "Set up API key now?", initialValue: true }); if (lD(shouldSetup) || !shouldSetup) return false; await setupApiKey(); return true; } async function startFlow(providerOverride, modelOverride, permissionOverride, portOverride, claudePassthroughArgs = []) { const provider = providerOverride ?? await selectProvider(); if (!await ensureProviderAuth(provider)) { ue("Authentication required."); process.exit(1); } const model = modelOverride ?? await selectModel(provider); const permMode = permissionOverride ?? await selectPermissionMode(); const config = getConfig(); config.provider = provider; config.lastModel = model; const preferredPort = portOverride ?? config.proxyPort ?? DEFAULT_PROXY_PORT; saveConfig(config); const freshConfig = getConfig(); const authToken = provider === "openai" ? freshConfig.openaiTokens.access : provider === "qwen" ? "qwen-rotated" : provider === "zai" ? "zai-token" : freshConfig.apiKey; const proxyPort = await startProxy(preferredPort, provider, PROXY_PORT_FALLBACK_ATTEMPTS); const proxyUrl = `http://localhost:${proxyPort}`; try { writeStatuslineState(buildStatuslineState({ provider, model, permissionMode: permMode, proxyUrl, cliVersion: await getPackageVersion() })); } catch (err) { f2.warn(`Statusline state was not written: ${err instanceof Error ? err.message : String(err)}`); } if (provider === "openai") { const usageConfig = getConfig(); refreshStatuslineCodexUsage({ accessToken: usageConfig.openaiTokens.access, accountId: usageConfig.openaiTokens.accountId }); } silenceLogger(); const permArgs = buildPermissionArgs(permMode); await runClaudeCode(model, proxyUrl, authToken, permArgs, claudePassthroughArgs); process.exit(0); } async function runClaudeCode(model, baseUrl, authToken, extraArgs, claudePassthroughArgs) { const config = getConfig(); const provider = config.provider || "opencode"; if (provider === "openai") { f2.success(`Provider: OpenAI`); f2.success(`Model: ${model}`); } else if (provider === "qwen") { f2.success(`Provider: Qwen (rotating across ${countAccounts()} account(s))`); f2.success(`Model: ${model}`); } else if (provider === "zai") { f2.success(`Provider: Z.ai (free GLM models)`); f2.success(`Model: ${model}`); } else { f2.success(`Provider: OpenCode Go`); f2.success(`Model: ${model}`); } if (extraArgs.length > 0) { f2.info(`Flags: ${extraArgs.join(" ")}`); } if (claudePassthroughArgs.length > 0) { f2.info(`Claude passthrough: ${claudePassthroughArgs.join(" ")}`); } const claudePath = resolveClaudePath(); const env = buildClaudeEnv(authToken, model, baseUrl); const spawnArgs = ["--model", model, ...extraArgs, ...claudePassthroughArgs]; const spinner = de(); spinner.start(`Starting Claude Code with ${model}...`); spinner.stop(`Launching Claude Code with ${model}`); return new Promise((resolve) => { const child = spawn2(claudePath, spawnArgs, { stdio: "inherit", env }); child.on("error", (err) => { f2.error(`Failed to start Claude Code: ${err.message}`); resolve(1); }); child.on("close", (code) => { resolve(code ?? 0); }); }); } function printHelp() { console.log(` OpenCode Go CLI \u2014 Use OpenCode Go or OpenAI models with Claude Code Usage: opencode-go [options] [-- ] Interactive (no args): opencode-go Select provider, model, and permission mode Options: --provider Provider: opencode (default), openai, qwen, or zai --model Model ID (skip model selection) --permission-mode default | acceptEdits | auto | bypassPermissions --setup Configure OpenCode Go API key --oauth-login Authenticate with OpenAI (ChatGPT Plus/Pro) --oauth-logout Remove OpenAI tokens --qwen-login Add a Qwen account via OAuth device flow --qwen-list List saved Qwen accounts and their status --qwen-remove Remove a Qwen account --qwen-test Test all Qwen accounts against the API --zai-login Login no Z.ai via navegador automatizado --reset Delete all configuration --list List available models --proxy Start proxy server only (for testing) --install-statusline Install opencode-go statusLine for Claude Code --statusline-snippet Print manual statusLine settings JSON --statusline-debug-on Capture raw Claude Code statusLine JSON locally --statusline-debug-off Stop statusLine debug capture --statusline-debug-show Print the latest captured statusLine JSON --statusline-debug-clear Delete captured statusLine debug files --port Proxy port (interactive mode auto-falls back; --proxy defaults to ${DEFAULT_PROXY_PORT}) --version, -v Show version --help, -h Show this help -- Pass remaining args directly to Claude Code Providers: opencode OpenCode Go models (MiniMax, Kimi, GLM) openai OpenAI models via OAuth (GPT-5.x family) qwen Qwen models via OAuth (qwen3-coder-plus/flash) with multi-account rotation and automatic fallback zai Z.ai free GLM models (glm-4.7, glm-5-turbo, glm-5.1, glm-5) Permission modes: default Ask permission for everything acceptEdits Auto-approve file edits, ask for commands auto Classifier reviews actions (experimental) bypassPermissions Skip all permission checks Examples: opencode-go opencode-go --provider openai --model gpt-5.4 opencode-go --model minimax-m2.7 --permission-mode acceptEdits opencode-go --provider openai --model gpt-5.2-codex --permission-mode auto opencode-go --list --provider openai opencode-go --install-statusline opencode-go --statusline-debug-on `); } async function main() { const rawArgs = process.argv.slice(2); const passthroughIndex = rawArgs.indexOf("--"); const claudePassthroughArgs = passthroughIndex === -1 ? [] : rawArgs.slice(passthroughIndex + 1); const args = passthroughIndex === -1 ? rawArgs : rawArgs.slice(0, passthroughIndex); if (args.includes("--help") || args.includes("-h")) { printHelp(); process.exit(0); } if (args.includes("--version") || args.includes("-v")) { console.log(`opencode-go v${await getPackageVersion()}`); process.exit(0); } if (args.includes("--statusline-snippet")) { console.log(buildStatuslineSnippet()); process.exit(0); } if (args.includes("--statusline-debug-on")) { enableStatuslineDebug(); f2.success("Statusline debug enabled."); f2.info(`Latest JSON: ${getStatuslineDebugLatestFile()}`); f2.info(`History: ${getStatuslineDebugLogFile()}`); f2.info("Run Claude Code and then inspect with `opencode-go --statusline-debug-show`."); process.exit(0); } if (args.includes("--statusline-debug-off")) { disableStatuslineDebug(); f2.success("Statusline debug disabled."); process.exit(0); } if (args.includes("--statusline-debug-clear")) { clearStatuslineDebugFiles(); f2.success("Statusline debug captures cleared."); process.exit(0); } if (args.includes("--statusline-debug-show")) { printStatuslineDebugCapture(); process.exit(0); } if (args.includes("--install-statusline")) { oe("OpenCode Go CLI"); try { const result = installStatusline(); if (result.status === "conflict") { f2.warn("Claude Code already has a custom statusLine. I did not overwrite it."); f2.info(`Script installed at: ${result.scriptPath}`); f2.info("Add this manually if you want to switch to opencode-go:"); console.log(result.manualSnippet); process.exit(1); } f2.success(result.status === "already-installed" ? "Statusline already installed." : "Statusline installed."); f2.info(`Settings: ${result.settingsPath}`); f2.info(`Script: ${result.scriptPath}`); } catch (err) { f2.error(err instanceof Error ? err.message : String(err)); f2.info("Manual Claude Code settings snippet:"); console.log(buildStatuslineSnippet()); process.exit(1); } process.exit(0); } if (args.includes("--setup")) { oe("OpenCode Go CLI"); await setupApiKey(); process.exit(0); } if (args.includes("--oauth-login")) { oe("OpenCode Go CLI"); await setupOpenAIOAuth(); process.exit(0); } if (args.includes("--oauth-logout")) { const config2 = getConfig(); delete config2.openaiTokens; if (config2.provider === "openai") config2.provider = "opencode"; saveConfig(config2); f2.success("OpenAI tokens removed."); process.exit(0); } if (args.includes("--qwen-login")) { oe("OpenCode Go CLI"); const ok = await setupQwenOAuth(); process.exit(ok ? 0 : 1); } if (args.includes("--qwen-list")) { printQwenAccountTable(listAccounts()); process.exit(0); } if (args.includes("--qwen-test")) { const targetIdx = args.indexOf("--qwen-test"); const maybeId = args[targetIdx + 1]; const accounts = maybeId && !maybeId.startsWith("--") ? [getAccountById(maybeId) ?? getAccountByEmail(maybeId)].filter((a3) => a3 !== null) : listAccounts(); if (accounts.length === 0) { console.log(` No Qwen accounts to test. `); process.exit(0); } for (const acc of accounts) { await testQwenAccount(acc); } process.exit(0); } if (args.includes("--qwen-remove")) { const idx = args.indexOf("--qwen-remove"); const idOrEmail = args[idx + 1]; if (!idOrEmail || idOrEmail.startsWith("--")) { f2.error("Usage: opencode-go --qwen-remove "); process.exit(1); } const account = getAccountById(idOrEmail) ?? getAccountByEmail(idOrEmail); if (!account) { f2.error(`Qwen account not found: ${idOrEmail}`); process.exit(1); } const label = account.email ?? account.id.slice(0, 8); const confirm = await se({ message: `Remove Qwen account ${label}?`, initialValue: false }); if (lD(confirm) || !confirm) { f2.info("Cancelled."); process.exit(0); } removeAccount(account.id); f2.success(`Removed: ${label}`); process.exit(0); } if (args.includes("--zai-login")) { oe("OpenCode Go CLI"); const ok = await setupZaiToken(); process.exit(ok ? 0 : 1); } if (args.includes("--reset")) { resetAll(); f2.success("Configuration deleted (config, Qwen DB, Z.ai profile)."); process.exit(0); } if (args.includes("--list")) { const providerIndex2 = args.indexOf("--provider"); const providerName = providerIndex2 !== -1 ? args[providerIndex2 + 1] : "opencode"; const refresh = args.includes("--refresh-models"); const models = await resolveModelsForProvider(providerName, { refresh }); const label = providerName === "openai" ? "OpenAI (ChatGPT/Codex)" : providerName === "qwen" ? "Qwen (OAuth)" : providerName === "zai" ? "Z.ai (free GLM)" : "OpenCode Go"; console.log(` Available models (${label}): `); for (const model of models) { console.log(` ${model.id.padEnd(28)} ${model.name}`); if (model.description) { console.log(` ${"".padEnd(28)} ${model.description}`); } console.log(); } process.exit(0); } if (args.includes("--refresh-models") && !args.includes("--list")) { const providerIndex2 = args.indexOf("--provider"); const providerName = providerIndex2 !== -1 ? args[providerIndex2 + 1] : "opencode"; if (providerName === "openai") { const config2 = getConfig(); if (!config2.openaiTokens) { f2.error("Not authenticated with OpenAI. Run 'opencode-go --oauth-login' first."); process.exit(1); } const spinner2 = de(); spinner2.start("Refreshing OpenAI models from ChatGPT..."); clearOpenAIModelsCache(); const result2 = await getOpenAIModels({ accessToken: config2.openaiTokens.access, accountId: config2.openaiTokens.accountId, refresh: true }); if (result2.source === "network") { spinner2.stop(`Cached ${result2.models.length} OpenAI models.`); process.exit(0); } spinner2.stop("Refresh failed \u2014 using fallback."); process.exit(1); } if (providerName === "qwen" || providerName === "zai") { f2.info(`${providerName} models are static in this CLI.`); process.exit(0); } const spinner = de(); spinner.start("Refreshing OpenCode models from opencode.ai..."); clearOpenCodeModelsCache(); const result = await getOpenCodeModels({ refresh: true }); if (result.source === "network") { spinner.stop(`Cached ${result.models.length} models.`); process.exit(0); } spinner.stop("Refresh failed \u2014 using fallback."); process.exit(1); } const config = getConfig(); if (args.includes("--proxy")) { const providerIndex2 = args.indexOf("--provider"); const provider = providerIndex2 !== -1 && args[providerIndex2 + 1] || "opencode"; const portIndex2 = args.indexOf("--port"); const port = portIndex2 !== -1 ? parseInt(args[portIndex2 + 1]) : config.proxyPort || DEFAULT_PROXY_PORT; if (provider === "openai" && !config.openaiTokens) { console.error("[cli] Not authenticated with OpenAI. Run 'opencode-go --oauth-login' first."); process.exit(1); } if (provider === "qwen" && countAccounts() === 0) { console.error("[cli] No Qwen accounts. Run 'opencode-go --qwen-login' first."); process.exit(1); } if (provider === "zai" && !config.zaiToken) { console.error("[cli] No Z.ai token. Run 'opencode-go --zai-login' first."); process.exit(1); } if (provider === "opencode" && !config.apiKey) { console.error("[cli] No API key configured. Run 'opencode-go --setup' first."); process.exit(1); } await startProxy(port, provider, PROXY_PORT_FALLBACK_ATTEMPTS); await new Promise(() => {}); return; } const providerIndex = args.indexOf("--provider"); const providerOverride = providerIndex !== -1 && args[providerIndex + 1] ? args[providerIndex + 1] : undefined; if (providerOverride && !PROVIDERS.includes(providerOverride)) { f2.error(`Unknown provider: ${providerOverride}. Options: ${PROVIDERS.join(", ")}`); process.exit(1); } const modelArgIndex = args.indexOf("--model"); const modelOverride = modelArgIndex !== -1 && args[modelArgIndex + 1] ? args[modelArgIndex + 1] : undefined; const permIndex = args.indexOf("--permission-mode"); const permOverride = permIndex !== -1 && args[permIndex + 1] ? args[permIndex + 1] : args.includes("--dangerously-skip-permissions") ? "bypassPermissions" : undefined; const portIndex = args.indexOf("--port"); const portOverride = portIndex !== -1 && args[portIndex + 1] ? parseInt(args[portIndex + 1]) : undefined; const hasOverrides = providerOverride || modelOverride || permOverride; if (hasOverrides) { if (modelOverride && providerOverride) { const modelList = await resolveModelsForProvider(providerOverride); if (!modelList.find((m2) => m2.id === modelOverride)) { f2.error(`Unknown model: ${modelOverride}`); f2.info(`Run 'opencode-go --list --provider ${providerOverride}' to see available models.`); process.exit(1); } } oe("OpenCode Go CLI"); await startFlow(providerOverride ?? "opencode", modelOverride, permOverride, portOverride, claudePassthroughArgs); return; } await interactiveMain(); } // src/index.ts main();