diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 6b8c35b..54e2ff6 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -5,19 +5,6 @@ }, "description": "Community plugins for the ZCode CLI runtime (unofficial).", "plugins": [ - { - "name": "zcode-start-plan", - "source": "./plugins/zcode-start-plan-plugin", - "description": "Use the free ZCode Start Plan trial quota (zcode-plan endpoints) from the ZCode CLI: entitlement check, delegated generation, and one-time Aliyun captcha bridging. Requires prior `zcode login` (OAuth). Unofficial.", - "description_i18n": { - "en": "Use the free ZCode Start Plan trial quota (zcode-plan endpoints) from the ZCode CLI: entitlement check, delegated generation, and one-time Aliyun captcha bridging. Requires prior `zcode login` (OAuth). Unofficial.", - "zh-CN": "在 ZCode CLI 中使用免费的 ZCode Start Plan 试用额度(zcode-plan 端点):额度查询、委托式生成、一次性阿里云验证码桥接。需先执行 `zcode login`(OAuth)。非官方实现。" - }, - "version": "0.1.0", - "author": { "name": "kingsword09" }, - "category": "provider", - "keywords": ["zai", "start-plan", "trial", "captcha", "glm", "mcp"] - }, { "name": "zcode-model-config", "source": "./plugins/zcode-model-config-plugin", diff --git a/AGENTS.md b/AGENTS.md index a4441c7..92477d6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -56,7 +56,7 @@ plugins/-plugin/ ### 2. package.json -Model it on `plugins/zcode-start-plan-plugin/package.json`: +Model it on `plugins/zcode-model-config-plugin/package.json`: - `name`: `@zcode-plugins/-plugin`, `"private": true`, `"type": "module"`, `"license": "MIT"`. - `main`: `./dist/mcp/server.js` (if there is an MCP server). diff --git a/README.md b/README.md index 31344db..69d370e 100644 --- a/README.md +++ b/README.md @@ -4,12 +4,8 @@ Unofficial community plugins for the [ZCode CLI](https://github.com/kingsword09/ | Plugin | Description | | --- | --- | -| [`zcode-start-plan`](./plugins/zcode-start-plan-plugin) | Spend the free "ZCode Start Plan" trial quota from the CLI via an MCP tool, with one-time Aliyun captcha bridging. | | [`zcode-model-config`](./plugins/zcode-model-config-plugin) | Local web UI (React + StyleX) to edit ZCode CLI model / provider config, with a models.dev catalog for one-click import. | -> [!WARNING] -> `zcode-start-plan` is an unofficial, community implementation reverse-engineered from the ZCode desktop app. It uses internal `zcode-plan` endpoints that Z.ai has not officially opened to CLI clients, and it drives the Aliyun captcha web SDK outside its intended browser context. Use at your own risk; heavy or abusive usage may trigger risk control on your account. If you rely on ZCode for production work, subscribe to a Coding Plan instead. - ## Install Plugins are installed from a **marketplace** (this repo). Two sources are supported: this GitHub repo, or a local checkout. Both go through the same two steps — register the marketplace, then install + enable the plugin you want. @@ -39,7 +35,6 @@ List what the marketplace offers, then install by fully-qualified id (`@ ```bash zcode plugins overview # see available plugins -zcode plugins install zcode-start-plan@zcode-plugins zcode plugins install zcode-model-config@zcode-plugins ``` @@ -58,7 +53,7 @@ No `bun install` / `bun run build` is needed on the consuming side: each plugin' Remove a single plugin (keeps the marketplace registered): ```bash -zcode plugins uninstall zcode-start-plan@zcode-plugins +zcode plugins uninstall zcode-model-config@zcode-plugins ``` Or remove everything this marketplace brought in at once — uninstalling the marketplace removes its installed plugins: @@ -75,10 +70,10 @@ The marketplace caches a copy of the repo, so upstream changes don't apply autom ```bash zcode plugins marketplace update zcode-plugins # re-fetch the repo / re-read the local dir -zcode plugins update zcode-start-plan@zcode-plugins # refresh installed plugin cache +zcode plugins update zcode-model-config@zcode-plugins # refresh installed plugin cache ``` -Then start a **new** ZCode session — running sessions keep the old MCP server process. For plugin authors iterating locally, see the [plugin README](./plugins/zcode-start-plan-plugin#development) for the full three-step flow (rebuild included). +Then start a **new** ZCode session — running sessions keep the old MCP server process. For plugin authors iterating locally, see the plugin's own README for the full three-step flow (rebuild included). ## Development @@ -94,7 +89,7 @@ bun test # run unit tests Per-plugin commands can be run inside `plugins/`, e.g.: ```bash -cd plugins/zcode-start-plan-plugin +cd plugins/zcode-model-config-plugin bun run build bun test ``` @@ -105,13 +100,12 @@ bun test . ├── .claude-plugin/marketplace.json # marketplace manifest (this is the file the CLI discovers) ├── plugins/ -│ ├── zcode-start-plan-plugin/ # one directory per plugin (bun workspace) -│ │ ├── .zcode-plugin/plugin.json # per-plugin manifest (name, version, MCP server, commands, skills) -│ │ ├── commands/ # slash commands -│ │ ├── skills/ # agent skills -│ │ ├── src/ # TypeScript sources -│ │ └── dist/ # build output (committed; the runtime runs this) -│ └── zcode-model-config-plugin/ +│ └── zcode-model-config-plugin/ # one directory per plugin (bun workspace) +│ ├── .zcode-plugin/plugin.json # per-plugin manifest (name, version, MCP server, commands, skills) +│ ├── commands/ # slash commands +│ ├── skills/ # agent skills +│ ├── src/ # TypeScript sources +│ └── dist/ # build output (committed; the runtime runs this) └── package.json # bun workspace root ``` diff --git a/bun.lock b/bun.lock index c0160b4..b8c6d37 100644 --- a/bun.lock +++ b/bun.lock @@ -30,19 +30,6 @@ "vite-plus": "^0.3.0", }, }, - "plugins/zcode-start-plan-plugin": { - "name": "@zcode-plugins/start-plan-plugin", - "version": "0.1.0", - "dependencies": { - "@modelcontextprotocol/sdk": "^1.30.0", - "zod": "^4.1.0", - }, - "devDependencies": { - "@types/bun": "^1.3.14", - "tsdown": "^0.22.14", - "typescript": "^7.0.2", - }, - }, }, "packages": { "@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], @@ -259,36 +246,6 @@ "@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="], - "@quansync/fs": ["@quansync/fs@1.0.0", "", { "dependencies": { "quansync": "^1.0.0" } }, "sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ=="], - - "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.2.4", "", { "os": "android", "cpu": "arm64" }, "sha512-jHC2cnyKz5xU2fhECtFl8OZ83cYNt13GZQD+0uMJ/X3o+ijmd56okHhTUwxVSHPx1IRVIJEZ1/1pPzeLCU6XKA=="], - - "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Dc5mPD8F5F/FS8i01syd7FTF6yB2fVthH/TRkjwJkzUK6EpoxHtqvZQP5Zwq80/5z19TWYHIg1KOHboCgVx/aQ=="], - - "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-fpDm4oBo6SqLvWUYCmFhdde3U9KH2fRNNMeAnAPAIwxRL345xutL0EtEUcuoxsoazdJGv/MuDBQHlCDrtbvqOg=="], - - "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.2.4", "", { "os": "freebsd", "cpu": "x64" }, "sha512-rSJoreDE/HoIzoaib6MTp5jQtCTdMHKIvItAKT/ImS6Y6Ww76oUaeMyp4Vc/fAgd/ehji068IxetHXAnqUwN9A=="], - - "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-/jm8OGHgn7oGaJu3i/qZI9spUGcJ+y/lk43ttQ/iO1tOd9NissG6o97bighBCiL+BKRngmcDuR6ikfwYdJmVuQ=="], - - "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-tIP06BeD9EqvECBrPZ+sqdPlYrT+aYaAiu1wYziVx5elRK/ftm33JxVDy2bXGbr6J0CrtirCkR87/X5a2euEng=="], - - "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-Ql1Q0EQqVThvn9VAVlwNzsUvbSFtCMGjLpRRi4pk5i7NZZ4n5ISiLMjHYtus4VQ2PvkSw24zyaCVsiS+sXPj1w=="], - - "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.2.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-GjbjXD4XXfN19D0LZNbmiCBUoDiRACsYHr0yaIbbn8aFsXjHZifcYqu/W5Er5X2X990WjHXFrxarn5chzItorQ=="], - - "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.2.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-p5WR0NOwaRmJ/B1b6IjEFLLivwEsf3PrdBIhRbhTCQisbo2SvHHpG4ELB/+FgQNnB88LTOF86upmJmbvZdQ2lw=="], - - "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-4/GyVjmhR+Tc6HLJvwc1sOhPqAZtySiSMesOZyX6JQ5XBxoTDEMKQzvo07NIK6nTon/SivlZqvhzvuVBNQhObQ=="], - - "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-l9eeLsCNvPpmSXUej0etw/J1eqV0Jj1D5G/xG6YTijmE6dkv6E2QezgWbTfQk63v952DPqrjOCoiqxq7Bw0YUQ=="], - - "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.2.4", "", { "os": "none", "cpu": "arm64" }, "sha512-e0F355MSTMm3+UOqtV3L24gFUp2N5m1f8L/7d56deik6va+AXdrt9F8LbzGpeWGWRbZEDq4m8NVnJDeBtf9DZg=="], - - "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.2.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-AWLi0uBRYh6QlE7OKhiz+phZC0qwtij2QZmhmOdsLdFn64m7oMpooE9ICE3lhm9xMb4SpDo2WbHcxX1iFLFtqw=="], - - "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.2.4", "", { "os": "win32", "cpu": "x64" }, "sha512-UwSDJOg3dqCAejWdxclJjCsh3Qq4vLYMDxmyHqo1btz3stK2VqgwNd3mm5tuIwzSlGIQ/1H9Hr+Zn09mrezNqQ=="], - "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.3", "", {}, "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q=="], "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.63.1", "", { "os": "android", "cpu": "arm" }, "sha512-UZ8sUxPTiHWYX9QNdJedb1kDZSpS1t/VPWBWGSgqHNi9w3Cu6IXvu2mzbhiTiPvtrqgTQJ+zqiAq2iPIPilpaQ=="], @@ -457,8 +414,6 @@ "@voidzero-dev/vite-plus-win32-x64-msvc": ["@voidzero-dev/vite-plus-win32-x64-msvc@0.3.0", "", { "os": "win32", "cpu": "x64" }, "sha512-7+G+GxGmxdpQO0zjiGnkZFXKGqm0CrVduebRsJd6ccuOuxCQYPxLcoHq4WOaGrh56SrAGS7XjhnQCrXRkzKUVQ=="], - "@yuku-codegen/binding-android-arm64": ["@yuku-codegen/binding-android-arm64@0.8.7", "", { "os": "android", "cpu": "arm64" }, "sha512-C/0zV5IhgVdYhGJTwrY0v8dknxlhiKwtVJkMUaexu9/QvRmzlV4vfU3hZlUSgqc2BxQHntL1mCVbDq8j0FRFDw=="], - "@yuku-codegen/binding-darwin-arm64": ["@yuku-codegen/binding-darwin-arm64@0.5.48", "", { "os": "darwin", "cpu": "arm64" }, "sha512-yo96Oef12WzqnphInfz/eexVse3+kWgfGS5g2S3rFS3dcGn1ENW9xLFDZUP9rh+yP76DOq38wBoFi1+I9+6qBg=="], "@yuku-codegen/binding-darwin-x64": ["@yuku-codegen/binding-darwin-x64@0.5.48", "", { "os": "darwin", "cpu": "x64" }, "sha512-aRCTw0EZC4bVosmw//0OMYP5tGWFE0Cu5yUBFkUbhXx/iBzvORcJ2xPNlOp/vtCCo9Ys4vp8b0DigJV6uOVb2g=="], @@ -481,8 +436,6 @@ "@yuku-codegen/binding-win32-x64": ["@yuku-codegen/binding-win32-x64@0.5.48", "", { "os": "win32", "cpu": "x64" }, "sha512-X5YWJLO6EfBZpeBqO0AYESnUizbpFDWArcvVD61w0PEWQ3CaFRLnbQXs+kpM4ZZfGMfIE22zfA08QSY67q7TNQ=="], - "@yuku-parser/binding-android-arm64": ["@yuku-parser/binding-android-arm64@0.8.7", "", { "os": "android", "cpu": "arm64" }, "sha512-eGKYiUDX7Y0V7tDTmg+JTVnXnjMqfXXsorZ+EDf5kxwchQ3Or1HS14MzI2fw+jFhHR85fCWt+mtX33Yao73hIQ=="], - "@yuku-parser/binding-darwin-arm64": ["@yuku-parser/binding-darwin-arm64@0.5.48", "", { "os": "darwin", "cpu": "arm64" }, "sha512-If8mb7HH3vqghJ2NNZ8SuHfhsnjVzOxJpB8xcNOXS5WjYrs2mUhHIh5KOIvK13hDOzh0htGeGK3A6MsiEqE7HQ=="], "@yuku-parser/binding-darwin-x64": ["@yuku-parser/binding-darwin-x64@0.5.48", "", { "os": "darwin", "cpu": "x64" }, "sha512-EimvPXfspzxf1K11eB6tCW5oiQEXB8g84T2wP1TwzQagdDKo33bkmmVF0B32vTIpXnk/Ifu5IB61izZ1MylljA=="], @@ -509,8 +462,6 @@ "@zcode-plugins/model-config-plugin": ["@zcode-plugins/model-config-plugin@workspace:plugins/zcode-model-config-plugin"], - "@zcode-plugins/start-plan-plugin": ["@zcode-plugins/start-plan-plugin@workspace:plugins/zcode-start-plan-plugin"], - "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], "acorn": ["acorn@8.18.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ=="], @@ -523,8 +474,6 @@ "ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], - "ansis": ["ansis@4.3.1", "", {}, "sha512-BJ8/l4R5LRE7hW9WdSuGYrLSHi2ynxeFpDFbH0K/CgNeY/tyhk+vO6TYxXC5r5CpUhNVX310xzPsN/H9lCdfOA=="], - "aria-query": ["aria-query@5.3.0", "", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A=="], "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], @@ -539,8 +488,6 @@ "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], - "cac": ["cac@7.0.0", "", {}, "sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ=="], - "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], @@ -569,8 +516,6 @@ "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], - "defu": ["defu@6.1.7", "", {}, "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ=="], - "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], @@ -579,16 +524,12 @@ "dom-accessibility-api": ["dom-accessibility-api@0.5.16", "", {}, "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg=="], - "dts-resolver": ["dts-resolver@3.0.0", "", { "peerDependencies": { "oxc-resolver": ">=11.0.0" }, "optionalPeers": ["oxc-resolver"] }, "sha512-1T1f+z+4tl9XD+m+0HBgWoL/nm0bOIffyWaUuUSBlFg/86IWvfx+wjNaO/ybU0AJzG9/Mi5hBUgGV6zCmWEN7Q=="], - "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], "electron-to-chromium": ["electron-to-chromium@1.5.418", "", {}, "sha512-UzS26r3AEbG5wSoGVpJKqwHIU9zwQN7LHdVIThDrJpS0I5KdlXFMEb8543fhc9dVnIIAST6ar8rhwa00AL5MlA=="], - "empathic": ["empathic@2.0.1", "", {}, "sha512-YGRs8knHhKHVShLkFET/rWAU8kmHbOV5LwN938RHI0pljAJ1Gf6SzXsSmRaEzcXTtOOmVqJ5+WtQPL5uigY50Q=="], - "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], @@ -641,8 +582,6 @@ "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], - "get-tsconfig": ["get-tsconfig@5.0.0-beta.5", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-/6gFNr0N04nob252sTQxyFLi3eKFRqIg1I87YcqAMT1i6SQrSF6KujUEQrtrjMV0H/eejTCltLdDSTEMzHbnsQ=="], - "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], @@ -651,14 +590,10 @@ "hono": ["hono@4.13.2", "", {}, "sha512-JydRilDRkYBQMt9qR9U92mXxmbGqsqSn/IKOrh4e7/gEbn+0zSr8igTu0obwJoNGN4sez28DIql7FBHWydoJpA=="], - "hookable": ["hookable@6.1.1", "", {}, "sha512-U9LYDy1CwhMCnprUfeAZWZGByVbhd54hwepegYTK7Pi5NvqEj63ifz5z+xukznehT7i6NIZRu89Ay1AZmRsLEQ=="], - "http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], "iconv-lite": ["iconv-lite@0.7.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ=="], - "import-without-cache": ["import-without-cache@0.4.0", "", {}, "sha512-NkJQA7oZ4YHQhd2+H3BoRFKF3d/XNsiKpHZCQEMH9pDX27hQQLsTyOocyRgaIVtf8gHX3Nt3LPkR4e5EdtPAGQ=="], - "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], "invariant": ["invariant@2.2.4", "", { "dependencies": { "loose-envify": "^1.0.0" } }, "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA=="], @@ -779,8 +714,6 @@ "qs": ["qs@6.15.3", "", { "dependencies": { "es-define-property": "^1.0.1", "side-channel": "^1.1.1" } }, "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A=="], - "quansync": ["quansync@1.0.0", "", {}, "sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA=="], - "range-parser": ["range-parser@1.3.0", "", {}, "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw=="], "raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="], @@ -795,12 +728,6 @@ "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], - "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], - - "rolldown": ["rolldown@1.2.4", "", { "dependencies": { "@oxc-project/types": "=0.144.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.2.4", "@rolldown/binding-darwin-arm64": "1.2.4", "@rolldown/binding-darwin-x64": "1.2.4", "@rolldown/binding-freebsd-x64": "1.2.4", "@rolldown/binding-linux-arm-gnueabihf": "1.2.4", "@rolldown/binding-linux-arm64-gnu": "1.2.4", "@rolldown/binding-linux-arm64-musl": "1.2.4", "@rolldown/binding-linux-ppc64-gnu": "1.2.4", "@rolldown/binding-linux-s390x-gnu": "1.2.4", "@rolldown/binding-linux-x64-gnu": "1.2.4", "@rolldown/binding-linux-x64-musl": "1.2.4", "@rolldown/binding-openharmony-arm64": "1.2.4", "@rolldown/binding-win32-arm64-msvc": "1.2.4", "@rolldown/binding-win32-x64-msvc": "1.2.4" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-rSr7irW0K7QRWzjdJXqZowkcRdDtjRduh43rBltnVKd0VFq839l1lJoDvGJb6gl7+4rTTCrPWu+YfujUL8Ug7w=="], - - "rolldown-plugin-dts": ["rolldown-plugin-dts@0.27.14", "", { "dependencies": { "dts-resolver": "^3.0.0", "get-tsconfig": "5.0.0-beta.5", "obug": "^2.1.4", "yuku-ast": "^0.8.0", "yuku-codegen": "^0.8.0", "yuku-parser": "^0.8.0" }, "peerDependencies": { "@typescript/native-preview": "*", "@volar/typescript": "~2.4.0", "rolldown": "^1.0.0", "typescript": "^5.0.0 || ^6.0.0 || ~7.0.0", "vue-tsc": "~3.2.0 || ~3.3.0" }, "optionalPeers": ["@typescript/native-preview", "@volar/typescript", "typescript", "vue-tsc"] }, "sha512-ZvuDDwoIpRK9RPxDXratCpklFO9QZZWndf/sd0VBFb4LEj0jj07UcHK9OCh7V4XiFz2Z89ziyBC2K6tJiDjrbw=="], - "rollup": ["rollup@4.63.1", "", { "dependencies": { "@types/estree": "1.0.9" }, "optionalDependencies": { "@napi-rs/lzma-linux-x64-gnu": "1.5.1", "@rollup/rollup-android-arm-eabi": "4.63.1", "@rollup/rollup-android-arm64": "4.63.1", "@rollup/rollup-darwin-arm64": "4.63.1", "@rollup/rollup-darwin-x64": "4.63.1", "@rollup/rollup-freebsd-arm64": "4.63.1", "@rollup/rollup-freebsd-x64": "4.63.1", "@rollup/rollup-linux-arm-gnueabihf": "4.63.1", "@rollup/rollup-linux-arm-musleabihf": "4.63.1", "@rollup/rollup-linux-arm64-gnu": "4.63.1", "@rollup/rollup-linux-arm64-musl": "4.63.1", "@rollup/rollup-linux-loong64-gnu": "4.63.1", "@rollup/rollup-linux-loong64-musl": "4.63.1", "@rollup/rollup-linux-ppc64-gnu": "4.63.1", "@rollup/rollup-linux-ppc64-musl": "4.63.1", "@rollup/rollup-linux-riscv64-gnu": "4.63.1", "@rollup/rollup-linux-riscv64-musl": "4.63.1", "@rollup/rollup-linux-s390x-gnu": "4.63.1", "@rollup/rollup-linux-x64-gnu": "4.63.1", "@rollup/rollup-linux-x64-musl": "4.63.1", "@rollup/rollup-openbsd-x64": "4.63.1", "@rollup/rollup-openharmony-arm64": "4.63.1", "@rollup/rollup-win32-arm64-msvc": "4.63.1", "@rollup/rollup-win32-ia32-msvc": "4.63.1", "@rollup/rollup-win32-x64-gnu": "4.63.1", "@rollup/rollup-win32-x64-msvc": "4.63.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-3Df9jsstwhccuEfmAMi9l8XUh/GOkVObmFTU7CCVBysEbcOZLl84jCtaAZMcPiMz2EGKsATzQcU+Xr3n/wU6cg=="], "router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="], @@ -857,16 +784,10 @@ "totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="], - "tree-kill": ["tree-kill@1.2.2", "", { "bin": { "tree-kill": "cli.js" } }, "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A=="], - - "tsdown": ["tsdown@0.22.14", "", { "dependencies": { "ansis": "^4.3.1", "cac": "^7.0.0", "defu": "^6.1.7", "empathic": "^2.0.1", "hookable": "^6.1.1", "import-without-cache": "^0.4.0", "obug": "^2.1.4", "picomatch": "^4.0.5", "rolldown": "~1.2.0", "rolldown-plugin-dts": "^0.27.13", "tinyexec": "^1.2.4", "tinyglobby": "^0.2.17", "tree-kill": "^1.2.2", "unconfig-core": "^7.5.0", "verkit": "^0.3.0" }, "peerDependencies": { "@arethetypeswrong/core": "^0.18.1", "@tsdown/css": "0.22.14", "@tsdown/exe": "0.22.14", "@vitejs/devtools": "*", "publint": "^0.3.8", "tsx": "*", "typescript": "^5.0.0 || ^6.0.0 || ^7.0.0", "unplugin-unused": "^0.5.0", "unrun": "*" }, "optionalPeers": ["@arethetypeswrong/core", "@tsdown/css", "@tsdown/exe", "@vitejs/devtools", "publint", "tsx", "typescript", "unplugin-unused", "unrun"], "bin": { "tsdown": "./dist/run.mjs" } }, "sha512-ule7Y+fsAN2iZbLDoo7C4KYljFJNJJ+fLshyn+9gozeTspVersWHxwdGB+Dm2hzA38s6muFnUTl0jK3vJm9ifQ=="], - "type-is": ["type-is@2.1.0", "", { "dependencies": { "content-type": "^2.0.0", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA=="], "typescript": ["typescript@7.0.2", "", { "optionalDependencies": { "@typescript/typescript-aix-ppc64": "7.0.2", "@typescript/typescript-darwin-arm64": "7.0.2", "@typescript/typescript-darwin-x64": "7.0.2", "@typescript/typescript-freebsd-arm64": "7.0.2", "@typescript/typescript-freebsd-x64": "7.0.2", "@typescript/typescript-linux-arm": "7.0.2", "@typescript/typescript-linux-arm64": "7.0.2", "@typescript/typescript-linux-loong64": "7.0.2", "@typescript/typescript-linux-mips64el": "7.0.2", "@typescript/typescript-linux-ppc64": "7.0.2", "@typescript/typescript-linux-riscv64": "7.0.2", "@typescript/typescript-linux-s390x": "7.0.2", "@typescript/typescript-linux-x64": "7.0.2", "@typescript/typescript-netbsd-arm64": "7.0.2", "@typescript/typescript-netbsd-x64": "7.0.2", "@typescript/typescript-openbsd-arm64": "7.0.2", "@typescript/typescript-openbsd-x64": "7.0.2", "@typescript/typescript-sunos-x64": "7.0.2", "@typescript/typescript-win32-arm64": "7.0.2", "@typescript/typescript-win32-x64": "7.0.2" }, "bin": { "tsc": "bin/tsc" } }, "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA=="], - "unconfig-core": ["unconfig-core@7.5.0", "", { "dependencies": { "@quansync/fs": "^1.0.0", "quansync": "^1.0.0" } }, "sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w=="], - "undici-types": ["undici-types@8.3.0", "", {}, "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ=="], "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], @@ -877,8 +798,6 @@ "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], - "verkit": ["verkit@0.3.2", "", {}, "sha512-zj/ob3UsvJGN0whEAKFp53REA5X66hvffVqoCtVQAakJKnKlH+/PcOfMoFwIG/o4rElqLv/ycAFlx8ZlXUorCg=="], - "vite": ["vite@7.3.6", "", { "dependencies": { "esbuild": "^0.27.0 || ^0.28.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg=="], "vite-plus": ["vite-plus@0.3.0", "", { "dependencies": { "@oxc-project/types": "=0.146.0", "@oxlint/plugins": "=1.79.0", "@vitest/browser": "4.1.11", "@vitest/browser-preview": "4.1.11", "@vitest/expect": "4.1.11", "@vitest/mocker": "4.1.11", "@vitest/pretty-format": "4.1.11", "@vitest/runner": "4.1.11", "@vitest/snapshot": "4.1.11", "@vitest/spy": "4.1.11", "@vitest/utils": "4.1.11", "@voidzero-dev/vite-plus-core": "0.3.0", "oxfmt": "=0.64.0", "oxlint": "=1.79.0", "oxlint-tsgolint": "=7.0.2001", "vitest": "4.1.11" }, "optionalDependencies": { "@voidzero-dev/vite-plus-darwin-arm64": "0.3.0", "@voidzero-dev/vite-plus-darwin-x64": "0.3.0", "@voidzero-dev/vite-plus-linux-arm64-gnu": "0.3.0", "@voidzero-dev/vite-plus-linux-arm64-musl": "0.3.0", "@voidzero-dev/vite-plus-linux-x64-gnu": "0.3.0", "@voidzero-dev/vite-plus-linux-x64-musl": "0.3.0", "@voidzero-dev/vite-plus-win32-arm64-msvc": "0.3.0", "@voidzero-dev/vite-plus-win32-x64-msvc": "0.3.0" }, "peerDependencies": { "@vitest/browser-playwright": "4.1.11", "@vitest/browser-webdriverio": "4.1.11" }, "optionalPeers": ["@vitest/browser-playwright", "@vitest/browser-webdriverio"], "bin": { "oxfmt": "./bin/oxfmt", "oxlint": "./bin/oxlint", "vp": "./bin/vp", "vpr": "./bin/vpr" } }, "sha512-GNWbWuWD37frCSFrz6MLzUo62bTv5IOJozHEgZYOkxsLkuQtTwm4TowzpfoGrSsfwhAAtfPd/sK1Y0+v1SwhZA=="], @@ -897,8 +816,6 @@ "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], - "yuku-ast": ["yuku-ast@0.8.7", "", { "dependencies": { "@yuku-toolchain/types": "^0.8.7" } }, "sha512-h6+4bDfyootiMB9vckk5uKo5r5j0GHrkr17FQTDNfEsFT3DWlN9uu1HJwQwc64pgmLCI945fWM3lbTIqxjT3GQ=="], - "yuku-codegen": ["yuku-codegen@0.5.48", "", { "dependencies": { "@yuku-toolchain/types": "0.5.43" }, "optionalDependencies": { "@yuku-codegen/binding-darwin-arm64": "0.5.48", "@yuku-codegen/binding-darwin-x64": "0.5.48", "@yuku-codegen/binding-freebsd-x64": "0.5.48", "@yuku-codegen/binding-linux-arm-gnu": "0.5.48", "@yuku-codegen/binding-linux-arm-musl": "0.5.48", "@yuku-codegen/binding-linux-arm64-gnu": "0.5.48", "@yuku-codegen/binding-linux-arm64-musl": "0.5.48", "@yuku-codegen/binding-linux-x64-gnu": "0.5.48", "@yuku-codegen/binding-linux-x64-musl": "0.5.48", "@yuku-codegen/binding-win32-arm64": "0.5.48", "@yuku-codegen/binding-win32-x64": "0.5.48" } }, "sha512-p7HxD5Xl4jzDzqMrGePAOeSHmRY4g58h4HuGq15weQFPxuPWd/W6e7nqp/+Lea6JfpOdBwJOAyXFqIZ/J9Zfnw=="], "yuku-parser": ["yuku-parser@0.5.48", "", { "dependencies": { "@yuku-toolchain/types": "0.5.43" }, "optionalDependencies": { "@yuku-parser/binding-darwin-arm64": "0.5.48", "@yuku-parser/binding-darwin-x64": "0.5.48", "@yuku-parser/binding-freebsd-x64": "0.5.48", "@yuku-parser/binding-linux-arm-gnu": "0.5.48", "@yuku-parser/binding-linux-arm-musl": "0.5.48", "@yuku-parser/binding-linux-arm64-gnu": "0.5.48", "@yuku-parser/binding-linux-arm64-musl": "0.5.48", "@yuku-parser/binding-linux-x64-gnu": "0.5.48", "@yuku-parser/binding-linux-x64-musl": "0.5.48", "@yuku-parser/binding-win32-arm64": "0.5.48", "@yuku-parser/binding-win32-x64": "0.5.48" } }, "sha512-OWBfhrpgK9+/4+IXG9oT8Bao4AhViQA7vdyNNH7EUg8dQYgwa70XtIBWTpCEme1P1ECyoDNYkn0wT63f8XRcVA=="], @@ -911,74 +828,14 @@ "@zcode-plugins/model-config-plugin/@types/bun": ["@types/bun@1.4.0", "", { "dependencies": { "bun-types": "1.4.0" } }, "sha512-K+lZULY23vRgK/CfTjFIV+tyifaNdSMlPh9j+6mQ/cLfpOznLyAuzgV/JQysyECpkBQLVMSyvjlr2fBUSA9wFQ=="], - "@zcode-plugins/start-plan-plugin/zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], - "body-parser/content-type": ["content-type@2.1.0", "", {}, "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag=="], "bun-types/@types/node": ["@types/node@26.2.0", "", { "dependencies": { "undici-types": "~8.3.0" } }, "sha512-5IviulTZeRNp2vAJ514cc/HUlY5nZ9fCbq9DMyC52BrhFZACo3nI0R7qBxhQmo/d27NFe96ur/b7Wwxklda+kg=="], - "rolldown/@oxc-project/types": ["@oxc-project/types@0.144.0", "", {}, "sha512-nuhZIOLuI6TFQ32I/WnUx+SCPY7SdSKwgnFHydAuoS1+Z4BRcaP+RRJmGzl9lw+0OFF7UmaESf7KQRXaNLHypg=="], - - "rolldown/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.1", "", {}, "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw=="], - - "rolldown-plugin-dts/yuku-codegen": ["yuku-codegen@0.8.7", "", { "dependencies": { "@yuku-toolchain/types": "^0.8.7" }, "optionalDependencies": { "@yuku-codegen/binding-android-arm64": "0.8.7", "@yuku-codegen/binding-darwin-arm64": "0.8.7", "@yuku-codegen/binding-darwin-x64": "0.8.7", "@yuku-codegen/binding-freebsd-x64": "0.8.7", "@yuku-codegen/binding-linux-arm-gnu": "0.8.7", "@yuku-codegen/binding-linux-arm-musl": "0.8.7", "@yuku-codegen/binding-linux-arm64-gnu": "0.8.7", "@yuku-codegen/binding-linux-arm64-musl": "0.8.7", "@yuku-codegen/binding-linux-x64-gnu": "0.8.7", "@yuku-codegen/binding-linux-x64-musl": "0.8.7", "@yuku-codegen/binding-win32-arm64": "0.8.7", "@yuku-codegen/binding-win32-x64": "0.8.7" } }, "sha512-adwDZSh8oVDzhE6Du9PwVWxcOxeV0e2EVhUuMKWfhSY4wkrDq9eqixlxFF3l/XGUV1E7UFzhpz9393MUumkyNw=="], - - "rolldown-plugin-dts/yuku-parser": ["yuku-parser@0.8.7", "", { "dependencies": { "@yuku-toolchain/types": "^0.8.7", "yuku-ast": "^0.8.7" }, "optionalDependencies": { "@yuku-parser/binding-android-arm64": "0.8.7", "@yuku-parser/binding-darwin-arm64": "0.8.7", "@yuku-parser/binding-darwin-x64": "0.8.7", "@yuku-parser/binding-freebsd-x64": "0.8.7", "@yuku-parser/binding-linux-arm-gnu": "0.8.7", "@yuku-parser/binding-linux-arm-musl": "0.8.7", "@yuku-parser/binding-linux-arm64-gnu": "0.8.7", "@yuku-parser/binding-linux-arm64-musl": "0.8.7", "@yuku-parser/binding-linux-x64-gnu": "0.8.7", "@yuku-parser/binding-linux-x64-musl": "0.8.7", "@yuku-parser/binding-win32-arm64": "0.8.7", "@yuku-parser/binding-win32-x64": "0.8.7" } }, "sha512-vRD9nwt4L3aYpxNqeSC4WqLv58xrXef0Ong1Mc45CTXTIpvLafx7JO05sczmQZwdLEZvywrLOGdNC5+Rp5N1BQ=="], - "type-is/content-type": ["content-type@2.1.0", "", {}, "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag=="], - "yuku-ast/@yuku-toolchain/types": ["@yuku-toolchain/types@0.8.7", "", {}, "sha512-2Z53dNxAJL6UvFoIrDZvYf3zlO8s4VJK4O2hhaB4mXVwwpX/7ajtss3cmfqKvamlNLWyt9FSWs4eoYdlbxpnHA=="], - "@zcode-plugins/model-config-plugin/@types/bun/bun-types": ["bun-types@1.4.0", "", { "dependencies": { "@types/node": "*" } }, "sha512-iIKw23BspnQQYd3prITOBxeUsxBHnwzX6YJfGMuNOZzeNcMmVqzIIVGRm1l69ogaPQmb4wB6BN8mA5bE9YuC5Q=="], - "rolldown-plugin-dts/yuku-codegen/@yuku-codegen/binding-darwin-arm64": ["@yuku-codegen/binding-darwin-arm64@0.8.7", "", { "os": "darwin", "cpu": "arm64" }, "sha512-/u+REDMI4a0/lsJXTM4c53/w31OGZLOReZIyg62uhgLs0kc8NHsj/nOcxTdlQjq5gi0zhdkccD9LaLTXcdzPvw=="], - - "rolldown-plugin-dts/yuku-codegen/@yuku-codegen/binding-darwin-x64": ["@yuku-codegen/binding-darwin-x64@0.8.7", "", { "os": "darwin", "cpu": "x64" }, "sha512-sMMzFOwCo4WXR+/6zIBThOocSC50iIIZZdfiIDbaLvj0Ax/rWt/iavyfEAqajyvzydLyCqR/ZItdLWSRlu1umw=="], - - "rolldown-plugin-dts/yuku-codegen/@yuku-codegen/binding-freebsd-x64": ["@yuku-codegen/binding-freebsd-x64@0.8.7", "", { "os": "freebsd", "cpu": "x64" }, "sha512-MpdpKXix9P+Y1rKgjvcNeNtGjXeL1CmttNhYINrWls8kRpm4xM/oBGTmn6w7to8lAlwj5jm8q03dQPl5mRv4Qw=="], - - "rolldown-plugin-dts/yuku-codegen/@yuku-codegen/binding-linux-arm-gnu": ["@yuku-codegen/binding-linux-arm-gnu@0.8.7", "", { "os": "linux", "cpu": "arm" }, "sha512-rr1srFLlPAmC1vtxfc9C1YLDe3iH09YjfSeeIidBqKhzx1MATjOAq4mjlRUOnhr/L27MotWIYOFKwVsd5JZFOg=="], - - "rolldown-plugin-dts/yuku-codegen/@yuku-codegen/binding-linux-arm-musl": ["@yuku-codegen/binding-linux-arm-musl@0.8.7", "", { "os": "linux", "cpu": "arm" }, "sha512-eAufXh8qBRpiSO6ueaMDL+yyoXIGLhpUce72YbcACtZU2qhExwBIJyEtQf5kH2Ki0X2aqkSjSfog4OPtKXan3Q=="], - - "rolldown-plugin-dts/yuku-codegen/@yuku-codegen/binding-linux-arm64-gnu": ["@yuku-codegen/binding-linux-arm64-gnu@0.8.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-gw4w6wPoHObBrdIC4duVWLmJOvpdE25j5D7yrM5mACNlK4klRz/lv8hK+ssQk9EJHBgZjSaqZIJVFgqNYbfv7A=="], - - "rolldown-plugin-dts/yuku-codegen/@yuku-codegen/binding-linux-arm64-musl": ["@yuku-codegen/binding-linux-arm64-musl@0.8.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-L68N6Y4XkqcIaKo3Ra88JEvBEH4AHff44A4INcrxeVWZ8CZtu2tCpfVxe3hR8qQQMcvBSQlDn24KpkCKEhVvfA=="], - - "rolldown-plugin-dts/yuku-codegen/@yuku-codegen/binding-linux-x64-gnu": ["@yuku-codegen/binding-linux-x64-gnu@0.8.7", "", { "os": "linux", "cpu": "x64" }, "sha512-dzyAbltJmf3Cqlb8HcFuYIf5Yn0fl1vTr3XJ9HiVNNvOlhqPSArOqtw9vI1p6/VTXTjrMLXlU+s+/kNHiIy/Cw=="], - - "rolldown-plugin-dts/yuku-codegen/@yuku-codegen/binding-linux-x64-musl": ["@yuku-codegen/binding-linux-x64-musl@0.8.7", "", { "os": "linux", "cpu": "x64" }, "sha512-Otw4MH3404q0Bbvl+YTdW9aoUV5vXmUw8260bWvt1XlaoIX/ceSgI4ygheRDMfBPoHt6FDayQaO9OLVUZAgkFA=="], - - "rolldown-plugin-dts/yuku-codegen/@yuku-codegen/binding-win32-arm64": ["@yuku-codegen/binding-win32-arm64@0.8.7", "", { "os": "win32", "cpu": "arm64" }, "sha512-qo/jyrzryiBuKEsFiuWaBCBe3tRMynQ0qFWFgOEjcCMQeZfBm+wKiVEUEFXXLc7bh8YezguAWp0Mtnhq4ARNyA=="], - - "rolldown-plugin-dts/yuku-codegen/@yuku-codegen/binding-win32-x64": ["@yuku-codegen/binding-win32-x64@0.8.7", "", { "os": "win32", "cpu": "x64" }, "sha512-D5lDsVDx6m00E6bWySlWdH72Ca4TPSaphDqB6QjU6MpuNLIJqoGoatYyq2rOmBE8Zv/kunot/o58KGL03P3eiA=="], - - "rolldown-plugin-dts/yuku-codegen/@yuku-toolchain/types": ["@yuku-toolchain/types@0.8.7", "", {}, "sha512-2Z53dNxAJL6UvFoIrDZvYf3zlO8s4VJK4O2hhaB4mXVwwpX/7ajtss3cmfqKvamlNLWyt9FSWs4eoYdlbxpnHA=="], - - "rolldown-plugin-dts/yuku-parser/@yuku-parser/binding-darwin-arm64": ["@yuku-parser/binding-darwin-arm64@0.8.7", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Re0RHelKLnjEURulY2/KxW+Ngb8zuNA4BRZuMwgGQNzVumT6u4U2N2hc01oeYVNVof0i7GrXE4UCNBgbpRRnjQ=="], - - "rolldown-plugin-dts/yuku-parser/@yuku-parser/binding-darwin-x64": ["@yuku-parser/binding-darwin-x64@0.8.7", "", { "os": "darwin", "cpu": "x64" }, "sha512-Hn8DROtQkjlA1ACbPgj4a7eP9IuVOI504oiTwpkWPbpaDWD9KdmnVYCqW+1LfenNK/g7O9NhWGpXEdaCNX7lIA=="], - - "rolldown-plugin-dts/yuku-parser/@yuku-parser/binding-freebsd-x64": ["@yuku-parser/binding-freebsd-x64@0.8.7", "", { "os": "freebsd", "cpu": "x64" }, "sha512-bAP2OV8wRuzplX/jYxv9+vvqQT8JxyNphI8fLfXGL054Xs+4/J5u33cIm3y4rxY8rdoLmmdiJs2Tq7r7lrDRfA=="], - - "rolldown-plugin-dts/yuku-parser/@yuku-parser/binding-linux-arm-gnu": ["@yuku-parser/binding-linux-arm-gnu@0.8.7", "", { "os": "linux", "cpu": "arm" }, "sha512-kTYwJQQgmZeAWdDIWabiReIZMpmfLueIj1tCmjStUtFGhR1Z0qwxonKVfUC4N7h/VhGGzLZ//7O1kgt1QKqgCg=="], - - "rolldown-plugin-dts/yuku-parser/@yuku-parser/binding-linux-arm-musl": ["@yuku-parser/binding-linux-arm-musl@0.8.7", "", { "os": "linux", "cpu": "arm" }, "sha512-uL4jE8HPT2BLlxAXyD10LqgPuXa9eDa0BKpCdSANmzIJghq/2eZo3/gQNtaxPZMupWoxjYzSad9IXrwu7aYPXQ=="], - - "rolldown-plugin-dts/yuku-parser/@yuku-parser/binding-linux-arm64-gnu": ["@yuku-parser/binding-linux-arm64-gnu@0.8.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-3gVN4pWSKZmXiNX7cU164dR9MPvesCHnlH6nPfpK+yQsCuYphjKKplcb4SnZBrhiqmXbgb2HR0c2TS0IZUPhgA=="], - - "rolldown-plugin-dts/yuku-parser/@yuku-parser/binding-linux-arm64-musl": ["@yuku-parser/binding-linux-arm64-musl@0.8.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-S0mwfEjoLpxzXeZw802Wa4RaELsQiPtWqG6INcy8j4GtvNFtl4LCX3eGO1XLn9pyLAISLzTRyU3zUCBUPin8lg=="], - - "rolldown-plugin-dts/yuku-parser/@yuku-parser/binding-linux-x64-gnu": ["@yuku-parser/binding-linux-x64-gnu@0.8.7", "", { "os": "linux", "cpu": "x64" }, "sha512-lnbWdPmerE5D1uH1G4IEZKnPzCrWCStRGrtgpSIe1RibAo5bZIjDbbbPYXmMHCEh4F+x/JaJpElh26a3r+BPbg=="], - - "rolldown-plugin-dts/yuku-parser/@yuku-parser/binding-linux-x64-musl": ["@yuku-parser/binding-linux-x64-musl@0.8.7", "", { "os": "linux", "cpu": "x64" }, "sha512-769uwndMvMzUvATWbAcEvyLHKA+DzhHSCl/obBUrRdYfRo26yxui6S8y3z7uJ+Naup7UKrDxrpK7OnQkxkl9KQ=="], - - "rolldown-plugin-dts/yuku-parser/@yuku-parser/binding-win32-arm64": ["@yuku-parser/binding-win32-arm64@0.8.7", "", { "os": "win32", "cpu": "arm64" }, "sha512-mEB/9PlaAkisJ6KWGz0zvywXoU6+80dTlR2LwS7s/jcXXoU6fm2+sitBZXtqu3+Q4DcDgPxM45uWMCzPs0TSRw=="], - - "rolldown-plugin-dts/yuku-parser/@yuku-parser/binding-win32-x64": ["@yuku-parser/binding-win32-x64@0.8.7", "", { "os": "win32", "cpu": "x64" }, "sha512-8vNB2DP0ou61nGb8tc/qfi41gfyDXz1MHr2zqL3nR+cJ6CEbiuWV/l/a/vv151gCgiZLLAyGkQGENpozdg716w=="], - - "rolldown-plugin-dts/yuku-parser/@yuku-toolchain/types": ["@yuku-toolchain/types@0.8.7", "", {}, "sha512-2Z53dNxAJL6UvFoIrDZvYf3zlO8s4VJK4O2hhaB4mXVwwpX/7ajtss3cmfqKvamlNLWyt9FSWs4eoYdlbxpnHA=="], - "@zcode-plugins/model-config-plugin/@types/bun/bun-types/@types/node": ["@types/node@26.2.0", "", { "dependencies": { "undici-types": "~8.3.0" } }, "sha512-5IviulTZeRNp2vAJ514cc/HUlY5nZ9fCbq9DMyC52BrhFZACo3nI0R7qBxhQmo/d27NFe96ur/b7Wwxklda+kg=="], } } diff --git a/plugins/zcode-start-plan-plugin/.zcode-plugin/plugin.json b/plugins/zcode-start-plan-plugin/.zcode-plugin/plugin.json deleted file mode 100644 index 4e2f6c2..0000000 --- a/plugins/zcode-start-plan-plugin/.zcode-plugin/plugin.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "zcode-start-plan", - "version": "0.1.0", - "description": "Use the free ZCode Start Plan trial quota (zcode-plan endpoints) from the ZCode CLI through an MCP tool: status checks, delegated generation, and one-time Aliyun captcha bridging. Unofficial; requires prior `zcode login` (OAuth).", - "author": { "name": "kingsword09" }, - "license": "MIT", - "mcpServers": { - "zcode-start-plan": { - "type": "stdio", - "command": "node", - "args": ["${ZCODE_PLUGIN_ROOT}/dist/mcp/server.js"], - "env": {} - } - }, - "commands": "commands", - "skills": "skills" -} diff --git a/plugins/zcode-start-plan-plugin/README.md b/plugins/zcode-start-plan-plugin/README.md deleted file mode 100644 index 734b7e1..0000000 --- a/plugins/zcode-start-plan-plugin/README.md +++ /dev/null @@ -1,106 +0,0 @@ -# zcode-start-plan - -Unofficial ZCode plugin that lets the agent **spend the free "ZCode Start Plan" trial quota** (the one that ships with a new Z.ai account) from the ZCode CLI. - -The trial is desktop-app-only by design: its endpoint (`https://zcode.z.ai/api/v1/zcode-plan/anthropic`) authenticates with the shared ZCode JWT and requires an Aliyun captcha token on each request — the desktop app renders that captcha for you, a plain CLI cannot. This plugin re-implements the desktop wire protocol so the agent can delegate generation calls to your trial quota, including a one-time browser captcha bridge. - -> [!WARNING] -> - **Unofficial.** Reverse-engineered from the ZCode desktop app (3.7.x). The protocol may break at any time. -> - **Risk control.** The captcha bridge drives the Aliyun web SDK outside a desktop-app context; heavy usage may trigger risk control on your Z.ai account. -> - **Delegated only.** The main conversation model is unchanged. Trial quota is spent by explicit `start_plan_generate` calls. - -## Prerequisites - -- ZCode CLI installed and logged in via OAuth: `zcode login` (writes the shared JWT to `~/.zcode/v2/credentials.json`). -- A Z.ai account with an active ZCode Start Plan trial (free with new accounts; check in the desktop app's plan page). -- A browser for the one-time captcha (the plugin opens a tab automatically). - -## Install - -Register this repo as a marketplace, then install the plugin — from any directory: - -```bash -# from GitHub (no clone needed) -zcode plugins marketplace add kingsword09/zcode-plugins -# …or from a local checkout -zcode plugins marketplace add /path/to/zcode-plugins - -zcode plugins install zcode-start-plan@zcode-plugins -``` - -Install copies the plugin into `~/.zcode/cli/plugins/cache/` and enables it by default; verify with `zcode plugins list` or the `/plugins` panel in a session. - -No `bun install` / `bun run build` is needed on the consuming side — the built `dist/mcp/server.js` bundle and its dependencies are installed as-is. - -### Update - -```bash -zcode plugins marketplace update zcode-plugins -zcode plugins update zcode-start-plan@zcode-plugins -``` - -Start a new ZCode session afterwards — running sessions keep the old MCP server process. - -### Uninstall - -```bash -zcode plugins uninstall zcode-start-plan@zcode-plugins -# or remove the marketplace and every plugin it installed -zcode plugins marketplace remove zcode-plugins -``` - -## What it provides - -### MCP tools - -| Tool | Purpose | -| --- | --- | -| `start_plan_status` | Check whether the account has an active trial; list usable models (e.g. `GLM-5.3`, `GLM-5-Turbo`) and plan states. | -| `start_plan_generate` | Delegated generation via the trial endpoint. Handles the captcha automatically: acquires a token first (opening a browser tab when the traceless pass is unavailable), attaches `X-Aliyun-Captcha-Verify-Param/Region`, retries `3007` (captcha rejected) with a fresh token, and backs off on `3008/3009/3010` (busy). | -| `start_plan_captcha` | Run the captcha bridge interactively (e.g. right after login, or after a 3007 failure) to stage a fresh token. | - -### Slash command - -- `/start-plan ` — checks entitlement, then delegates the prompt to `start_plan_generate`. - -### Skill - -`start-plan` — teaches the agent when to route a request to the trial quota and how to recover from captcha/busy failures. - -## How it works - -``` -~/.zcode/v2/credentials.json ──AES-256-GCM──► zcodejwttoken (Bearer) - │ -GET /api/v1/client/configs ──► captcha {region, prefix, sceneId} - │ -local http://127.0.0.1 page ──► AliyunCaptcha.js ──► verify param - │ -POST /api/v1/zcode-plan/anthropic/v1/messages - Authorization: Bearer - X-Aliyun-Captcha-Verify-Param / -Region -``` - -- Credential decryption is self-contained (Node `crypto`): AES-256-GCM, key = sha256(`ZCODE_CREDENTIAL_SECRET` or the OS-derived fallback), format `enc:v1:iv.tag.ciphertext`. -- The captcha bridge is a one-shot localhost page mirroring the desktop renderer: `initAliyunCaptcha` with traceless verification preferred; a popup challenge is shown only when the traceless pass fails. -- Env overrides match the runtime: `ZCODE_BASE_URL` / `ZCODE_ENDPOINT_ORIGIN` / `ZCODE_ENV=test`, plus `ZCODE_DATA_BASE_DIR` for the credentials path. - -## Troubleshooting - -- **"No ZCode JWT token found"** — run `zcode login` (OAuth), not API-key login. -- **`3007` persists after retry** — complete the popup in the opened browser tab; corporate proxies blocking `o.alicdn.com` will break the SDK load. -- **`3008/3009/3010`** — free tier congestion; retry later. -- **Entitlement false** — the trial expired or was already consumed for the day (it resets daily). - -## Development - -```bash -bun install -bun run build # bundles dist/mcp/server.js via tsdown (Node >= 24 target) -bun run typecheck # tsgo (TypeScript 7) -bun test -``` - -## License - -MIT diff --git a/plugins/zcode-start-plan-plugin/commands/start-plan.md b/plugins/zcode-start-plan-plugin/commands/start-plan.md deleted file mode 100644 index f2f5f94..0000000 --- a/plugins/zcode-start-plan-plugin/commands/start-plan.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -description: Generate with the free ZCode Start Plan trial quota. -argument-hint: "[prompt]" -skills: start-plan ---- - -Use the `start-plan` skill for this request: - -$ARGUMENTS - -The agent should first check `start_plan_status`; if an active trial exists, delegate the generation to `start_plan_generate` instead of the configured chat model. diff --git a/plugins/zcode-start-plan-plugin/package.json b/plugins/zcode-start-plan-plugin/package.json deleted file mode 100644 index 29c89ed..0000000 --- a/plugins/zcode-start-plan-plugin/package.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "@zcode-plugins/start-plan-plugin", - "version": "0.1.0", - "private": true, - "description": "Unofficial ZCode plugin that lets the agent use the free ZCode Start Plan trial quota (zcode-plan endpoints) through an MCP tool, with Aliyun captcha bridging.", - "license": "MIT", - "type": "module", - "main": "./dist/mcp/server.js", - "scripts": { - "build": "tsdown", - "typecheck": "tsc --noEmit", - "test": "bun test" - }, - "dependencies": { - "@modelcontextprotocol/sdk": "^1.30.0", - "zod": "^4.1.0" - }, - "devDependencies": { - "@types/bun": "^1.3.14", - "tsdown": "^0.22.14", - "typescript": "^7.0.2" - } -} diff --git a/plugins/zcode-start-plan-plugin/skills/start-plan/SKILL.md b/plugins/zcode-start-plan-plugin/skills/start-plan/SKILL.md deleted file mode 100644 index b50fb68..0000000 --- a/plugins/zcode-start-plan-plugin/skills/start-plan/SKILL.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -name: start-plan -description: Use when the user wants to spend their free ZCode Start Plan trial quota (ZCode Start Plan, zcode-plan trial, "试用额度", GLM-5.3/GLM-5-Turbo free tier) instead of the configured Coding Plan / API key, or when model calls fail with "captcha verify failed" (code 3007) and the user asks to use the desktop-app trial from the CLI. Covers checking trial entitlement, listing trial models, delegated generation via the start_plan_generate MCP tool, and completing the one-time Aliyun captcha bridge. ---- - -# ZCode Start Plan (free trial quota) - -## Overview - -This skill routes generation work to the free "ZCode Start Plan" trial that ships with new Z.ai accounts. The trial is served by `https://zcode.z.ai/api/v1/zcode-plan/anthropic` (NOT `api.z.ai`), authenticates with the shared ZCode JWT from `zcode login`, and requires an Aliyun captcha token attached to each request. The plugin's MCP server implements all of that; never call these endpoints directly with plain HTTP tools. - -## Prerequisites - -- The user ran `zcode login` (OAuth) on this machine, so `~/.zcode/v2/credentials.json` contains the shared JWT. If missing, tell the user to run `zcode login` first; do NOT ask them to paste tokens. - -## Workflow - -1. Check entitlement with the `start_plan_status` MCP tool. - - `entitled: true` → continue. - - `entitled: false` → the account has no active trial; tell the user the trial is unavailable and offer to fall back to the normal configured model. -2. Pick the model from `models` (typical: `GLM-5.3`, `GLM-5-Turbo`). -3. Call `start_plan_generate` with the prompt (and optional `system`, `max_tokens`). - - On first use (or after a 3007 captcha failure) the server opens a one-time browser tab for the Aliyun captcha. Tell the user to complete it in the opened tab; the tool call waits, usually ≤ 2 minutes. -4. Return the generated `text` to the user. Mention that the call consumed trial quota if `usage` is present. - -## Failure handling - -- `code 3007` "captcha verify failed": retry once — the server automatically reacquires a captcha param and retries; if it fails again, suggest the `start_plan_captcha` tool to run the bridge interactively. -- `3008/3009/3010` "Start Plan busy": the server retries automatically with backoff; if it still fails, tell the user the free tier is congested and offer to retry later or use the normal model. -- Credential errors: user must re-run `zcode login`. - -## Notes - -- This only affects delegated generation calls made through this skill. The main conversation model is unchanged; do not promise otherwise. -- The trial resets daily and is intended for light evaluation — avoid sending entire files or huge prompts. diff --git a/plugins/zcode-start-plan-plugin/src/captcha.ts b/plugins/zcode-start-plan-plugin/src/captcha.ts deleted file mode 100644 index 4f1b4c5..0000000 --- a/plugins/zcode-start-plan-plugin/src/captcha.ts +++ /dev/null @@ -1,227 +0,0 @@ -import { spawn } from "node:child_process"; -import { createServer, type Server } from "node:http"; -import { setTimeout as sleep } from "node:timers/promises"; -import { - ALIYUN_CAPTCHA_SDK_URL, - CAPTCHA_INTERACTIVE_TIMEOUT_MS, -} from "./constants.ts"; -import type { CaptchaConfig } from "./client.ts"; - -export type { CaptchaConfig }; - -export interface CaptchaBridgeOptions { - config: CaptchaConfig; - timeoutMs?: number; - openBrowser?: boolean; - language?: "cn" | "en"; - logger?: (message: string) => void; - fetchImpl?: typeof fetch; -} - -export interface CaptchaVerification { - param: string; - region: string; - interactive: boolean; -} - -export class CaptchaBridgeError extends Error { - constructor(message: string, options?: { cause?: unknown }) { - super(message, options); - this.name = "CaptchaBridgeError"; - } -} - -export function buildCaptchaPage(config: CaptchaConfig, options: { language: "cn" | "en"; postUrl: string }): string { - return ` - - - - -ZCode Start Plan captcha - - - -

ZCode Start Plan — human verification

-

This one-time Aliyun captcha lets the ZCode CLI use your free Start Plan trial quota. It closes itself when done.

-
- -
- - - -`; -} - -function openInBrowser(url: string): void { - const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open"; - const args = process.platform === "win32" ? ["/c", "start", "", url] : [url]; - try { - const child = spawn(command, args, { stdio: "ignore", detached: true }); - child.unref(); - } catch { - // The URL is printed as a fallback. - } -} - -export async function acquireCaptchaParam(options: CaptchaBridgeOptions): Promise { - const timeoutMs = options.timeoutMs ?? CAPTCHA_INTERACTIVE_TIMEOUT_MS; - const logger = options.logger ?? (() => {}); - const config = options.config; - if (!config.enabled || !config.region || !config.prefix || !config.sceneId) { - throw new CaptchaBridgeError( - "Aliyun captcha is not enabled for the Start Plan endpoint; the server should accept requests without a captcha param.", - ); - } - - let server: Server | null = null; - let resolved: ((value: CaptchaVerification) => void) | null = null; - let rejected: ((error: Error) => void) | null = null; - const result = new Promise((resolve, reject) => { - resolved = resolve; - rejected = reject; - }); - const resultPromise = result; - let settledFlag = false; - - const finish = (fn: () => void) => { - if (settledFlag) return; - settledFlag = true; - fn(); - if (server) { - server.close(); - server = null; - } - }; - - try { - server = createServer((request, response) => { - const url = new URL(request.url ?? "/", "http://127.0.0.1"); - if (request.method === "GET" && (url.pathname === "/" || url.pathname === "/index.html")) { - const html = buildCaptchaPage(config, { - language: options.language ?? "cn", - postUrl: "/callback", - }); - response.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }); - response.end(html); - return; - } - if (request.method === "POST" && url.pathname === "/callback") { - const chunks: Buffer[] = []; - request.on("data", (chunk: Buffer) => chunks.push(chunk)); - request.on("end", () => { - const param = Buffer.concat(chunks).toString("utf-8").trim(); - response.writeHead(200, { "Content-Type": "application/json" }); - response.end(JSON.stringify({ ok: Boolean(param) })); - if (param) { - finish(() => { - (resolved as unknown as ((v: CaptchaVerification) => void))?.({ - param, - region: config.region, - interactive: true, - }); - }); - } - }); - return; - } - response.writeHead(404, { "Content-Type": "text/plain" }); - response.end("not found"); - }); - - await new Promise((resolve, reject) => { - const created = server as Server; - created.once("error", reject); - created.listen(0, "127.0.0.1", () => resolve()); - }); - - const address = (server as Server).address(); - if (typeof address !== "object" || address === null) { - throw new CaptchaBridgeError("Failed to bind the local captcha bridge server."); - } - const url = `http://127.0.0.1:${address.port}/`; - logger(`captcha bridge listening at ${url}`); - if (options.openBrowser !== false) { - openInBrowser(url); - } else { - logger(`open ${url} in a browser to complete verification`); - } - - const timer = setTimeout(() => { - finish(() => { - (rejected as unknown as ((e: Error) => void))?.( - new CaptchaBridgeError( - `Captcha verification timed out after ${timeoutMs} ms. Re-run the tool and complete the challenge in the opened browser tab.`, - ), - ); - }); - }, timeoutMs); - timer.unref?.(); - - return await resultPromise; - } finally { - if (server) server.close(); - await sleep(0); - } -} diff --git a/plugins/zcode-start-plan-plugin/src/client.ts b/plugins/zcode-start-plan-plugin/src/client.ts deleted file mode 100644 index 342e2e8..0000000 --- a/plugins/zcode-start-plan-plugin/src/client.ts +++ /dev/null @@ -1,156 +0,0 @@ -import { - billingBalanceUrl, - CAPTCHA_CONFIG_CACHE_MS, - clientConfigsUrl, - DEFAULT_REQUEST_TIMEOUT_MS, - resolveOrigin, -} from "./constants.ts"; - -const APP_VERSION = "3.7.7"; - -function clientPlatformKey(): string { - return `${process.platform}-${process.arch}`; -} - -export interface CaptchaConfig { - enabled: boolean; - region: string; - prefix: string; - sceneId: string; -} - -export interface StartPlanBalance { - plans: Array<{ name: string | null; planId: string | null; status: string | null }>; - models: string[]; - raw: unknown; -} - -export class StartPlanHttpError extends Error { - readonly status: number; - readonly body: string; - constructor(status: number, body: string) { - super(`Start Plan endpoint returned HTTP ${status}`); - this.name = "StartPlanHttpError"; - this.status = status; - this.body = body; - } -} - -export interface StartPlanClientOptions { - env?: Record; - bearerToken?: string; - fetchImpl?: typeof fetch; - timeoutMs?: number; -} - -export class StartPlanClient { - readonly origin: string; - private readonly bearerToken: string; - private readonly fetchImpl: typeof fetch; - private readonly timeoutMs: number; - private captchaConfigCache: { value: CaptchaConfig | null; expiresAt: number } | null = null; - - constructor(options: StartPlanClientOptions) { - this.origin = resolveOrigin(options.env); - this.bearerToken = options.bearerToken ?? ""; - this.fetchImpl = options.fetchImpl ?? fetch; - this.timeoutMs = options.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS; - } - - private async fetchJson(url: string | URL, init: RequestInit & { headers?: Record } = {}): Promise { - const controller = new AbortController(); - const timer = setTimeout(() => controller.abort(), this.timeoutMs); - try { - const response = await this.fetchImpl(url, { ...init, signal: controller.signal }); - const text = await response.text(); - if (!response.ok) throw new StartPlanHttpError(response.status, text); - try { - return JSON.parse(text) as unknown; - } catch (error) { - throw new Error(`Start Plan endpoint returned non-JSON body from ${url}`, { cause: error }); - } - } finally { - clearTimeout(timer); - } - } - - async getCaptchaConfig(forceRefresh = false): Promise { - const now = Date.now(); - if (!forceRefresh && this.captchaConfigCache && this.captchaConfigCache.expiresAt > now) { - return this.captchaConfigCache.value; - } - const url = clientConfigsUrl(this.origin); - url.searchParams.set("app_version", APP_VERSION); - url.searchParams.set("platform", clientPlatformKey()); - const payload = (await this.fetchJson(url, { headers: { Accept: "application/json" } })) as - | Record - | null; - const configs = readPath(payload, ["data", "configs", "captcha"]) as Record | null; - const value = configs && typeof configs === "object" - ? { - enabled: configs.enabled !== false, - region: typeof configs.region === "string" ? configs.region : "", - prefix: typeof configs.prefix === "string" ? configs.prefix : "", - sceneId: typeof configs.sceneId === "string" ? configs.sceneId : "", - } - : null; - this.captchaConfigCache = { value, expiresAt: now + CAPTCHA_CONFIG_CACHE_MS }; - return value; - } - - async fetchBalance(bearerToken = this.bearerToken): Promise { - const url = billingBalanceUrl(this.origin); - url.searchParams.set("app_version", APP_VERSION); - const payload = (await this.fetchJson(url, { - headers: { Authorization: `Bearer ${bearerToken}`, Accept: "application/json" }, - })) as Record | null; - const data = readPath(payload, ["data"]) as Record | null; - const plansRaw = (data?.plans ?? []) as Array>; - const balancesRaw = (data?.balances ?? []) as Array>; - const models: string[] = []; - const seen = new Set(); - for (const balance of balancesRaw) { - const capabilities = Array.isArray(balance.capabilities) ? (balance.capabilities as unknown[]) : []; - const capabilityModels = capabilities - .map((item) => (typeof item === "string" && item.toLowerCase().startsWith("model:") ? item.slice(6).trim() : "")) - .filter(Boolean); - const names = capabilityModels.length > 0 - ? capabilityModels - : typeof balance.show_name === "string" ? [balance.show_name] : []; - for (const name of names) { - const key = name.toLowerCase(); - if (key && !seen.has(key)) { - seen.add(key); - models.push(name); - } - } - } - return { - plans: plansRaw.map((plan) => ({ - name: typeof plan.name === "string" ? plan.name : null, - planId: typeof plan.plan_id === "string" ? plan.plan_id : null, - status: typeof plan.status === "string" ? plan.status : null, - })), - models, - raw: payload, - }; - } - - hasActiveStartPlan(balance: StartPlanBalance): boolean { - return balance.plans.some((plan) => { - const status = plan.status?.trim().toLowerCase(); - const identity = plan.planId ?? plan.name ?? ""; - const isStartPlan = identity.includes("start-plan") || identity.includes("start plan"); - return status === "active" && isStartPlan; - }); - } -} - -export function readPath(source: unknown, path: Array): unknown { - let current: unknown = source; - for (const segment of path) { - if (typeof current !== "object" || current === null) return null; - current = (current as Record)[segment] ?? null; - } - return current; -} diff --git a/plugins/zcode-start-plan-plugin/src/constants.ts b/plugins/zcode-start-plan-plugin/src/constants.ts deleted file mode 100644 index 18292f4..0000000 --- a/plugins/zcode-start-plan-plugin/src/constants.ts +++ /dev/null @@ -1,44 +0,0 @@ -export const PRODUCTION_ORIGIN = "https://zcode.z.ai"; -export const TEST_ORIGIN = "https://zcode.chatglm.site"; - -export const CREDENTIALS_PATH_KEYS = { - activeProvider: "oauth:active_provider", - zaiAccessToken: "oauth:zai:access_token", - zaiRefreshToken: "oauth:zai:refresh_token", - zaiUserInfo: "oauth:zai:user_info", - zcodeJwtToken: "zcodejwttoken", -} as const; - -export const CIPHER_PREFIX = "enc:v1:"; -export const CIPHER_ALGO = "aes-256-gcm"; -export const CREDENTIAL_SECRET_ENV = "ZCODE_CREDENTIAL_SECRET"; - -export const ALIYUN_CAPTCHA_SDK_URL = "https://o.alicdn.com/captcha-frontend/aliyunCaptcha/AliyunCaptcha.js"; -export const CAPTCHA_HEADER_PARAM = "x-aliyun-captcha-verify-param"; -export const CAPTCHA_HEADER_REGION = "x-aliyun-captcha-verify-region"; - -export const CAPTCHA_CONFIG_CACHE_MS = 60_000; -export const CAPTCHA_INTERACTIVE_TIMEOUT_MS = 120_000; -export const BUSY_RETRY_DELAYS_MS = [1_000, 2_000]; -export const DEFAULT_REQUEST_TIMEOUT_MS = 120_000; - -export const DEFAULT_START_PLAN_MODELS = ["GLM-5.3", "GLM-5-Turbo"] as const; - -export function resolveOrigin(env: Record = process.env): string { - const override = env.ZCODE_BASE_URL?.trim() || env.ZCODE_ENDPOINT_ORIGIN?.trim(); - if (override) return override.replace(/\/+$/u, ""); - if ((env.ZCODE_ENV ?? "").trim().toLowerCase() === "test") return TEST_ORIGIN; - return PRODUCTION_ORIGIN; -} - -export function anthropicMessagesUrl(origin: string): string { - return `${origin}/api/v1/zcode-plan/anthropic/v1/messages`; -} - -export function billingBalanceUrl(origin: string): URL { - return new URL(`${origin}/api/v1/zcode-plan/billing/balance`); -} - -export function clientConfigsUrl(origin: string): URL { - return new URL(`${origin}/api/v1/client/configs`); -} diff --git a/plugins/zcode-start-plan-plugin/src/credentials.ts b/plugins/zcode-start-plan-plugin/src/credentials.ts deleted file mode 100644 index 4323372..0000000 --- a/plugins/zcode-start-plan-plugin/src/credentials.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { createCipheriv, createDecipheriv, createHash, randomBytes } from "node:crypto"; -import { homedir, platform, userInfo } from "node:os"; -import { join, resolve } from "node:path"; -import { readFile } from "node:fs/promises"; -import { - CIPHER_ALGO, - CIPHER_PREFIX, - CREDENTIAL_SECRET_ENV, - CREDENTIALS_PATH_KEYS, -} from "./constants.ts"; - -export class CredentialStoreError extends Error { - constructor(message: string, options?: { cause?: unknown }) { - super(message, options); - this.name = "CredentialStoreError"; - } -} - -export function resolveCredentialsPath(env: Record = process.env): string { - const baseDir = env.ZCODE_DATA_BASE_DIR?.trim() || homedir(); - return join(resolvePath(baseDir), ".zcode", "v2", "credentials.json"); -} - -function resolvePath(input: string): string { - if (input === "~") return homedir(); - if (input.startsWith("~/")) return join(homedir(), input.slice(2)); - return resolve(input); -} - -function credentialFallbackSeed(env: Record): string { - let username = "unknown"; - try { - username = userInfo().username; - } catch { - // keep "unknown" - } - return `zcode-credential-fallback:${platform()}:${homedir()}:${username}`; -} - -function deriveCipherKey(env: Record): Buffer { - const secret = env[CREDENTIAL_SECRET_ENV]?.trim() || credentialFallbackSeed(env); - return createHash("sha256").update(secret).digest(); -} - -function decryptValue(encrypted: string, key: Buffer): string { - if (!encrypted.startsWith(CIPHER_PREFIX)) return encrypted; - const parts = encrypted.slice(CIPHER_PREFIX.length).split("."); - if (parts.length !== 3) { - throw new CredentialStoreError("Credential decrypt failed: invalid ciphertext format"); - } - const [ivPart, tagPart, dataPart] = parts; - const iv = Buffer.from(ivPart, "base64url"); - const tag = Buffer.from(tagPart, "base64url"); - const data = Buffer.from(dataPart, "base64url"); - if (iv.length !== 12) throw new CredentialStoreError("Credential decrypt failed: invalid IV length"); - if (tag.length !== 16) throw new CredentialStoreError("Credential decrypt failed: invalid auth tag length"); - try { - const decipher = createDecipheriv(CIPHER_ALGO, key, iv); - decipher.setAuthTag(tag); - return Buffer.concat([decipher.update(data), decipher.final()]).toString("utf-8"); - } catch (error) { - throw new CredentialStoreError("Credential decrypt failed: key mismatch or corrupted ciphertext", { - cause: error, - }); - } -} - -export function encryptValue(plaintext: string, env: Record = process.env): string { - const key = deriveCipherKey(env); - const iv = randomBytes(12); - const cipher = createCipheriv(CIPHER_ALGO, key, iv); - const data = Buffer.concat([cipher.update(plaintext, "utf-8"), cipher.final()]); - const tag = cipher.getAuthTag(); - return [CIPHER_PREFIX, iv.toString("base64url"), ".", tag.toString("base64url"), ".", data.toString("base64url")].join(""); -} - -export interface ZcodeCredentials { - activeProvider: string | null; - zaiAccessToken: string | null; - zcodeJwtToken: string | null; - zaiUserInfo: string | null; -} - -export async function loadCredentials( - options: { env?: Record; filePath?: string } = {}, -): Promise { - const env = options.env ?? process.env; - const filePath = options.filePath ?? resolveCredentialsPath(env); - let raw: string; - try { - raw = await readFile(filePath, "utf-8"); - } catch (error) { - throw new CredentialStoreError( - `Unable to read ZCode credentials at ${filePath}. Run \`zcode login\` first.`, - { cause: error }, - ); - } - let record: Record; - try { - record = JSON.parse(raw) as Record; - } catch (error) { - throw new CredentialStoreError("ZCode credentials file is not valid JSON", { cause: error }); - } - if (typeof record !== "object" || record === null || Array.isArray(record)) { - throw new CredentialStoreError("ZCode credentials file has an unexpected shape"); - } - const key = deriveCipherKey(env); - const read = (field: string): string | null => { - const value = record[field]; - if (typeof value !== "string" || value.length === 0) return null; - return decryptValue(value, key); - }; - return { - activeProvider: read(CREDENTIALS_PATH_KEYS.activeProvider), - zaiAccessToken: read(CREDENTIALS_PATH_KEYS.zaiAccessToken), - zcodeJwtToken: read(CREDENTIALS_PATH_KEYS.zcodeJwtToken), - zaiUserInfo: read(CREDENTIALS_PATH_KEYS.zaiUserInfo), - }; -} - -export async function loadStartPlanBearerToken( - options: { env?: Record; filePath?: string } = {}, -): Promise { - const credentials = await loadCredentials(options); - const token = credentials.zcodeJwtToken?.trim(); - if (!token) { - throw new CredentialStoreError( - "No ZCode JWT token found in credentials. Run `zcode login` (OAuth) first; the Start Plan endpoint authenticates with the shared ZCode JWT.", - ); - } - return token; -} diff --git a/plugins/zcode-start-plan-plugin/src/mcp/server.ts b/plugins/zcode-start-plan-plugin/src/mcp/server.ts deleted file mode 100644 index f142e2b..0000000 --- a/plugins/zcode-start-plan-plugin/src/mcp/server.ts +++ /dev/null @@ -1,146 +0,0 @@ -#!/usr/bin/env node -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; -import { z } from "zod"; -import { loadStartPlanBearerToken } from "../credentials.ts"; -import { DEFAULT_START_PLAN_MODELS } from "../constants.ts"; -import { StartPlanService, ProviderBusinessError } from "../service.ts"; -import { acquireCaptchaParam } from "../captcha.ts"; - -const server = new McpServer({ name: "zcode-start-plan", version: "0.1.0" }); - -interface ServiceHolder { - service: StartPlanService | null; -} - -const holder: ServiceHolder = { service: null }; - -async function getService(logger?: (message: string) => void): Promise { - if (holder.service) return holder.service; - const bearerToken = await loadStartPlanBearerToken(); - holder.service = new StartPlanService({ bearerToken }); - logger?.("loaded ZCode JWT from ~/.zcode/v2/credentials.json"); - return holder.service; -} - -function errorMessage(error: unknown): string { - if (error instanceof ProviderBusinessError) { - const code = error.providerCode ? ` (code ${error.providerCode})` : ""; - return `Start Plan request failed${code}: ${error.message}`; - } - return error instanceof Error ? error.message : String(error); -} - -server.tool( - "start_plan_status", - "Check whether the logged-in ZCode account has an active free Start Plan trial, and list its usable models and quota plans.", - {}, - async () => { - try { - const service = await getService(); - const status = await service.checkEntitlement(); - return { - content: [ - { - type: "text" as const, - text: JSON.stringify( - { - entitled: status.entitled, - models: status.models.length > 0 ? status.models : [...DEFAULT_START_PLAN_MODELS], - plans: status.plans, - }, - null, - 2, - ), - }, - ], - }; - } catch (error) { - return { content: [{ type: "text" as const, text: errorMessage(error) }], isError: true }; - } - }, -); - -interface GenerateParams { - prompt: string; - model?: string; - system?: string; - max_tokens?: number; -} - -server.registerTool( - "start_plan_generate", - { - description: - "Generate text with the free ZCode Start Plan trial quota (zcode-plan endpoint). Delegated generation only; the main conversation model is unaffected. May open a one-time browser captcha tab on first use.", - inputSchema: { - prompt: z.string().min(1).describe("The user prompt to send."), - model: z - .string() - .optional() - .describe( - `Model id, e.g. ${[...DEFAULT_START_PLAN_MODELS].join(" or ")}. Defaults to ${DEFAULT_START_PLAN_MODELS[0]}.`, - ), - system: z.string().optional().describe("Optional system prompt."), - max_tokens: z.number().int().positive().max(32768).optional().describe("Max output tokens (default 4096)."), - } satisfies Record, - }, - async (args: unknown) => { - const params = args as GenerateParams; - try { - const service = await getService(); - const result = await service.generate({ - model: params.model ?? DEFAULT_START_PLAN_MODELS[0], - prompt: params.prompt, - system: params.system, - maxTokens: params.max_tokens, - logger: (message) => process.stderr.write(`[start-plan] ${message}\n`), - }); - return { - content: [ - { - type: "text" as const, - text: JSON.stringify( - { text: result.text, model: result.model, stop_reason: result.stopReason, usage: result.usage }, - null, - 2, - ), - }, - ], - }; - } catch (error) { - return { content: [{ type: "text" as const, text: errorMessage(error) }], isError: true }; - } - }, -); - -server.tool( - "start_plan_captcha", - "Run the one-time Aliyun captcha bridge now so the next start_plan_generate call does not need to open a browser (useful right after login or after a 3007 captcha failure).", - {}, - async () => { - try { - const service = await getService(); - const config = await service.client.getCaptchaConfig(true); - if (!config || !config.enabled) { - return { content: [{ type: "text" as const, text: "Captcha is not enabled for the Start Plan endpoint; nothing to do." }] }; - } - const verification = await acquireCaptchaParam({ config, logger: (m) => process.stderr.write(`[start-plan] ${m}\n`) }); - return { - content: [{ type: "text" as const, text: `Captcha acquired (${verification.param.length} chars). It is staged for the next generate call in this server process.` }], - }; - } catch (error) { - return { content: [{ type: "text" as const, text: errorMessage(error) }], isError: true }; - } - }, -); - -async function main(): Promise { - const transport = new StdioServerTransport(); - await server.connect(transport); -} - -main().catch((error) => { - process.stderr.write(`[start-plan] fatal: ${error instanceof Error ? error.message : String(error)}\n`); - process.exit(1); -}); diff --git a/plugins/zcode-start-plan-plugin/src/service.ts b/plugins/zcode-start-plan-plugin/src/service.ts deleted file mode 100644 index e7cf495..0000000 --- a/plugins/zcode-start-plan-plugin/src/service.ts +++ /dev/null @@ -1,225 +0,0 @@ -import { - anthropicMessagesUrl, - BUSY_RETRY_DELAYS_MS, - CAPTCHA_HEADER_PARAM, - CAPTCHA_HEADER_REGION, - DEFAULT_REQUEST_TIMEOUT_MS, -} from "./constants.ts"; -import { StartPlanClient, StartPlanHttpError, type StartPlanBalance } from "./client.ts"; -import { acquireCaptchaParam, type CaptchaVerification } from "./captcha.ts"; - -export class ProviderBusinessError extends Error { - readonly providerCode: string | null; - readonly status: number | null; - readonly bodySummary: string; - constructor(options: { providerCode?: string | null; status?: number | null; bodySummary?: string; message: string }) { - super(options.message); - this.name = "ProviderBusinessError"; - this.providerCode = options.providerCode ?? null; - this.status = options.status ?? null; - this.bodySummary = options.bodySummary ?? ""; - } -} - -export interface GenerateOptions { - model: string; - prompt: string; - system?: string; - maxTokens?: number; - temperature?: number; - captcha?: CaptchaVerification | null; - maxBusyRetries?: number; - timeoutMs?: number; - logger?: (message: string) => void; -} - -export interface GenerateResult { - text: string; - model: string; - stopReason: string | null; - usage: { inputTokens: number; outputTokens: number } | null; -} - -export interface StartPlanServiceOptions { - bearerToken: string; - env?: Record; - fetchImpl?: typeof fetch; -} - -export class StartPlanService { - readonly client: StartPlanClient; - private readonly bearerToken: string; - private readonly fetchImpl: typeof fetch; - - constructor(options: StartPlanServiceOptions) { - this.client = new StartPlanClient({ env: options.env, bearerToken: options.bearerToken, fetchImpl: options.fetchImpl }); - this.bearerToken = options.bearerToken; - this.fetchImpl = options.fetchImpl ?? fetch; - } - - async checkEntitlement(): Promise<{ entitled: boolean; models: string[]; plans: StartPlanBalance["plans"] }> { - const balance = await this.client.fetchBalance(); - return { entitled: this.client.hasActiveStartPlan(balance), models: balance.models, plans: balance.plans }; - } - - async generate(options: GenerateOptions): Promise { - const logger = options.logger ?? (() => {}); - let attempt = 0; - let captcha = options.captcha ?? null; - for (;;) { - attempt += 1; - try { - return await this.generateOnce(options, captcha); - } catch (error) { - const busy = busyProviderCode(error); - const busyDelays = BUSY_RETRY_DELAYS_MS.slice( - 0, - Math.max(0, (options.maxBusyRetries ?? BUSY_RETRY_DELAYS_MS.length) ), - ); - if (busy && attempt <= busyDelays.length) { - const delay = busyDelays[attempt - 1]; - logger(`Start Plan busy (${busy}); retrying in ${delay} ms`); - await new Promise((resolve) => setTimeout(resolve, delay)); - continue; - } - if (error instanceof ProviderBusinessError && error.providerCode === "3007") { - if (captcha || options.captcha === null) { - logger("captcha verify failed (3007); acquiring a fresh captcha param via the local bridge"); - const config = await this.client.getCaptchaConfig(true); - if (!config) { - throw new ProviderBusinessError({ - providerCode: "3007", - message: - "Start Plan rejected the request with captcha code 3007 and the captcha config endpoint returned no configuration.", - }); - } - captcha = await acquireCaptchaParam({ config, logger }); - continue; - } - } - throw error; - } - } - } - - private async generateOnce(options: GenerateOptions, captcha: CaptchaVerification | null): Promise { - const url = anthropicMessagesUrl(this.client.origin); - const body: Record = { - model: options.model, - max_tokens: options.maxTokens ?? 4096, - messages: [{ role: "user", content: options.prompt }], - }; - if (options.system) body.system = options.system; - if (typeof options.temperature === "number") body.temperature = options.temperature; - - const headers: Record = { - "Content-Type": "application/json", - Accept: "application/json", - Authorization: `Bearer ${this.bearerToken}`, - "anthropic-version": "2023-06-01", - }; - if (captcha) { - headers[CAPTCHA_HEADER_PARAM] = captcha.param; - headers[CAPTCHA_HEADER_REGION] = captcha.region; - } - - const controller = new AbortController(); - const timer = setTimeout(() => controller.abort(), options.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS); - let response: Response; - try { - response = await this.fetchImpl(url, { - method: "POST", - headers, - body: JSON.stringify(body), - signal: controller.signal, - }); - } finally { - clearTimeout(timer); - } - - const text = await response.text(); - let payload: Record | null = null; - try { - payload = JSON.parse(text) as Record; - } catch { - payload = null; - } - - if (response.ok && payload) { - const errorObj = payload.error as Record | undefined; - if (errorObj && typeof errorObj.message === "string") { - throw toBusinessError(errorObj, response.status, text); - } - const content = Array.isArray(payload.content) ? (payload.content as Array>) : []; - const textParts = content - .filter((block) => block.type === "text" && typeof block.text === "string") - .map((block) => block.text as string); - const usage = payload.usage as Record | undefined; - if (textParts.length === 0) { - // The desktop runtime treats empty streams with a captcha header as a 3007 captcha failure. - if (captcha) { - throw new ProviderBusinessError({ - providerCode: "3007", - status: 403, - bodySummary: summarize(text), - message: "Captcha verification failed or the verify token was rejected.", - }); - } - throw new ProviderBusinessError({ - status: response.status, - bodySummary: summarize(text), - message: "Start Plan returned an empty completion.", - }); - } - return { - text: textParts.join("\n"), - model: typeof payload.model === "string" ? payload.model : options.model, - stopReason: typeof payload.stop_reason === "string" ? payload.stop_reason : null, - usage: usage - ? { inputTokens: usage.input_tokens ?? 0, outputTokens: usage.output_tokens ?? 0 } - : null, - }; - } - - throw toBusinessError(payload ?? {}, response.status, text); - } -} - -function toBusinessError( - source: Record, - status: number, - rawBody: string, -): ProviderBusinessError { - const nested = typeof source.error === "object" && source.error !== null - ? (source.error as Record) - : source; - const code = - typeof nested.providerCode === "string" - ? nested.providerCode - : typeof nested.code === "string" || typeof nested.code === "number" - ? String(nested.code) - : null; - const message = - typeof nested.message === "string" - ? nested.message - : typeof nested.msg === "string" - ? nested.msg - : `Start Plan request failed with HTTP ${status}`; - return new ProviderBusinessError({ - providerCode: code, - status, - bodySummary: summarize(rawBody), - message, - }); -} - -function busyProviderCode(error: unknown): string | null { - if (error instanceof ProviderBusinessError && error.providerCode) { - return ["3008", "3009", "3010"].includes(error.providerCode) ? error.providerCode : null; - } - return null; -} - -function summarize(body: string): string { - return body.length > 500 ? `${body.slice(0, 500)}…` : body; -} diff --git a/plugins/zcode-start-plan-plugin/test/credentials.test.ts b/plugins/zcode-start-plan-plugin/test/credentials.test.ts deleted file mode 100644 index 614cb1b..0000000 --- a/plugins/zcode-start-plan-plugin/test/credentials.test.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { describe, expect, test } from "bun:test"; -import { - encryptValue, - loadCredentials, - resolveCredentialsPath, - CredentialStoreError, -} from "../src/credentials.ts"; -import { createHash, randomBytes } from "node:crypto"; -import { mkdtemp, writeFile, mkdir } from "node:fs/promises"; -import { tmpdir } from "node:os"; -import { join } from "node:path"; - -const ENV = { HOME: "/tmp/fake-home", ZCODE_CREDENTIAL_SECRET: "test-secret" }; - -function encryptWithSecret(secret: string, plaintext: string): string { - const key = createHash("sha256").update(secret).digest(); - const iv = randomBytes(12); - const { createCipheriv } = require("node:crypto") as typeof import("node:crypto"); - const cipher = createCipheriv("aes-256-gcm", key, iv); - const data = Buffer.concat([cipher.update(plaintext, "utf-8"), cipher.final()]); - const tag = cipher.getAuthTag(); - return ["enc:v1:", iv.toString("base64url"), ".", tag.toString("base64url"), ".", data.toString("base64url")].join(""); -} - -test("resolveCredentialsPath honors ZCODE_DATA_BASE_DIR", () => { - const path = resolveCredentialsPath({ ...ENV, ZCODE_DATA_BASE_DIR: "/data/dir" }); - expect(path).toBe(join("/data/dir", ".zcode", "v2", "credentials.json")); -}); - -test("encryptValue/decrypt roundtrip via loadCredentials", async () => { - const dir = await mkdtemp(join(tmpdir(), "zcode-cred-")); - const credentialsPath = join(dir, "credentials.json"); - const record = { - "oauth:active_provider": encryptValue("zai", ENV), - "oauth:zai:access_token": encryptValue("access-token", ENV), - zcodejwttoken: encryptValue("jwt-token", ENV), - }; - await writeFile(credentialsPath, JSON.stringify(record, null, 2)); - const credentials = await loadCredentials({ env: ENV, filePath: credentialsPath }); - expect(credentials.activeProvider).toBe("zai"); - expect(credentials.zaiAccessToken).toBe("access-token"); - expect(credentials.zcodeJwtToken).toBe("jwt-token"); -}); - -test("decrypts values produced by the runtime cipher", async () => { - const dir = await mkdtemp(join(tmpdir(), "zcode-cred-")); - const credentialsPath = join(dir, "credentials.json"); - await writeFile(credentialsPath, JSON.stringify({ zcodejwttoken: encryptWithSecret("test-secret", "jwt-from-runtime") })); - const credentials = await loadCredentials({ env: ENV, filePath: credentialsPath }); - expect(credentials.zcodeJwtToken).toBe("jwt-from-runtime"); -}); - -test("missing file raises CredentialStoreError with guidance", async () => { - const dir = await mkdtemp(join(tmpdir(), "zcode-cred-")); - await expect(loadCredentials({ env: ENV, filePath: join(dir, "none.json") })).rejects.toBeInstanceOf( - CredentialStoreError, - ); -}); diff --git a/plugins/zcode-start-plan-plugin/test/service.test.ts b/plugins/zcode-start-plan-plugin/test/service.test.ts deleted file mode 100644 index c8b0d5f..0000000 --- a/plugins/zcode-start-plan-plugin/test/service.test.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { describe, expect, test } from "bun:test"; -import { resolveOrigin, anthropicMessagesUrl, billingBalanceUrl, clientConfigsUrl } from "../src/constants.ts"; -import { StartPlanClient } from "../src/client.ts"; -import { StartPlanService, ProviderBusinessError } from "../src/service.ts"; - -test("resolveOrigin honors env overrides", () => { - expect(resolveOrigin({})).toBe("https://zcode.z.ai"); - expect(resolveOrigin({ ZCODE_ENV: "test" })).toBe("https://zcode.chatglm.site"); - expect(resolveOrigin({ ZCODE_BASE_URL: "https://example.com/" })).toBe("https://example.com"); -}); - -test("url builders", () => { - expect(anthropicMessagesUrl("https://zcode.z.ai")).toBe( - "https://zcode.z.ai/api/v1/zcode-plan/anthropic/v1/messages", - ); - expect(billingBalanceUrl("https://zcode.z.ai").href).toBe( - "https://zcode.z.ai/api/v1/zcode-plan/billing/balance", - ); - expect(clientConfigsUrl("https://zcode.z.ai").href).toBe("https://zcode.z.ai/api/v1/client/configs"); -}); - -function jsonResponse(payload: unknown, status = 200): Response { - return new Response(JSON.stringify(payload), { status, headers: { "Content-Type": "application/json" } }); -} - -test("client parses balance models and entitlement", async () => { - const client = new StartPlanClient({ - bearerToken: "jwt", - fetchImpl: (async (url: string | URL | Request) => - jsonResponse({ - data: { - plans: [{ name: "ZCode Start Plan", plan_id: "start-plan-monthly", status: "active" }], - balances: [ - { show_name: "GLM-5.3", capabilities: [] }, - { show_name: "ignored", capabilities: ["model:GLM-5-Turbo", "other"] }, - ], - }, - })) as unknown as typeof fetch, - }); - const balance = await client.fetchBalance(); - expect(balance.models).toEqual(["GLM-5.3", "GLM-5-Turbo"]); - expect(client.hasActiveStartPlan(balance)).toBe(true); -}); - -test("client reads captcha config with caching", async () => { - let calls = 0; - const client = new StartPlanClient({ - bearerToken: "jwt", - fetchImpl: (async () => { - calls += 1; - return jsonResponse({ data: { configs: { captcha: { enabled: true, region: "cn", prefix: "x", sceneId: "s" } } } }); - }) as unknown as typeof fetch, - }); - const first = await client.getCaptchaConfig(); - const second = await client.getCaptchaConfig(); - expect(first?.sceneId).toBe("s"); - expect(second?.sceneId).toBe("s"); - expect(calls).toBe(1); -}); - -test("service maps error envelope to ProviderBusinessError", async () => { - const service = new StartPlanService({ - bearerToken: "jwt", - fetchImpl: (async () => - jsonResponse({ error: { code: "3007", message: "captcha verify failed" } }, 200)) as unknown as typeof fetch, - }); - try { - await service.generate({ model: "GLM-5.3", prompt: "hi", captcha: null, maxBusyRetries: 0 }); - throw new Error("expected generate to throw"); - } catch (error) { - expect(error).toBeInstanceOf(ProviderBusinessError); - expect((error as ProviderBusinessError).providerCode).toBe("3007"); - } -}); - -test("service returns text content on success", async () => { - const service = new StartPlanService({ - bearerToken: "jwt", - fetchImpl: (async () => - jsonResponse({ - model: "GLM-5.3", - stop_reason: "end_turn", - content: [{ type: "text", text: "hello" }], - usage: { input_tokens: 3, output_tokens: 2 }, - })) as unknown as typeof fetch, - }); - const result = await service.generate({ model: "GLM-5.3", prompt: "hi", maxBusyRetries: 0 }); - expect(result.text).toBe("hello"); - expect(result.usage).toEqual({ inputTokens: 3, outputTokens: 2 }); -}); diff --git a/plugins/zcode-start-plan-plugin/tsconfig.json b/plugins/zcode-start-plan-plugin/tsconfig.json deleted file mode 100644 index 1def70a..0000000 --- a/plugins/zcode-start-plan-plugin/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "types": ["@types/bun"] - }, - "include": ["src/**/*.ts", "test/**/*.ts", "tsdown.config.ts"] -} diff --git a/plugins/zcode-start-plan-plugin/tsdown.config.ts b/plugins/zcode-start-plan-plugin/tsdown.config.ts deleted file mode 100644 index 38d98ba..0000000 --- a/plugins/zcode-start-plan-plugin/tsdown.config.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { defineConfig } from "tsdown"; - -export default defineConfig({ - entry: { server: "src/mcp/server.ts" }, - outDir: "dist", - outExtensions: () => ({ js: ".js" }), - format: "esm", - platform: "node", - target: "node24", - clean: true, - dts: false, - sourcemap: false, - deps: { - neverBundle: ["@modelcontextprotocol/sdk"], - onlyBundle: false - } -});