tako-cli 0.1.40 → 0.1.42

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.
package/dist/index.js CHANGED
@@ -50,7 +50,7 @@ ${c.default.gray(D)} ${r}
50
50
  `)}
51
51
  `)},info:(r)=>{d.message(r,{symbol:c.default.blue(pm)})},success:(r)=>{d.message(r,{symbol:c.default.green(lm)})},step:(r)=>{d.message(r,{symbol:c.default.green(p$)})},warn:(r)=>{d.message(r,{symbol:c.default.yellow(qm)})},warning:(r)=>{d.warn(r)},error:(r)=>{d.message(r,{symbol:c.default.red(Wm)})}},S=()=>{const r=M$?["\u25D2","\u25D0","\u25D3","\u25D1"]:["\u2022","o","O","0"],m=M$?80:120;let b,h,v=!1,x="";const $=(w="")=>{v=!0,b=vr(),x=w.replace(/\.+$/,""),process.stdout.write(`${c.default.gray(M)}
52
52
  `);let g=0,i=0;h=setInterval(()=>{const J=c.default.magenta(r[g]),K=".".repeat(Math.floor(i)).slice(0,3);process.stdout.write(B.cursor.move(-999,0)),process.stdout.write(B.erase.down(1)),process.stdout.write(`${J} ${x}${K}`),g=g+1<r.length?g+1:0,i=i<r.length?i+0.125:0},m)},a=(w="",g=0)=>{x=w??x,v=!1,clearInterval(h);const i=g===0?c.default.green(p$):g===1?c.default.red(xr):c.default.red(ar);process.stdout.write(B.cursor.move(-999,0)),process.stdout.write(B.erase.down(1)),process.stdout.write(`${i} ${x}
53
- `),b()},s=(w="")=>{x=w??x},o=(w)=>{const g=w>1?"Something went wrong":"Canceled";v&&a(g,w)};return process.on("uncaughtExceptionMonitor",()=>o(2)),process.on("unhandledRejection",()=>o(2)),process.on("SIGINT",()=>o(1)),process.on("SIGTERM",()=>o(1)),process.on("exit",o),{start:$,stop:a,message:s}};function Zm(r){return r.startsWith("cr_")&&r.length>10}async function zm(r){try{const b=await(await fetch(`${Q}/apiStats/api/get-key-id`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({apiKey:r})})).json();if(b.success&&b.data?.id)return{valid:!0,apiId:b.data.id};return{valid:!1,error:b.error||"Key \u9A8C\u8BC1\u5931\u8D25"}}catch(m){return{valid:!1,error:m instanceof Error?m.message:"\u7F51\u7EDC\u8BF7\u6C42\u5931\u8D25"}}}async function sr(r){if(!Zm(r))return{success:!1,error:"Key \u683C\u5F0F\u4E0D\u6B63\u786E\uFF0C\u5FC5\u987B\u4EE5 cr_ \u5F00\u5934"};const m=await zm(r);if(!m.valid)return{success:!1,error:m.error};return await N({apiKey:r,apiId:m.apiId}),{success:!0}}import{join as W$} from"path";async function T(){if($$)return $$;const r=[Qm,Sm,Ym];for(let m of r)try{const b=await Promise.race([m(),new Promise((h,v)=>setTimeout(()=>v(new Error("timeout")),3000))]);if(b)return $$=b,b}catch{}return $$="cn","cn"}async function Qm(){try{const r=await fetch("http://ip-api.com/json/?fields=countryCode",{signal:AbortSignal.timeout(2000)});if(!r.ok)return null;return(await r.json()).countryCode==="CN"?"cn":"global"}catch{return null}}async function Sm(){try{const r=await fetch("https://ipinfo.io/json",{signal:AbortSignal.timeout(2000)});if(!r.ok)return null;return(await r.json()).country==="CN"?"cn":"global"}catch{return null}}async function Ym(){try{const r=await fetch("https://api.ip.sb/geoip",{signal:AbortSignal.timeout(2000)});if(!r.ok)return null;return(await r.json()).country_code==="CN"?"cn":"global"}catch{return null}}async function Jm(){const r=await T();return q$[r]}async function V(){return(await Jm()).npm}async function dr(){if(await T()==="cn")return`curl -fsSL https://bun.sh/install | BUN_INSTALL_MIRROR="${q$.cn.bunMirror}" bash`;return"curl -fsSL https://bun.sh/install | bash"}async function wr(){const r=await T(),m=q$[r];if(r==="cn")d.info("\u68C0\u6D4B\u5230\u4E2D\u56FD\u5927\u9646\u7F51\u7EDC\uFF0C\u4F7F\u7528\u56FD\u5185\u955C\u50CF\u6E90"),d.message(` npm: ${m.npm}`);else d.info("\u4F7F\u7528\u56FD\u9645\u6E90")}var $$=null,q$={cn:{npm:"https://registry.npmmirror.com",bun:"https://bun.sh/install",bunMirror:"https://registry.npmmirror.com/-/binary/bun"},global:{npm:"https://registry.npmjs.org",bun:"https://bun.sh/install",bunMirror:null}};var Hm=function(r){return r.startsWith(H)};async function er(){try{if(!await Bun.file(l).exists())return!1;const m=Bun.spawn([l,"--version"],{stdout:"pipe",stderr:"pipe"});return await m.exited,m.exitCode===0}catch{return!1}}async function Xm(){if(process.platform==="win32")return!0;try{const m=Bun.spawn(["which","unzip"],{stdout:"pipe",stderr:"pipe"});if(await m.exited,m.exitCode===0)return!0}catch{}d.warn("\u6B63\u5728\u5B89\u88C5\u7CFB\u7EDF\u4F9D\u8D56 (unzip)...");const r=["apt-get update -qq && apt-get install -y -qq unzip","yum install -y -q unzip","dnf install -y -q unzip","pacman -S --noconfirm unzip","apk add --quiet unzip"];for(let m of r)try{const b=Bun.spawn(["bash","-c",`sudo ${m} 2>/dev/null || ${m}`],{stdout:"pipe",stderr:"pipe"});if(await b.exited,b.exitCode===0)return!0}catch{}return!1}async function Vm(){const r=S();if(!await Xm())return d.error("\u8BF7\u5148\u624B\u52A8\u5B89\u88C5 unzip: apt install unzip / yum install unzip"),!1;await wr(),r.start("\u6B63\u5728\u5B89\u88C5 Tako \u4E13\u5C5E Bun \u8FD0\u884C\u65F6...");try{await(await import("fs/promises")).mkdir(H,{recursive:!0});let b;if(process.platform==="win32"){const v=await T()==="cn"?'$env:BUN_INSTALL_MIRROR="https://registry.npmmirror.com/-/binary/bun"; ':"",x=`\$env:BUN_INSTALL="${H}"; ${v}irm bun.sh/install.ps1 | iex`;b=await Bun.spawn(["powershell","-Command",x],{stdout:"inherit",stderr:"inherit"}).exited}else{const h=await dr(),v=`BUN_INSTALL="${H}" ${h}`;b=await Bun.spawn(["bash","-c",v],{stdout:"inherit",stderr:"inherit"}).exited}if(b!==0)return r.stop("Bun \u5B89\u88C5\u5931\u8D25"),!1;return j=l,r.stop("Tako \u4E13\u5C5E Bun \u5B89\u88C5\u5B8C\u6210"),d.info(`\u5B89\u88C5\u4F4D\u7F6E: ${H}`),!0}catch(m){return r.stop("Bun \u5B89\u88C5\u5931\u8D25"),!1}}async function jm(){if(await er())return j=l,!0;return d.warn("\u672A\u68C0\u6D4B\u5230 Tako \u4E13\u5C5E Bun\uFF0C\u6B63\u5728\u81EA\u52A8\u5B89\u88C5..."),d.info("\uFF08\u4E0D\u4F1A\u5F71\u54CD\u60A8\u7CFB\u7EDF\u4E2D\u5DF2\u5B89\u88C5\u7684 Node.js \u6216 Bun\uFF09"),await Vm()}async function G$(){if(j){if(!Hm(j))j=l;return j}if(await er())return j=l,l;return l}async function tr(r){try{const m=await V(),b=await fetch(`${m}/${r}/latest`);if(!b.ok)return null;return(await b.json()).version}catch{return null}}async function Lm(r){return(await X()).installedClients[r.id]?.version||null}async function ir(r){const m=n(r.id),b=W$(m,"package.json");try{return await Bun.file(b).exists()}catch{return!1}}async function Mr(r){const m=await Lm(r);if(!m)return!0;const b=await tr(r.package);if(!b)return!1;return m!==b}async function yr(r,m=!1){const b=S(),h=n(r.id);try{if(!await jm())return{success:!1,error:"Tako \u4E13\u5C5E Bun \u5B89\u88C5\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u7F51\u7EDC\u6216\u624B\u52A8\u5220\u9664 ~/.tako/bun \u76EE\u5F55\u540E\u91CD\u8BD5"};const v=await ir(r),x=m||await Mr(r);if(v&&!x)return{success:!0};const $=v?"\u66F4\u65B0":"\u5B89\u88C5";b.start(`\u6B63\u5728${$} ${r.name}...`);const a=await import("fs/promises");await a.mkdir(h,{recursive:!0});const s=W$(h,"package.json");if(!await Bun.file(s).exists())await Bun.write(s,JSON.stringify({name:`tako-${r.id}`,private:!0,dependencies:{}},null,2));const w=await G$(),g=await V();if(v){const L=W$(h,"bun.lock");try{await a.unlink(L)}catch{}}const i=Bun.spawn([w,"add",`${r.package}@latest`],{cwd:h,stdout:"pipe",stderr:"pipe",env:{...process.env,BUN_CONFIG_REGISTRY:g}});if(await i.exited!==0){const L=await new Response(i.stderr).text();return b.stop(`${$} ${r.name} \u5931\u8D25`),{success:!1,error:L}}const K=await tr(r.package);if(K){const L=await X();L.installedClients[r.id]={version:K,installedAt:(new Date()).toISOString()},await N(L)}return b.stop(`${r.name} ${$}\u5B8C\u6210`),{success:!0}}catch(v){return b.stop("\u64CD\u4F5C\u5931\u8D25"),{success:!1,error:v instanceof Error?v.message:"\u672A\u77E5\u9519\u8BEF"}}}async function pr(r){if(!await ir(r))return await yr(r);if(await Mr(r))return await yr(r,!0);return{success:!0}}var j=null;async function P(r){try{const m=await pr(r);if(!m.success)return m;const b=await j$();if(!b)return{success:!1,error:"\u672A\u914D\u7F6E API Key"};if(r.setupConfigFiles)await r.setupConfigFiles(b);let h=await f$(r);if(!h)h=B$(r);if(!await Bun.file(h).exists())return{success:!1,error:`\u627E\u4E0D\u5230\u53EF\u6267\u884C\u6587\u4EF6: ${h}`};const x=r.getEnvVars(b),$={...process.env,...x};d.info(`\u542F\u52A8 ${r.name}...`);let a;if(r.runtime==="native")a=[h];else a=[await G$(),h];return await Bun.spawn(a,{env:$,stdio:["inherit","inherit","inherit"],cwd:process.cwd()}).exited,{success:!0}}catch(m){return{success:!1,error:m instanceof Error?m.message:"\u542F\u52A8\u5931\u8D25"}}}async function Z$(){try{const r=await L$();if(!r)return{success:!1,error:"\u672A\u627E\u5230 API ID\uFF0C\u8BF7\u91CD\u65B0\u914D\u7F6E Key"};const b=await(await fetch(`${Q}/apiStats/api/user-stats`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({apiId:r})})).json(),v=await(await fetch(`${Q}/apiStats/api/user-model-stats`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({apiId:r,period:"daily"})})).json();if(!b.success)return{success:!1,error:b.error||b.message||"\u83B7\u53D6\u7EDF\u8BA1\u5931\u8D25"};const x=(v.data||[]).map(($)=>({model:$.model,requests:$.requests,cost:$.formatted?.total||"$0.00"}));return{success:!0,data:{totalRequests:b.data?.usage?.total?.requests||0,totalCost:b.data?.usage?.total?.formattedCost||"$0.00",todayCost:`\$${(b.data?.limits?.currentDailyCost||0).toFixed(2)}`,modelStats:x}}}catch(r){return{success:!1,error:r instanceof Error?r.message:"\u7F51\u7EDC\u8BF7\u6C42\u5931\u8D25"}}}function z$(r){return r.toLocaleString("en-US")}import{join as z} from"path";var fm=function(r,m){const b=r.split(".").map(Number),h=m.split(".").map(Number);for(let v=0;v<3;v++){const x=b[v]||0,$=h[v]||0;if(x>$)return 1;if(x<$)return-1}return 0};async function Bm(){try{const r=await V(),m=await fetch(`${r}/${Q$}/latest`,{signal:AbortSignal.timeout(5000)});if(!m.ok)return null;return(await m.json()).version||null}catch{return null}}async function km(){const r=await Bm();if(!r)return{hasUpdate:!1,currentVersion:k};return{hasUpdate:fm(r,k)>0,latestVersion:r,currentVersion:k}}async function lr(){const r=await import("fs/promises"),m=z(G,"bin"),b=z(m,"tako"),h=z(Z,"node_modules/tako-cli/dist/index.js");if(await r.mkdir(m,{recursive:!0}),process.platform==="win32"){const x=`@echo off\r\n"${l}" "${h}" %*\r\n`;await r.writeFile(z(m,"tako.cmd"),x)}else{const x=`#!/bin/bash\nexec "${l}" "${h}" "\$@"\n`;await r.writeFile(b,x,{mode:493})}}async function nm(r){const m=S();m.start(`\u6B63\u5728\u66F4\u65B0\u5230 v${r}...`);try{const b=await V(),h=await import("fs/promises");await h.mkdir(Z,{recursive:!0});const v=z(Z,"package.json");try{await h.access(v)}catch{await h.writeFile(v,JSON.stringify({name:"tako-local",private:!0},null,2))}const x=Bun.spawn([l,"update",Q$,"--latest"],{cwd:Z,stdout:"pipe",stderr:"pipe",env:{...process.env,BUN_CONFIG_REGISTRY:b}});if(await x.exited,x.exitCode===0)return await lr(),m.stop(`\u66F4\u65B0\u6210\u529F\uFF01\u5DF2\u66F4\u65B0\u5230 v${r}`),!0;else{const $=await new Response(x.stderr).text();return m.stop(`\u66F4\u65B0\u5931\u8D25: ${$||"\u672A\u77E5\u9519\u8BEF"}`),!1}}catch(b){return m.stop(`\u66F4\u65B0\u5931\u8D25: ${b instanceof Error?b.message:"\u672A\u77E5\u9519\u8BEF"}`),!1}}async function Em(){const r=await import("fs/promises"),m=z(Z,"bun.lock");try{await r.unlink(m)}catch{}}async function Um(){const r=await import("fs/promises");try{const m=await V();await r.mkdir(Z,{recursive:!0});const b=z(Z,"package.json");try{await r.access(b)}catch{await r.writeFile(b,JSON.stringify({name:"tako-local",private:!0},null,2))}await Em();const h=Bun.spawn([l,"add",`${Q$}@latest`],{cwd:Z,stdout:"pipe",stderr:"pipe",env:{...process.env,BUN_CONFIG_REGISTRY:m}});return await h.exited,h.exitCode===0}catch{return!1}}async function Tm(){const r=await import("fs/promises"),m=z(G,"bin"),b=z(m,"tako"),h=z(Z,"node_modules/tako-cli/dist/index.js");try{const v=await r.readFile(b,"utf-8");if(v.includes("bun run tako-cli")||v.includes("run tako-cli")){d.info("\u68C0\u6D4B\u5230\u65E7\u7248\u5B89\u88C5\u65B9\u5F0F\uFF0C\u6B63\u5728\u8FC1\u79FB...");let x=!1;try{await r.access(h),x=!0}catch{x=!1}if(!x){const $=S();if($.start("\u6B63\u5728\u5B89\u88C5\u5230\u672C\u5730\u76EE\u5F55..."),!await Um())return $.stop("\u672C\u5730\u5B89\u88C5\u5931\u8D25\uFF0C\u8DF3\u8FC7\u8FC1\u79FB"),!1;$.stop("\u672C\u5730\u5B89\u88C5\u5B8C\u6210")}return await lr(),!0}}catch{}return!1}async function r$(){try{if(await Tm())d.info("\u68C0\u6D4B\u5230\u65E7\u7248\u5B89\u88C5\uFF0C\u5DF2\u5B8C\u6210\u8FC1\u79FB\uFF0C\u8BF7\u91CD\u65B0\u542F\u52A8 Tako CLI"),process.exit(0);const m=await km();if(m.hasUpdate&&m.latestVersion){if(d.warn(`\u53D1\u73B0\u65B0\u7248\u672C v${m.latestVersion}\uFF08\u5F53\u524D v${k}\uFF09`),await nm(m.latestVersion))d.info("\u8BF7\u91CD\u65B0\u542F\u52A8 Tako CLI \u4EE5\u4F7F\u7528\u65B0\u7248\u672C"),process.exit(0)}}catch{}}var Q$="tako-cli",k="0.1.40";var Pm=function(){console.log(),console.log(` \uD83D\uDC19 Tako CLI v${k}`),console.log(" \x1B[90m\u6B22\u8FCE\u4F7F\u7528\uFF01\x1B[0m"),console.log()};async function qr(r=!1){if(!r)d.warn("\u672A\u68C0\u6D4B\u5230 API Key"),console.log();const m=await or({message:r?"\u8BF7\u8F93\u5165\u65B0\u7684 API Key (cr_xxx):":"\u8BF7\u8F93\u5165\u4F60\u7684 API Key (cr_xxx):",placeholder:"cr_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",validate(v){if(!v)return"\u8BF7\u8F93\u5165 API Key";if(!v.startsWith("cr_"))return"Key \u5FC5\u987B\u4EE5 cr_ \u5F00\u5934";if(v.length<10)return"Key \u683C\u5F0F\u4E0D\u6B63\u786E"}});if(U(m))return!1;const b=S();b.start("\u9A8C\u8BC1 Key \u4E2D...");const h=await sr(m);if(h.success)return b.stop("Key \u9A8C\u8BC1\u6210\u529F\uFF01\u5DF2\u4FDD\u5B58\u5230\u914D\u7F6E\u6587\u4EF6"),!0;else return b.stop(`\u9A8C\u8BC1\u5931\u8D25: ${h.error}`),!1}var Km=function(r,m){const b=[];if(b.push(""),b.push(" \uD83D\uDCCA \u7528\u91CF\u7EDF\u8BA1"),b.push(""),b.push(` \u603B\u8C03\u7528\u6B21\u6570: ${z$(r.totalRequests)} \u6B21`),b.push(` \u603B\u6D88\u8D39: ${r.totalCost}`),b.push(` \u4ECA\u65E5\u6D88\u8D39: ${r.todayCost}`),r.modelStats.length>0)b.push(""),b.push(" \u6A21\u578B\u4F7F\u7528\u5206\u5E03 (\u4ECA\u65E5):"),r.modelStats.forEach((h,v)=>{const x=v===r.modelStats.length-1?"\u2514\u2500":"\u251C\u2500",$=h.model.length>25?h.model.slice(0,22)+"...":h.model;b.push(` ${x} ${$.padEnd(25)} ${z$(h.requests).padStart(6)} \u6B21 ${h.cost.padStart(8)}`)});return b.push(""),b.push(` \x1B[90m${m}s \u540E\u5237\u65B0 | \u6309\u4EFB\u610F\u952E\u8FD4\u56DE\x1B[0m`),b.join("\n")};async function Om(){const m=S();m.start("\u83B7\u53D6\u7528\u91CF\u7EDF\u8BA1...");let b=await Z$();if(!b.success){m.stop(`\u83B7\u53D6\u5931\u8D25: ${b.error}`);return}m.stop("");let h=10,v="";const x=()=>{if(v){const a=v.split("\n").length;process.stdout.write(`\x1B[${a}A\x1B[0J`)}v=Km(b.data,h),console.log(v)};x();const $=process.stdin.isRaw;if(process.stdin.isTTY)process.stdin.setRawMode(!0);return process.stdin.resume(),new Promise((a)=>{let s=!0;const o=()=>{if(!s)return;s=!1,g(),a()},w=setInterval(async()=>{if(!s)return;if(h--,h<=0){h=10;const i=await Z$();if(i.success)b=i}x()},1000),g=()=>{if(clearInterval(w),process.stdin.removeListener("data",o),process.stdin.isTTY)process.stdin.setRawMode($??!1);process.stdin.pause()};process.stdin.on("data",o)})}async function Nm(){const m=[...k$().map((h)=>({value:{type:"launch",client:h},label:`\u542F\u52A8 ${h.name}`,hint:h.package})),{value:{type:"stats"},label:"\u67E5\u770B\u7528\u91CF\u7EDF\u8BA1"},{value:{type:"config"},label:"\u914D\u7F6E API Key"},{value:{type:"exit"},label:"\u9000\u51FA"}],b=await gr({message:"\u8BF7\u9009\u62E9\u64CD\u4F5C:",options:m});if(U(b))return null;return b}async function Am(r){switch(r.type){case"launch":const m=await P(r.client);if(!m.success)d.error(m.error||"\u542F\u52A8\u5931\u8D25");return!0;case"stats":return await Om(),!0;case"config":return await qr(!0),!0;case"exit":return!1}}async function Wr(){if(Pm(),!await V$()){if(!await qr()){l$("\u518D\u89C1\uFF01");return}}while(!0){const m=await Nm();if(m===null)break;if(!await Am(m))break}l$("\u518D\u89C1\uFF01")}var Rm=function(){console.log(`
53
+ `),b()},s=(w="")=>{x=w??x},o=(w)=>{const g=w>1?"Something went wrong":"Canceled";v&&a(g,w)};return process.on("uncaughtExceptionMonitor",()=>o(2)),process.on("unhandledRejection",()=>o(2)),process.on("SIGINT",()=>o(1)),process.on("SIGTERM",()=>o(1)),process.on("exit",o),{start:$,stop:a,message:s}};function Zm(r){return r.startsWith("cr_")&&r.length>10}async function zm(r){try{const b=await(await fetch(`${Q}/apiStats/api/get-key-id`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({apiKey:r})})).json();if(b.success&&b.data?.id)return{valid:!0,apiId:b.data.id};return{valid:!1,error:b.error||"Key \u9A8C\u8BC1\u5931\u8D25"}}catch(m){return{valid:!1,error:m instanceof Error?m.message:"\u7F51\u7EDC\u8BF7\u6C42\u5931\u8D25"}}}async function sr(r){if(!Zm(r))return{success:!1,error:"Key \u683C\u5F0F\u4E0D\u6B63\u786E\uFF0C\u5FC5\u987B\u4EE5 cr_ \u5F00\u5934"};const m=await zm(r);if(!m.valid)return{success:!1,error:m.error};return await N({apiKey:r,apiId:m.apiId}),{success:!0}}import{join as W$} from"path";async function T(){if($$)return $$;const r=[Qm,Sm,Ym];for(let m of r)try{const b=await Promise.race([m(),new Promise((h,v)=>setTimeout(()=>v(new Error("timeout")),3000))]);if(b)return $$=b,b}catch{}return $$="cn","cn"}async function Qm(){try{const r=await fetch("http://ip-api.com/json/?fields=countryCode",{signal:AbortSignal.timeout(2000)});if(!r.ok)return null;return(await r.json()).countryCode==="CN"?"cn":"global"}catch{return null}}async function Sm(){try{const r=await fetch("https://ipinfo.io/json",{signal:AbortSignal.timeout(2000)});if(!r.ok)return null;return(await r.json()).country==="CN"?"cn":"global"}catch{return null}}async function Ym(){try{const r=await fetch("https://api.ip.sb/geoip",{signal:AbortSignal.timeout(2000)});if(!r.ok)return null;return(await r.json()).country_code==="CN"?"cn":"global"}catch{return null}}async function Jm(){const r=await T();return q$[r]}async function V(){return(await Jm()).npm}async function dr(){if(await T()==="cn")return`curl -fsSL https://bun.sh/install | BUN_INSTALL_MIRROR="${q$.cn.bunMirror}" bash`;return"curl -fsSL https://bun.sh/install | bash"}async function wr(){const r=await T(),m=q$[r];if(r==="cn")d.info("\u68C0\u6D4B\u5230\u4E2D\u56FD\u5927\u9646\u7F51\u7EDC\uFF0C\u4F7F\u7528\u56FD\u5185\u955C\u50CF\u6E90"),d.message(` npm: ${m.npm}`);else d.info("\u4F7F\u7528\u56FD\u9645\u6E90")}var $$=null,q$={cn:{npm:"https://registry.npmmirror.com",bun:"https://bun.sh/install",bunMirror:"https://registry.npmmirror.com/-/binary/bun"},global:{npm:"https://registry.npmjs.org",bun:"https://bun.sh/install",bunMirror:null}};var Hm=function(r){return r.startsWith(H)};async function er(){try{if(!await Bun.file(l).exists())return!1;const m=Bun.spawn([l,"--version"],{stdout:"pipe",stderr:"pipe"});return await m.exited,m.exitCode===0}catch{return!1}}async function Xm(){if(process.platform==="win32")return!0;try{const m=Bun.spawn(["which","unzip"],{stdout:"pipe",stderr:"pipe"});if(await m.exited,m.exitCode===0)return!0}catch{}d.warn("\u6B63\u5728\u5B89\u88C5\u7CFB\u7EDF\u4F9D\u8D56 (unzip)...");const r=["apt-get update -qq && apt-get install -y -qq unzip","yum install -y -q unzip","dnf install -y -q unzip","pacman -S --noconfirm unzip","apk add --quiet unzip"];for(let m of r)try{const b=Bun.spawn(["bash","-c",`sudo ${m} 2>/dev/null || ${m}`],{stdout:"pipe",stderr:"pipe"});if(await b.exited,b.exitCode===0)return!0}catch{}return!1}async function Vm(){const r=S();if(!await Xm())return d.error("\u8BF7\u5148\u624B\u52A8\u5B89\u88C5 unzip: apt install unzip / yum install unzip"),!1;await wr(),r.start("\u6B63\u5728\u5B89\u88C5 Tako \u4E13\u5C5E Bun \u8FD0\u884C\u65F6...");try{await(await import("fs/promises")).mkdir(H,{recursive:!0});let b;if(process.platform==="win32"){const v=await T()==="cn"?'$env:BUN_INSTALL_MIRROR="https://registry.npmmirror.com/-/binary/bun"; ':"",x=`\$env:BUN_INSTALL="${H}"; ${v}irm bun.sh/install.ps1 | iex`;b=await Bun.spawn(["powershell","-Command",x],{stdout:"inherit",stderr:"inherit"}).exited}else{const h=await dr(),v=`BUN_INSTALL="${H}" ${h}`;b=await Bun.spawn(["bash","-c",v],{stdout:"inherit",stderr:"inherit"}).exited}if(b!==0)return r.stop("Bun \u5B89\u88C5\u5931\u8D25"),!1;return j=l,r.stop("Tako \u4E13\u5C5E Bun \u5B89\u88C5\u5B8C\u6210"),d.info(`\u5B89\u88C5\u4F4D\u7F6E: ${H}`),!0}catch(m){return r.stop("Bun \u5B89\u88C5\u5931\u8D25"),!1}}async function jm(){if(await er())return j=l,!0;return d.warn("\u672A\u68C0\u6D4B\u5230 Tako \u4E13\u5C5E Bun\uFF0C\u6B63\u5728\u81EA\u52A8\u5B89\u88C5..."),d.info("\uFF08\u4E0D\u4F1A\u5F71\u54CD\u60A8\u7CFB\u7EDF\u4E2D\u5DF2\u5B89\u88C5\u7684 Node.js \u6216 Bun\uFF09"),await Vm()}async function G$(){if(j){if(!Hm(j))j=l;return j}if(await er())return j=l,l;return l}async function tr(r){try{const m=await V(),b=await fetch(`${m}/${r}/latest`);if(!b.ok)return null;return(await b.json()).version}catch{return null}}async function Lm(r){return(await X()).installedClients[r.id]?.version||null}async function ir(r){const m=n(r.id),b=W$(m,"package.json");try{return await Bun.file(b).exists()}catch{return!1}}async function Mr(r){const m=await Lm(r);if(!m)return!0;const b=await tr(r.package);if(!b)return!1;return m!==b}async function yr(r,m=!1){const b=S(),h=n(r.id);try{if(!await jm())return{success:!1,error:"Tako \u4E13\u5C5E Bun \u5B89\u88C5\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u7F51\u7EDC\u6216\u624B\u52A8\u5220\u9664 ~/.tako/bun \u76EE\u5F55\u540E\u91CD\u8BD5"};const v=await ir(r),x=m||await Mr(r);if(v&&!x)return{success:!0};const $=v?"\u66F4\u65B0":"\u5B89\u88C5";b.start(`\u6B63\u5728${$} ${r.name}...`);const a=await import("fs/promises");await a.mkdir(h,{recursive:!0});const s=W$(h,"package.json");if(!await Bun.file(s).exists())await Bun.write(s,JSON.stringify({name:`tako-${r.id}`,private:!0,dependencies:{}},null,2));const w=await G$(),g=await V();if(v){const L=W$(h,"bun.lock");try{await a.unlink(L)}catch{}}const i=Bun.spawn([w,"add",`${r.package}@latest`],{cwd:h,stdout:"pipe",stderr:"pipe",env:{...process.env,BUN_CONFIG_REGISTRY:g}});if(await i.exited!==0){const L=await new Response(i.stderr).text();return b.stop(`${$} ${r.name} \u5931\u8D25`),{success:!1,error:L}}const K=await tr(r.package);if(K){const L=await X();L.installedClients[r.id]={version:K,installedAt:(new Date()).toISOString()},await N(L)}return b.stop(`${r.name} ${$}\u5B8C\u6210`),{success:!0}}catch(v){return b.stop("\u64CD\u4F5C\u5931\u8D25"),{success:!1,error:v instanceof Error?v.message:"\u672A\u77E5\u9519\u8BEF"}}}async function pr(r){if(!await ir(r))return await yr(r);if(await Mr(r))return await yr(r,!0);return{success:!0}}var j=null;async function P(r){try{const m=await pr(r);if(!m.success)return m;const b=await j$();if(!b)return{success:!1,error:"\u672A\u914D\u7F6E API Key"};if(r.setupConfigFiles)await r.setupConfigFiles(b);let h=await f$(r);if(!h)h=B$(r);if(!await Bun.file(h).exists())return{success:!1,error:`\u627E\u4E0D\u5230\u53EF\u6267\u884C\u6587\u4EF6: ${h}`};const x=r.getEnvVars(b),$={...process.env,...x};d.info(`\u542F\u52A8 ${r.name}...`);let a;if(r.runtime==="native")a=[h];else a=[await G$(),h];return await Bun.spawn(a,{env:$,stdio:["inherit","inherit","inherit"],cwd:process.cwd()}).exited,{success:!0}}catch(m){return{success:!1,error:m instanceof Error?m.message:"\u542F\u52A8\u5931\u8D25"}}}async function Z$(){try{const r=await L$();if(!r)return{success:!1,error:"\u672A\u627E\u5230 API ID\uFF0C\u8BF7\u91CD\u65B0\u914D\u7F6E Key"};const b=await(await fetch(`${Q}/apiStats/api/user-stats`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({apiId:r})})).json(),v=await(await fetch(`${Q}/apiStats/api/user-model-stats`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({apiId:r,period:"daily"})})).json();if(!b.success)return{success:!1,error:b.error||b.message||"\u83B7\u53D6\u7EDF\u8BA1\u5931\u8D25"};const x=(v.data||[]).map(($)=>({model:$.model,requests:$.requests,cost:$.formatted?.total||"$0.00"}));return{success:!0,data:{totalRequests:b.data?.usage?.total?.requests||0,totalCost:b.data?.usage?.total?.formattedCost||"$0.00",todayCost:`\$${(b.data?.limits?.currentDailyCost||0).toFixed(2)}`,modelStats:x}}}catch(r){return{success:!1,error:r instanceof Error?r.message:"\u7F51\u7EDC\u8BF7\u6C42\u5931\u8D25"}}}function z$(r){return r.toLocaleString("en-US")}import{join as z} from"path";var fm=function(r,m){const b=r.split(".").map(Number),h=m.split(".").map(Number);for(let v=0;v<3;v++){const x=b[v]||0,$=h[v]||0;if(x>$)return 1;if(x<$)return-1}return 0};async function Bm(){try{const r=await V(),m=await fetch(`${r}/${Q$}/latest`,{signal:AbortSignal.timeout(5000)});if(!m.ok)return null;return(await m.json()).version||null}catch{return null}}async function km(){const r=await Bm();if(!r)return{hasUpdate:!1,currentVersion:k};return{hasUpdate:fm(r,k)>0,latestVersion:r,currentVersion:k}}async function lr(){const r=await import("fs/promises"),m=z(G,"bin"),b=z(m,"tako"),h=z(Z,"node_modules/tako-cli/dist/index.js");if(await r.mkdir(m,{recursive:!0}),process.platform==="win32"){const x=`@echo off\r\n"${l}" "${h}" %*\r\n`;await r.writeFile(z(m,"tako.cmd"),x)}else{const x=`#!/bin/bash\nexec "${l}" "${h}" "\$@"\n`;await r.writeFile(b,x,{mode:493})}}async function nm(r){const m=S();m.start(`\u6B63\u5728\u66F4\u65B0\u5230 v${r}...`);try{const b=await V(),h=await import("fs/promises");await h.mkdir(Z,{recursive:!0});const v=z(Z,"package.json");try{await h.access(v)}catch{await h.writeFile(v,JSON.stringify({name:"tako-local",private:!0},null,2))}const x=Bun.spawn([l,"update",Q$,"--latest"],{cwd:Z,stdout:"pipe",stderr:"pipe",env:{...process.env,BUN_CONFIG_REGISTRY:b}});if(await x.exited,x.exitCode===0)return await lr(),m.stop(`\u66F4\u65B0\u6210\u529F\uFF01\u5DF2\u66F4\u65B0\u5230 v${r}`),!0;else{const $=await new Response(x.stderr).text();return m.stop(`\u66F4\u65B0\u5931\u8D25: ${$||"\u672A\u77E5\u9519\u8BEF"}`),!1}}catch(b){return m.stop(`\u66F4\u65B0\u5931\u8D25: ${b instanceof Error?b.message:"\u672A\u77E5\u9519\u8BEF"}`),!1}}async function Em(){const r=await import("fs/promises"),m=z(Z,"bun.lock");try{await r.unlink(m)}catch{}}async function Um(){const r=await import("fs/promises");try{const m=await V();await r.mkdir(Z,{recursive:!0});const b=z(Z,"package.json");try{await r.access(b)}catch{await r.writeFile(b,JSON.stringify({name:"tako-local",private:!0},null,2))}await Em();const h=Bun.spawn([l,"add",`${Q$}@latest`],{cwd:Z,stdout:"pipe",stderr:"pipe",env:{...process.env,BUN_CONFIG_REGISTRY:m}});return await h.exited,h.exitCode===0}catch{return!1}}async function Tm(){const r=await import("fs/promises"),m=z(G,"bin"),b=z(m,"tako"),h=z(Z,"node_modules/tako-cli/dist/index.js");try{const v=await r.readFile(b,"utf-8");if(v.includes("bun run tako-cli")||v.includes("run tako-cli")){d.info("\u68C0\u6D4B\u5230\u65E7\u7248\u5B89\u88C5\u65B9\u5F0F\uFF0C\u6B63\u5728\u8FC1\u79FB...");let x=!1;try{await r.access(h),x=!0}catch{x=!1}if(!x){const $=S();if($.start("\u6B63\u5728\u5B89\u88C5\u5230\u672C\u5730\u76EE\u5F55..."),!await Um())return $.stop("\u672C\u5730\u5B89\u88C5\u5931\u8D25\uFF0C\u8DF3\u8FC7\u8FC1\u79FB"),!1;$.stop("\u672C\u5730\u5B89\u88C5\u5B8C\u6210")}return await lr(),!0}}catch{}return!1}async function r$(){try{if(await Tm())d.info("\u68C0\u6D4B\u5230\u65E7\u7248\u5B89\u88C5\uFF0C\u5DF2\u5B8C\u6210\u8FC1\u79FB\uFF0C\u8BF7\u91CD\u65B0\u542F\u52A8 Tako CLI"),process.exit(0);const m=await km();if(m.hasUpdate&&m.latestVersion){if(d.warn(`\u53D1\u73B0\u65B0\u7248\u672C v${m.latestVersion}\uFF08\u5F53\u524D v${k}\uFF09`),await nm(m.latestVersion))d.info("\u8BF7\u91CD\u65B0\u542F\u52A8 Tako CLI \u4EE5\u4F7F\u7528\u65B0\u7248\u672C"),process.exit(0)}}catch{}}var Q$="tako-cli",k="0.1.42";var Pm=function(){console.log(),console.log(` \uD83D\uDC19 Tako CLI v${k}`),console.log(" \x1B[90m\u6B22\u8FCE\u4F7F\u7528\uFF01\x1B[0m"),console.log()};async function qr(r=!1){if(!r)d.warn("\u672A\u68C0\u6D4B\u5230 API Key"),console.log();const m=await or({message:r?"\u8BF7\u8F93\u5165\u65B0\u7684 API Key (cr_xxx):":"\u8BF7\u8F93\u5165\u4F60\u7684 API Key (cr_xxx):",placeholder:"cr_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",validate(v){if(!v)return"\u8BF7\u8F93\u5165 API Key";if(!v.startsWith("cr_"))return"Key \u5FC5\u987B\u4EE5 cr_ \u5F00\u5934";if(v.length<10)return"Key \u683C\u5F0F\u4E0D\u6B63\u786E"}});if(U(m))return!1;const b=S();b.start("\u9A8C\u8BC1 Key \u4E2D...");const h=await sr(m);if(h.success)return b.stop("Key \u9A8C\u8BC1\u6210\u529F\uFF01\u5DF2\u4FDD\u5B58\u5230\u914D\u7F6E\u6587\u4EF6"),!0;else return b.stop(`\u9A8C\u8BC1\u5931\u8D25: ${h.error}`),!1}var Km=function(r,m){const b=[];if(b.push(""),b.push(" \uD83D\uDCCA \u7528\u91CF\u7EDF\u8BA1"),b.push(""),b.push(` \u603B\u8C03\u7528\u6B21\u6570: ${z$(r.totalRequests)} \u6B21`),b.push(` \u603B\u6D88\u8D39: ${r.totalCost}`),b.push(` \u4ECA\u65E5\u6D88\u8D39: ${r.todayCost}`),r.modelStats.length>0)b.push(""),b.push(" \u6A21\u578B\u4F7F\u7528\u5206\u5E03 (\u4ECA\u65E5):"),r.modelStats.forEach((h,v)=>{const x=v===r.modelStats.length-1?"\u2514\u2500":"\u251C\u2500",$=h.model.length>25?h.model.slice(0,22)+"...":h.model;b.push(` ${x} ${$.padEnd(25)} ${z$(h.requests).padStart(6)} \u6B21 ${h.cost.padStart(8)}`)});return b.push(""),b.push(` \x1B[90m${m}s \u540E\u5237\u65B0 | \u6309\u4EFB\u610F\u952E\u8FD4\u56DE\x1B[0m`),b.join("\n")};async function Om(){const m=S();m.start("\u83B7\u53D6\u7528\u91CF\u7EDF\u8BA1...");let b=await Z$();if(!b.success){m.stop(`\u83B7\u53D6\u5931\u8D25: ${b.error}`);return}m.stop("");let h=10,v="";const x=()=>{if(v){const a=v.split("\n").length;process.stdout.write(`\x1B[${a}A\x1B[0J`)}v=Km(b.data,h),console.log(v)};x();const $=process.stdin.isRaw;if(process.stdin.isTTY)process.stdin.setRawMode(!0);return process.stdin.resume(),new Promise((a)=>{let s=!0;const o=()=>{if(!s)return;s=!1,g(),a()},w=setInterval(async()=>{if(!s)return;if(h--,h<=0){h=10;const i=await Z$();if(i.success)b=i}x()},1000),g=()=>{if(clearInterval(w),process.stdin.removeListener("data",o),process.stdin.isTTY)process.stdin.setRawMode($??!1);process.stdin.pause()};process.stdin.on("data",o)})}async function Nm(){const m=[...k$().map((h)=>({value:{type:"launch",client:h},label:`\u542F\u52A8 ${h.name}`,hint:h.package})),{value:{type:"stats"},label:"\u67E5\u770B\u7528\u91CF\u7EDF\u8BA1"},{value:{type:"config"},label:"\u914D\u7F6E API Key"},{value:{type:"exit"},label:"\u9000\u51FA"}],b=await gr({message:"\u8BF7\u9009\u62E9\u64CD\u4F5C:",options:m});if(U(b))return null;return b}async function Am(r){switch(r.type){case"launch":const m=await P(r.client);if(!m.success)d.error(m.error||"\u542F\u52A8\u5931\u8D25");return!0;case"stats":return await Om(),!0;case"config":return await qr(!0),!0;case"exit":return!1}}async function Wr(){if(Pm(),!await V$()){if(!await qr()){l$("\u518D\u89C1\uFF01");return}}while(!0){const m=await Nm();if(m===null)break;if(!await Am(m))break}l$("\u518D\u89C1\uFF01")}var Rm=function(){console.log(`
54
54
  Tako CLI v${Gr}
55
55
 
56
56
  \u7528\u6CD5: tako [\u9009\u9879] [\u547D\u4EE4]
@@ -67,4 +67,4 @@ Tako CLI v${Gr}
67
67
  tako \u4EA4\u4E92\u5F0F\u9009\u62E9\u5DE5\u5177
68
68
  tako --claude \u76F4\u63A5\u542F\u52A8 Claude Code
69
69
  tako --codex \u76F4\u63A5\u542F\u52A8 Codex
70
- `)};async function Im(){const r=process.argv.slice(2);if(r.includes("-v")||r.includes("--version")){console.log(`Tako CLI v${Gr}`);return}if(r.includes("-h")||r.includes("--help")){Rm();return}if(r.includes("--claude")){const m=v$("claude-code");if(!m)console.error("Claude Code \u5BA2\u6237\u7AEF\u672A\u627E\u5230"),process.exit(1);await r$();const b=await P(m);if(!b.success)console.error(b.error),process.exit(1);return}if(r.includes("--codex")){const m=v$("codex");if(!m)console.error("Codex \u5BA2\u6237\u7AEF\u672A\u627E\u5230"),process.exit(1);await r$();const b=await P(m);if(!b.success)console.error(b.error),process.exit(1);return}await r$(),await Wr()}var Gr="0.1.40";Im().catch((r)=>{console.error("Tako CLI \u53D1\u751F\u9519\u8BEF:",r),process.exit(1)});
70
+ `)};async function Im(){const r=process.argv.slice(2);if(r.includes("-v")||r.includes("--version")){console.log(`Tako CLI v${Gr}`);return}if(r.includes("-h")||r.includes("--help")){Rm();return}if(r.includes("--claude")){const m=v$("claude-code");if(!m)console.error("Claude Code \u5BA2\u6237\u7AEF\u672A\u627E\u5230"),process.exit(1);await r$();const b=await P(m);if(!b.success)console.error(b.error),process.exit(1);return}if(r.includes("--codex")){const m=v$("codex");if(!m)console.error("Codex \u5BA2\u6237\u7AEF\u672A\u627E\u5230"),process.exit(1);await r$();const b=await P(m);if(!b.success)console.error(b.error),process.exit(1);return}await r$(),await Wr()}var Gr="0.1.42";Im().catch((r)=>{console.error("Tako CLI \u53D1\u751F\u9519\u8BEF:",r),process.exit(1)});
package/install.ps1 CHANGED
@@ -1,23 +1,23 @@
1
- # Tako CLI Windows 安装脚本
2
- # 用法: irm https://cdn.jsdelivr.net/npm/tako-cli/install.ps1 | iex
3
- # 或者: powershell -c "irm https://cdn.jsdelivr.net/npm/tako-cli/install.ps1 | iex"
1
+ # Tako CLI Windows Install Script
2
+ # Usage: irm https://cdn.jsdelivr.net/npm/tako-cli/install.ps1 | iex
3
+ # Or: powershell -c "irm https://cdn.jsdelivr.net/npm/tako-cli/install.ps1 | iex"
4
4
 
5
5
  $ErrorActionPreference = "Stop"
6
6
 
7
- # 配置
7
+ # Config
8
8
  $TAKO_DIR = "$env:USERPROFILE\.tako"
9
9
  $TAKO_BUN_DIR = "$TAKO_DIR\bun"
10
10
  $TAKO_CLI_DIR = "$TAKO_DIR\cli"
11
11
  $TAKO_BIN_DIR = "$TAKO_DIR\bin"
12
12
 
13
- # 颜色输出
13
+ # Output helpers
14
14
  function Write-Info { Write-Host "[INFO] $args" -ForegroundColor Green }
15
15
  function Write-Warn { Write-Host "[WARN] $args" -ForegroundColor Yellow }
16
16
  function Write-Err { Write-Host "[ERROR] $args" -ForegroundColor Red; exit 1 }
17
17
 
18
- # 检测网络环境
18
+ # Detect network region
19
19
  function Detect-Region {
20
- Write-Info "检测网络环境..."
20
+ Write-Info "Detecting network region..."
21
21
 
22
22
  $region = "global"
23
23
 
@@ -36,33 +36,33 @@ function Detect-Region {
36
36
  }
37
37
 
38
38
  if ($region -eq "cn") {
39
- Write-Info "检测到中国大陆网络,使用国内镜像"
39
+ Write-Info "Detected China network, using mirror"
40
40
  } else {
41
- Write-Info "使用国际源"
41
+ Write-Info "Using global registry"
42
42
  }
43
43
 
44
44
  return $region
45
45
  }
46
46
 
47
- # 安装 Tako 专属 Bun
47
+ # Install Tako dedicated Bun
48
48
  function Install-Bun {
49
49
  param([string]$Region)
50
50
 
51
51
  $bunExe = "$TAKO_BUN_DIR\bin\bun.exe"
52
52
 
53
53
  if (Test-Path $bunExe) {
54
- Write-Info "Tako 专属 Bun 已安装: $TAKO_BUN_DIR"
54
+ Write-Info "Tako Bun already installed: $TAKO_BUN_DIR"
55
55
  return
56
56
  }
57
57
 
58
- Write-Info "安装 Tako 专属 Bun 运行时..."
59
- Write-Info "(不会影响您系统中已安装的 Node.js Bun"
58
+ Write-Info "Installing Tako dedicated Bun runtime..."
59
+ Write-Info "(This won't affect your system Node.js or Bun)"
60
60
 
61
- # 创建目录
61
+ # Create directories
62
62
  New-Item -ItemType Directory -Force -Path $TAKO_BUN_DIR | Out-Null
63
63
  New-Item -ItemType Directory -Force -Path "$TAKO_BUN_DIR\bin" | Out-Null
64
64
 
65
- # 中国用户直接从镜像下载,不依赖官方脚本
65
+ # China users download directly from mirror
66
66
  if ($Region -eq "cn") {
67
67
  Install-Bun-Direct -Mirror "https://registry.npmmirror.com/-/binary/bun"
68
68
  } else {
@@ -70,86 +70,85 @@ function Install-Bun {
70
70
  }
71
71
 
72
72
  if (-not (Test-Path $bunExe)) {
73
- Write-Err "Bun 安装失败,请检查网络连接"
73
+ Write-Err "Bun installation failed, please check network connection"
74
74
  }
75
75
 
76
- Write-Info "Tako 专属 Bun 安装完成: $TAKO_BUN_DIR"
76
+ Write-Info "Tako Bun installed: $TAKO_BUN_DIR"
77
77
  }
78
78
 
79
- # 直接下载 Bun(不依赖官方脚本)
79
+ # Direct download Bun (without official script)
80
80
  function Install-Bun-Direct {
81
81
  param([string]$Mirror)
82
82
 
83
83
  $bunExe = "$TAKO_BUN_DIR\bin\bun.exe"
84
84
  $zipFile = "$TAKO_BUN_DIR\bun.zip"
85
85
 
86
- # 检测架构
86
+ # Detect architecture
87
87
  $arch = if ([Environment]::Is64BitOperatingSystem) { "x64" } else { "x64" }
88
88
  $zipName = "bun-windows-$arch.zip"
89
89
 
90
- # 构建下载 URL
90
+ # Build download URL
91
91
  if ($Mirror -like "*npmmirror*") {
92
- # npmmirror 格式: https://registry.npmmirror.com/-/binary/bun/bun-v1.1.38/bun-windows-x64.zip
93
- # 先获取最新版本
94
- Write-Info "获取 Bun 最新版本..."
92
+ # npmmirror format: https://registry.npmmirror.com/-/binary/bun/bun-v1.1.38/bun-windows-x64.zip
93
+ Write-Info "Fetching latest Bun version..."
95
94
  try {
96
95
  $versions = Invoke-RestMethod -Uri "https://registry.npmmirror.com/-/binary/bun/" -TimeoutSec 10
97
- # 解析版本列表,找最新的 bun-v* 版本
96
+ # Parse version list, find latest bun-v* version
98
97
  $latestVersion = ($versions | Select-String -Pattern 'bun-v[\d.]+' -AllMatches).Matches.Value |
99
98
  Sort-Object { [version]($_ -replace 'bun-v', '') } -Descending |
100
99
  Select-Object -First 1
101
100
  if (-not $latestVersion) {
102
- $latestVersion = "bun-v1.1.38" # 回退版本
101
+ $latestVersion = "bun-v1.1.38" # Fallback version
103
102
  }
104
- Write-Info "最新版本: $latestVersion"
103
+ Write-Info "Latest version: $latestVersion"
105
104
  $downloadUrl = "$Mirror/$latestVersion/$zipName"
106
105
  } catch {
107
- Write-Warn "获取版本失败,使用默认版本"
106
+ Write-Warn "Failed to fetch version, using default"
108
107
  $downloadUrl = "$Mirror/bun-v1.1.38/$zipName"
109
108
  }
110
109
  } else {
111
- # GitHub 格式
110
+ # GitHub format
112
111
  $downloadUrl = "$Mirror/$zipName"
113
112
  }
114
113
 
115
- Write-Info "下载 Bun: $downloadUrl"
114
+ Write-Info "Downloading Bun: $downloadUrl"
116
115
 
117
- # 下载
116
+ # Download
118
117
  try {
119
118
  Invoke-WebRequest -Uri $downloadUrl -OutFile $zipFile -UseBasicParsing -TimeoutSec 120
120
119
  } catch {
121
- Write-Err "下载 Bun 失败: $_"
120
+ Write-Err "Failed to download Bun: $_"
122
121
  }
123
122
 
124
- # 解压
125
- Write-Info "解压 Bun..."
123
+ # Extract
124
+ Write-Info "Extracting Bun..."
126
125
  try {
127
126
  Expand-Archive -Path $zipFile -DestinationPath $TAKO_BUN_DIR -Force
128
127
 
129
- # Bun zip 内有一层目录,需要移动文件
128
+ # Bun zip has a nested directory, need to move files
130
129
  $extractedDir = Get-ChildItem -Path $TAKO_BUN_DIR -Directory | Where-Object { $_.Name -like "bun-*" } | Select-Object -First 1
131
130
  if ($extractedDir) {
132
- # 移动 bun.exe bin 目录
131
+ # Move bun.exe to bin directory
133
132
  $bunExeInZip = Join-Path $extractedDir.FullName "bun.exe"
134
133
  if (Test-Path $bunExeInZip) {
135
134
  Move-Item -Path $bunExeInZip -Destination $bunExe -Force
136
135
  }
137
- # 清理解压的目录
136
+ # Clean up extracted directory
138
137
  Remove-Item -Path $extractedDir.FullName -Recurse -Force -ErrorAction SilentlyContinue
139
138
  }
140
139
  } catch {
141
- Write-Err "解压 Bun 失败: $_"
140
+ Write-Err "Failed to extract Bun: $_"
142
141
  } finally {
143
- # 清理 zip 文件
142
+ # Clean up zip file
144
143
  Remove-Item -Path $zipFile -Force -ErrorAction SilentlyContinue
145
144
  }
146
145
  }
147
146
 
148
- # 安装 Tako CLI
147
+ # Install Tako CLI
149
148
  function Install-Tako {
150
149
  param([string]$Region)
151
150
 
152
- Write-Info "安装 Tako CLI..."
151
+ Write-Info "Installing Tako CLI..."
153
152
 
154
153
  $bun = "$TAKO_BUN_DIR\bin\bun.exe"
155
154
  $registry = "https://registry.npmjs.org"
@@ -158,108 +157,103 @@ function Install-Tako {
158
157
  $registry = "https://registry.npmmirror.com"
159
158
  }
160
159
 
161
- # 创建本地安装目录
160
+ # Create local install directory
162
161
  New-Item -ItemType Directory -Force -Path $TAKO_CLI_DIR | Out-Null
163
162
 
164
- # 切换到安装目录
163
+ # Switch to install directory
165
164
  Push-Location $TAKO_CLI_DIR
166
165
 
167
166
  try {
168
- # 初始化 package.json
167
+ # Initialize package.json
169
168
  if (-not (Test-Path "package.json")) {
170
169
  '{"name":"tako-local","private":true}' | Out-File -FilePath "package.json" -Encoding UTF8
171
170
  }
172
171
 
173
- # 禁用 Bun 颜色输出,避免 ANSI 代码污染
172
+ # Disable Bun color output to avoid ANSI code pollution
174
173
  $env:NO_COLOR = "1"
175
174
 
176
- # 安装 tako-cli
177
- # 临时允许 stderr 输出(bun 会输出 "Saved lockfile" 到 stderr)
175
+ # Install tako-cli
178
176
  $prevErrorAction = $ErrorActionPreference
179
177
  $ErrorActionPreference = "Continue"
180
178
  & $bun add tako-cli --registry $registry 2>&1 | ForEach-Object { Write-Host $_ }
181
179
  $ErrorActionPreference = $prevErrorAction
182
180
 
183
181
  if ($LASTEXITCODE -ne 0) {
184
- Write-Err "Tako CLI 安装失败"
182
+ Write-Err "Tako CLI installation failed"
185
183
  }
186
184
  } finally {
187
185
  Pop-Location
188
- # 清理环境变量
189
186
  Remove-Item Env:NO_COLOR -ErrorAction SilentlyContinue
190
187
  }
191
188
 
192
189
  $takoEntry = "$TAKO_CLI_DIR\node_modules\tako-cli\dist\index.js"
193
190
  if (-not (Test-Path $takoEntry)) {
194
- Write-Err "Tako CLI 安装异常: $takoEntry 不存在"
191
+ Write-Err "Tako CLI installation error: $takoEntry not found"
195
192
  }
196
193
 
197
- Write-Info "Tako CLI 安装完成: $TAKO_CLI_DIR"
194
+ Write-Info "Tako CLI installed: $TAKO_CLI_DIR"
198
195
 
199
196
  return $takoEntry
200
197
  }
201
198
 
202
- # 创建命令
199
+ # Create command
203
200
  function Create-Command {
204
201
  param([string]$TakoEntry)
205
202
 
206
- Write-Info "配置 tako 命令..."
203
+ Write-Info "Setting up tako command..."
207
204
 
208
205
  $bun = "$TAKO_BUN_DIR\bin\bun.exe"
209
206
 
210
- # 创建 bin 目录
207
+ # Create bin directory
211
208
  New-Item -ItemType Directory -Force -Path $TAKO_BIN_DIR | Out-Null
212
209
 
213
- # 创建 tako.cmd 批处理文件
214
- # 使用 ASCII 编码避免乱码问题
210
+ # Create tako.cmd batch file
215
211
  $cmdContent = @"
216
212
  @echo off
217
213
  "$bun" "$TakoEntry" %*
218
214
  exit /b %ERRORLEVEL%
219
215
  "@
220
216
 
221
- # 使用 ASCII 字节数组直接写入,避免编码问题
222
217
  $bytes = [System.Text.Encoding]::ASCII.GetBytes($cmdContent)
223
218
  [System.IO.File]::WriteAllBytes("$TAKO_BIN_DIR\tako.cmd", $bytes)
224
219
 
225
- # 创建 tako.ps1 PowerShell 脚本(可选)
220
+ # Create tako.ps1 PowerShell script (optional)
226
221
  $ps1Content = @"
227
222
  & "$bun" "$TakoEntry" @args
228
223
  exit `$LASTEXITCODE
