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 +2 -2
- package/install.ps1 +75 -81
- package/package.json +1 -1
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.
|
|
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
|
-
#
|
|
3
|
-
#
|
|
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
|
-
#
|
|
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
|
|
54
|
+
Write-Info "Tako Bun already installed: $TAKO_BUN_DIR"
|
|
55
55
|
return
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
Write-Info "
|
|
59
|
-
Write-Info "
|
|
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
|
|
76
|
+
Write-Info "Tako Bun installed: $TAKO_BUN_DIR"
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
#
|
|
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
|
-
#
|
|
90
|
+
# Build download URL
|
|
91
91
|
if ($Mirror -like "*npmmirror*") {
|
|
92
|
-
# npmmirror
|
|
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
|
-
#
|
|
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 "
|
|
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 "
|
|
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 "
|
|
120
|
+
Write-Err "Failed to download Bun: $_"
|
|
122
121
|
}
|
|
123
122
|
|
|
124
|
-
#
|
|
125
|
-
Write-Info "
|
|
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
|
-
#
|
|
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 "
|
|
140
|
+
Write-Err "Failed to extract Bun: $_"
|
|
142
141
|
} finally {
|
|
143
|
-
#
|
|
142
|
+
# Clean up zip file
|
|
144
143
|
Remove-Item -Path $zipFile -Force -ErrorAction SilentlyContinue
|
|
145
144
|
}
|
|
146
145
|
}
|
|
147
146
|
|
|
148
|
-
#
|
|
147
|
+
# Install Tako CLI
|
|
149
148
|
function Install-Tako {
|
|
150
149
|
param([string]$Region)
|
|
151
150
|
|
|
152
|
-
Write-Info "
|
|
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
|
-
#
|
|
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
|
-
#
|
|
172
|
+
# Disable Bun color output to avoid ANSI code pollution
|
|
174
173
|
$env:NO_COLOR = "1"
|
|
175
174
|
|
|
176
|
-
#
|
|
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
|
|
191
|
+
Write-Err "Tako CLI installation error: $takoEntry not found"
|
|
195
192
|
}
|
|
196
193
|
|
|
197
|
-
Write-Info "Tako CLI
|
|
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 "
|
|
203
|
+
Write-Info "Setting up tako command..."
|
|
207
204
|
|
|
208
205
|
$bun = "$TAKO_BUN_DIR\bin\bun.exe"
|
|
209
206
|
|
|
210
|
-
#
|
|
207
|
+
# Create bin directory
|
|
211
208
|
New-Item -ItemType Directory -Force -Path $TAKO_BIN_DIR | Out-Null
|
|
212
209
|
|
|
213
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
233
|
+
# Setup PATH environment variable
|
|
240
234
|
function Setup-Path {
|
|
241
|
-
Write-Info "
|
|
235
|
+
Write-Info "Configuring PATH..."
|
|
242
236
|
|
|
243
|
-
#
|
|
237
|
+
# Get current user PATH
|
|
244
238
|
$userPath = [Environment]::GetEnvironmentVariable("Path", "User")
|
|
245
239
|
|
|
246
|
-
#
|
|
240
|
+
# Check if Tako bin directory is already in PATH
|
|
247
241
|
if ($userPath -notlike "*$TAKO_BIN_DIR*") {
|
|
248
|
-
#
|
|
242
|
+
# Add to user PATH
|
|
249
243
|
$newPath = "$TAKO_BIN_DIR;$userPath"
|
|
250
244
|
[Environment]::SetEnvironmentVariable("Path", $newPath, "User")
|
|
251
|
-
Write-Info "
|
|
245
|
+
Write-Info "Added $TAKO_BIN_DIR to user PATH"
|
|
252
246
|
|
|
253
|
-
#
|
|
247
|
+
# Also update current session PATH
|
|
254
248
|
$env:Path = "$TAKO_BIN_DIR;$env:Path"
|
|
255
249
|
} else {
|
|
256
|
-
Write-Info "PATH
|
|
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 "
|
|
264
|
+
Write-Host " [OK] Installation successful!" -ForegroundColor Green
|
|
271
265
|
Write-Host ""
|
|
272
|
-
Write-Host "
|
|
266
|
+
Write-Host " Run 'tako' to get started" -ForegroundColor Cyan
|
|
273
267
|
Write-Host ""
|
|
274
268
|
|
|
275
|
-
#
|
|
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 "
|
|
273
|
+
Write-Host " [OK] tako command is ready" -ForegroundColor Green
|
|
280
274
|
}
|
|
281
275
|
} catch {
|
|
282
|
-
Write-Host "
|
|
276
|
+
Write-Host " Note: If 'tako' command is not recognized, try:" -ForegroundColor Yellow
|
|
283
277
|
Write-Host ""
|
|
284
|
-
Write-Host " 1.
|
|
278
|
+
Write-Host " 1. Reopen your terminal (recommended)" -ForegroundColor Cyan
|
|
285
279
|
Write-Host ""
|
|
286
|
-
Write-Host " 2.
|
|
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.
|
|
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 "
|
|
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
|
|
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
|