@keepdb/cc 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/bin/cli.js +27 -0
  2. package/package.json +30 -0
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env node
2
+ import{z as p}from"zod";import{readFileSync as q,existsSync as k}from"fs";import{resolve as T,join as N}from"path";import{homedir as _}from"os";import{parse as U}from"@keepdb/kit/yaml";var A=["haiku","sonnet","opus"],W=t=>{if(typeof t!="string")return null;let[e,o]=t.split("@");return{model:e,weight:Number(o)||1}},D=p.object({name:p.string().min(1),url:p.string().url(),apiKey:p.string().min(1),models:p.record(p.string(),p.string()).refine(t=>Object.keys(t).some(e=>A.includes(e)),{message:"\u81F3\u5C11\u9700\u8981\u4E00\u4E2A\u6709\u6548\u7684 tier (haiku/sonnet/opus)"})}),J=p.object({providers:p.array(D).min(1),fetchTimeout:p.number().int().positive().default(3e4)}),V=t=>{if(t)return T(t);let e=T("ismartify.config.yaml");return k(e)?e:N(_(),".keepdb","ismartify.config.yaml")},E=t=>{let e=V(t);if(!k(e))throw new Error(t?`\u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728: ${e}`:"\u672A\u627E\u5230\u914D\u7F6E\u6587\u4EF6\uFF0C\u8BF7\u521B\u5EFA ismartify.config.yaml \u6216 ~/.keepdb/ismartify.config.yaml");let o=U(q(e,"utf-8"));if(!o.codeplan)throw new Error(`\u914D\u7F6E\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF: \u7F3A\u5C11\u9876\u5C42 "codeplan:" key\u3002
3
+ \u8BF7\u53C2\u8003\u683C\u5F0F:
4
+
5
+ codeplan:
6
+ fetchTimeout: 30000
7
+ providers:
8
+ - name: example
9
+ url: https://api.example.com/anthropic
10
+ apiKey: sk-xxx
11
+ models:
12
+ sonnet: model-name@1`);let r=J.parse(o.codeplan),s=Object.fromEntries(r.providers.map(n=>[n.name,{name:n.name,url:n.url,apiKey:n.apiKey}])),i=r.providers.flatMap(n=>Object.entries(n.models).filter(([c])=>A.includes(c)).map(([c,a])=>{let m=W(a);return m?{tier:c,targetModel:m.model,weight:m.weight,provider:n.name}:null}).filter(Boolean));return{providers:s,slots:i,fetchTimeout:r.fetchTimeout,getProvider:n=>s[n]}};import{Hono as X}from"hono";import{logger as B}from"hono/logger";import{Hono as z}from"hono";var h=t=>{let e=new z;return e.get("/",o=>{let r=Object.values(t.providers).map(({name:s,url:i})=>({name:s,url:i,models:t.slots.filter(n=>n.provider===s).map(({tier:n,targetModel:c,weight:a})=>({tier:n,targetModel:c,weight:a}))}));return o.json({fetchTimeout:t.fetchTimeout,providers:r})}),e};import{Hono as G}from"hono";var S="sonnet",g=["opus","sonnet","haiku"],$=t=>{if(!t||typeof t!="string")return S;let e=t.toLowerCase();return e.includes("haiku")?"haiku":e.includes("opus")?"opus":e.includes("sonnet")?"sonnet":S},C=t=>{let e=g.indexOf(t);return e===-1?g:g.slice(e)};var R=t=>{let e=new Map;return t.forEach((o,r)=>{let s=e.get(o.tier)||[];s.push({...o,originalIndex:r}),e.set(o.tier,s)}),{select:(o,r=[])=>{let s=(e.get(o)||[]).filter(c=>!r.includes(c.originalIndex));if(s.length===0)return null;let i=s.reduce((c,a)=>c+a.weight,0),n=Math.random()*i;for(let c of s){if(n<c.weight)return c;n-=c.weight}return null},countByTier:o=>(e.get(o)||[]).length}};var I=t=>{let e=R(t.slots);return{forward:async({path:r,method:s,jsonBody:i,signal:n})=>{var y;let c=$(i==null?void 0:i.model),a=C(c),m=[];for(let b of a){let F=e.countByTier(b);for(let w=0;w<F;w++){let d=e.select(b,m);if(!d)break;m.push(d.originalIndex);let u=t.getProvider(d.provider);if(!u)continue;let M=i?JSON.stringify({...i,model:d.targetModel}):void 0;try{let f=new AbortController,x=()=>f.abort();n==null||n.addEventListener("abort",x,{once:!0});let j=setTimeout(()=>f.abort(),t.fetchTimeout);try{let l=await fetch(`${u.url}${r}`,{method:s,headers:{"Content-Type":"application/json","Anthropic-Version":"2023-06-01","x-api-key":u.apiKey,"Accept-Encoding":"identity"},body:M,duplex:"half",signal:f.signal});if(l.status===429||l.status>=500){console.warn(`[Fallback] ${u.name} (${d.targetModel}) -> ${l.status}`),await((y=l.body)==null?void 0:y.cancel());continue}let{readable:H,writable:K}=new TransformStream;return l.body.pipeTo(K).catch(v=>{v.name!=="AbortError"&&console.warn(`[Stream] ${u.name}: ${v.message}`)}),new Response(H,{status:l.status,headers:l.headers})}finally{clearTimeout(j),n==null||n.removeEventListener("abort",x)}}catch(f){f.name!=="AbortError"&&console.warn(`[Error] ${u.name}: ${f.message}`)}}}return new Response(JSON.stringify({error:`\u8BE5\u6863\u4F4D (${c}) \u53CA\u5176\u964D\u7EA7\u6863\u4F4D\u6240\u6709\u914D\u7F6E\u7684\u6A21\u578B\u63D2\u69FD\u5747\u5C1D\u8BD5\u5931\u8D25`}),{status:502,headers:{"Content-Type":"application/json"}})}}};var L=t=>{let e=I(t),o=new G;return o.all("/*",async r=>{let{pathname:s,search:i}=new URL(r.req.url),n=s.replace(/^\/anthropic/,"")+i,c=r.req.method,a=null;if(c!=="GET"&&c!=="HEAD")try{a=await r.req.json()}catch{}return e.forward({path:n,method:c,jsonBody:a,signal:r.req.raw.signal})}),o};var O=t=>{let e=new X;return e.use("*",B()),e.route("/config",h(t)),e.route("/status",h(t)),e.route("/anthropic/v1",L(t)),e};import{serve as Q}from"@hono/node-server";var Y=`
13
+ @keepdb/cc - Anthropic API \u4EE3\u7406\u670D\u52A1
14
+
15
+ Usage:
16
+ keepdb-cc start [--config <path>] [--port <number>]
17
+ npx @keepdb/cc start [--config <path>] [--port <number>]
18
+
19
+ Options:
20
+ --config <path> \u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84 (\u9ED8\u8BA4: ./ismartify.config.yaml \u2192 ~/.keepdb/ismartify.config.yaml)
21
+ --port <number> \u76D1\u542C\u7AEF\u53E3 (\u9ED8\u8BA4: 6110)
22
+
23
+ Examples:
24
+ keepdb-cc start
25
+ keepdb-cc start --port 8080
26
+ keepdb-cc start --config /path/to/ismartify.config.yaml
27
+ `.trim(),P=t=>t&&!t.startsWith("-"),Z=t=>{let e=t.slice(2),o={command:null,config:null,port:null};for(let r=0;r<e.length;r++){let s=e[r];if(s==="--config"&&P(e[r+1]))o.config=e[++r];else if(s==="--port"&&P(e[r+1])){let i=Number(e[++r]);(!Number.isFinite(i)||i<1||i>65535)&&(console.error(`\u65E0\u6548\u7AEF\u53E3\u53F7: ${e[r]}`),process.exit(1)),o.port=i}else!s.startsWith("-")&&!o.command&&(o.command=s)}return o},ee=()=>{let{command:t,config:e,port:o}=Z(process.argv);t!=="start"&&(console.log(Y),process.exit(t?1:0));let r;try{r=E(e)}catch(i){console.error(`\u914D\u7F6E\u52A0\u8F7D\u5931\u8D25: ${i.message}`),process.exit(1)}let s=O(r);Q({fetch:s.fetch,port:o||6110},i=>{console.log(`@keepdb/cc \u4EE3\u7406\u670D\u52A1\u5DF2\u542F\u52A8: http://localhost:${i.port}`)})};ee();
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@keepdb/cc",
3
+ "version": "0.1.7",
4
+ "description": "Anthropic API 代理 - 按模型档位路由、权重负载均衡、故障转移",
5
+ "type": "module",
6
+ "bin": {
7
+ "keepdb-cc": "./dist/bin/cli.js"
8
+ },
9
+ "files": [
10
+ "dist/"
11
+ ],
12
+ "dependencies": {
13
+ "@hono/node-server": "^1.14.0",
14
+ "@keepdb/kit": "^0.4.22",
15
+ "hono": "^4.12.9",
16
+ "zod": "^3.24.0"
17
+ },
18
+ "devDependencies": {
19
+ "tsup": "^8.4.0",
20
+ "typescript": "^6.0.2",
21
+ "vitest": "^4.1.1"
22
+ },
23
+ "scripts": {
24
+ "build": "tsup",
25
+ "dev": "tsx src/cli.js",
26
+ "test": "vitest run",
27
+ "test:watch": "vitest",
28
+ "release": "pnpm version patch --no-git-tag-version --no-git-checks && pnpm build && pnpm publish --no-git-checks --access public --registry https://registry.npmjs.org/"
29
+ }
30
+ }