From 5bc69bcd5bf9dd30de150be9fa440f6d0d64123a Mon Sep 17 00:00:00 2001 From: wolves Date: Thu, 23 Apr 2026 03:11:00 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=98=E6=96=B9sdk=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 32 + apikey | 4 + package-lock.json | 1198 +++++++++++++++++ package.json | 16 + .../__pycache__/mitm-redact.cpython-312.pyc | Bin 0 -> 8248 bytes scripts/mitm-redact.py | 159 +++ src/codebuddy.ts | 125 ++ src/config.ts | 124 ++ src/prompt.ts | 39 + src/protocols.ts | 104 ++ src/server.ts | 113 ++ tsconfig.json | 13 + 抓包.md | 60 + 13 files changed, 1987 insertions(+) create mode 100644 .gitignore create mode 100644 apikey create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 scripts/__pycache__/mitm-redact.cpython-312.pyc create mode 100644 scripts/mitm-redact.py create mode 100644 src/codebuddy.ts create mode 100644 src/config.ts create mode 100644 src/prompt.ts create mode 100644 src/protocols.ts create mode 100644 src/server.ts create mode 100644 tsconfig.json create mode 100644 抓包.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6e33043 --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +# Dependencies +node_modules/ + +# Build output +dist/ +*.tsbuildinfo + +# Environment variables +.env +.env.* + +# Logs +logs/ +*.log +npm-debug.log* + +# IDE +.vscode/ +.idea/ +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db + +# Sensitive files +# apikey + +# CodeBuddy +.codex +.codebuddy-accounts/ diff --git a/apikey b/apikey new file mode 100644 index 0000000..4048c83 --- /dev/null +++ b/apikey @@ -0,0 +1,4 @@ +ck_fjvu1ygb83r4.ldwWDa9Q6RP-Ntjih7_lSWFO0XuNWDMkJE6epPmMYtM +ck_fjvu2i7nq58g.rLkYuYkOAnlCv4Ym2zYqJZ9IH4yNMg5LTwAVy2mI6KE +ck_fjvu794w7zls.OAPfiIY9PSKEgT512lp-guT8SIFPB6FqJCEFc9D18b4 +ck_fjvu7kqkrocg.0T3gTbRESr126rwbASeaaTb9_eWvllluDL_b3IRAspE \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..aa3826e --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1198 @@ +{ + "name": "codebuddy2api", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "codebuddy2api", + "version": "0.1.0", + "dependencies": { + "@tencent-ai/agent-sdk": "0.3.136" + }, + "devDependencies": { + "@types/node": "^20.19.0", + "typescript": "^5.9.3" + } + }, + "node_modules/@agentclientprotocol/sdk": { + "version": "0.18.2", + "resolved": "https://registry.npmmirror.com/@agentclientprotocol/sdk/-/sdk-0.18.2.tgz", + "integrity": "sha512-l/o9NKvUc00GPa6RFJ4AccQq2O/PAf83xQ75mThHuL3H571iN4+PEdwnTBez67sS8Nv2aSA373xCZ5CbTXEwzA==", + "license": "Apache-2.0", + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + }, + "node_modules/@hono/node-server": { + "version": "1.19.14", + "resolved": "https://registry.npmmirror.com/@hono/node-server/-/node-server-1.19.14.tgz", + "integrity": "sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==", + "license": "MIT", + "engines": { + "node": ">=18.14.1" + }, + "peerDependencies": { + "hono": "^4" + } + }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.29.0", + "resolved": "https://registry.npmmirror.com/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", + "integrity": "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==", + "license": "MIT", + "dependencies": { + "@hono/node-server": "^1.19.9", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.2.1", + "express-rate-limit": "^8.2.1", + "hono": "^4.11.4", + "jose": "^6.1.3", + "json-schema-typed": "^8.0.2", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } + } + }, + "node_modules/@tencent-ai/agent-sdk": { + "version": "0.3.136", + "resolved": "https://registry.npmmirror.com/@tencent-ai/agent-sdk/-/agent-sdk-0.3.136.tgz", + "integrity": "sha512-WbSBmLMW2cO7o+8W2JaJ7k393UhhOww7ksGmg/2kK3+r0QU7gpmEgcyI/cOL9oOC4U7rUK127RxG7VVMJgAvuA==", + "license": "MIT", + "dependencies": { + "@agentclientprotocol/sdk": "^0.18.2", + "@modelcontextprotocol/sdk": "^1.0.0" + }, + "peerDependencies": { + "zod": "^4.0.0" + } + }, + "node_modules/@types/node": { + "version": "20.19.39", + "resolved": "https://registry.npmmirror.com/@types/node/-/node-20.19.39.tgz", + "integrity": "sha512-orrrD74MBUyK8jOAD/r0+lfa1I2MO6I+vAkmAWzMYbCcgrN4lCrmK52gRFQq/JRxfYPfonkr4b0jcY7Olqdqbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmmirror.com/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/content-disposition": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/content-disposition/-/content-disposition-1.1.0.tgz", + "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmmirror.com/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmmirror.com/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmmirror.com/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmmirror.com/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.0.8", + "resolved": "https://registry.npmmirror.com/eventsource-parser/-/eventsource-parser-3.0.8.tgz", + "integrity": "sha512-70QWGkr4snxr0OXLRWsFLeRBIRPuQOvt4s8QYjmUlmlkyTZkRqS7EDVRZtzU3TiyDbXSzaOeF0XUKy8PchzukQ==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "peer": true, + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-rate-limit": { + "version": "8.3.2", + "resolved": "https://registry.npmmirror.com/express-rate-limit/-/express-rate-limit-8.3.2.tgz", + "integrity": "sha512-77VmFeJkO0/rvimEDuUC5H30oqUC4EyOhyGccfqoLebB0oiEYfM7nwPrsDsBL1gsTpwfzX8SFy2MT3TDyRq+bg==", + "license": "MIT", + "dependencies": { + "ip-address": "10.1.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hono": { + "version": "4.12.14", + "resolved": "https://registry.npmmirror.com/hono/-/hono-4.12.14.tgz", + "integrity": "sha512-am5zfg3yu6sqn5yjKBNqhnTX7Cv+m00ox+7jbaKkrLMRJ4rAdldd1xPd/JzbBWspqaQv6RSTrgFN95EsfhC+7w==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ip-address": { + "version": "10.1.0", + "resolved": "https://registry.npmmirror.com/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jose": { + "version": "6.2.2", + "resolved": "https://registry.npmmirror.com/jose/-/jose-6.2.2.tgz", + "integrity": "sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/json-schema-typed": { + "version": "8.0.2", + "resolved": "https://registry.npmmirror.com/json-schema-typed/-/json-schema-typed-8.0.2.tgz", + "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", + "license": "BSD-2-Clause" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmmirror.com/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmmirror.com/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.4.2", + "resolved": "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/pkce-challenge": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", + "license": "MIT", + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmmirror.com/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.15.1", + "resolved": "https://registry.npmmirror.com/qs/-/qs-6.15.1.tgz", + "integrity": "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmmirror.com/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/zod": { + "version": "4.3.6", + "resolved": "https://registry.npmmirror.com/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.25.2", + "resolved": "https://registry.npmmirror.com/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", + "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.25.28 || ^4" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..761d9ea --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "name": "codebuddy2api", + "version": "0.1.0", + "private": true, + "scripts": { + "build": "tsc -p tsconfig.json", + "start": "node dist/server.js" + }, + "dependencies": { + "@tencent-ai/agent-sdk": "0.3.136" + }, + "devDependencies": { + "@types/node": "^20.19.0", + "typescript": "^5.9.3" + } +} diff --git a/scripts/__pycache__/mitm-redact.cpython-312.pyc b/scripts/__pycache__/mitm-redact.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dea1cd0d118a1dcc8ade5576f4cc26914da8a86c GIT binary patch literal 8248 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 CKd;pQ literal 0 HcmV?d00001 diff --git a/scripts/mitm-redact.py b/scripts/mitm-redact.py new file mode 100644 index 0000000..470a650 --- /dev/null +++ b/scripts/mitm-redact.py @@ -0,0 +1,159 @@ +import json +import os +import time +from mitmproxy import http + +OUT = os.environ.get("MITM_REDACT_LOG", "/tmp/codebuddy-mitm-events.jsonl") +SENSITIVE_KEYS = { + "authorization", + "proxy-authorization", + "cookie", + "set-cookie", + "x-api-key", + "api-key", + "apikey", + "token", + "access_token", + "refresh_token", + "id_token", + "codebuddy_api_key", + "codebuddy_auth_token", +} + + +def request(flow: http.HTTPFlow) -> None: + flow.metadata["started_at"] = time.time() + + +def response(flow: http.HTTPFlow) -> None: + req = flow.request + resp = flow.response + event = { + "ts": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()), + "duration_ms": round((time.time() - flow.metadata.get("started_at", time.time())) * 1000), + "method": req.method, + "scheme": req.scheme, + "host": req.pretty_host, + "port": req.port, + "path": req.path.split("?")[0], + "query_keys": sorted(req.query.keys()), + "request_headers": sanitize_headers(req.headers), + "request_body": summarize_body(req.headers.get("content-type", ""), safe_content(req)), + "status_code": resp.status_code, + "response_headers": sanitize_headers(resp.headers), + "response_body": summarize_body(resp.headers.get("content-type", ""), safe_content(resp)), + } + append(event) + + +def error(flow: http.HTTPFlow) -> None: + req = flow.request + append({ + "ts": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()), + "method": req.method, + "scheme": req.scheme, + "host": req.pretty_host, + "port": req.port, + "path": req.path.split("?")[0], + "error": str(flow.error) if flow.error else "unknown", + }) + + +def sanitize_headers(headers) -> dict: + clean = {} + for key, value in headers.items(): + lower = key.lower() + clean[key] = "" if lower in SENSITIVE_KEYS or "token" in lower or "secret" in lower or "key" in lower else trim(value) + return clean + + +def safe_content(message): + try: + return message.content + except Exception: + return message.raw_content + + +def summarize_body(content_type: str, raw: bytes | None): + if not raw: + return {"bytes": 0} + if len(raw) > 2_000_000: + return {"bytes": len(raw), "too_large": True} + text = raw.decode("utf-8", errors="replace") + if "json" in content_type.lower() or looks_like_json(text): + try: + return {"bytes": len(raw), "json_shape": sanitize_json(json.loads(text))} + except Exception: + pass + if "text/event-stream" in content_type.lower(): + return {"bytes": len(raw), "sse_events": summarize_sse(text)} + return {"bytes": len(raw), "preview": trim(text)} + + +KEEP_STRING_KEYS = { + "model", + "role", + "type", + "name", + "object", + "finish_reason", + "reasoning_effort", +} + + +def sanitize_json(value, key_context: str | None = None): + if isinstance(value, dict): + out = {} + for key, item in value.items(): + lower = str(key).lower() + if lower in SENSITIVE_KEYS or "token" in lower or "secret" in lower or "key" in lower: + out[key] = "" + else: + out[key] = sanitize_json(item, lower) + return out + if isinstance(value, list): + return [sanitize_json(item, key_context) for item in value[:20]] + if isinstance(value, str): + if key_context in KEEP_STRING_KEYS: + return value + return f"" + if isinstance(value, (int, float, bool)) or value is None: + return value + return f"<{type(value).__name__}>" + + +def summarize_sse(text: str): + events = [] + current = {} + for line in text.splitlines()[:200]: + if line.startswith("event:"): + current["event"] = line[6:].strip() + elif line.startswith("data:"): + current["data"] = summarize_data_line(line[5:].strip()) + events.append(current) + current = {} + return events[:20] + + +def summarize_data_line(text: str): + if text == "[DONE]": + return text + try: + return sanitize_json(json.loads(text)) + except Exception: + return trim(text) + + +def looks_like_json(text: str) -> bool: + stripped = text.strip() + return stripped.startswith("{") or stripped.startswith("[") + + +def trim(text: str, limit: int = 240) -> str: + text = text.replace("\r", "\\r").replace("\n", "\\n") + return text if len(text) <= limit else text[:limit] + "..." + + +def append(event: dict) -> None: + with open(OUT, "a", encoding="utf-8") as f: + f.write(json.dumps(event, ensure_ascii=False) + "\n") diff --git a/src/codebuddy.ts b/src/codebuddy.ts new file mode 100644 index 0000000..43fbe8b --- /dev/null +++ b/src/codebuddy.ts @@ -0,0 +1,125 @@ +import { unstable_v2_createSession } from '@tencent-ai/agent-sdk'; +import type { Message, PermissionMode, Session } from '@tencent-ai/agent-sdk'; +import type { AccountConfig, AppConfig } from './config'; + +export type TextHandler = (text: string) => void; + +export class CodeBuddyPool { + private readonly workers: AccountWorker[]; + private next = 0; + + constructor(config: AppConfig) { + this.workers = config.accounts.map((account) => new AccountWorker(config, account)); + } + + async run(prompt: string, requestedModel: string | undefined, onText?: TextHandler): Promise { + const worker = this.workers[this.next % this.workers.length]; + this.next += 1; + return worker.run(prompt, requestedModel, onText); + } + + async close(): Promise { + for (const worker of this.workers) worker.close(); + } +} + +class AccountWorker { + private session?: Session; + private queue: Promise = Promise.resolve(); + + constructor( + private readonly app: AppConfig, + private readonly account: AccountConfig, + ) {} + + async run(prompt: string, requestedModel: string | undefined, onText?: TextHandler): Promise { + return this.withLock(async () => { + const session = await this.getSession(requestedModel); + await session.send(prompt); + + let resultText = ''; + let assistantText = ''; + let streamedAny = false; + + for await (const message of session.stream()) { + if (message.type === 'stream_event') { + const delta = message.event.type === 'content_block_delta' && message.event.delta.type === 'text_delta' + ? message.event.delta.text + : ''; + if (delta) { + streamedAny = true; + resultText += delta; + onText?.(delta); + } + continue; + } + + if (message.type === 'assistant') { + assistantText += extractAssistantText(message); + if (onText && !streamedAny) { + const text = extractAssistantText(message); + if (text) onText(text); + } + continue; + } + + if (message.type === 'result') { + if (message.subtype === 'success') { + return resultText || message.result || assistantText; + } + throw new Error(message.errors?.join('; ') || message.subtype); + } + } + + return resultText || assistantText; + }); + } + + close(): void { + this.session?.close(); + this.session = undefined; + } + + private async getSession(requestedModel: string | undefined): Promise { + if (!this.session) { + this.session = unstable_v2_createSession({ + cwd: this.app.cwd, + model: requestedModel || this.app.model, + permissionMode: this.app.permissionMode as PermissionMode, + includePartialMessages: true, + settingSources: [], + env: { + CODEBUDDY_API_KEY: this.account.apiKey, + CODEBUDDY_AUTH_TOKEN: this.account.authToken, + CODEBUDDY_INTERNET_ENVIRONMENT: this.account.internetEnvironment, + CODEBUDDY_CONFIG_DIR: this.account.configDir, + }, + }); + await this.session.connect(); + } else if (requestedModel && requestedModel !== this.session.getModel()) { + await this.session.setModel(requestedModel); + } + return this.session; + } + + private async withLock(task: () => Promise): Promise { + const previous = this.queue; + let release!: () => void; + this.queue = new Promise((resolve) => { + release = resolve; + }); + await previous; + try { + return await task(); + } finally { + release(); + } + } +} + +function extractAssistantText(message: Extract): string { + return message.message.content + .map((block) => block.type === 'text' ? block.text : '') + .filter(Boolean) + .join(''); +} diff --git a/src/config.ts b/src/config.ts new file mode 100644 index 0000000..00eaffd --- /dev/null +++ b/src/config.ts @@ -0,0 +1,124 @@ +import { existsSync, mkdirSync, readFileSync } from 'node:fs'; +import { join, resolve } from 'node:path'; + +export type AccountConfig = { + id: string; + apiKey?: string; + authToken?: string; + internetEnvironment?: string; + configDir: string; +}; + +export type AppConfig = { + port: number; + proxyApiKey?: string; + model?: string; + passRequestModel: boolean; + permissionMode: 'default' | 'acceptEdits' | 'bypassPermissions' | 'plan' | 'delegate' | 'dontAsk'; + cwd: string; + accounts: AccountConfig[]; +}; + +type AccountInput = { + id?: string; + apiKey?: string; + authToken?: string; + internetEnvironment?: string; + configDir?: string; +}; + +export function loadConfig(): AppConfig { + const cwd = process.cwd(); + const accounts = loadAccounts(cwd); + if (accounts.length === 0) { + throw new Error('No CodeBuddy credential found. Provide CODEBUDDY_ACCOUNTS_JSON, CODEBUDDY_API_KEY, CODEBUDDY_AUTH_TOKEN, or an apikey file.'); + } + + return { + port: Number(process.env.PORT ?? 8787), + proxyApiKey: process.env.PROXY_API_KEY, + model: emptyToUndefined(process.env.CODEBUDDY_MODEL), + passRequestModel: process.env.CODEBUDDY_PASS_REQUEST_MODEL === '1', + permissionMode: (process.env.CODEBUDDY_PERMISSION_MODE as AppConfig['permissionMode'] | undefined) ?? 'bypassPermissions', + cwd, + accounts, + }; +} + +function loadAccounts(cwd: string): AccountConfig[] { + const fromJson = parseAccountsJson(process.env.CODEBUDDY_ACCOUNTS_JSON, cwd); + if (fromJson.length > 0) return fromJson; + + if (process.env.CODEBUDDY_API_KEY || process.env.CODEBUDDY_AUTH_TOKEN) { + return [normalizeAccount({ + id: 'env-1', + apiKey: process.env.CODEBUDDY_API_KEY, + authToken: process.env.CODEBUDDY_AUTH_TOKEN, + internetEnvironment: process.env.CODEBUDDY_INTERNET_ENVIRONMENT, + }, cwd, 0)]; + } + + const file = resolve(cwd, process.env.CODEBUDDY_APIKEY_FILE ?? 'apikey'); + if (!existsSync(file)) return []; + + const raw = readFileSync(file, 'utf8').trim(); + if (!raw) return []; + + const parsed = parseAccountsJson(raw, cwd); + if (parsed.length > 0) return parsed; + + const tokenKind = process.env.CODEBUDDY_TOKEN_KIND === 'auth_token' ? 'auth_token' : 'api_key'; + return raw + .split(/\r?\n/) + .map((line) => line.trim()) + .filter((line) => line && !line.startsWith('#')) + .map((line, index) => parseTokenLine(line, tokenKind, index, cwd)); +} + +function parseAccountsJson(raw: string | undefined, cwd: string): AccountConfig[] { + if (!raw) return []; + try { + const value = JSON.parse(raw) as unknown; + const items = Array.isArray(value) + ? value + : typeof value === 'object' && value !== null && Array.isArray((value as { accounts?: unknown }).accounts) + ? (value as { accounts: unknown[] }).accounts + : []; + return items.map((item, index) => normalizeAccount(item as AccountInput, cwd, index)); + } catch { + return []; + } +} + +function parseTokenLine(line: string, tokenKind: 'api_key' | 'auth_token', index: number, cwd: string): AccountConfig { + const eq = line.indexOf('='); + if (eq > 0) { + const key = line.slice(0, eq).trim(); + const value = line.slice(eq + 1).trim(); + if (key === 'CODEBUDDY_AUTH_TOKEN') return normalizeAccount({ id: `file-${index + 1}`, authToken: value }, cwd, index); + if (key === 'CODEBUDDY_API_KEY') return normalizeAccount({ id: `file-${index + 1}`, apiKey: value }, cwd, index); + } + return normalizeAccount({ + id: `file-${index + 1}`, + apiKey: tokenKind === 'api_key' ? line : undefined, + authToken: tokenKind === 'auth_token' ? line : undefined, + }, cwd, index); +} + +function normalizeAccount(input: AccountInput, cwd: string, index: number): AccountConfig { + const id = input.id ?? `account-${index + 1}`; + const configDir = input.configDir ?? join(cwd, '.codebuddy-accounts', id); + mkdirSync(configDir, { recursive: true }); + return { + id, + apiKey: emptyToUndefined(input.apiKey), + authToken: emptyToUndefined(input.authToken), + internetEnvironment: emptyToUndefined(input.internetEnvironment), + configDir, + }; +} + +function emptyToUndefined(value: string | undefined): string | undefined { + const trimmed = value?.trim(); + return trimmed ? trimmed : undefined; +} diff --git a/src/prompt.ts b/src/prompt.ts new file mode 100644 index 0000000..87607a3 --- /dev/null +++ b/src/prompt.ts @@ -0,0 +1,39 @@ +export type ChatMessage = { + role: string; + content?: unknown; +}; + +export function openAIChatToPrompt(messages: ChatMessage[]): string { + return messages.map((message) => { + const role = message.role || 'user'; + return `${role.toUpperCase()}:\n${contentToText(message.content)}`; + }).join('\n\n'); +} + +export function anthropicMessagesToPrompt(system: unknown, messages: ChatMessage[]): string { + const parts: string[] = []; + const systemText = contentToText(system); + if (systemText) parts.push(`SYSTEM:\n${systemText}`); + parts.push(openAIChatToPrompt(messages)); + return parts.filter(Boolean).join('\n\n'); +} + +export function contentToText(content: unknown): string { + if (typeof content === 'string') return content; + if (Array.isArray(content)) { + return content.map((part) => { + if (typeof part === 'string') return part; + if (typeof part === 'object' && part !== null) { + const value = part as Record; + if (typeof value.text === 'string') return value.text; + if (typeof value.content === 'string') return value.content; + } + return ''; + }).filter(Boolean).join('\n'); + } + if (typeof content === 'object' && content !== null) { + const value = content as Record; + if (typeof value.text === 'string') return value.text; + } + return ''; +} diff --git a/src/protocols.ts b/src/protocols.ts new file mode 100644 index 0000000..e528612 --- /dev/null +++ b/src/protocols.ts @@ -0,0 +1,104 @@ +import { randomUUID } from 'node:crypto'; +import type { ServerResponse } from 'node:http'; +import { anthropicMessagesToPrompt, openAIChatToPrompt, type ChatMessage } from './prompt'; + +export type OpenAIChatRequest = { + model?: string; + messages?: ChatMessage[]; + stream?: boolean; +}; + +export type AnthropicMessagesRequest = { + model?: string; + system?: unknown; + messages?: ChatMessage[]; + stream?: boolean; +}; + +export function openAIPrompt(req: OpenAIChatRequest): string { + return openAIChatToPrompt(req.messages ?? []); +} + +export function anthropicPrompt(req: AnthropicMessagesRequest): string { + return anthropicMessagesToPrompt(req.system, req.messages ?? []); +} + +export function writeOpenAIResponse(res: ServerResponse, model: string, text: string): void { + writeJson(res, 200, { + id: `chatcmpl-${randomUUID()}`, + object: 'chat.completion', + created: Math.floor(Date.now() / 1000), + model, + choices: [{ index: 0, message: { role: 'assistant', content: text }, finish_reason: 'stop' }], + usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 }, + }); +} + +export function startOpenAIStream(res: ServerResponse, model: string): (text: string) => void { + const id = `chatcmpl-${randomUUID()}`; + sseHeaders(res); + return (text: string) => { + res.write(`data: ${JSON.stringify({ + id, + object: 'chat.completion.chunk', + created: Math.floor(Date.now() / 1000), + model, + choices: [{ index: 0, delta: { content: text }, finish_reason: null }], + })}\n\n`); + }; +} + +export function endOpenAIStream(res: ServerResponse): void { + res.write('data: [DONE]\n\n'); + res.end(); +} + +export function writeAnthropicResponse(res: ServerResponse, model: string, text: string): void { + writeJson(res, 200, { + id: `msg_${randomUUID().replaceAll('-', '')}`, + type: 'message', + role: 'assistant', + model, + content: [{ type: 'text', text }], + stop_reason: 'end_turn', + stop_sequence: null, + usage: { input_tokens: 0, output_tokens: 0 }, + }); +} + +export function startAnthropicStream(res: ServerResponse, model: string): (text: string) => void { + const id = `msg_${randomUUID().replaceAll('-', '')}`; + sseHeaders(res); + res.write(`event: message_start\ndata: ${JSON.stringify({ + type: 'message_start', + message: { id, type: 'message', role: 'assistant', model, content: [], stop_reason: null, stop_sequence: null, usage: { input_tokens: 0, output_tokens: 0 } }, + })}\n\n`); + res.write(`event: content_block_start\ndata: ${JSON.stringify({ type: 'content_block_start', index: 0, content_block: { type: 'text', text: '' } })}\n\n`); + return (text: string) => { + res.write(`event: content_block_delta\ndata: ${JSON.stringify({ type: 'content_block_delta', index: 0, delta: { type: 'text_delta', text } })}\n\n`); + }; +} + +export function endAnthropicStream(res: ServerResponse): void { + res.write(`event: content_block_stop\ndata: ${JSON.stringify({ type: 'content_block_stop', index: 0 })}\n\n`); + res.write(`event: message_delta\ndata: ${JSON.stringify({ type: 'message_delta', delta: { stop_reason: 'end_turn', stop_sequence: null }, usage: { output_tokens: 0 } })}\n\n`); + res.write(`event: message_stop\ndata: ${JSON.stringify({ type: 'message_stop' })}\n\n`); + res.end(); +} + +export function writeJson(res: ServerResponse, statusCode: number, value: unknown): void { + res.writeHead(statusCode, { 'content-type': 'application/json; charset=utf-8' }); + res.end(JSON.stringify(value)); +} + +export function writeError(res: ServerResponse, statusCode: number, message: string): void { + writeJson(res, statusCode, { error: { type: 'api_error', message } }); +} + +function sseHeaders(res: ServerResponse): void { + res.writeHead(200, { + 'content-type': 'text/event-stream; charset=utf-8', + 'cache-control': 'no-cache, no-transform', + connection: 'keep-alive', + }); +} diff --git a/src/server.ts b/src/server.ts new file mode 100644 index 0000000..4781494 --- /dev/null +++ b/src/server.ts @@ -0,0 +1,113 @@ +import { createServer, type IncomingMessage, type ServerResponse } from 'node:http'; +import { loadConfig } from './config'; +import { CodeBuddyPool } from './codebuddy'; +import { + anthropicPrompt, + endAnthropicStream, + endOpenAIStream, + openAIPrompt, + startAnthropicStream, + startOpenAIStream, + writeAnthropicResponse, + writeError, + writeJson, + writeOpenAIResponse, + type AnthropicMessagesRequest, + type OpenAIChatRequest, +} from './protocols'; + +const config = loadConfig(); +const pool = new CodeBuddyPool(config); + +const server = createServer(async (req, res) => { + try { + if (!authorize(req)) { + writeError(res, 401, 'Unauthorized'); + return; + } + + const url = new URL(req.url ?? '/', `http://${req.headers.host ?? '127.0.0.1'}`); + if (req.method === 'GET' && url.pathname === '/health') { + writeJson(res, 200, { ok: true, accounts: config.accounts.length }); + return; + } + if (req.method === 'GET' && url.pathname === '/debug/memory') { + writeJson(res, 200, { pid: process.pid, memory: process.memoryUsage() }); + return; + } + if (req.method === 'GET' && url.pathname === '/v1/models') { + writeJson(res, 200, { object: 'list', data: [{ id: config.model ?? 'codebuddy', object: 'model', owned_by: 'codebuddy' }] }); + return; + } + if (req.method === 'POST' && (url.pathname === '/v1/chat/completions' || url.pathname === '/chat/completions')) { + await handleOpenAI(req, res); + return; + } + if (req.method === 'POST' && url.pathname === '/v1/messages') { + await handleAnthropic(req, res); + return; + } + + writeError(res, 404, 'Not found'); + } catch (error) { + if (!res.headersSent) writeError(res, 500, error instanceof Error ? error.message : String(error)); + else res.end(); + } +}); + +server.listen(config.port, '127.0.0.1', () => { + console.log(`codebuddy2api listening on http://127.0.0.1:${config.port}`); + console.log(`accounts loaded: ${config.accounts.length}`); +}); + +process.on('SIGINT', shutdown); +process.on('SIGTERM', shutdown); + +async function handleOpenAI(req: IncomingMessage, res: ServerResponse): Promise { + const body = await readJson(req); + const model = body.model || config.model || 'codebuddy'; + const prompt = openAIPrompt(body); + if (body.stream) { + const write = startOpenAIStream(res, model); + await pool.run(prompt, sdkModel(body.model), write); + endOpenAIStream(res); + return; + } + const text = await pool.run(prompt, sdkModel(body.model)); + writeOpenAIResponse(res, model, text); +} + +async function handleAnthropic(req: IncomingMessage, res: ServerResponse): Promise { + const body = await readJson(req); + const model = body.model || config.model || 'codebuddy'; + const prompt = anthropicPrompt(body); + if (body.stream) { + const write = startAnthropicStream(res, model); + await pool.run(prompt, sdkModel(body.model), write); + endAnthropicStream(res); + return; + } + const text = await pool.run(prompt, sdkModel(body.model)); + writeAnthropicResponse(res, model, text); +} + +function sdkModel(requestModel: string | undefined): string | undefined { + return config.passRequestModel ? requestModel : undefined; +} + +function authorize(req: IncomingMessage): boolean { + if (!config.proxyApiKey) return true; + const header = req.headers.authorization ?? ''; + return header === `Bearer ${config.proxyApiKey}`; +} + +async function readJson(req: IncomingMessage): Promise { + const chunks: Buffer[] = []; + for await (const chunk of req) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)); + if (chunks.length === 0) return {} as T; + return JSON.parse(Buffer.concat(chunks).toString('utf8')) as T; +} + +function shutdown(): void { + pool.close().finally(() => server.close(() => process.exit(0))); +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..8cf5b06 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "CommonJS", + "moduleResolution": "Node", + "outDir": "dist", + "rootDir": "src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true + }, + "include": ["src/**/*.ts"] +} diff --git a/抓包.md b/抓包.md new file mode 100644 index 0000000..7ad56e9 --- /dev/null +++ b/抓包.md @@ -0,0 +1,60 @@ +抓到了。CodeBuddy SDK/CLI 会走代理,HTTPS 明文能被 `mitmdump` 解出来。 + +我新增了一个打码抓包脚本: + +[srcipts/mitm-redact.py](/home/wolves/project/codebuddy2api/scripts/mitm-redact.py) + +注意路径是 `scripts/mitm-redact.py`。它只记录摘要,Authorization、API key、token、cookie 都会打码。 + +本次关键结果: + +```text +POST https://copilot.tencent.com/v2/chat/completions +POST https://copilot.tencent.com/v2/report +``` + +主请求是 OpenAI Chat Completions 风格: + +```json +{ + "model": "glm-5.1", + "messages": [ + { "role": "system", "content": "..." }, + { "role": "user", "content": [{ "type": "text" }, { "type": "text" }] } + ], + "tools": ["Agent", "Read", "Write", "Edit", "MultiEdit", "Bash", "Glob", "Grep", "EnterPlanMode", "ExitPlanMode", "TaskCreate", "TaskGet", "TaskUpdate", "TaskList", "WebFetch", "WebSearch", "TaskStop", "TaskOutput", "Skill", "AskUserQuestion"], + "temperature": 1, + "stream": true, + "stream_options": { "include_usage": true }, + "reasoning_effort": "medium" +} +``` + +请求头关键项: + +```text +X-API-Key: +Authorization: +X-Conversation-ID +X-Conversation-Request-ID +X-Conversation-Message-ID +X-Agent-Intent: craft +X-IDE-Type: CLI +X-IDE-Name: CLI +X-IDE-Version: 2.93.3 +x-codebuddy-request: 1 +User-Agent: CLI/2.93.3 CodeBuddy/2.93.3 ... +Content-Encoding: gzip +``` + +响应也是 OpenAI SSE chunk: + +```text +content-type: text/event-stream +object: chat.completion.chunk +model: glm-5.1-tencent +data: {...} +data: [DONE] +``` + +结论:低内存直连方向是可行的,至少主链路不是私有二进制协议,而是 `copilot.tencent.com/v2/chat/completions` 的 OpenAI-compatible SSE。下一步要做的是把当前 TS 中转从 SDK 改成直接调用这个上游:复刻必要 headers、gzip 请求、SSE 转发,并验证只用 `apikey` 能不能稳定鉴权。 \ No newline at end of file