229
224
  "@
230
- # PowerShell 脚本使用 UTF8 编码
231
225
  $ps1Bytes = [System.Text.Encoding]::UTF8.GetBytes($ps1Content)
232
226
  [System.IO.File]::WriteAllBytes("$TAKO_BIN_DIR\tako.ps1", $ps1Bytes)
233
227
 
234
- Write-Info "tako 命令已创建:"
228
+ Write-Info "tako command created:"
235
229
  Write-Info " - $TAKO_BIN_DIR\tako.cmd"
236
230
  Write-Info " - $TAKO_BIN_DIR\tako.ps1"
237
231
  }
238
232
 
239
- # 配置 PATH 环境变量
233
+ # Setup PATH environment variable
240
234
  function Setup-Path {
241
- Write-Info "配置 PATH 环境变量..."
235
+ Write-Info "Configuring PATH..."
242
236
 
243
- # 获取当前用户 PATH
237
+ # Get current user PATH
244
238
  $userPath = [Environment]::GetEnvironmentVariable("Path", "User")
245
239
 
246
- # 检查是否已包含 Tako bin 目录
240
+ # Check if Tako bin directory is already in PATH
247
241
  if ($userPath -notlike "*$TAKO_BIN_DIR*") {
248
- # 添加到用户 PATH
242
+ # Add to user PATH
249
243
  $newPath = "$TAKO_BIN_DIR;$userPath"
250
244
  [Environment]::SetEnvironmentVariable("Path", $newPath, "User")
251
- Write-Info "已将 $TAKO_BIN_DIR 添加到用户 PATH"
245
+ Write-Info "Added $TAKO_BIN_DIR to user PATH"
252
246
 
253
- # 同时更新当前会话的 PATH
247
+ # Also update current session PATH
254
248
  $env:Path = "$TAKO_BIN_DIR;$env:Path"
255
249
  } else {
256
- Write-Info "PATH 已包含 Tako bin 目录"
250
+ Write-Info "PATH already contains Tako bin directory"
257
251
  }
258
252
  }
