{"_id":"@boxfoxs/ai-router","_rev":"12-5964c95d73d1a4bd38ff7605be83f718","name":"@boxfoxs/ai-router","dist-tags":{"latest":"2.3.0"},"versions":{"1.0.0":{"name":"@boxfoxs/ai-router","version":"1.0.0","_id":"@boxfoxs/ai-router@1.0.0","maintainers":[{"name":"boxfoxsg619","email":"rlatjdfo112@naver.com"}],"dist":{"shasum":"588616545491cff7d88c34566ee9fdc84b1afeff","tarball":"https://registry.npmjs.org/@boxfoxs/ai-router/-/ai-router-1.0.0.tgz","fileCount":29,"integrity":"sha512-y23w0CiFXvt/PoXWT3ETK03hEeCLAcwSNuqm475QkIt+Zho30gcZzEo/IG0KPmfp9pppYs7obvJnj2kwJPRVqQ==","signatures":[{"sig":"MEQCIA6nZ0dEg/yTSF8O+aJy/BS85Qux4ECfGa74M9bbcknmAiA3I0Y1wFlwKabNF41zhI6T57THWDz3CIiZHXiJ6Xa8gA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":104716},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/esm","gitHead":"40a166ec7024694f1e3a4edc45ab5c27b1476c58","scripts":{"build":"rm -rf dist && tsc -p tsconfig.json && tsc -p tsconfig.esm.json","deploy":"yarn build && yarn publish --access=public"},"_npmUser":{"name":"boxfoxsg619","email":"rlatjdfo112@naver.com"},"_npmVersion":"11.3.0","description":"여러 Google AI 키를 공유 쿼터 상태로 돌려 쓰는 라우터","directories":{},"_nodeVersion":"24.2.0","publishConfig":{"registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"ioredis":"^5.4.1","typescript":"^4.8.4","@types/node":"^18.19.0","@google/genai":"^1.34.0"},"peerDependencies":{"ioredis":">=5.0.0","@google/genai":">=1.0.0"},"peerDependenciesMeta":{"ioredis":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/ai-router_1.0.0_1786603078140_0.44370674161832335","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@boxfoxs/ai-router","version":"1.1.0","_id":"@boxfoxs/ai-router@1.1.0","maintainers":[{"name":"boxfoxsg619","email":"rlatjdfo112@naver.com"}],"dist":{"shasum":"fd1d0768e6f98adb27d05c736d53dea7bd44f136","tarball":"https://registry.npmjs.org/@boxfoxs/ai-router/-/ai-router-1.1.0.tgz","fileCount":30,"integrity":"sha512-cosOeb/rmFU+923IqIvdUMqYQbFXsjTsxA79Z19CGWV/+/h0GPVCRMGk2USUaP9uj5pJQBefNlNDhr5eL20X+A==","signatures":[{"sig":"MEUCIGlSp1RrbNqBCek+8r4qBBGAy6UrJnNdWWkpS+/BV7rCAiEAyZe+Gf2qyPzmc5VeQjG1grIbQBawQ+uNEgHPMZWqXFI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":114435},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/esm","gitHead":"40a166ec7024694f1e3a4edc45ab5c27b1476c58","scripts":{"build":"rm -rf dist && tsc -p tsconfig.json && tsc -p tsconfig.esm.json","deploy":"yarn build && yarn publish --access=public"},"_npmUser":{"name":"boxfoxsg619","email":"rlatjdfo112@naver.com"},"_npmVersion":"11.3.0","description":"여러 Google AI 키를 공유 쿼터 상태로 돌려 쓰는 라우터","directories":{},"_nodeVersion":"24.2.0","publishConfig":{"registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"ioredis":"^5.4.1","typescript":"^4.8.4","@types/node":"^18.19.0","@google/genai":"^1.34.0"},"peerDependencies":{"ioredis":">=5.0.0","@google/genai":">=1.0.0"},"peerDependenciesMeta":{"ioredis":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/ai-router_1.1.0_1786606136447_0.11198829713036673","host":"s3://npm-registry-packages-npm-production"}},"1.2.0":{"name":"@boxfoxs/ai-router","version":"1.2.0","_id":"@boxfoxs/ai-router@1.2.0","maintainers":[{"name":"boxfoxsg619","email":"rlatjdfo112@naver.com"}],"dist":{"shasum":"28bec91adeaa85773dc52606498b836ff7feae37","tarball":"https://registry.npmjs.org/@boxfoxs/ai-router/-/ai-router-1.2.0.tgz","fileCount":30,"integrity":"sha512-sHnoSPqLVNOpY7h7YFGs/M1fb3swJqcl3S4zEtsHD4TI+i37HNFSPQfEk3Hz2OiDYOBc11x9SBKFScObY+kO0A==","signatures":[{"sig":"MEUCIQCA7Xqd3QXBUxuYwf2gDHMMXvBdRN5Zwuxmh3fOoWNpRwIgefz7mgj1Zho9IToKyTaGJEPGfzgpwZ5YSvSQrqAn9rw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":123015},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/esm","gitHead":"f801dc535326a1691f6112a6372289df8bc0fe2a","scripts":{"build":"rm -rf dist && tsc -p tsconfig.json && tsc -p tsconfig.esm.json","deploy":"yarn build && yarn publish --access=public"},"_npmUser":{"name":"boxfoxsg619","email":"rlatjdfo112@naver.com"},"_npmVersion":"11.3.0","description":"여러 Google AI 키를 공유 쿼터 상태로 돌려 쓰는 라우터","directories":{},"_nodeVersion":"24.2.0","publishConfig":{"registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"ioredis":"^5.4.1","typescript":"^4.8.4","@types/node":"^18.19.0","@google/genai":"^1.34.0"},"peerDependencies":{"ioredis":">=5.0.0","@google/genai":">=1.0.0"},"peerDependenciesMeta":{"ioredis":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/ai-router_1.2.0_1786606652192_0.5553143903148301","host":"s3://npm-registry-packages-npm-production"}},"1.3.0":{"name":"@boxfoxs/ai-router","version":"1.3.0","_id":"@boxfoxs/ai-router@1.3.0","maintainers":[{"name":"boxfoxsg619","email":"rlatjdfo112@naver.com"}],"dist":{"shasum":"50faa8b3db605f6e7bd7c35faa3b8e132f644602","tarball":"https://registry.npmjs.org/@boxfoxs/ai-router/-/ai-router-1.3.0.tgz","fileCount":30,"integrity":"sha512-ebt7Wve365z+JqBACX+IvaO74h8sUE4pKELzUN+7ChZ0WTcQGknz1bytViGs7v2L9mVSoCcdxuL8tbXNMdhCRg==","signatures":[{"sig":"MEUCID+Jnc7u2jog4ecI4JethIDhbH60sOa2ONXTP54UC1VVAiEAlYyzjvBJ7/9cyw+FKmiEnV8N9lBn/c3YzlvFv/rz9PA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":125335},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/esm","gitHead":"f801dc535326a1691f6112a6372289df8bc0fe2a","scripts":{"build":"rm -rf dist && tsc -p tsconfig.json && tsc -p tsconfig.esm.json","deploy":"yarn build && yarn publish --access=public"},"_npmUser":{"name":"boxfoxsg619","email":"rlatjdfo112@naver.com"},"_npmVersion":"11.3.0","description":"여러 Google AI 키를 공유 쿼터 상태로 돌려 쓰는 라우터","directories":{},"_nodeVersion":"24.2.0","publishConfig":{"registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"ioredis":"^5.4.1","typescript":"^4.8.4","@types/node":"^18.19.0","@google/genai":"^1.34.0"},"peerDependencies":{"ioredis":">=5.0.0","@google/genai":">=1.0.0"},"peerDependenciesMeta":{"ioredis":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/ai-router_1.3.0_1786609720230_0.5371928621342565","host":"s3://npm-registry-packages-npm-production"}},"1.4.0":{"name":"@boxfoxs/ai-router","version":"1.4.0","_id":"@boxfoxs/ai-router@1.4.0","maintainers":[{"name":"boxfoxsg619","email":"rlatjdfo112@naver.com"}],"dist":{"shasum":"f608d02901dfdef9c4ec789a4492eecda9ae6c01","tarball":"https://registry.npmjs.org/@boxfoxs/ai-router/-/ai-router-1.4.0.tgz","fileCount":31,"integrity":"sha512-EDsh6ItwOXf5O7RjXfBvMOD0HKP4Y7NZA0LxPYk+aV7fq1sX0kG5gfWp6UcTmzi/XGe2rJmxcEqjIg8yIgistA==","signatures":[{"sig":"MEYCIQDKj2mx1dxYCZdVsjjQZ7z9WWnvKdBylz+KRMPoJERNPwIhAOFcwDFPgsPHjbKFj6WVBVT6rW85hU0Mr2jjz3Orau0E","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEYCIQC9GnSuRjPZ9Jhrrnz6X/A81wBFqQfQdlLhdYU5landkAIhAPseVStOC0NTdLDqS35xt8cO5XJc7cswkMaffdEei9Ug","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":157341},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/esm","gitHead":"2c3cb7f55127cec420ef77bdc2485bd6d01a8991","scripts":{"build":"rm -rf dist && tsc -p tsconfig.json && tsc -p tsconfig.esm.json","deploy":"yarn build && yarn publish --access=public"},"_npmUser":{"name":"boxfoxsg619","email":"rlatjdfo112@naver.com"},"deprecated":"실수로 미완성 Vertex 지원 코드가 포함된 채 배포되었습니다. 1.4.1 을 쓰세요 (동일 기능 + 정상 소스).","_npmVersion":"11.3.0","description":"여러 Google AI 키를 공유 쿼터 상태로 돌려 쓰는 라우터","directories":{},"_nodeVersion":"24.2.0","publishConfig":{"registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"ioredis":"^5.4.1","typescript":"^4.8.4","@types/node":"^18.19.0","@google/genai":"^1.34.0"},"peerDependencies":{"ioredis":">=5.0.0","@google/genai":">=1.0.0"},"peerDependenciesMeta":{"ioredis":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/ai-router_1.4.0_1787805850731_0.2253040094265304","host":"s3://npm-registry-packages-npm-production"}},"1.4.1":{"name":"@boxfoxs/ai-router","version":"1.4.1","_id":"@boxfoxs/ai-router@1.4.1","maintainers":[{"name":"boxfoxsg619","email":"rlatjdfo112@naver.com"}],"dist":{"shasum":"99c81434e4b704c7093bd1ae71fc89866fb1d141","tarball":"https://registry.npmjs.org/@boxfoxs/ai-router/-/ai-router-1.4.1.tgz","fileCount":31,"integrity":"sha512-LOpjVMJ6uaZmDhWgT+7g/NkjOOgoc32i8cJqI6gT+t0cBoODV8MfndQTc18/Mgfpsvt6i+2BP5YaIvGLf/2UNA==","signatures":[{"sig":"MEQCIBN4BZuUwkV+H3DcmsAh0oCq7YcE4VTBrvKv9HQBNhHTAiAL9Pcm+U+OFq7rs0oOfuIhGSvhqxgfIG85DPumMpXRPA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEQCIErDN+b4n2ZbffEjsS6B+bFKuMT5IWF7xHMcx3ZzdT1ZAiAmgNweFjlrVg5EnAVSEqS4E4fx92Y8d0RxY5qXsqRN7A==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":149804},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/esm","scripts":{"build":"rm -rf dist && tsc -p tsconfig.json && tsc -p tsconfig.esm.json","deploy":"yarn build && yarn publish --access=public"},"_npmUser":{"name":"boxfoxsg619","email":"rlatjdfo112@naver.com"},"_npmVersion":"11.3.0","description":"여러 Google AI 키를 공유 쿼터 상태로 돌려 쓰는 라우터","directories":{},"_nodeVersion":"24.2.0","publishConfig":{"registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"ioredis":"^5.4.1","typescript":"^4.8.4","@types/node":"^18.19.0","@google/genai":"^1.34.0"},"peerDependencies":{"ioredis":">=5.0.0","@google/genai":">=1.0.0"},"peerDependenciesMeta":{"ioredis":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/ai-router_1.4.1_1787806120266_0.9197634742552934","host":"s3://npm-registry-packages-npm-production"}},"1.5.0":{"name":"@boxfoxs/ai-router","version":"1.5.0","_id":"@boxfoxs/ai-router@1.5.0","maintainers":[{"name":"boxfoxsg619","email":"rlatjdfo112@naver.com"}],"dist":{"shasum":"676b22c098cb16485b05aad3f5b3a827055da85f","tarball":"https://registry.npmjs.org/@boxfoxs/ai-router/-/ai-router-1.5.0.tgz","fileCount":32,"integrity":"sha512-S91slzNQ2ykPfvJn148bVJX6aoYRAoEky4SwHA3AiOaNb8c+hf+CKjfS3FJ+AqB3ejJ02wu+IKgXq4QtTIipOQ==","signatures":[{"sig":"MEUCICszdAskR2s6kMHwB0e3k6mjdHCq1X43XWhK0S63RhcEAiEAvjmx3JlVs927eT8cTAQ5jpbwW6VFWho4qTnKyadGFcM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIQDU4VIWRmlLO4GN/Ta7R8nmjkn5I7RRWIBy7ANMZzwCVwIgJEeuvL0msk6eUJn2rZ0Wbfj1oOTfRqOuQdmc0ccV+aY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":209649},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/esm","gitHead":"c428b7c8a03d285280fba0f6edb4a8968bdcd946","scripts":{"build":"rm -rf dist && tsc -p tsconfig.json && tsc -p tsconfig.esm.json","deploy":"yarn build && yarn publish --access=public","verify":"yarn build && node scripts/verify-vertex-fallback.cjs"},"_npmUser":{"name":"boxfoxsg619","email":"rlatjdfo112@naver.com"},"_npmVersion":"11.3.0","description":"여러 Google AI 키를 공유 쿼터 상태로 돌려 쓰는 라우터","directories":{},"_nodeVersion":"24.2.0","publishConfig":{"registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"ioredis":"^5.4.1","typescript":"^4.8.4","@types/node":"^18.19.0","@google/genai":"^1.34.0"},"peerDependencies":{"ioredis":">=5.0.0","@google/genai":">=1.0.0"},"peerDependenciesMeta":{"ioredis":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/ai-router_1.5.0_1787806479782_0.06534846455363086","host":"s3://npm-registry-packages-npm-production"}},"2.0.0":{"name":"@boxfoxs/ai-router","version":"2.0.0","_id":"@boxfoxs/ai-router@2.0.0","maintainers":[{"name":"boxfoxsg619","email":"rlatjdfo112@naver.com"}],"dist":{"shasum":"429a1fc23c31b2c979724d509e1404cdb7346c43","tarball":"https://registry.npmjs.org/@boxfoxs/ai-router/-/ai-router-2.0.0.tgz","fileCount":23,"integrity":"sha512-YhOFFoSu4yzLENKdXxPHnSW4MJ65rgpf9VQ98JZeXXq0YvJuPejz+um2AMH+khUOEhSHB0B0aPfe1J/6VTz6iQ==","signatures":[{"sig":"MEQCIAHrzxwQU8wStgiMQobz0GCD2Nf/tmubG3HjMZSit7WWAiA3GW/4CfFNldT2o1x2VU8o83tcNq1LLDkowTIJROMkTQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIGA1CsUExMd+wC5yFha4s+LB0bHGgYFS6VaBuFCGmheRAiEAmL8Zd6MImdXflR66246InQVUhahKQ1DbCnxX06+OE+Y=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":74413},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/esm","engines":{"node":">=18"},"scripts":{"build":"rm -rf dist && tsc -p tsconfig.json && tsc -p tsconfig.esm.json","deploy":"yarn build && yarn publish --access=public"},"_npmUser":{"name":"boxfoxsg619","email":"rlatjdfo112@naver.com"},"_npmVersion":"11.3.0","description":"공용 AI 라우터 서버로 요청을 보내는 얇은 클라이언트","directories":{},"_nodeVersion":"24.2.0","publishConfig":{"registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^4.8.4","@types/node":"^18.19.0"},"_npmOperationalInternal":{"tmp":"tmp/ai-router_2.0.0_1788229759337_0.14546157442872132","host":"s3://npm-registry-packages-npm-production"}},"2.1.0":{"name":"@boxfoxs/ai-router","version":"2.1.0","_id":"@boxfoxs/ai-router@2.1.0","maintainers":[{"name":"boxfoxsg619","email":"rlatjdfo112@naver.com"}],"dist":{"shasum":"a83e8f0c2db26eba0bc6e187200fb1eba5d2718f","tarball":"https://registry.npmjs.org/@boxfoxs/ai-router/-/ai-router-2.1.0.tgz","fileCount":23,"integrity":"sha512-gngUipCh844lna4aT+aMpycaK120OceH34hOzbM5yb02MvqQRZNEk+Nn7Z08AQPsRDqRiyC2iI7GnACAdXAgSg==","signatures":[{"sig":"MEUCIQC1ow3GS5Uwpd5BGXbXZATSVPGs3DnQ2mPgKSsmHibJIgIgFgVIL9jGx2NN0rgefcpEd6T0EmFzOh/IzzKz8YMbm1w=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIQCmjtvS2xSd3dkZleYQANgCkuAChAM+lkeoOCExqty0hwIgf1T4POmNHbUCL86k0ohyRvJD5zBVEkjog//NzsHSzOs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":91347},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/esm","engines":{"node":">=18"},"scripts":{"build":"rm -rf dist && tsc -p tsconfig.json && tsc -p tsconfig.esm.json","deploy":"yarn build && yarn publish --access=public"},"_npmUser":{"name":"boxfoxsg619","email":"rlatjdfo112@naver.com"},"_npmVersion":"10.9.7","description":"공용 AI 라우터 서버로 요청을 보내는 얇은 클라이언트","directories":{},"_nodeVersion":"22.22.2","publishConfig":{"registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^4.9.5","@types/node":"^18.19.130"},"_npmOperationalInternal":{"tmp":"tmp/ai-router_2.1.0_1788238848540_0.4754501406564742","host":"s3://npm-registry-packages-npm-production"}},"2.2.0":{"name":"@boxfoxs/ai-router","version":"2.2.0","_id":"@boxfoxs/ai-router@2.2.0","maintainers":[{"name":"boxfoxsg619","email":"rlatjdfo112@naver.com"}],"dist":{"shasum":"3fe314c17a2047ff075591efe74c57995467db02","tarball":"https://registry.npmjs.org/@boxfoxs/ai-router/-/ai-router-2.2.0.tgz","fileCount":23,"integrity":"sha512-T5lZYx9B/6jryLGjeTEjjsr0rRA6+X5CLPawG95X2gy2Xd54Kufr+j0mXzOLIYbs8M50AJBXzJ1S2T+/VpU3lA==","signatures":[{"sig":"MEYCIQCBeIFSCvHIx5K1KBolLiLrtvx4VJ8sLNBcS+fMjNL+/AIhAPXL/h1FSyxz8cC5Q6Kj97wa+6Df8EaijcRdLSPcZx0+","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIQD0G6VXsxS1iaVagImaQFDNl6njbRvBtUcChzC+9+1x8AIgSwxoD6dlC5tA3R0nA9xllWv7I6yWWcTmreF8y4dcMLk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":93762},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/esm","engines":{"node":">=18"},"scripts":{"build":"rm -rf dist && tsc -p tsconfig.json && tsc -p tsconfig.esm.json","deploy":"yarn build && yarn publish --access=public"},"_npmUser":{"name":"boxfoxsg619","email":"rlatjdfo112@naver.com"},"_npmVersion":"10.9.7","description":"공용 AI 라우터 서버로 요청을 보내는 얇은 클라이언트","directories":{},"_nodeVersion":"22.22.2","publishConfig":{"registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^4.9.5","@types/node":"^18.19.130"},"_npmOperationalInternal":{"tmp":"tmp/ai-router_2.2.0_1788239324887_0.00309911792233164","host":"s3://npm-registry-packages-npm-production"}},"2.3.0":{"_id":"@boxfoxs/ai-router@2.3.0","dist":{"shasum":"cbe5be1c73a508086a9406dc4d520502070325ed","tarball":"https://registry.npmjs.org/@boxfoxs/ai-router/-/ai-router-2.3.0.tgz","fileCount":23,"integrity":"sha512-jewrhPtuXti4H+2XJVjzUK+hpS/McceoZeQpWmoeC0XDbBCFtOcp7fbgBc9msCcr5C5M+BqWsdkFMl7DajRSFw==","signatures":[{"sig":"MEUCID8E/p60MDzCDY6OOpAaEOCRJ9/VOPzXotuZlpoHbzRCAiEAsiY+e2bfE4vPVEY+6MAFq2dJyz9nkuucqGcAELODRQ4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIDkNsb/8QmC+5qf2xP9wROa2IXJKhOdiCHDZu78Qdvz8AiEAinxVfNQXTbpEuJ+v/i2EFLzlMFIENpgxsOnD5i6KAKY="}],"unpackedSize":96777},"main":"dist/index.js","name":"@boxfoxs/ai-router","types":"dist/index.d.ts","module":"dist/esm","engines":{"node":">=18"},"gitHead":"e77e54c1b18aba082e3de3ab128663423896730a","scripts":{"build":"rm -rf dist && tsc -p tsconfig.json && tsc -p tsconfig.esm.json","deploy":"yarn build && yarn publish --access=public"},"version":"2.3.0","_npmUser":{"name":"boxfoxsg619","email":"rlatjdfo112@naver.com"},"_npmVersion":"10.9.7","description":"공용 AI 라우터 서버로 요청을 보내는 얇은 클라이언트","directories":{},"maintainers":[{"name":"boxfoxsg619","email":"rlatjdfo112@naver.com"}],"_nodeVersion":"22.22.2","publishConfig":{"registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^4.9.5","@types/node":"^18.19.130"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/ai-router_2.3.0_1788252182660_0.7812601224964268"}}},"time":{"created":"2026-08-13T06:37:57.894Z","modified":"2026-09-01T08:43:02.949Z","1.0.0":"2026-08-13T06:37:58.299Z","1.1.0":"2026-08-13T07:28:56.584Z","1.2.0":"2026-08-13T07:37:32.350Z","1.3.0":"2026-08-13T08:28:40.384Z","1.4.0":"2026-08-27T04:44:10.829Z","1.4.1":"2026-08-27T04:48:40.382Z","1.5.0":"2026-08-27T04:54:39.869Z","2.0.0":"2026-09-01T02:29:19.419Z","2.1.0":"2026-09-01T05:00:48.642Z","2.2.0":"2026-09-01T05:08:44.968Z","2.3.0":"2026-09-01T08:43:02.759Z"},"description":"공용 AI 라우터 서버로 요청을 보내는 얇은 클라이언트","maintainers":[{"name":"boxfoxsg619","email":"rlatjdfo112@naver.com"}],"readme":"# @boxfoxs/ai-router\n\n공용 AI 라우터 서버로 요청을 보내는 얇은 클라이언트.\n\n```ts\nimport { AiRouter } from '@boxfoxs/ai-router';\n\nconst router = new AiRouter({ serviceName: 'bake-cs' });\n\nconst { text, model, usage } = await router.generateText({\n  systemInstruction: '너는 친절한 상담원이다',\n  contents: [{ role: 'user', parts: [{ text: '주문 취소하고 싶어요' }] }],\n});\n```\n\n---\n\n## v1 에서 뭐가 바뀌었나\n\nv1 은 이 패키지 안에 라우팅 엔진이 통째로 들어 있었다. 서비스마다 API 키·모델 목록·\nRedis·Vertex 자격증명을 들고 있어야 했고, **축을 하나 붙이거나 키를 바꾸면 물려 있는\n서비스를 전부 다시 배포**해야 했다. 그래서 실제로 버전이 1.1.0 부터 1.5.0 까지 네 갈래로\n갈라졌고, 유료 폴백은 그중 일부에만 들어가 있었다.\n\nv2 는 그 전부를 [ai-router-server](https://github.com/Compounding-Co-Ltd/ai-router-server)\n로 넘겼다. 이 패키지에 남은 일은 HTTP 요청 한 번과 재시도뿐이다.\n\n|  | v1 | v2 |\n| --- | --- | --- |\n| 키·모델·축 관리 | 서비스마다 | 서버 한 곳 |\n| 필요한 환경변수 | 10개 | **2개** |\n| Redis 연결 | 서비스마다 (`ioredis` 필요) | 불필요 |\n| 축 추가 시 배포 | 물려 있는 서비스 전부 | 서버만 |\n\n**호출부 코드는 그대로다.** `generateText`/`generateJson` 의 시그니처와 반환 모양이\nv1 과 같다. 바뀌는 건 생성자에 넘기는 것뿐이다.\n\n---\n\n## 옮기기\n\n### 1. 올린다\n\n```bash\nyarn add @boxfoxs/ai-router@^2.0.0\n```\n\n`ioredis` 를 이 패키지 때문에 넣었던 거라면 이제 지워도 된다.\n\n### 2. 생성자를 줄인다\n\n```diff\n  const router = new AiRouter({\n    serviceName: 'bake-cs',\n-   apiKeys: process.env.GOOGLE_AI_API_KEYS,\n-   models: process.env.GEMINI_MODELS,\n-   redisOptions: {\n-     host: process.env.AI_ROUTER_REDIS_HOST,\n-     password: process.env.AI_ROUTER_REDIS_PASSWORD,\n-   },\n-   vertex: {\n-     project: process.env.VERTEX_PROJECT,\n-     clientEmail: process.env.VERTEX_CLIENT_EMAIL,\n-     privateKey: process.env.VERTEX_PRIVATE_KEY?.replace(/\\\\n/g, '\\n'),\n-   },\n  });\n```\n\n`baseUrl` 과 `serviceKey` 는 환경변수에서 읽으므로 대개 아무것도 안 넘겨도 된다.\n\n지운 옵션을 실수로 남겨둬도 **안 깨진다.** 대신 뜰 때 이런 경고가 찍힌다.\n\n```\n[ai-router:bake-cs] 이제 서버가 관리하는 옵션이 넘어왔습니다 — 무시합니다: apiKeys, vertex\n```\n\n### 3. 환경변수를 바꾼다\n\n```bash\nAI_ROUTER_URL=http://127.0.0.1:4447\nAI_ROUTER_SERVICE_KEY=<이 서비스에 발급받은 키>\n```\n\n**배포 설정에서 옛 변수를 지우는 걸 잊지 말 것.** 코드에서 지워도\n`ecosystem.config.js` 의 passthrough 목록과 Jenkins 자격증명 바인딩에 남아 있으면\n시크릿이 계속 서비스로 흘러간다.\n\n```diff\n  const passthrough = [\n-   'GOOGLE_AI_API_KEYS', 'GEMINI_MODELS',\n-   'AI_ROUTER_REDIS_HOST', 'AI_ROUTER_REDIS_PASSWORD',\n-   'VERTEX_PROJECT', 'VERTEX_LOCATION', 'VERTEX_MODELS',\n-   'VERTEX_CLIENT_EMAIL', 'VERTEX_PRIVATE_KEY', 'VERTEX_DAILY_CALL_CAP',\n+   'AI_ROUTER_URL', 'AI_ROUTER_SERVICE_KEY',\n  ];\n```\n\n---\n\n## API\n\n### `new AiRouter(options)`\n\n| 옵션 | 기본값 | 설명 |\n| --- | --- | --- |\n| `baseUrl` | `process.env.AI_ROUTER_URL` | 라우터 서버 주소 |\n| `serviceKey` | `process.env.AI_ROUTER_SERVICE_KEY` | 이 서비스에 발급된 키 |\n| `serviceName` | `'unknown'` | 로그 태그용 |\n| `timeoutMs` | `65000` | 요청 하나의 제한 시간 |\n| `maxRetries` | `2` | 재시도 횟수 |\n| `logger` | `console` | `log`/`warn`/`error` 를 가진 객체 |\n\n`baseUrl` 이나 `serviceKey` 가 없으면 **뜰 때 error 로그를 남기고, 실제 호출 시점에\n던진다.** 생성자에서 던지면 AI 가 곁다리인 서비스가 env 하나 빠졌다고 통째로 못 뜨는데,\n그건 막으려던 사고보다 크다. 미리 확인하고 싶으면 `isConfigured` 를 본다.\n\n```ts\nif (!router.isConfigured) {\n  // AI 기능만 끄고 나머지는 정상 동작시키는 식으로\n}\n```\n\n### `generateText(request)` · `generateJson<T>(request)`\n\nv1 과 동일하다.\n\n```ts\nconst { text, model, key, keyId, usage } = await router.generateText({\n  systemInstruction: '…',\n  contents: [{ role: 'user', parts: [{ text: '…' }] }],\n  temperature: 0.7,        // 선택\n  maxOutputTokens: 2000,   // 선택\n});\n\nconst { data } = await router.generateJson<{ sentiment: string }>({ … });\n```\n\n그림·소리·영상은 `inlineData` 로 올린다 (base64, `data:` 접두사 없이).\n\n```ts\ncontents: [{\n  role: 'user',\n  parts: [\n    { text: '이 영수증에서 총액만 뽑아줘' },\n    { inlineData: { mimeType: 'image/jpeg', data: base64 } },\n  ],\n}]\n```\n\n### `generateImage(request)`  *(2.1.0~)*\n\n그림을 **만들어** 받는다. 위 `inlineData` 는 그림을 **넣는** 것이고, 이건 반대다.\n\n```ts\nconst { images, provider, attempts } = await router.generateImage({\n  tag: 'panel-render',\n  prompt: '만화 한 컷을 그린다. 화풍은 …',\n  referenceImages: [                       // 선택. 순서가 그대로 유지된다\n    { mimeType: 'image/png', data: base64 },\n  ],\n});\n\nconst buffer = Buffer.from(images[0].data, 'base64');\n```\n\n**프롬프트를 만드는 일은 호출부의 몫이다.** 화풍·구도·네거티브 프롬프트는 그\n서비스의 도메인 지식이라 라우터로 올리지 않는다. 라우터는 키·축·쿼터만 고른다.\n\n#### 레퍼런스가 여러 종류면 `parts` 를 쓴다  *(2.2.0~)*\n\n어느 그림이 캐릭터 앵커고 어느 게 직전 패널인지는 **그림 바로 앞에 붙은 글**이\n알려준다. 위 간단형은 글을 전부 앞으로 모으고 그림을 뒤에 몰기 때문에 그 대응이\n사라진다. 순서가 의미를 갖는다면 `parts` 로 번갈아 넣는다.\n\n```ts\nawait router.generateImage({\n  tag: 'panel-render',\n  parts: [\n    { text: '만화 한 컷을 그린다. …' },\n    { text: '[캐릭터 레퍼런스 — 얼굴·머리·옷을 동일하게]' },\n    { image: anchor },\n    { text: '[직전 패널 — 화풍만 참고, 장면은 달라야 함]' },\n    { image: previousPanel },\n  ],\n});\n```\n\n`parts` 를 주면 `prompt`/`referenceImages` 는 무시된다 — 두 벌이 섞이면 순서가 깨진다.\n\n#### `attempts` — 과금된 횟수가 여기 있다\n\n폴백이 돌면 **체인이 넘어갈 때마다 과금되는 호출이 한 번씩 더 나간다.** 응답에는\n성공한 것 하나만 오지만, `attempts` 에는 나간 호출이 전부 들어 있다.\n\n```ts\nattempts // [{ provider:'vertex', ok:false, latencyMs:2100, error:'429 …' },\n         //  { provider:'aistudio', ok:true,  latencyMs:18400 }]\n```\n\n자기 비용 장부를 따로 쓰는 서비스는 이 배열을 펼쳐 넣어야 맞는다. 성공 건만 세면\n과소 집계된다. 실패해서 던질 때도 오류 객체(`error.payload`)에 함께 실려 온다.\n\n#### 타임아웃과 재시도\n\n기본 제한 시간이 **190초**다(텍스트는 65초). 이미지는 10~30초가 보통이고 서버가\n축을 폴백하면 더 걸려서, 텍스트 기준으로 자르면 정상 호출이 클라이언트에서 잘린다.\n그렇게 잘리면 **서버는 계속 그리고 클라이언트만 포기한 상태** — 과금은 되고 그림은\n못 받는 가장 나쁜 실패가 된다.\n\n재시도는 하지 않는다. 이미지 한 장이 수십 초 걸리는데 다시 보내면 서버에서 이미\n그리고 있는 것과 별개로 한 번 더 과금된다. 축 폴백은 서버가 이미 다 돌아본 뒤다.\n\n### `getStatus()`\n\n키·모델 조합의 차단 현황. 헬스체크나 대시보드에 쓴다. 2.1.0 부터 각 슬롯에\n`modality`(`'text' | 'image'`) 가 붙는다 — 글 슬롯과 그림 슬롯은 매트릭스가 다르다.\n\n### `close()`\n\nv1 호환용 no-op. 클라이언트는 붙잡고 있는 자원이 없다. 종료 훅을 고치지 않아도 되도록 남겨뒀다.\n\n---\n\n## 오류 다루기\n\n실패는 전부 `AiRouterServerError` 로 온다.\n\n```ts\nimport { AiRouterServerError } from '@boxfoxs/ai-router';\n\ntry {\n  await router.generateText(req);\n} catch (e) {\n  if (e instanceof AiRouterServerError) {\n    if (e.isQuotaExhausted) { /* 429 — 모든 축 소진 */ }\n    if (e.isUnauthorized)   { /* 401 — 서비스 키 문제 */ }\n  }\n}\n```\n\n**직접 재시도 루프를 두지 말 것.** 서버는 이미 모든 키·모델·축을 순회한 뒤에 답한다.\n여기까지 온 429 는 \"한 키가 막혔다\"가 아니라 **\"지금 쓸 수 있는 조합이 없다\"** 는 뜻이라,\n다시 보내면 소진 상태만 더 두드린다.\n\n클라이언트가 자동으로 재시도하는 건 **연결 실패와 5xx 뿐**이다(지수 백오프, 기본 2회).\n\n---\n\n## 축 정책 — OpenAI 는 옵트인이다\n\n서버는 세 축을 순서대로 탄다.\n\n```\n무료 Gemini(키 여러 개) → 무료 OpenAI → 유료 Vertex\n```\n\n이 중 **무료 OpenAI 축은 프롬프트·응답을 OpenAI 에 제공하는 대가로 받는 무료 토큰**을\n쓴다. 그래서 기본값은 **꺼져 있고**, 서비스 키에 매달린 정책으로만 켜진다.\n\n민감한 걸 다루는 서비스(영수증·고객 문의 원문·화주 서류 등)는 켜지 않는다.\n켤지 말지는 클라이언트가 정하는 게 아니라 **서버에서 키에 붙여** 정하므로,\n호출부에서 실수로 켤 수 없다.\n\n---\n\n## 서버가 없으면\n\n로컬 개발처럼 서버가 없는 환경에서는 생성자가 던진다. 붙일 서버를 띄우거나,\n개발용 라우터 주소를 `AI_ROUTER_URL` 로 지정할 것.\n\n운영에서는 서버가 단일 통로다 — 죽으면 물려 있는 서비스가 전부 영향을 받는다.\n그래서 서버는 pm2 cluster 로 띄우고 `/health` 로 감시한다. 자세한 건 서버 저장소 참고.\n","readmeFilename":"README.md"}