{"_id":"@anpiluo/dsh-proxy","_rev":"5-63e223a706a560e7a5f19c8d38d1e94f","name":"@anpiluo/dsh-proxy","dist-tags":{"latest":"2.1.0"},"versions":{"2.0.0":{"name":"@anpiluo/dsh-proxy","version":"2.0.0","keywords":["dsh","dsh-plugin","deepseek-harness","llm","proxy","clash","undici","rate-limit","failover","cordis","settings","web-ui"],"author":{"name":"anpiluo"},"license":"MIT","_id":"@anpiluo/dsh-proxy@2.0.0","maintainers":[{"name":"anpiluo","email":"leg@anpiluo.top"}],"homepage":"https://github.com/leg-anpiluo/dsh-proxy","bugs":{"url":"https://github.com/leg-anpiluo/dsh-proxy/issues"},"dsh":{"bundle":{"patch":"./cordis.patch.yml"},"client":{"inject":["slots","locale","settingsScope","remote"],"platform":"web"}},"dist":{"shasum":"a4889dd45b7bd116e6adfa24a67cc28589118a41","tarball":"https://registry.npmjs.org/@anpiluo/dsh-proxy/-/dsh-proxy-2.0.0.tgz","fileCount":19,"integrity":"sha512-GKRxtw4p01JVI0yddxtAM+BdpIvSGxXxkvI43tqmbe1C8H8mDf6dCGgxKFZJ2jTGPZ92exVxnxIMHfXKdblM6w==","signatures":[{"sig":"MEYCIQCGL0nVB42Rh+FwdNxCual61W5bIZOEjAGS689Ltl6wLQIhAIyzuqbIX3FLt0gUF97oqXQnNEGqhhK8rdPo8L4hzUzo","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":285063},"main":"lib/index.js","type":"module","engines":{"node":">=24"},"exports":{".":"./lib/index.js","./client":"./lib/client.js","./package.json":"./package.json","./cordis.patch.yml":"./cordis.patch.yml"},"gitHead":"41e16c667083ac695f9c06e3e91b010090adbdaf","scripts":{"test":"node --test test/routing-dispatcher.test.js test/failover-dispatcher.test.js test/settings.test.js test/retry-mirror.test.js test/multimodal-mirror.test.js test/connection-test.test.js","build":"tsdown","prepare":"tsdown","test:load":"node test/load-test.mjs","typecheck":"tsc --noEmit","test:smoke":"node test/smoke-test.mjs","build:watch":"tsdown --watch","test:client":"node test/client-build.test.js","test:global":"node test/global-fetch-probe.mjs","test:smoke:failover":"node test/failover-smoke-test.mjs"},"_npmUser":{"name":"anpiluo","email":"leg@anpiluo.top"},"repository":{"url":"git+https://github.com/leg-anpiluo/dsh-proxy.git","type":"git"},"_npmVersion":"10.9.8","description":"DSH 模型代理插件：按模型走代理（Clash 等）+ 失败自动重试 + 直连失败自动回退代理，设置页实时生效。","directories":{},"_nodeVersion":"22.23.2","dependencies":{"undici":"^8.10.0","@deepseek-ai/schemastery":"^3.18.1","@deepseek-ai/dsh-settings":"^0.1.0-rc.7"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"react":"^18.2.0","tsdown":"0.22.2","react-dom":"^18.2.0","typescript":"~5.7.2","@types/react":"~18.3.1","lightningcss":"1.32.0","@types/react-dom":"^18.3.5","@deepseek-ai/cordis":"^4.0.1","@deepseek-ai/dsh-client-locale":"^0.1.0-rc.7","@deepseek-ai/dsh-client-runtime":"^0.1.0-rc.7","@deepseek-ai/dsh-client-ui-slots":"^0.1.0-rc.7","@deepseek-ai/dsh-client-ui-settings":"^0.1.0-rc.7","@deepseek-ai/dsh-client-ui-primitives":"^0.1.0-rc.7","@deepseek-ai/dsh-client-ui-settings-plugins":"0.1.0-rc.7"},"peerDependencies":{"@deepseek-ai/cordis":">=4.0.0 <5.0.0"},"_npmOperationalInternal":{"tmp":"tmp/dsh-proxy_2.0.0_1788691953180_0.08792044412786715","host":"s3://npm-registry-packages-npm-production"}},"2.0.1":{"name":"@anpiluo/dsh-proxy","version":"2.0.1","keywords":["dsh","dsh-plugin","deepseek-harness","llm","proxy","clash","undici","rate-limit","failover","cordis","settings","web-ui"],"author":{"name":"anpiluo"},"license":"MIT","_id":"@anpiluo/dsh-proxy@2.0.1","maintainers":[{"name":"anpiluo","email":"leg@anpiluo.top"}],"homepage":"https://github.com/leg-anpiluo/dsh-proxy","bugs":{"url":"https://github.com/leg-anpiluo/dsh-proxy/issues"},"dsh":{"bundle":{"patch":"./cordis.patch.yml"},"client":{"inject":["slots","locale","settingsScope","remote"],"platform":"web"}},"dist":{"shasum":"e0ad38a5c5d210428cfdb6bba3956ce0239f8ccc","tarball":"https://registry.npmjs.org/@anpiluo/dsh-proxy/-/dsh-proxy-2.0.1.tgz","fileCount":19,"integrity":"sha512-AYyyl+QRUwLIHZ+ATeEkm5uqvSjz/tYG4YGQuzKwL9Pw+CrQPK1uZ7fHVvq0qdYX5O7DZkcojMEXnQlb3N2X2g==","signatures":[{"sig":"MEYCIQDKNNSNIh+xlkMfiA9snLu1+eq7Jjf1ICpEkG2Qwp10UQIhAPinoH+JQw9QLrpVIAMW5ZiOxJxT0oZ5Ikoh6GmtCufR","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@anpiluo%2fdsh-proxy@2.0.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":287608},"main":"lib/index.js","type":"module","engines":{"node":">=24"},"exports":{".":"./lib/index.js","./client":"./lib/client.js","./package.json":"./package.json","./cordis.patch.yml":"./cordis.patch.yml"},"gitHead":"b3d57bb06a7cb9dd0f36c537554927a9660a5e64","scripts":{"test":"node --test test/routing-dispatcher.test.js test/failover-dispatcher.test.js test/installer.test.js test/settings.test.js test/retry-mirror.test.js test/multimodal-mirror.test.js test/connection-test.test.js","build":"tsdown","prepare":"tsdown","test:load":"node test/load-test.mjs","typecheck":"tsc --noEmit","test:smoke":"node test/smoke-test.mjs","build:watch":"tsdown --watch","test:client":"node test/client-build.test.js","test:global":"node test/global-fetch-probe.mjs","test:smoke:failover":"node test/failover-smoke-test.mjs"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:dccd1d8a-e0c2-4317-89d0-e81fb7c76a13"}},"repository":{"url":"git+https://github.com/leg-anpiluo/dsh-proxy.git","type":"git"},"_npmVersion":"11.19.0","description":"DSH 模型代理插件：按模型走代理（Clash 等）+ 失败自动重试 + 直连失败自动回退代理，设置页实时生效。","directories":{},"_nodeVersion":"24.20.0","dependencies":{"undici":"^8.10.0","@deepseek-ai/schemastery":"^3.18.1","@deepseek-ai/dsh-settings":"^0.1.0-rc.7"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"react":"^18.2.0","tsdown":"0.22.2","react-dom":"^18.2.0","typescript":"~5.7.2","@types/react":"~18.3.1","lightningcss":"1.32.0","@types/react-dom":"^18.3.5","@deepseek-ai/cordis":"^4.0.1","@deepseek-ai/dsh-client-locale":"^0.1.0-rc.7","@deepseek-ai/dsh-client-runtime":"^0.1.0-rc.7","@deepseek-ai/dsh-client-ui-slots":"^0.1.0-rc.7","@deepseek-ai/dsh-client-ui-settings":"^0.1.0-rc.7","@deepseek-ai/dsh-client-ui-primitives":"^0.1.0-rc.7","@deepseek-ai/dsh-client-ui-settings-plugins":"0.1.0-rc.7"},"peerDependencies":{"@deepseek-ai/cordis":">=4.0.0 <5.0.0"},"_npmOperationalInternal":{"tmp":"tmp/dsh-proxy_2.0.1_1788702871118_0.4594384036593324","host":"s3://npm-registry-packages-npm-production"}},"2.0.2":{"name":"@anpiluo/dsh-proxy","version":"2.0.2","keywords":["dsh","dsh-plugin","deepseek-harness","llm","proxy","clash","undici","rate-limit","failover","cordis","settings","web-ui"],"author":{"name":"anpiluo"},"license":"MIT","_id":"@anpiluo/dsh-proxy@2.0.2","maintainers":[{"name":"anpiluo","email":"leg@anpiluo.top"}],"homepage":"https://github.com/leg-anpiluo/dsh-proxy","bugs":{"url":"https://github.com/leg-anpiluo/dsh-proxy/issues"},"dsh":{"bundle":{"patch":"./cordis.patch.yml"},"client":{"inject":["slots","locale","settingsScope","remote"],"platform":"web"}},"dist":{"shasum":"c826fdc5ce03b54477d25936d8c57c438e1c2fb7","tarball":"https://registry.npmjs.org/@anpiluo/dsh-proxy/-/dsh-proxy-2.0.2.tgz","fileCount":19,"integrity":"sha512-jsQWjTVqbwRwaTVnT9XmJLhma3BRG9hII+HabuyTRGdS3JeBEDifVzAqHPu8rc4iHiP7zktGWSH/1si6eGVsOw==","signatures":[{"sig":"MEUCIQDI+UhfUYfQFZ7/8UlErLF8TLtK0av7IeX6ItUYGjCfSwIgBs2q5yJ36eIn49dnRVODZW4Ysf/ZeSSnjcZcCa3auko=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@anpiluo%2fdsh-proxy@2.0.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":290595},"main":"lib/index.js","type":"module","engines":{"node":">=24"},"exports":{".":"./lib/index.js","./client":"./lib/client.js","./package.json":"./package.json","./cordis.patch.yml":"./cordis.patch.yml"},"gitHead":"3d9d514fdcae1486849e9047bc2dcc3ac54519a8","scripts":{"test":"node --test test/routing-dispatcher.test.js test/failover-dispatcher.test.js test/installer.test.js test/settings.test.js test/retry-mirror.test.js test/multimodal-mirror.test.js test/connection-test.test.js","build":"tsdown","prepare":"tsdown","test:load":"node test/load-test.mjs","typecheck":"tsc --noEmit","test:smoke":"node test/smoke-test.mjs","build:watch":"tsdown --watch","test:client":"node test/client-build.test.js","test:global":"node test/global-fetch-probe.mjs","test:smoke:failover":"node test/failover-smoke-test.mjs"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:dccd1d8a-e0c2-4317-89d0-e81fb7c76a13"}},"repository":{"url":"git+https://github.com/leg-anpiluo/dsh-proxy.git","type":"git"},"_npmVersion":"11.19.0","description":"DSH 模型代理插件：按模型走代理（Clash 等）+ 失败自动重试 + 直连失败自动回退代理，设置页实时生效。","directories":{},"_nodeVersion":"24.20.0","dependencies":{"undici":"^8.10.0","@deepseek-ai/schemastery":"^3.18.1"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"react":"^18.2.0","tsdown":"0.22.2","react-dom":"^18.2.0","typescript":"~5.7.2","@types/react":"~18.3.1","lightningcss":"1.32.0","@types/react-dom":"^18.3.5","@deepseek-ai/cordis":"^4.0.1","@deepseek-ai/dsh-settings":"^0.1.0-rc.7","@deepseek-ai/dsh-client-store":"^0.1.2-rc.1","@deepseek-ai/dsh-client-locale":"^0.1.0-rc.7","@deepseek-ai/dsh-client-ui-slots":"^0.1.0-rc.7","@deepseek-ai/dsh-client-ui-settings":"^0.1.0-rc.7","@deepseek-ai/dsh-client-ui-primitives":"^0.1.0-rc.7","@deepseek-ai/dsh-client-ui-settings-plugins":"0.1.0-rc.7"},"peerDependencies":{"@deepseek-ai/cordis":">=4.0.0 <5.0.0","@earendil-works/pi-ai":"*"},"peerDependenciesMeta":{"@earendil-works/pi-ai":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/dsh-proxy_2.0.2_1788753172860_0.09226150470522199","host":"s3://npm-registry-packages-npm-production"}},"2.0.3":{"name":"@anpiluo/dsh-proxy","version":"2.0.3","keywords":["dsh","dsh-plugin","deepseek-harness","llm","proxy","clash","undici","rate-limit","failover","cordis","settings","web-ui"],"author":{"name":"anpiluo"},"license":"MIT","_id":"@anpiluo/dsh-proxy@2.0.3","maintainers":[{"name":"anpiluo","email":"leg@anpiluo.top"}],"homepage":"https://github.com/leg-anpiluo/dsh-proxy","bugs":{"url":"https://github.com/leg-anpiluo/dsh-proxy/issues"},"dsh":{"bundle":{"patch":"./cordis.patch.yml"},"client":{"inject":["slots","locale","settingsScope","remote"],"platform":"web"}},"dist":{"shasum":"0633dd09a472e8008869b15f1fc34abc951f2ae2","tarball":"https://registry.npmjs.org/@anpiluo/dsh-proxy/-/dsh-proxy-2.0.3.tgz","fileCount":19,"integrity":"sha512-zXm6KnLGU1Aa9aD0118bR2sO5VMmGiY326nEeh+3FofNbBEhd+YS2v6DQh/khp6JWJaeHer/DFUqrUZBQL1w9w==","signatures":[{"sig":"MEQCIGJhvZCGEnKOm8Xb63GBpKfPlVn2OsbP4r0wUX0EsY2nAiAj86YbPbJjjZuEiaJkgvMn8fJqmXo8j5Sq1kNPfJSang==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@anpiluo%2fdsh-proxy@2.0.3","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":290730},"main":"lib/index.js","type":"module","engines":{"node":">=24"},"exports":{".":"./lib/index.js","./client":"./lib/client.js","./package.json":"./package.json","./cordis.patch.yml":"./cordis.patch.yml"},"gitHead":"fedd11deffd04611bd833efcbe6059474ea55bee","scripts":{"test":"node --test test/routing-dispatcher.test.js test/failover-dispatcher.test.js test/installer.test.js test/settings.test.js test/retry-mirror.test.js test/multimodal-mirror.test.js test/connection-test.test.js","build":"tsdown","prepare":"tsdown","test:load":"node test/load-test.mjs","typecheck":"tsc --noEmit","test:smoke":"node test/smoke-test.mjs","build:watch":"tsdown --watch","test:client":"node test/client-build.test.js","test:global":"node test/global-fetch-probe.mjs","test:smoke:failover":"node test/failover-smoke-test.mjs"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:dccd1d8a-e0c2-4317-89d0-e81fb7c76a13"}},"repository":{"url":"git+https://github.com/leg-anpiluo/dsh-proxy.git","type":"git"},"_npmVersion":"11.19.0","description":"DSH 模型代理插件：按模型走代理（Clash 等）+ 失败自动重试 + 直连失败自动回退代理，设置页实时生效。","directories":{},"_nodeVersion":"24.20.0","dependencies":{"undici":"^8.10.0","@deepseek-ai/schemastery":"^3.18.1"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"react":"^18.2.0","tsdown":"0.22.2","react-dom":"^18.2.0","typescript":"~5.7.2","@types/react":"~18.3.1","lightningcss":"1.32.0","@types/react-dom":"^18.3.5","@deepseek-ai/cordis":"^4.0.1","@deepseek-ai/dsh-settings":"^0.1.0-rc.7","@deepseek-ai/dsh-client-store":"^0.1.2-rc.1","@deepseek-ai/dsh-client-locale":"^0.1.0-rc.7","@deepseek-ai/dsh-client-ui-slots":"^0.1.0-rc.7","@deepseek-ai/dsh-client-ui-settings":"^0.1.0-rc.7","@deepseek-ai/dsh-client-ui-primitives":"^0.1.0-rc.7","@deepseek-ai/dsh-client-ui-settings-plugins":"0.1.0-rc.7"},"peerDependencies":{"@deepseek-ai/cordis":">=4.0.0 <5.0.0","@earendil-works/pi-ai":"*"},"peerDependenciesMeta":{"@earendil-works/pi-ai":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/dsh-proxy_2.0.3_1788755289741_0.28744115733462805","host":"s3://npm-registry-packages-npm-production"}},"2.1.0":{"_id":"@anpiluo/dsh-proxy@2.1.0","dsh":{"bundle":{"patch":"./cordis.patch.yml"},"client":{"inject":["slots","locale"],"platform":"web"}},"bugs":{"url":"https://github.com/leg-anpiluo/dsh-proxy/issues"},"dist":{"shasum":"79608362ca5c62bdf67cc4114ff12b752927eb4a","tarball":"https://registry.npmjs.org/@anpiluo/dsh-proxy/-/dsh-proxy-2.1.0.tgz","fileCount":20,"integrity":"sha512-Bx1Uc0Zk70qUc4uiMkFiRsutmceNG1TzqlPIPz4EwliVFH5DPn+RR69kXRAH6W+OPBNoP/gJ7Nyixywb/HJAsQ==","signatures":[{"sig":"MEUCID1pspwbbzp5TnJzXXJKSfvpPvHGkWKEPRcud8Pi0MWDAiEAjpyFFfwsOMNFfTwoKEuZwFzoHkD15/qn59hfY2DDhRk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIGbJeLC8Z+f6YXB6MYMYpgpT4NwAsLFeYIsQVYbQcvcFAiA61oBkYJjxLhi/AR7jXXCIbsoRjkjd3+pdoD0C026xIQ=="}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@anpiluo%2fdsh-proxy@2.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":342410},"main":"lib/index.js","name":"@anpiluo/dsh-proxy","type":"module","author":{"name":"anpiluo"},"engines":{"node":">=24"},"exports":{".":"./lib/index.js","./client":"./lib/client.js","./package.json":"./package.json","./cordis.patch.yml":"./cordis.patch.yml"},"gitHead":"66bec6eae377246a949e6ab37b7a9efb13e33d3a","license":"MIT","scripts":{"test":"node --test test/routing-dispatcher.test.js test/failover-dispatcher.test.js test/installer.test.js test/settings.test.js test/retry-mirror.test.js test/multimodal-mirror.test.js test/connection-test.test.js test/volatile-settings.test.js test/client-chevron.test.js","build":"tsdown","prepare":"tsdown","test:load":"node test/load-test.mjs","typecheck":"tsc --noEmit","test:smoke":"node test/smoke-test.mjs","build:watch":"tsdown --watch","test:client":"node test/client-build.test.js && node test/client-shape-check.mjs","test:global":"node test/global-fetch-probe.mjs","test:smoke:failover":"node test/failover-smoke-test.mjs"},"version":"2.1.0","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"dccd1d8a-e0c2-4317-89d0-e81fb7c76a13"}},"homepage":"https://github.com/leg-anpiluo/dsh-proxy","keywords":["dsh","dsh-plugin","deepseek-harness","llm","proxy","clash","undici","rate-limit","failover","cordis","settings","web-ui"],"repository":{"url":"git+https://github.com/leg-anpiluo/dsh-proxy.git","type":"git"},"_npmVersion":"11.19.0","description":"DSH 模型代理插件：按模型走代理（Clash 等）+ 失败自动重试 + 直连失败自动回退代理，设置页实时生效。","directories":{},"maintainers":[{"name":"anpiluo","email":"leg@anpiluo.top"}],"_nodeVersion":"24.21.0","dependencies":{"undici":"^8.10.0","@deepseek-ai/schemastery":"^3.18.4"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"react":"^18.2.0","tsdown":"0.22.2","react-dom":"^18.2.0","typescript":"~5.7.2","@types/react":"~18.3.1","lightningcss":"1.32.0","@types/react-dom":"^18.3.5","@deepseek-ai/cordis":"^4.0.1","@deepseek-ai/dsh-settings":"^0.1.0-rc.7","@deepseek-ai/dsh-client-store":"^0.1.2-rc.1","@deepseek-ai/dsh-client-locale":"^0.1.0-rc.7","@deepseek-ai/dsh-client-ui-slots":"^0.1.0-rc.7","@deepseek-ai/dsh-client-ui-settings":"^0.1.0-rc.7","@deepseek-ai/dsh-client-ui-primitives":"^0.1.0-rc.7","@deepseek-ai/dsh-client-ui-settings-plugins":"0.1.0-rc.7"},"peerDependencies":{"@deepseek-ai/cordis":">=4.0.0 <5.0.0","@earendil-works/pi-ai":"*"},"peerDependenciesMeta":{"@earendil-works/pi-ai":{"optional":true}},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/dsh-proxy_2.1.0_1790705298531_0.4049484997976669"}}},"time":{"created":"2026-09-06T10:52:33.084Z","modified":"2026-09-29T18:08:19.100Z","2.0.0":"2026-09-06T10:52:33.325Z","2.0.1":"2026-09-06T13:54:31.274Z","2.0.2":"2026-09-07T03:52:53.022Z","2.0.3":"2026-09-07T04:28:09.886Z","2.1.0":"2026-09-29T18:08:18.696Z"},"bugs":{"url":"https://github.com/leg-anpiluo/dsh-proxy/issues"},"author":{"name":"anpiluo"},"license":"MIT","homepage":"https://github.com/leg-anpiluo/dsh-proxy","keywords":["dsh","dsh-plugin","deepseek-harness","llm","proxy","clash","undici","rate-limit","failover","cordis","settings","web-ui"],"repository":{"url":"git+https://github.com/leg-anpiluo/dsh-proxy.git","type":"git"},"description":"DSH 模型代理插件：按模型走代理（Clash 等）+ 失败自动重试 + 直连失败自动回退代理，设置页实时生效。","maintainers":[{"name":"anpiluo","email":"leg@anpiluo.top"}],"readme":"# @anpiluo/dsh-proxy\n\n[![npm](https://img.shields.io/npm/v/@anpiluo/dsh-proxy)](https://www.npmjs.com/package/@anpiluo/dsh-proxy)\n\n> Fork 自 [@superfish058/dsh-llm-proxy](https://github.com/superfish058/dsh-llm-proxy)（v1.1.0）。\n>\n> 支持 DSH 0.1.2-rc.1，新增「直连失败自动回退代理」（参考[@sg88/dsh-proxy-switch](https://github.com/sg88/dsh-proxy-switch)），~~由 [@leg-anpiluo](https://github.com/leg-anpiluo) 维护。~~ ~~并不会维护~~ \n\nDSH 模型代理插件：给请求按「目标域名」分流——选中的模型走代理，其余直连并在**直连失败时自动回退代理**，另带失败自动重试。\n\n## 它是干嘛的\n\n- **按模型走代理**：在 DSH 设置页（插件 → 可配置插件 → 模型代理）勾选需要走代理的模型（如 `deepseek-v4-flash`），该模型的请求自动经 `proxyHost:proxyPort`（默认 `127.0.0.1:7897`，即 Clash）转发；未勾选的模型（DeepSeek、小米、通义等国内 API）保持直连。路由按模型的 **API 地址（baseURL host）** 生效：选中一个模型后，同一地址下的所有模型都会走代理（例如 B.AI 的 `deepseek-v4-flash` 与 `deepseek-v4-flash-vision-exp` 共享 `api.b.ai`）。\n- **直连失败自动回退（v2.0.0）**：所有**没有**走代理的请求——网络搜索、web_fetch、国内模型 API 等一切走全局 `fetch` 的目标——直连传输层失败（连接拒绝 / DNS 失败 / 超时 / 连接重置，且响应尚未开始）时，自动经回退代理重发一次。回退端点默认复用主代理（`proxyHost:proxyPort`），可用 `failoverProxy` 指定专用地址（`http://` / `https://` / `socks5://`）。走代理的模型不参与回退（它们本就没有直连路径）；本机服务（localhost / 127.0.0.1 / ::1）永不回退；流式请求体不可重放时不回退。\n- **失败主机负缓存**：某地址回退成功后，在 `negativeCacheTtlMs` 窗口内（默认 60s）直接走代理，不再重复支付直连超时；窗口内直连一旦成功立即恢复直连优先。\n- **失败自动重试**：对断连（连接已建立后的 ECONNRESET 等）、HTTP 429 限流、5xx 错误自动重试（默认 3 次、间隔 1s），减少免费额度被瞬时错误打断。CONNECT 级失败由回退层处理，重试只管「已连上后」的中途断开与状态码，不会反复锤打死路径。\n- **模型列表与官方一致**：只配了 `apiKeyEnv`、没写 `models` 的 provider（如 `xiaomi`），其模型从 pi-ai 内置目录（`@earendil-works/pi-ai`）回退补齐；`llm-deepseek` 命名空间即使保持默认空文档（`llm-deepseek: {}`）也回退官方内置目录（`https://api.deepseek.com` + `DEEPSEEK_API_KEY`），`deepseek-official/*` 模型开箱可用。勾选列表与 DSH 官方模型选择器完全同步。\n- **retryPolicy 镜像**：卡片上的 `retries`/`retryIntervalMs` 会镜像进被勾选 provider 的官方 `retryPolicy`（驱动设置页可见的 `(retry/maximum)` 提示），取消勾选自动还原官方默认值——一套配置同时驱动传输层重试与官方重试 UI。\n- **多模态模型镜像**：DSH 官方模型声明里，部分**支持图像识别**的模型（如 `deepseek-v4-flash-vision-exp`）没有可供用户勾选「图像输入」的配置入口，选中后发图会被 DSH 以 `UNSUPPORTED_CONTENT` 拒绝。在设置卡「多模态模型」区勾选这些模型后，插件把 `image` 写进所属 provider 的模型声明（pi-ai 的 `models[].input` / 目录型 `modelOverrides[].input`，官方 DeepSeek 的 `models[].inputModalities`），使 DSH 允许对该模型发图；取消勾选自动还原官方默认。注意：该功能只对真正支持图像输入的模型（如 vision 模型）有意义，纯文本模型（如 `deepseek-v4-flash`）勾选后 DSH 虽放行，实际请求仍会因模型不支持图像而报错。\n- **测试连接**：走代理的模型列表每行新增「测试连接」按钮，探测请求走插件自己的全局 dispatcher（即真实代理路径：勾选模型经代理、其余直连），返回 HTTP 状态 / 耗时 / 经代理或直连 / 多模态开启状态；失败时直接显示提供方返回的错误 body（脱敏、截断），如 B.AI 的 `max_tokens` 限制一眼可见。注意：测试走**已保存**的配置——改了勾选后请先点「保存」再测试。\n- **保存即生效，无需重启**：设置写入 `llm-proxy` 命名空间后运行时整体替换 dispatcher，不碰 `settings.yaml` 里的供应商配置。冷启动时若 provider 命名空间（`llm-pi-ai`/`llm-deepseek`）尚未注册，插件会带退避重试直到可解析代理域名，不再需要手动\"恢复默认再保存\"。\n\n## 用什么技术\n\n- **undici 全局 Dispatcher 注入**：`RetryAgent(FailoverDispatcher(RoutingDispatcher))` 挂到 Node 全局——`RoutingDispatcher`（按 hostname 路由：选中模型 host → ProxyAgent，其余直连）、`FailoverDispatcher`（直连路径的回退 + 负缓存）、官方 `RetryAgent`（429/5xx 与中途断开重试）。LLM 请求与搜索、web_fetch 等（OpenAI SDK → undici fetch）自动经过它，位于 LLM 适配器之下、供应商之上。\n- **Cordis 插件**：宿主侧注册 `llm-proxy` 设置命名空间（`lib/settings.js`）；浏览器侧设置卡片（`src/client/`，挂 `settings.plugin.item` slot，走官方 transport、bridge 兜底）。\n- **客户端构建**：tsdown（Rolldown）打包 `lib/client.js`，经 `window.__ModuleLoader__` 注入前端。\n\n## 适合什么场景\n\n- 国内网络访问**境外模型 API**（如 `api.b.ai`）超时/不可达——代理已就绪，只想让特定模型走。\n- **免费额度**被 429/5xx 打断，需要自动重试扛过限流窗口。\n- **直连不稳定**：搜索 / web_fetch / 国内 API 偶发连接失败，希望自动经代理兜底而不用改系统代理。\n- 想**按模型粒度**控制代理，而不是全局开代理连累国内直连 API。\n\n## 安装\n\n```sh\n# 推荐：npm 包（最新版，预构建 lib，秒装）\ndsh plugin --profile web add @anpiluo/dsh-proxy\n\n# 本地源码联调（改源码后需 npm run build 重建）\ndsh plugin --profile web add C:/path/to/dsh-llm-proxy\n```\n\n若提示 build 授权，把 `@anpiluo/dsh-proxy` 加进 profile 的 `pnpm-workspace.yaml` → `onlyBuiltDependencies`。装完重启 `dsh web`（托盘退出 → 启动）。\n\n## 版本兼容\n\n| 组件 | 要求 | 说明 |\n|---|---|---|\n| 客户端设置卡（DSH ≥ 0.1.7，含 0.2.0） | 插件页根列表槽位 `plugins.item` | 设置文档不再是「注册的命名空间」，而是由插件 `volatile()` 后的 Config 派生、按 **loader entry id `dsh-proxy`** 寻址；浏览器半边经官方 `configForms` 服务读写它，配置改动由 loader 就地提交并触发 `loader/volatile-update` |\n| 客户端设置卡（DSH 0.1.2–0.1.6） | 可配置插件 tab 的 keyed 槽位 `settings.plugin.item` | 继续走 `settings.register('llm-proxy')`；同一条 bundle 两代宿主都能用 |\n| 宿主半边（dispatcher / 设置桥 / 路由） | DSH ≥ 0.1.0-rc.7 | 纯 Node ESM。≥ 0.1.7 上宿主不再有 `settings.register()`，插件把它适配成同一套读/监听面（`volatileDocumentSeam`），install/热更新逻辑两代共用 |\n| 与官方出站代理共存 | DSH ≥ 0.1.3 | 宿主在插件加载前已装好 `@deepseek-ai/dsh-http-proxy` 的全局 dispatcher（读 `HTTP_PROXY`/`HTTPS_PROXY`）。插件不再用私有 Agent 覆盖**直连**路径，而是**链接**到它——本机出站策略继续生效，插件只接管「走代理的模型」那部分 host；卸载时也绝不关闭宿主的 dispatcher |\n| `@earendil-works/pi-ai` | 可选（peer） | 宿主（dsh-llm-pi-ai）提供时模型目录与官方选择器同步；未提供时目录型 provider 显示占位行 |\n\n> **多模态镜像的适用范围**：`multimodalModels` 只在 DSH ≤ 0.1.6 生效。≥ 0.1.7 的官方模型设置页自己拥有 `inputModalities`，插件会主动让位（日志 `owns model input modalities`），避免两个写者互相覆盖；请用官方页配置。该字段在新宿主上保留但不再写入。\n\n## 配置\n\n| 字段 | 默认 | 说明 |\n|---|---|---|\n| `proxyHost` / `proxyPort` | `127.0.0.1:7897` | 代理地址（Clash 等），可不在本机。`proxyHost` 也接受整条 URL（`http://10.0.0.9:1080`、`socks5://host`）或裸 `host:port`，scheme 与内嵌端口会一并采用（socks5 走 SOCKS agent） |\n| `proxiedModels` | `[]` | 走代理的模型，`<providerId>/<modelId>`，其余直连 |\n| `multimodalModels` | `[]` | 多模态镜像（**仅 DSH ≤ 0.1.6**）：勾选**支持图像识别但官方声明/UI 没有图像输入入口**的模型（如 `deepseek-v4-flash-vision-exp`），插件在所属 provider 声明中标记支持图片输入（pi-ai 写 `input`、官方 DeepSeek 写 `inputModalities`），发图不再被 DSH 拒绝；纯文本模型（如 `deepseek-v4-flash`）勾选无意义；取消勾选自动还原。DSH ≥ 0.1.7 上由官方模型设置页接管，此字段失效 |\n| `retries` / `retryIntervalMs` | `3` / `1000` | 失败重试次数与间隔（ms） |\n| `failoverEnabled` | `true` | 直连失败自动回退：非代理目标的直连传输层失败时经代理重发一次 |\n| `failoverProxy` | `''`（复用主代理） | 专用回退端点，`http://` / `https://` / `socks5://` URL |\n| `negativeCacheTtlMs` | `60000` | 回退成功后该地址跳过直连的时长（ms），`0` 关闭缓存 |\n| `trustedOrigins` | `[]` | 反向代理部署时额外信任的公网 origin（如 `https://dsh.example.com`）。默认只信本机；填写后公网域名可达设置桥，但仍强制同源校验（跨站页面拿不到数据） |\n\n## 验证\n\n**最快方式**：设置页（DSH ≤ 0.1.6：插件 → 可配置插件 → 模型代理；DSH ≥ 0.1.7：插件 → 模型代理）的「走代理的模型」列表里，每行有「测试连接」按钮，点击即向该模型发一次最小探测请求（走插件自己的全局 dispatcher，即真实代理路径）：\n\n- ✓ 连接成功：显示 `状态 · 耗时 · 经代理/直连 · 多模态已开启`（如 `✓ 连接成功 · 200 · 38ms · 经代理 · 多模态已开启`）\n- ✗ 连接失败：直接显示脱敏后的提供方错误原因（认证失败、限流、`max_tokens` 限制等），一眼定位问题\n\n> 注意：测试走的是**已保存**的配置——改了代理勾选/代理地址后，先点「保存」再测试；测试只验证一次非流式探测，流式/长对话仍建议用真实会话确认。\n\n**日志方式**：重启后日志出现：\n\n```\ndsh-proxy: global dispatcher → RetryAgent(FailoverDispatcher(RoutingDispatcher)) (proxy=127.0.0.1:7897, proxiedHosts=[...], retries=3×1000ms, failover=on (main-proxy), negCacheTtl=60000ms)\n```\n\n直连失败触发回退时，日志出现 `dsh-proxy: direct → <host> failed (<错误码>) — retrying via main-proxy failover proxy`。模型选择器里选中代理模型，流式响应正常、仅该模型域名走代理即成功。\n\n## 遇到问题？让大模型帮你排查\n\n插件出问题时，可在 DSH 中让其他模型帮忙排查——把下面这段提示词发给模型即可，模型会自行排查，无需用户提供报错信息或执行任何操作：\n\n```text\n检查当前 DSH 插件 @anpiluo/dsh-proxy 是否正常可用，按以下步骤自行排查：\n\n1. 查看插件配置确定当前代理端口号，自行通过该端口访问外网（如 github.com 等）判断端口是否连通，并确认本机可访问的端口和域名；\n2. 检查「走代理的模型」是否已勾选；\n3. 检查已勾选模型能否连通（可通过设置页「测试连接」验证）；\n4. 检查 dsh web 日志中 dsh-proxy 相关输出。\n\n根据排查结果判断插件是否可用；如不可用，给出全面修复方案。\n```\n\n## 测试\n\n```sh\nnpm test                    # 单元测试（路由 + 回退 + 重试镜像等，无网络）\nnpm run test:smoke          # 路由端到端（真实 undici + 本地服务器）\nnpm run test:smoke:failover # 回退端到端（直连死亡 → 代理重发、负缓存、禁用、流式）\n```\n","readmeFilename":"README.md"}