259
253
 
260
- # 验证安装
254
+ # Verify installation
261
255
  function Verify-Installation {
262
- # 刷新当前会话的环境变量
256
+ # Refresh current session environment
263
257
  $env:Path = "$TAKO_BIN_DIR;$env:Path"
264
258
 
265
259
  $takoCmd = "$TAKO_BIN_DIR\tako.cmd"
@@ -267,39 +261,39 @@ function Verify-Installation {
267
261
 
268
262
  if (Test-Path $takoCmd) {
269
263
  Write-Host ""
270
- Write-Host " 安装成功!" -ForegroundColor Green
264
+ Write-Host " [OK] Installation successful!" -ForegroundColor Green
271
265
  Write-Host ""
272
- Write-Host " 运行 'tako' 开始使用" -ForegroundColor Cyan
266
+ Write-Host " Run 'tako' to get started" -ForegroundColor Cyan
273
267
  Write-Host ""
274
268
 
275
- # 测试 tako 命令是否可用
269
+ # Test if tako command is available
276
270
  try {
277
271
  $testResult = & $takoCmd --version 2>&1
278
272
  if ($LASTEXITCODE -eq 0 -or $testResult) {
279
- Write-Host " tako 命令已可用" -ForegroundColor Green
273
+ Write-Host " [OK] tako command is ready" -ForegroundColor Green
280
274
  }
281
275
  } catch {
282
- Write-Host " 注意: 如果 'tako' 命令无法识别,请尝试:" -ForegroundColor Yellow
276
+ Write-Host " Note: If 'tako' command is not recognized, try:" -ForegroundColor Yellow
283
277
  Write-Host ""
284
- Write-Host " 1. 重新打开终端窗口 (推荐)" -ForegroundColor Cyan
278
+ Write-Host " 1. Reopen your terminal (recommended)" -ForegroundColor Cyan
285
279
  Write-Host ""
286
- Write-Host " 2. 或在当前终端运行:" -ForegroundColor Cyan
280
+ Write-Host " 2. Or run in current terminal:" -ForegroundColor Cyan
287
281
  Write-Host " `$env:Path = `"$TAKO_BIN_DIR;`$env:Path`"" -ForegroundColor Gray
288
282
  Write-Host ""
289
- Write-Host " 3. 或直接使用完整路径:" -ForegroundColor Cyan
283
+ Write-Host " 3. Or use full path:" -ForegroundColor Cyan
290
284
  Write-Host " $takoCmd" -ForegroundColor Gray
291
285
  }
292
286
  Write-Host ""
293
287
  } else {
294
- Write-Err "安装验证失败: tako.cmd 文件不存在"
288
+ Write-Err "Installation verification failed: tako.cmd not found"
295
289
  }
296
290
  }
297
291
 
298
- # 主流程
292
+ # Main
299
293
  function Main {
300
294
  Write-Host ""
301
295
  Write-Host " +====================================+" -ForegroundColor Cyan
302
- Write-Host " | Tako CLI 安装程序 |" -ForegroundColor Cyan
296
+ Write-Host " | Tako CLI Installer |" -ForegroundColor Cyan
303
297
  Write-Host " +====================================+" -ForegroundColor Cyan
304
298
  Write-Host ""
305
299
 
@@ -313,5 +307,5 @@ function Main {
313
307
  Write-Host ""
314
308
  }
315
309
 
316
- # 执行
310
+ # Run
317
311
  Main
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tako-cli",
3
- "version": "0.1.40",
3
+ "version": "0.1.42",
4
4
  "description": "Tako CLI - AI coding tools launcher",
5
5
  "type": "module",
6
6
  "bin": {