tako-cli 0.1.9 → 0.1.11

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,6 +50,6 @@ ${i.default.gray(R)} ${r}
50
50
  `)}
51
51
  `)},info:(r)=>{g.message(r,{symbol:i.default.blue(lm)})},success:(r)=>{g.message(r,{symbol:i.default.green(gm)})},step:(r)=>{g.message(r,{symbol:i.default.green(h$)})},warn:(r)=>{g.message(r,{symbol:i.default.yellow(xm)})},warning:(r)=>{g.warn(r)},error:(r)=>{g.message(r,{symbol:i.default.red(dm)})}},W=()=>{let r=c$?["\u25D2","\u25D0","\u25D3","\u25D1"]:["\u2022","o","O","0"],m=c$?80:120,e,a,t=!1,o="",$=(v="")=>{t=!0,e=u$(),o=v.replace(/\.+$/,""),process.stdout.write(`${i.default.gray(n)}
52
52
  `);let h=0,d=0;a=setInterval(()=>{let G=i.default.magenta(r[h]),B=".".repeat(Math.floor(d)).slice(0,3);process.stdout.write(Y.cursor.move(-999,0)),process.stdout.write(Y.erase.down(1)),process.stdout.write(`${G} ${o}${B}`),h=h+1<r.length?h+1:0,d=d<r.length?d+0.125:0},m)},s=(v="",h=0)=>{o=v??o,t=!1,clearInterval(a);let d=h===0?i.default.green(h$):h===1?i.default.red(F$):i.default.red(D$);process.stdout.write(Y.cursor.move(-999,0)),process.stdout.write(Y.erase.down(1)),process.stdout.write(`${d} ${o}
53
- `),e()},b=(v="")=>{o=v??o},c=(v)=>{let h=v>1?"Something went wrong":"Canceled";t&&s(h,v)};return process.on("uncaughtExceptionMonitor",()=>c(2)),process.on("unhandledRejection",()=>c(2)),process.on("SIGINT",()=>c(1)),process.on("SIGTERM",()=>c(1)),process.on("exit",c),{start:$,stop:s,message:b}};function ym(r){return r.startsWith("cr_")&&r.length>10}async function pm(r){try{let e=await(await fetch(`${M}/apiStats/api/get-key-id`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({apiKey:r})})).json();if(e.success&&e.data?.id)return{valid:!0,apiId:e.data.id};return{valid:!1,error:e.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 er(r){if(!ym(r))return{success:!1,error:"Key \u683C\u5F0F\u4E0D\u6B63\u786E\uFF0C\u5FC5\u987B\u4EE5 cr_ \u5F00\u5934"};let m=await pm(r);if(!m.valid)return{success:!1,error:m.error};return await T({apiKey:r,apiId:m.apiId}),{success:!0}}import{join as ir}from"path";var N=null,v$={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}};async function l$(){if(N)return N;let r=[wm,Sm,Mm];for(let m of r)try{let e=await Promise.race([m(),new Promise((a,t)=>setTimeout(()=>t(Error("timeout")),3000))]);if(e)return N=e,e}catch{}return N="cn","cn"}async function wm(){try{let 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{let 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 Mm(){try{let 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 fm(){let r=await l$();return v$[r]}async function j(){return(await fm()).npm}async function ar(){if(await l$()==="cn")return`curl -fsSL https://bun.sh/install | BUN_INSTALL_MIRROR="${v$.cn.bunMirror}" bash`;return"curl -fsSL https://bun.sh/install | bash"}async function tr(){let r=await l$(),m=v$[r];if(r==="cn")g.info("\u68C0\u6D4B\u5230\u4E2D\u56FD\u5927\u9646\u7F51\u7EDC\uFF0C\u4F7F\u7528\u56FD\u5185\u955C\u50CF\u6E90"),g.message(` npm: ${m.npm}`);else g.info("\u4F7F\u7528\u56FD\u9645\u6E90")}var z=null;function qm(r){return r.startsWith(Q)}async function sr(){try{if(!await Bun.file(w).exists())return!1;let m=Bun.spawn([w,"--version"],{stdout:"pipe",stderr:"pipe"});return await m.exited,m.exitCode===0}catch{return!1}}async function Wm(){try{let m=Bun.spawn(["which","unzip"],{stdout:"pipe",stderr:"pipe"});if(await m.exited,m.exitCode===0)return!0}catch{}g.warn("\u6B63\u5728\u5B89\u88C5\u7CFB\u7EDF\u4F9D\u8D56 (unzip)...");let 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{let e=Bun.spawn(["bash","-c",`sudo ${m} 2>/dev/null || ${m}`],{stdout:"pipe",stderr:"pipe"});if(await e.exited,e.exitCode===0)return!0}catch{}return!1}async function Gm(){let r=W();if(!await Wm())return g.error("\u8BF7\u5148\u624B\u52A8\u5B89\u88C5 unzip: apt install unzip / yum install unzip"),!1;await tr(),r.start("\u6B63\u5728\u5B89\u88C5 Tako \u4E13\u5C5E Bun \u8FD0\u884C\u65F6...");try{await(await import("fs/promises")).mkdir(Q,{recursive:!0});let e=await ar(),a=`BUN_INSTALL="${Q}" ${e}`;if(await Bun.spawn(["bash","-c",a],{stdout:"inherit",stderr:"inherit"}).exited!==0)return r.stop("Bun \u5B89\u88C5\u5931\u8D25"),!1;return z=w,r.stop("Tako \u4E13\u5C5E Bun \u5B89\u88C5\u5B8C\u6210"),g.info(`\u5B89\u88C5\u4F4D\u7F6E: ${Q}`),!0}catch(m){return r.stop("Bun \u5B89\u88C5\u5931\u8D25"),!1}}async function Zm(){if(await sr())return z=w,!0;return g.warn("\u672A\u68C0\u6D4B\u5230 Tako \u4E13\u5C5E Bun\uFF0C\u6B63\u5728\u81EA\u52A8\u5B89\u88C5..."),g.info("\uFF08\u4E0D\u4F1A\u5F71\u54CD\u60A8\u7CFB\u7EDF\u4E2D\u5DF2\u5B89\u88C5\u7684 Node.js \u6216 Bun\uFF09"),await Gm()}async function g$(){if(z){if(!qm(z))z=w;return z}if(await sr())return z=w,w;return w}async function cr(r){try{let m=await j(),e=await fetch(`${m}/${r}/latest`);if(!e.ok)return null;return(await e.json()).version}catch{return null}}async function zm(r){return(await Z()).installedClients[r.id]?.version||null}async function hr(r){let m=P(r.id),e=ir(m,"package.json");try{return await Bun.file(e).exists()}catch{return!1}}async function br(r){let m=await zm(r);if(!m)return!0;let e=await cr(r.package);if(!e)return!1;return m!==e}async function or(r,m=!1){let e=W(),a=P(r.id);try{if(!await Zm())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"};let t=await hr(r),o=m||await br(r);if(t&&!o)return{success:!0};let $=t?"\u66F4\u65B0":"\u5B89\u88C5";e.start(`\u6B63\u5728${$} ${r.name}...`),await(await import("fs/promises")).mkdir(a,{recursive:!0});let b=ir(a,"package.json");if(!await Bun.file(b).exists())await Bun.write(b,JSON.stringify({name:`tako-${r.id}`,private:!0,dependencies:{}},null,2));let v=await g$(),h=await j(),d=Bun.spawn([v,"add",r.package,"--registry",h],{cwd:a,stdout:"pipe",stderr:"pipe"});if(await d.exited!==0){let k=await new Response(d.stderr).text();return e.stop(`${$} ${r.name} \u5931\u8D25`),{success:!1,error:k}}let B=await cr(r.package);if(B){let k=await Z();k.installedClients[r.id]={version:B,installedAt:new Date().toISOString()},await T(k)}return e.stop(`${r.name} ${$}\u5B8C\u6210`),{success:!0}}catch(t){return e.stop("\u64CD\u4F5C\u5931\u8D25"),{success:!1,error:t instanceof Error?t.message:"\u672A\u77E5\u9519\u8BEF"}}}async function vr(r){if(!await hr(r))return await or(r);if(await br(r))return await or(r,!0);return{success:!0}}async function lr(r){try{let m=await vr(r);if(!m.success)return m;let e=await f$();if(!e)return{success:!1,error:"\u672A\u914D\u7F6E API Key"};if(r.setupConfigFiles)await r.setupConfigFiles(e);let a=Z$(r);if(!await Bun.file(a).exists())return{success:!1,error:`\u627E\u4E0D\u5230\u53EF\u6267\u884C\u6587\u4EF6: ${a}`};let o=r.getEnvVars(e),$={...process.env,...o};g.info(`\u542F\u52A8 ${r.name}...`);let s;if(r.runtime==="bun")s=[await g$(),a];else s=[a];return await Bun.spawn(s,{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 x$(){try{let r=await q$();if(!r)return{success:!1,error:"\u672A\u627E\u5230 API ID\uFF0C\u8BF7\u91CD\u65B0\u914D\u7F6E Key"};let e=await(await fetch(`${M}/apiStats/api/user-stats`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({apiId:r})})).json(),t=await(await fetch(`${M}/apiStats/api/user-model-stats`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({apiId:r,period:"daily"})})).json();if(!e.success)return{success:!1,error:e.error||e.message||"\u83B7\u53D6\u7EDF\u8BA1\u5931\u8D25"};let o=(t.data||[]).map(($)=>({model:$.model,requests:$.requests,cost:$.formatted?.total||"$0.00"}));return{success:!0,data:{totalRequests:e.data?.usage?.total?.requests||0,totalCost:e.data?.usage?.total?.formattedCost||"$0.00",todayCost:`$${(e.data?.limits?.currentDailyCost||0).toFixed(2)}`,modelStats:o}}}catch(r){return{success:!1,error:r instanceof Error?r.message:"\u7F51\u7EDC\u8BF7\u6C42\u5931\u8D25"}}}function d$(r){return r.toLocaleString("en-US")}var gr="tako-cli",H="0.1.9";function Jm(r,m){let e=r.split(".").map(Number),a=m.split(".").map(Number);for(let t=0;t<3;t++){let o=e[t]||0,$=a[t]||0;if(o>$)return 1;if(o<$)return-1}return 0}async function Qm(){try{let r=await j(),m=await fetch(`${r}/${gr}/latest`,{signal:AbortSignal.timeout(5000)});if(!m.ok)return null;return(await m.json()).version||null}catch{return null}}async function Ym(){let r=await Qm();if(!r)return{hasUpdate:!1,currentVersion:H};return{hasUpdate:Jm(r,H)>0,latestVersion:r,currentVersion:H}}async function jm(r){let m=W();m.start(`\u6B63\u5728\u66F4\u65B0\u5230 v${r}...`);try{let e=await j(),a=Bun.spawn([w,"add","-g",`${gr}@latest`,"--registry",e],{stdout:"pipe",stderr:"pipe"});if(await a.exited,a.exitCode===0)return m.stop(`\u66F4\u65B0\u6210\u529F\uFF01\u5DF2\u66F4\u65B0\u5230 v${r}`),!0;else{let t=await new Response(a.stderr).text();return m.stop(`\u66F4\u65B0\u5931\u8D25: ${t||"\u672A\u77E5\u9519\u8BEF"}`),!1}}catch(e){return m.stop(`\u66F4\u65B0\u5931\u8D25: ${e instanceof Error?e.message:"\u672A\u77E5\u9519\u8BEF"}`),!1}}async function xr(){try{let r=await Ym();if(r.hasUpdate&&r.latestVersion){if(g.warn(`\u53D1\u73B0\u65B0\u7248\u672C v${r.latestVersion}\uFF08\u5F53\u524D v${H}\uFF09`),await jm(r.latestVersion))g.info("\u8BF7\u91CD\u65B0\u542F\u52A8 Tako CLI \u4EE5\u4F7F\u7528\u65B0\u7248\u672C"),process.exit(0)}}catch{}}function Hm(){console.log(),console.log(` \uD83D\uDC19 Tako CLI v${H}`),console.log(" \x1B[90m\u6B22\u8FCE\u4F7F\u7528\uFF01\x1B[0m"),console.log()}async function dr(r=!1){if(!r)g.warn("\u672A\u68C0\u6D4B\u5230 API Key"),console.log();let m=await rr({message:r?"\u8BF7\u8F93\u5165\u65B0\u7684 API Key (cr_xxx):":"\u8BF7\u8F93\u5165\u4F60\u7684 API Key (cr_xxx):",placeholder:"cr_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",validate(t){if(!t)return"\u8BF7\u8F93\u5165 API Key";if(!t.startsWith("cr_"))return"Key \u5FC5\u987B\u4EE5 cr_ \u5F00\u5934";if(t.length<10)return"Key \u683C\u5F0F\u4E0D\u6B63\u786E"}});if(X(m))return!1;let e=W();e.start("\u9A8C\u8BC1 Key \u4E2D...");let a=await er(m);if(a.success)return e.stop("Key \u9A8C\u8BC1\u6210\u529F\uFF01\u5DF2\u4FDD\u5B58\u5230\u914D\u7F6E\u6587\u4EF6"),!0;else return e.stop(`\u9A8C\u8BC1\u5931\u8D25: ${a.error}`),!1}function Lm(r,m){let e=[];if(e.push(""),e.push(" \uD83D\uDCCA \u7528\u91CF\u7EDF\u8BA1"),e.push(""),e.push(` \u603B\u8C03\u7528\u6B21\u6570: ${d$(r.totalRequests)} \u6B21`),e.push(` \u603B\u6D88\u8D39: ${r.totalCost}`),e.push(` \u4ECA\u65E5\u6D88\u8D39: ${r.todayCost}`),r.modelStats.length>0)e.push(""),e.push(" \u6A21\u578B\u4F7F\u7528\u5206\u5E03 (\u4ECA\u65E5):"),r.modelStats.forEach((a,t)=>{let o=t===r.modelStats.length-1?"\u2514\u2500":"\u251C\u2500",$=a.model.length>25?a.model.slice(0,22)+"...":a.model;e.push(` ${o} ${$.padEnd(25)} ${d$(a.requests).padStart(6)} \u6B21 ${a.cost.padStart(8)}`)});return e.push(""),e.push(` \x1B[90m${m}s \u540E\u5237\u65B0 | \u6309\u4EFB\u610F\u952E\u8FD4\u56DE\x1B[0m`),e.join(`
53
+ `),e()},b=(v="")=>{o=v??o},c=(v)=>{let h=v>1?"Something went wrong":"Canceled";t&&s(h,v)};return process.on("uncaughtExceptionMonitor",()=>c(2)),process.on("unhandledRejection",()=>c(2)),process.on("SIGINT",()=>c(1)),process.on("SIGTERM",()=>c(1)),process.on("exit",c),{start:$,stop:s,message:b}};function ym(r){return r.startsWith("cr_")&&r.length>10}async function pm(r){try{let e=await(await fetch(`${M}/apiStats/api/get-key-id`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({apiKey:r})})).json();if(e.success&&e.data?.id)return{valid:!0,apiId:e.data.id};return{valid:!1,error:e.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 er(r){if(!ym(r))return{success:!1,error:"Key \u683C\u5F0F\u4E0D\u6B63\u786E\uFF0C\u5FC5\u987B\u4EE5 cr_ \u5F00\u5934"};let m=await pm(r);if(!m.valid)return{success:!1,error:m.error};return await T({apiKey:r,apiId:m.apiId}),{success:!0}}import{join as ir}from"path";var N=null,v$={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}};async function l$(){if(N)return N;let r=[wm,Sm,Mm];for(let m of r)try{let e=await Promise.race([m(),new Promise((a,t)=>setTimeout(()=>t(Error("timeout")),3000))]);if(e)return N=e,e}catch{}return N="cn","cn"}async function wm(){try{let 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{let 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 Mm(){try{let 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 fm(){let r=await l$();return v$[r]}async function j(){return(await fm()).npm}async function ar(){if(await l$()==="cn")return`curl -fsSL https://bun.sh/install | BUN_INSTALL_MIRROR="${v$.cn.bunMirror}" bash`;return"curl -fsSL https://bun.sh/install | bash"}async function tr(){let r=await l$(),m=v$[r];if(r==="cn")g.info("\u68C0\u6D4B\u5230\u4E2D\u56FD\u5927\u9646\u7F51\u7EDC\uFF0C\u4F7F\u7528\u56FD\u5185\u955C\u50CF\u6E90"),g.message(` npm: ${m.npm}`);else g.info("\u4F7F\u7528\u56FD\u9645\u6E90")}var z=null;function qm(r){return r.startsWith(Q)}async function sr(){try{if(!await Bun.file(w).exists())return!1;let m=Bun.spawn([w,"--version"],{stdout:"pipe",stderr:"pipe"});return await m.exited,m.exitCode===0}catch{return!1}}async function Wm(){try{let m=Bun.spawn(["which","unzip"],{stdout:"pipe",stderr:"pipe"});if(await m.exited,m.exitCode===0)return!0}catch{}g.warn("\u6B63\u5728\u5B89\u88C5\u7CFB\u7EDF\u4F9D\u8D56 (unzip)...");let 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{let e=Bun.spawn(["bash","-c",`sudo ${m} 2>/dev/null || ${m}`],{stdout:"pipe",stderr:"pipe"});if(await e.exited,e.exitCode===0)return!0}catch{}return!1}async function Gm(){let r=W();if(!await Wm())return g.error("\u8BF7\u5148\u624B\u52A8\u5B89\u88C5 unzip: apt install unzip / yum install unzip"),!1;await tr(),r.start("\u6B63\u5728\u5B89\u88C5 Tako \u4E13\u5C5E Bun \u8FD0\u884C\u65F6...");try{await(await import("fs/promises")).mkdir(Q,{recursive:!0});let e=await ar(),a=`BUN_INSTALL="${Q}" ${e}`;if(await Bun.spawn(["bash","-c",a],{stdout:"inherit",stderr:"inherit"}).exited!==0)return r.stop("Bun \u5B89\u88C5\u5931\u8D25"),!1;return z=w,r.stop("Tako \u4E13\u5C5E Bun \u5B89\u88C5\u5B8C\u6210"),g.info(`\u5B89\u88C5\u4F4D\u7F6E: ${Q}`),!0}catch(m){return r.stop("Bun \u5B89\u88C5\u5931\u8D25"),!1}}async function Zm(){if(await sr())return z=w,!0;return g.warn("\u672A\u68C0\u6D4B\u5230 Tako \u4E13\u5C5E Bun\uFF0C\u6B63\u5728\u81EA\u52A8\u5B89\u88C5..."),g.info("\uFF08\u4E0D\u4F1A\u5F71\u54CD\u60A8\u7CFB\u7EDF\u4E2D\u5DF2\u5B89\u88C5\u7684 Node.js \u6216 Bun\uFF09"),await Gm()}async function g$(){if(z){if(!qm(z))z=w;return z}if(await sr())return z=w,w;return w}async function cr(r){try{let m=await j(),e=await fetch(`${m}/${r}/latest`);if(!e.ok)return null;return(await e.json()).version}catch{return null}}async function zm(r){return(await Z()).installedClients[r.id]?.version||null}async function hr(r){let m=P(r.id),e=ir(m,"package.json");try{return await Bun.file(e).exists()}catch{return!1}}async function br(r){let m=await zm(r);if(!m)return!0;let e=await cr(r.package);if(!e)return!1;return m!==e}async function or(r,m=!1){let e=W(),a=P(r.id);try{if(!await Zm())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"};let t=await hr(r),o=m||await br(r);if(t&&!o)return{success:!0};let $=t?"\u66F4\u65B0":"\u5B89\u88C5";e.start(`\u6B63\u5728${$} ${r.name}...`),await(await import("fs/promises")).mkdir(a,{recursive:!0});let b=ir(a,"package.json");if(!await Bun.file(b).exists())await Bun.write(b,JSON.stringify({name:`tako-${r.id}`,private:!0,dependencies:{}},null,2));let v=await g$(),h=await j(),d=Bun.spawn([v,"add",r.package,"--registry",h],{cwd:a,stdout:"pipe",stderr:"pipe"});if(await d.exited!==0){let k=await new Response(d.stderr).text();return e.stop(`${$} ${r.name} \u5931\u8D25`),{success:!1,error:k}}let B=await cr(r.package);if(B){let k=await Z();k.installedClients[r.id]={version:B,installedAt:new Date().toISOString()},await T(k)}return e.stop(`${r.name} ${$}\u5B8C\u6210`),{success:!0}}catch(t){return e.stop("\u64CD\u4F5C\u5931\u8D25"),{success:!1,error:t instanceof Error?t.message:"\u672A\u77E5\u9519\u8BEF"}}}async function vr(r){if(!await hr(r))return await or(r);if(await br(r))return await or(r,!0);return{success:!0}}async function lr(r){try{let m=await vr(r);if(!m.success)return m;let e=await f$();if(!e)return{success:!1,error:"\u672A\u914D\u7F6E API Key"};if(r.setupConfigFiles)await r.setupConfigFiles(e);let a=Z$(r);if(!await Bun.file(a).exists())return{success:!1,error:`\u627E\u4E0D\u5230\u53EF\u6267\u884C\u6587\u4EF6: ${a}`};let o=r.getEnvVars(e),$={...process.env,...o};g.info(`\u542F\u52A8 ${r.name}...`);let s;if(r.runtime==="bun")s=[await g$(),a];else s=[a];return await Bun.spawn(s,{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 x$(){try{let r=await q$();if(!r)return{success:!1,error:"\u672A\u627E\u5230 API ID\uFF0C\u8BF7\u91CD\u65B0\u914D\u7F6E Key"};let e=await(await fetch(`${M}/apiStats/api/user-stats`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({apiId:r})})).json(),t=await(await fetch(`${M}/apiStats/api/user-model-stats`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({apiId:r,period:"daily"})})).json();if(!e.success)return{success:!1,error:e.error||e.message||"\u83B7\u53D6\u7EDF\u8BA1\u5931\u8D25"};let o=(t.data||[]).map(($)=>({model:$.model,requests:$.requests,cost:$.formatted?.total||"$0.00"}));return{success:!0,data:{totalRequests:e.data?.usage?.total?.requests||0,totalCost:e.data?.usage?.total?.formattedCost||"$0.00",todayCost:`$${(e.data?.limits?.currentDailyCost||0).toFixed(2)}`,modelStats:o}}}catch(r){return{success:!1,error:r instanceof Error?r.message:"\u7F51\u7EDC\u8BF7\u6C42\u5931\u8D25"}}}function d$(r){return r.toLocaleString("en-US")}var gr="tako-cli",H="0.1.11";function Jm(r,m){let e=r.split(".").map(Number),a=m.split(".").map(Number);for(let t=0;t<3;t++){let o=e[t]||0,$=a[t]||0;if(o>$)return 1;if(o<$)return-1}return 0}async function Qm(){try{let r=await j(),m=await fetch(`${r}/${gr}/latest`,{signal:AbortSignal.timeout(5000)});if(!m.ok)return null;return(await m.json()).version||null}catch{return null}}async function Ym(){let r=await Qm();if(!r)return{hasUpdate:!1,currentVersion:H};return{hasUpdate:Jm(r,H)>0,latestVersion:r,currentVersion:H}}async function jm(r){let m=W();m.start(`\u6B63\u5728\u66F4\u65B0\u5230 v${r}...`);try{let e=await j(),a=Bun.spawn([w,"add","-g",`${gr}@latest`,"--registry",e],{stdout:"pipe",stderr:"pipe"});if(await a.exited,a.exitCode===0)return m.stop(`\u66F4\u65B0\u6210\u529F\uFF01\u5DF2\u66F4\u65B0\u5230 v${r}`),!0;else{let t=await new Response(a.stderr).text();return m.stop(`\u66F4\u65B0\u5931\u8D25: ${t||"\u672A\u77E5\u9519\u8BEF"}`),!1}}catch(e){return m.stop(`\u66F4\u65B0\u5931\u8D25: ${e instanceof Error?e.message:"\u672A\u77E5\u9519\u8BEF"}`),!1}}async function xr(){try{let r=await Ym();if(r.hasUpdate&&r.latestVersion){if(g.warn(`\u53D1\u73B0\u65B0\u7248\u672C v${r.latestVersion}\uFF08\u5F53\u524D v${H}\uFF09`),await jm(r.latestVersion))g.info("\u8BF7\u91CD\u65B0\u542F\u52A8 Tako CLI \u4EE5\u4F7F\u7528\u65B0\u7248\u672C"),process.exit(0)}}catch{}}function Hm(){console.log(),console.log(` \uD83D\uDC19 Tako CLI v${H}`),console.log(" \x1B[90m\u6B22\u8FCE\u4F7F\u7528\uFF01\x1B[0m"),console.log()}async function dr(r=!1){if(!r)g.warn("\u672A\u68C0\u6D4B\u5230 API Key"),console.log();let m=await rr({message:r?"\u8BF7\u8F93\u5165\u65B0\u7684 API Key (cr_xxx):":"\u8BF7\u8F93\u5165\u4F60\u7684 API Key (cr_xxx):",placeholder:"cr_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",validate(t){if(!t)return"\u8BF7\u8F93\u5165 API Key";if(!t.startsWith("cr_"))return"Key \u5FC5\u987B\u4EE5 cr_ \u5F00\u5934";if(t.length<10)return"Key \u683C\u5F0F\u4E0D\u6B63\u786E"}});if(X(m))return!1;let e=W();e.start("\u9A8C\u8BC1 Key \u4E2D...");let a=await er(m);if(a.success)return e.stop("Key \u9A8C\u8BC1\u6210\u529F\uFF01\u5DF2\u4FDD\u5B58\u5230\u914D\u7F6E\u6587\u4EF6"),!0;else return e.stop(`\u9A8C\u8BC1\u5931\u8D25: ${a.error}`),!1}function Lm(r,m){let e=[];if(e.push(""),e.push(" \uD83D\uDCCA \u7528\u91CF\u7EDF\u8BA1"),e.push(""),e.push(` \u603B\u8C03\u7528\u6B21\u6570: ${d$(r.totalRequests)} \u6B21`),e.push(` \u603B\u6D88\u8D39: ${r.totalCost}`),e.push(` \u4ECA\u65E5\u6D88\u8D39: ${r.todayCost}`),r.modelStats.length>0)e.push(""),e.push(" \u6A21\u578B\u4F7F\u7528\u5206\u5E03 (\u4ECA\u65E5):"),r.modelStats.forEach((a,t)=>{let o=t===r.modelStats.length-1?"\u2514\u2500":"\u251C\u2500",$=a.model.length>25?a.model.slice(0,22)+"...":a.model;e.push(` ${o} ${$.padEnd(25)} ${d$(a.requests).padStart(6)} \u6B21 ${a.cost.padStart(8)}`)});return e.push(""),e.push(` \x1B[90m${m}s \u540E\u5237\u65B0 | \u6309\u4EFB\u610F\u952E\u8FD4\u56DE\x1B[0m`),e.join(`
54
54
  `)}async function Vm(){let m=W();m.start("\u83B7\u53D6\u7528\u91CF\u7EDF\u8BA1...");let e=await x$();if(!e.success){m.stop(`\u83B7\u53D6\u5931\u8D25: ${e.error}`);return}m.stop("");let a=10,t="",o=()=>{if(t){let s=t.split(`
55
55
  `).length;process.stdout.write(`\x1B[${s}A\x1B[0J`)}t=Lm(e.data,a),console.log(t)};o();let $=process.stdin.isRaw;if(process.stdin.isTTY)process.stdin.setRawMode(!0);return process.stdin.resume(),new Promise((s)=>{let b=!0,c=()=>{if(!b)return;b=!1,h(),s()},v=setInterval(async()=>{if(!b)return;if(a--,a<=0){a=10;let d=await x$();if(d.success)e=d}o()},1000),h=()=>{if(clearInterval(v),process.stdin.removeListener("data",c),process.stdin.isTTY)process.stdin.setRawMode($??!1);process.stdin.pause()};process.stdin.on("data",c)})}async function Xm(){let m=[...J$().map((a)=>({value:{type:"launch",client:a},label:`\u542F\u52A8 ${a.name}`,hint:a.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"}],e=await mr({message:"\u8BF7\u9009\u62E9\u64CD\u4F5C:",options:m});if(X(e))return null;return e}async function Bm(r){switch(r.type){case"launch":let m=await lr(r.client);if(!m.success)g.error(m.error||"\u542F\u52A8\u5931\u8D25");return!0;case"stats":return await Vm(),!0;case"config":return await dr(!0),!0;case"exit":return!1}}async function nr(){if(Hm(),!await M$()){if(!await dr()){b$("\u518D\u89C1\uFF01");return}}while(!0){let m=await Xm();if(m===null)break;if(!await Bm(m))break}b$("\u518D\u89C1\uFF01")}async function km(){await xr(),await nr()}km().catch((r)=>{console.error("Tako CLI \u53D1\u751F\u9519\u8BEF:",r),process.exit(1)});
package/install.sh CHANGED
@@ -90,7 +90,7 @@ install_dependencies() {
90
90
  install_bun() {
91
91
  # 检查是否已安装
92
92
  if [ -x "$TAKO_BUN_DIR/bin/bun" ]; then
93
- info "Tako 专属 Bun 已安装"
93
+ info "Tako 专属 Bun 已安装: $TAKO_BUN_DIR"
94
94
  return 0
95
95
  fi
96
96
 
@@ -99,17 +99,22 @@ install_bun() {
99
99
 
100
100
  mkdir -p "$TAKO_BUN_DIR"
101
101
 
102
- local install_cmd="curl -fsSL https://bun.sh/install | BUN_INSTALL=\"$TAKO_BUN_DIR\" bash"
102
+ # 设置环境变量确保 bun 安装到正确位置
103
+ export BUN_INSTALL="$TAKO_BUN_DIR"
103
104
 
104
105
  # 国内用户使用镜像
105
106
  if [ "$REGION" = "cn" ]; then
106
- install_cmd="curl -fsSL https://bun.sh/install | BUN_INSTALL=\"$TAKO_BUN_DIR\" BUN_INSTALL_MIRROR=\"https://registry.npmmirror.com/-/binary/bun\" bash"
107
+ export BUN_INSTALL_MIRROR="https://registry.npmmirror.com/-/binary/bun"
107
108
  fi
108
109
 
109
- eval "$install_cmd"
110
+ # 执行 bun 安装脚本
111
+ curl -fsSL https://bun.sh/install | bash
112
+
113
+ # 清理环境变量
114
+ unset BUN_INSTALL_MIRROR
110
115
 
111
116
  if [ ! -x "$TAKO_BUN_DIR/bin/bun" ]; then
112
- error "Bun 安装失败"
117
+ error "Bun 安装失败,请检查网络连接"
113
118
  fi
114
119
 
115
120
  info "Tako 专属 Bun 安装完成: $TAKO_BUN_DIR"
@@ -127,70 +132,84 @@ install_tako() {
127
132
  fi
128
133
 
129
134
  # 使用 Tako 专属 Bun 全局安装 tako-cli
130
- # 必须设置 BUN_INSTALL 让 bun 安装到 Tako 专属目录
131
- BUN_INSTALL="$TAKO_BUN_DIR" "$bun" add -g tako-cli --registry "$registry"
135
+ export BUN_INSTALL="$TAKO_BUN_DIR"
136
+ if ! "$bun" add -g tako-cli --registry "$registry"; then
137
+ error "Tako CLI 安装失败"
138
+ fi
139
+
140
+ # 检测 tako 实际安装位置(bun 可能安装到 ~/.bun 或 ~/.tako/bun)
141
+ if [ -f "$TAKO_BUN_DIR/bin/tako" ]; then
142
+ TAKO_BIN="$TAKO_BUN_DIR/bin/tako"
143
+ elif [ -f "$HOME/.bun/bin/tako" ]; then
144
+ TAKO_BIN="$HOME/.bun/bin/tako"
145
+ warn "Tako 安装到了 ~/.bun(bun 默认目录)"
146
+ else
147
+ error "Tako CLI 安装异常: 找不到 tako 可执行文件"
148
+ fi
149
+
150
+ info "Tako CLI 安装完成: $TAKO_BIN"
151
+ }
152
+
153
+ # 添加 PATH 配置到指定文件
154
+ add_path_to_file() {
155
+ local file="$1"
156
+ local content="$2"
157
+
158
+ # 检查是否已存在 Tako CLI 配置
159
+ if grep -q "Tako CLI PATH" "$file" 2>/dev/null; then
160
+ return 0
161
+ fi
132
162
 
133
- info "Tako CLI 安装完成"
163
+ echo "" >> "$file"
164
+ echo "# Tako CLI PATH" >> "$file"
165
+ echo "$content" >> "$file"
166
+ info "已添加 PATH 配置到 $file"
134
167
  }
135
168
 
136
169
  # 配置 PATH 到 shell 配置文件
137
170
  setup_shell_path() {
138
- local bun_bin="$TAKO_BUN_DIR/bin"
171
+ # 使用动态检测的目录(由 create_command 设置)或默认值
172
+ local bun_bin="${TAKO_BIN_DIR:-$TAKO_BUN_DIR/bin}"
139
173
  local path_export="export PATH=\"$bun_bin:\$PATH\""
140
174
  local configured=false
141
175
 
142
176
  # 检测当前 shell
143
177
  local current_shell=""
144
- current_shell=$(basename "$SHELL" 2>/dev/null || echo "")
178
+ current_shell=$(basename "$SHELL" 2>/dev/null || echo "bash")
145
179
 
146
180
  # ===== Bash =====
147
- # .bashrc (交互式 shell) .bash_profile (登录 shell)
148
- for bashrc in "$HOME/.bashrc" "$HOME/.bash_profile"; do
149
- if [ -f "$bashrc" ] || [ "$current_shell" = "bash" ]; then
150
- if ! grep -q "$bun_bin" "$bashrc" 2>/dev/null; then
151
- echo "" >> "$bashrc"
152
- echo "# Tako CLI PATH" >> "$bashrc"
153
- echo "$path_export" >> "$bashrc"
154
- info "已添加 PATH 配置到 $bashrc"
155
- configured=true
156
- fi
181
+ if [ "$current_shell" = "bash" ]; then
182
+ # 优先使用 .bashrc(交互式),其次 .bash_profile(登录)
183
+ if [ -f "$HOME/.bashrc" ]; then
184
+ add_path_to_file "$HOME/.bashrc" "$path_export"
185
+ configured=true
186
+ elif [ -f "$HOME/.bash_profile" ]; then
187
+ add_path_to_file "$HOME/.bash_profile" "$path_export"
188
+ configured=true
189
+ else
190
+ # 创建 .bashrc
191
+ add_path_to_file "$HOME/.bashrc" "$path_export"
192
+ configured=true
157
193
  fi
158
- done
194
+ fi
159
195
 
160
196
  # ===== Zsh =====
161
- local zshrc="$HOME/.zshrc"
162
- if [ -f "$zshrc" ] || [ "$current_shell" = "zsh" ]; then
163
- if ! grep -q "$bun_bin" "$zshrc" 2>/dev/null; then
164
- echo "" >> "$zshrc"
165
- echo "# Tako CLI PATH" >> "$zshrc"
166
- echo "$path_export" >> "$zshrc"
167
- info "已添加 PATH 配置到 $zshrc"
168
- configured=true
169
- fi
197
+ if [ "$current_shell" = "zsh" ] || [ -f "$HOME/.zshrc" ]; then
198
+ add_path_to_file "$HOME/.zshrc" "$path_export"
199
+ configured=true
170
200
  fi
171
201
 
172
202
  # ===== Fish =====
173
203
  local fish_config="$HOME/.config/fish/config.fish"
174
- if [ -f "$fish_config" ] || [ "$current_shell" = "fish" ]; then
175
- if ! grep -q "$bun_bin" "$fish_config" 2>/dev/null; then
176
- mkdir -p "$(dirname "$fish_config")"
177
- echo "" >> "$fish_config"
178
- echo "# Tako CLI PATH" >> "$fish_config"
179
- echo "set -gx PATH \"$bun_bin\" \$PATH" >> "$fish_config"
180
- info "已添加 PATH 配置到 $fish_config"
181
- configured=true
182
- fi
204
+ if [ "$current_shell" = "fish" ] || [ -f "$fish_config" ]; then
205
+ mkdir -p "$(dirname "$fish_config")"
206
+ add_path_to_file "$fish_config" "set -gx PATH \"$bun_bin\" \$PATH"
207
+ configured=true
183
208
  fi
184
209
 
185
210
  # ===== 通用 .profile (sh, dash 等 POSIX shell) =====
186
- local profile_rc="$HOME/.profile"
187
- if [ -f "$profile_rc" ] || [ "$configured" = false ]; then
188
- if ! grep -q "$bun_bin" "$profile_rc" 2>/dev/null; then
189
- echo "" >> "$profile_rc"
190
- echo "# Tako CLI PATH" >> "$profile_rc"
191
- echo "$path_export" >> "$profile_rc"
192
- info "已添加 PATH 配置到 $profile_rc"
193
- fi
211
+ if [ "$configured" = false ] || [ -f "$HOME/.profile" ]; then
212
+ add_path_to_file "$HOME/.profile" "$path_export"
194
213
  fi
195
214
 
196
215
  # 导出到当前 session
@@ -201,10 +220,11 @@ setup_shell_path() {
201
220
  create_command() {
202
221
  info "配置 tako 命令..."
203
222
 
204
- local bun_bin="$TAKO_BUN_DIR/bin"
205
- local tako_source="$bun_bin/tako"
223
+ # 使用 install_tako 中检测到的实际路径
224
+ local tako_source="$TAKO_BIN"
225
+ local tako_bin_dir="$(dirname "$TAKO_BIN")"
206
226
 
207
- # 检查 bun 安装的 tako 是否存在
227
+ # 检查 tako 是否存在
208
228
  if [ ! -f "$tako_source" ]; then
209
229
  error "tako 可执行文件未找到: $tako_source"
210
230
  fi
@@ -227,9 +247,11 @@ create_command() {
227
247
 
228
248
  # 方案3: 配置 PATH(无需 sudo)
229
249
  warn "无法写入 $INSTALL_DIR,使用 PATH 配置方案"
250
+ # 设置正确的 bin 目录用于 PATH 配置
251
+ TAKO_BIN_DIR="$tako_bin_dir"
230
252
  setup_shell_path
231
253
 
232
- info "tako 命令已配置到 $bun_bin"
254
+ info "tako 命令已配置到 $tako_bin_dir"
233
255
  }
234
256
 
235
257
  # 验证安装
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tako-cli",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "description": "Tako CLI - AI coding tools launcher",
5
5
  "type": "module",
6
6
  "bin": {