pcs-cli 1.6.1 → 1.7.1
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/README.md +4 -4
- package/dist/cli.js +1 -1
- package/package.json +44 -37
package/README.md
CHANGED
|
@@ -15,12 +15,12 @@ npm i -g pcs-cli
|
|
|
15
15
|
## 使用
|
|
16
16
|
在终端执行下面命令:
|
|
17
17
|
|
|
18
|
-
```
|
|
18
|
+
```bash
|
|
19
19
|
pcs-cli [command] [options]
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
### 可用命令
|
|
23
|
-
```
|
|
23
|
+
```bash
|
|
24
24
|
Commands:
|
|
25
25
|
init [options] initialize baidu pcs
|
|
26
26
|
refresh [options] refresh token
|
|
@@ -40,7 +40,7 @@ Commands:
|
|
|
40
40
|
```sh
|
|
41
41
|
pcs init -n name -k key -s secret
|
|
42
42
|
# 或者 根据提示填写应用的name key secret
|
|
43
|
-
pcs init
|
|
43
|
+
pcs init
|
|
44
44
|
# 如果本地已存在有效的access_token 再次运行该命令将会有如下提示
|
|
45
45
|
Your access token has not expired (expiration date: 2023-12-10 22:49:14).
|
|
46
46
|
? Do you want to continue initializing? › (y/N)
|
|
@@ -144,7 +144,7 @@ pcs download
|
|
|
144
144
|
删除远程文件
|
|
145
145
|
|
|
146
146
|
```sh
|
|
147
|
-
pcs rm
|
|
147
|
+
pcs rm
|
|
148
148
|
```
|
|
149
149
|
|
|
150
150
|
### fetch
|
package/dist/cli.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{Command as e}from"@commander-js/extra-typings";import t from"chalk";import o,{existsSync as r,statSync as n,mkdtempSync as s,mkdirSync as a,createReadStream as i,createWriteStream as c,promises as p}from"fs";import d from"open";import l from"prompts";import h from"axios";import m,{homedir as u,tmpdir as g}from"os";import f,{sep as y,join as w,basename as k,dirname as _,relative as b}from"path";import{pipeline as v}from"stream/promises";import $ from"bytes";import x from"cliui";import S from"dayjs";import z from"progress";import{glob as C}from"glob";import{sep as O,join as T}from"path/posix";import E from"https";var A=class{constructor(e){this.config=e,this.config={scope:"basic,netdisk",...e},this.axios=h.create({timeout:1e4})}axios;async getUserInfo(e){const{data:t}=await h.get("https://pan.baidu.com/rest/2.0/xpan/nas",{params:{method:"uinfo",access_token:e}});return t}async refreshToken(e){const{client_secret:t,client_id:o}=this.config,{data:r}=await h.get("https://openapi.baidu.com/oauth/2.0/token",{params:{grant_type:"refresh_token",client_id:o,client_secret:t,refresh_token:e}});return r}},U=class extends A{constructor(e){super({...e,response_type:"device_code"})}async getDeviceCode(){const{data:e}=await this.axios.get("https://openapi.baidu.com/oauth/2.0/device/code",{params:{response_type:"device_code",client_id:this.config.client_id,scope:this.config.scope}});return e}getAuthorizeURL(e){return`${e.verification_url}?code=${e.user_code}`}async authorize(e){const{client_id:t,client_secret:o,scope:r}=this.config,{data:n}=await this.axios.get("https://openapi.baidu.com/oauth/2.0/token",{params:{grant_type:"device_token",code:e,client_id:t,client_secret:o,scope:r}});return n}},B=class extends A{constructor(e){super({...e,response_type:"token",redirect_uri:"oob"})}getAuthorizeURL(){const{client_secret:e,...t}=this.config;return`https://openapi.baidu.com/oauth/2.0/authorize?${new URLSearchParams(t)}`}async authorize(e){let t;return t=e.startsWith("http")?this.parseTokenFromUrl(e):e.includes("access_token=")?this.parseTokenFromHash(e):{access_token:e,expires_in:2592e3,scope:this.config.scope},t}parseTokenFromUrl(e){const t=new URL(e);return this.parseTokenFromHash(t.hash.substring(1))}parseTokenFromHash(e){const t=new URLSearchParams(e),o=t.get("access_token"),r=t.get("expires_in"),n=t.get("error");if(n)throw new Error(`OAuth error: ${n}`);if(!o)throw new Error("Access token not found");return{access_token:o,expires_in:parseInt(r||"2592000",10),scope:t.get("scope")||this.config.scope,session_key:t.get("session_key")||void 0,session_secret:t.get("session_secret")||void 0}}};function L(e,t){return`]8;;${t}${e}]8;;`}async function F(e,t,o=s(w(g(),"splitFile-"))){if(!e||"string"!=typeof e)throw new TypeError("path must be a non-empty string");if(!Number.isInteger(t)||t<=0)throw new TypeError("chunkSize must be a positive integer");if(!o||"string"!=typeof o)throw new TypeError("outputDir must be a non-empty string");const r=[],{size:p}=n(e),d=k(e);a(o,{recursive:!0});for(let n=0;n<p;n+=t){const s=Math.min(n+t-1,p-1),a=w(o,`${d}.${r.length+1}`),l=i(e,{start:n,end:s}),h=c(a);await v(l,h),r.push(a)}return r}var P=f.join(u(),".pcs"),R=f.join(P,"config.json"),N="https://pan.baidu.com/union/console/createapp",q="https://pan.baidu.com/union/console/applist";async function I(e,t){try{const o=f.dirname(e);await p.mkdir(o,{recursive:!0}),await p.writeFile(e,JSON.stringify(t,null,2),"utf8")}catch(e){if(e instanceof Error)throw new Error(`写入文件失败: ${e.message}`)}}var V=new e("init").description("initialize baidu pcs").option("-n, --name <string>","app name","").option("-k, --key <string>","app key","").option("-s, --secret <string>","app secret","").action(async e=>{if(r(R)){console.log(t.redBright("Baidu pcs initialization will be begin. If you have already configured before, your old settings will be overwritten."));const{confirm:e}=await l({type:"confirm",name:"confirm",message:"Can you confirm?",initial:!1});if(!e)return}const o=[];["name","key","secret"].forEach(t=>{e[t]||o.push({type:"text",name:t,message:`Please enter baidu app ${t}`})}),(e={...e,...await l(o)}).key&&(e.secret?await async function(e){const o=new U({client_id:e.key,client_secret:e.secret}),r=await o.getDeviceCode(),n=o.getAuthorizeURL(r);console.log("Launch your favorite web browser and visit: "),console.log(L(n,n)),console.log(`Input ${t.bold.red(r.user_code)} as the user code if asked.`),console.log("After granting access to the application, come back here and "),await d(n);const{confirm:s}=await l({type:"confirm",name:"confirm",message:"Press Enter to continue",initial:!0});if(s){const n=await o.authorize(r.device_code);console.log(t.green("Successfully initialized")),console.log(`access_token: ${t.yellowBright(n.access_token)}`),console.log(`refresh_token: ${t.yellowBright(n.refresh_token)}`),await I(R,{...e,...n})}}(e):await async function(e){const o=new B({client_id:e.key}).getAuthorizeURL();await d(o),console.log(t.hex("#FFA500")("You'll have to grab the access_token generated by Baidu.")),console.log(""),console.log(`1. Visit ${t.blue.underline(L(o,o))}`),console.log("2. After the page is being redirected (it should show something like OAuth 2.0), copy the current URL to your favorite text editor"),console.log('3. Grab the access_token part, take only the part between "access_token=" and the next "&" symbol (without quotes).'),console.log("4. Copy it and paste here, then press Enter.");const{access_token:r}=await l({type:"text",name:"access_token",message:"access_token"});r&&await I(R,{...e,access_token:r})}(e))}),D=new e("list").description("list directory contents").argument("[path]","path","/").alias("ll").option("-t --token <token>","access token","").action(async(e,o,{pcs:r})=>{try{const o=x({}),{list:n}=await r.list(e);n.map(e=>{const{server_mtime:r,size:n,server_filename:s,isdir:a}=e,i=1===a?t.blueBright(s):s;return o.div({text:t.gray(S.unix(r).format("YYYY/MM/DD HH:mm:ss")),width:22,padding:[0,0,0,0]},{text:$(n),width:15,align:"right",padding:[0,2,0,0]},{text:i,padding:[0,0,0,0]}),e}),console.log(o.toString())}catch(e){const{response:{data:o}}=e;console.error(t.red(`error code ${o.error_code} : ${o.error_msg}`))}}),H=new e("meta").description("get path meta").argument("[path]","meta path","/").option("-t --token [token]","access token").action(async(e,o,{pcs:r})=>{try{const{list:t}=await r.meta(e);console.log(t[0])}catch(e){const{response:{data:o}}=e;console.log(t.red(`error code ${o.error_code}: ${o.error_msg}`))}}),Y=new e("quota").description("check your pcs status").option("-t --token [token]","access token").action(async(e,{pcs:o})=>{try{const{quota:e,used:r}=await o.quota();new z(":bar :used/:quota :percent",{complete:"█",incomplete:t.green("░"),width:30,total:e}).tick(r,{used:$(r),quota:$(e)}),console.log("")}catch(e){const{response:{data:o}}=e;console.log(t.red`error code ${o.error_code} : ${o.error_msg}`)}}),j=new e("fetch").description("offline download").argument("[url]","url",y).argument("[remote]","remote path",".").option("-t --token <token>","access token","").action(async(e,o,r,{pcs:n})=>{try{await n.fetch(e,o)}catch(e){const{response:{data:o}}=e;console.log(t.red(`error code ${o.error_code} : ${o.error_msg}`))}}),J=new e("delete").alias("rm").description("delete remote file").argument("<remote>","remote path").option("-t --token <token>","access token","").action(async(e,o,{pcs:r})=>{if(r.resolve(e)===r.resolve("/")){console.log(t.redBright("You are about to delete the root directory of the application, which will lose all data"));const{confirm:e}=await l({type:"confirm",name:"confirm",message:"Are you sure you want to continue?",initial:!1});if(!e)return}try{await r.delete(e)}catch(e){const{response:{data:o}}=e;console.log(t.red(`error code ${o.error_code} : ${o.error_msg}`))}}),M=new e("upload").description("upload local file").argument("[pattern]","glob pattern","*").argument("[remote]","remote path",y).option("-t --token [token]","access token").option("-b --bytes <number>","split upload bytes size",Number,1073741824).option("--thread","Thread").action(async(e,o,r,{pcs:s})=>{const a=await C(e,{nodir:!0}),{bytes:i}=r,c=w(m.tmpdir(),"pcs-cli");try{for await(const e of a){const r=n(e);if(Number.isInteger(i)&&r.size>i){const t=await F(e,i,c),r=[];for(const e of t){const{md5:t}=await s.upload(e,"","overwrite","tmpfile");r.push(t)}const n={block_list:r};await s.createSuperFile(w(o,e),n)}console.log(`${t.blueBright("==>")} Uploading ${e}`),await s.upload(e,w(o,e))}}catch(e){const{response:{data:t}}=e;console.log(`error code ${t.error_code} : ${t.error_msg}`)}}),W=new e("refresh").description("refresh token").option("-k, --key <string>","app key","").option("-s, --secret <string>","app secret","").option("-t --refresh-token <refresh token>","refresh token","").action(async e=>{const o=new U({client_id:e.key,client_secret:e.secret});try{const r=await o.refreshToken(e.refreshToken);await I(R,{...e,...r}),console.log(t.green("Successfully refreshed token"))}catch(e){const{response:{data:o}}=e;console.log(t.red(`OAuth error ${o.error} : ${o.error_description}`))}}),G=new e("download").alias("dl").description("download remote file").argument("[remote]","remote path",O).argument("[local]","local path",".").option("-t --token <token>","access token","").action(async(e,o,r,{pcs:n})=>{try{const{list:r}=await n.list(e);r.reduce(async(e,r)=>{await e;const s=T(o,n.normalize(r.path));return 1===r.isdir?Promise.resolve():(console.log(`${t.blueBright("==>")} Downloading ${t.green(s)}`),n.download(r.path,s))},Promise.resolve())}catch(e){const{response:{data:o}}=e;console.log(t.red(`error code ${o.error_code||o.statusCode} : ${o.error_msg||o.statusMessage}`))}}),K=class{constructor(e,t){this.name=e,this.token=t,this.axios=h.create({timeout:1e4,baseURL:"https://pcs.baidu.com/rest/2.0"})}axios;async quota(){const{data:e}=await this.axios.get("/pcs/quota",{params:{method:"info",access_token:this.token}});return e}async meta(e){const{data:t}=await this.axios.get("/pcs/file",{params:{method:"meta",access_token:this.token,path:this.resolve(e)}});return t}async list(e){const{data:t}=await this.axios.get("/pcs/file",{params:{method:"list",access_token:this.token,path:this.resolve(e)}});return t}async delete(e){const{data:t}=await h.get("/pcs/file",{params:{method:"delete",access_token:this.token,path:this.resolve(e)}});return t}async fetch(e,t){const{data:o}=await h.get("/pcs/services/cloud_dl",{params:{method:"add_task",access_token:this.token,save_path:this.resolve(t),source_url:e}});return o}async download(e,t){o.mkdirSync(_(t),{recursive:!0});const r=o.createWriteStream(t),{data:n,headers:s}=await this.axios.get("/pcs/file",{params:{method:"download",access_token:this.token,path:this.resolve(e)},responseType:"stream"}),a=s["content-length"],i=new z(" downloading [:bar] :rate/bps :percent :etas",{complete:"=",incomplete:" ",width:40,total:parseInt(a,10)});return n.on("data",e=>i.tick(e.length)),n.pipe(r),new Promise((e,t)=>{r.on("finish",e),r.on("error",t)})}async upload(e,t,r="overwrite",n){let s=`/rest/2.0/pcs/file?method=upload&access_token=${this.token}&path=${encodeURIComponent(this.resolve(t))}&ondup=${r}`;n&&(s=`${s}&type=${n}`);const a=o.statSync(e),i=Math.random().toString(16),c=`--${i}\r\nContent-Type: text/plain\r\nContent-Disposition: form-data; name="file"; filename="${t}"\r\n\r\n`,p=`\r\n--${i}--`,d=Buffer.byteLength(c)+Buffer.byteLength(p)+a.size,l=new z("[:bar] :rate/bps :percent :etas",{complete:"=",incomplete:" ",width:40,total:a.size});return new Promise((t,r)=>{const n=E.request({hostname:"pcs.baidu.com",method:"POST",path:s},e=>{e.on("data",e=>{t(JSON.parse(e))}),e.on("end",()=>{}),e.on("error",e=>{r(e)})});n.setHeader("Content-Type",`multipart/form-data; boundary=${i}`),n.setHeader("Content-Length",d),n.write(c);const a=o.createReadStream(e);a.pipe(n,{end:!1}),a.on("end",()=>{n.end(p)}),a.on("data",e=>{l.tick(e.length)})})}async createSuperFile(e,t){const{data:o}=await h.get("/pcs/file",{params:{method:"createsuperfile",access_token:this.token,path:this.resolve(e),param:JSON.stringify(t)}});return o}get rootdir(){return w("/apps",this.name)}resolve(e){return w(this.rootdir,e)}normalize(e){return b(this.rootdir,e)}},Q=new e("pcs-cli").description(`${t.bold("Baidu Personal Cloud Storage Scaffold")}.\n\nYou can get app key by visit ${t.blue.underline(L(N,N))}.\nIf you have already created an app, you can visit ${t.blue.underline(L(q,q))} and get it in your app's info.`).version("1.6.1").hook("preAction",async(e,o)=>{const r=o.name();if("init"!==r)try{const e=await async function(e){try{return JSON.parse(await p.readFile(e,"utf8"))}catch(t){if(t instanceof Error&&"code"in t&&"ENOENT"===t.code)throw new Error(`文件不存在: ${e}`);if(t instanceof SyntaxError)throw new Error(`JSON 格式错误: ${t.message}`);throw t}}(R);"refresh"===r?(o.setOptionValue("key",o.getOptionValue("key")||e.key),o.setOptionValue("secret",o.getOptionValue("secret")||e.secret),o.setOptionValue("refreshToken",o.getOptionValue("refreshToken")||e.refresh_token)):o.pcs=new K(o.getOptionValue("name")||e.name,o.getOptionValue("token")||e.access_token)}catch(e){throw new Error(t.redBright('Please use the "pcs init" command to initialize'))}});Q.addCommand(V).addCommand(D).addCommand(H).addCommand(Y).addCommand(j).addCommand(J).addCommand(M).addCommand(W).addCommand(G),Q.exitOverride();try{await Q.parseAsync(process.argv)}catch(e){}
|
|
2
|
+
import e from"chalk";import{Command as t}from"commander";import o,{existsSync as r,statSync as n,mkdirSync as s,createReadStream as a,createWriteStream as i,mkdtempSync as c,promises as p}from"fs";import l from"open";import d from"prompts";import m,{isAxiosError as h}from"axios";import"crypto";import u,{homedir as g,tmpdir as f}from"os";import y,{sep as w,join as k,basename as _,dirname as b,relative as v}from"path";import{pipeline as $}from"stream/promises";import x from"bytes";import S from"cliui";import z from"dayjs";import T from"progress";import{glob as C}from"glob";import{sep as O,join as B}from"path/posix";import E from"https";var A=class{constructor(e){this.config=e,this.config={scope:"basic,netdisk",...e},this.axios=m.create({timeout:1e4})}axios;async refreshToken(e){const{client_secret:t,client_id:o}=this.config,{data:r}=await m.get("https://openapi.baidu.com/oauth/2.0/token",{params:{grant_type:"refresh_token",client_id:o,client_secret:t,refresh_token:e}});return r}},L=class extends A{constructor(e){super({...e,response_type:"device_code"})}async getDeviceCode(){const{data:e}=await this.axios.get("https://openapi.baidu.com/oauth/2.0/device/code",{params:{response_type:"device_code",client_id:this.config.client_id,scope:this.config.scope}});return e}getAuthorizeURL(e){return`${e.verification_url}?code=${e.user_code}`}async authorize(e){const{client_id:t,client_secret:o,scope:r}=this.config,{data:n}=await this.axios.get("https://openapi.baidu.com/oauth/2.0/token",{params:{grant_type:"device_token",code:e,client_id:t,client_secret:o,scope:r}});return n}},U=class extends A{constructor(e){super({...e,response_type:"token",redirect_uri:"oob"})}getAuthorizeURL(){const{client_secret:e,...t}=this.config;return`https://openapi.baidu.com/oauth/2.0/authorize?${new URLSearchParams(t)}`}async authorize(e){let t;return t=e.startsWith("http")?this.parseTokenFromUrl(e):e.includes("access_token=")?this.parseTokenFromHash(e):{access_token:e,expires_in:2592e3,scope:this.config.scope},Promise.resolve(t)}parseTokenFromUrl(e){const t=new URL(e);return this.parseTokenFromHash(t.hash.substring(1))}parseTokenFromHash(e){const t=new URLSearchParams(e),o=t.get("access_token")??"",r=t.get("expires_in"),n=t.get("error");if(Boolean(n))throw new Error(`OAuth error: ${n}`);if(!Boolean(o))throw new Error("Access token not found");return{access_token:o,expires_in:parseInt(r??"2592000",10),scope:t.get("scope")??this.config.scope,session_key:t.get("session_key")??void 0,session_secret:t.get("session_secret")??void 0}}};function F(e,t){return`]8;;${t}${e}]8;;`}async function R(e,t,o=c(k(f(),"splitFile-"))){if(!Boolean(e)||"string"!=typeof e)throw new TypeError("path must be a non-empty string");if(!Number.isInteger(t)||t<=0)throw new TypeError("chunkSize must be a positive integer");if(!Boolean(o)||"string"!=typeof o)throw new TypeError("outputDir must be a non-empty string");const r=[],{size:p}=n(e),l=_(e);s(o,{recursive:!0});for(let n=0;n<p;n+=t){const s=Math.min(n+t-1,p-1),c=k(o,`${l}.${r.length+1}`),d=a(e,{start:n,end:s}),m=i(c);await $(d,m),r.push(c)}return r}var N=y.join(g(),".pcs"),P=y.join(N,"config.json"),q="https://pan.baidu.com/union/console/createapp",I="https://pan.baidu.com/union/console/applist";async function V(e,t){try{const o=y.dirname(e);await p.mkdir(o,{recursive:!0}),await p.writeFile(e,JSON.stringify(t,null,2),"utf8")}catch(e){if(e instanceof Error)throw new Error(`写入文件失败: ${e.message}`,{cause:e})}}var D=new t("init").description("initialize baidu pcs").option("-n, --name <string>","app name","").option("-k, --key <string>","app key","").option("-s, --secret <string>","app secret","").action(async t=>{if(r(P)){console.log(e.redBright("Baidu pcs initialization will be begin. If you have already configured before, your old settings will be overwritten."));const{confirm:t}=await d({type:"confirm",name:"confirm",message:"Can you confirm?",initial:!1});if(!t)return}const o=[];["name","key","secret"].forEach(e=>{""===t[e]&&o.push({type:"text",name:e,message:`Please enter baidu app ${e}`})}),Object.assign(t,await d(o)),""!==t.key&&(""===t.secret?await async function(t){const o=new U({client_id:t.key}).getAuthorizeURL();await l(o),console.log(e.hex("#FFA500")("You'll have to grab the access_token generated by Baidu.")),console.log(""),console.log(`1. Visit ${e.blue.underline(F(o,o))}`),console.log("2. After the page is being redirected (it should show something like OAuth 2.0), copy the current URL to your favorite text editor"),console.log('3. Grab the access_token part, take only the part between "access_token=" and the next "&" symbol (without quotes).'),console.log("4. Copy it and paste here, then press Enter.");const{accessToken:r}=await d({type:"text",name:"accessToken",message:"access token"});""!==r&&await V(P,{...t,access_token:r})}(t):await async function(t){const o=new L({client_id:t.key,client_secret:t.secret}),r=await o.getDeviceCode(),n=o.getAuthorizeURL(r);console.log("Launch your favorite web browser and visit: "),console.log(F(n,n)),console.log(`Input ${e.bold.red(r.user_code)} as the user code if asked.`),console.log("After granting access to the application, come back here and "),await l(n);const{confirm:s}=await d({type:"confirm",name:"confirm",message:"Press Enter to continue",initial:!0});if(s){const n=await o.authorize(r.device_code);console.log(e.green("Successfully initialized")),console.log(`access_token: ${e.yellowBright(n.access_token)}`),console.log(`refresh_token: ${e.yellowBright(n.refresh_token)}`),await V(P,{...t,...n})}}(t))}),H=new t("list").description("list directory contents").argument("[path]","path","/").alias("ll").option("-t --token <token>","access token").action(async(t,o,{pcs:r})=>{try{const o=S({}),{list:n}=await r.list(t);n.map(t=>{const{server_mtime:r,size:n,server_filename:s,isdir:a}=t,i=1===a?e.blueBright(s):s;return o.div({text:e.gray(z.unix(r).format("YYYY/MM/DD HH:mm:ss")),width:22,padding:[0,0,0,0]},{text:x(n)??"",width:15,align:"right",padding:[0,2,0,0]},{text:i,padding:[0,0,0,0]}),t}),console.log(o.toString())}catch(t){if(h(t)){const{response:{data:o}={}}=t;console.log(e.red(`error code ${o?.error_code} : ${o?.error_msg}`))}}}),Y=new t("meta").description("get path meta").argument("[path]","meta path","/").option("-t --token [token]","access token").action(async(t,o,{pcs:r})=>{try{const{list:e}=await r.meta(t);console.log(e[0])}catch(t){if(h(t)){const{response:{data:o}={}}=t;console.log(e.red(`error code ${o?.error_code} : ${o?.error_msg}`))}}}),j=new t("quota").description("check your pcs status").option("-t --token [token]","access token").action(async(t,{pcs:o})=>{try{const{quota:t,used:r}=await o.quota();new T(":bar :used/:quota :percent",{complete:"█",incomplete:e.green("░"),width:30,total:t}).tick(r,{used:x(r),quota:x(t)}),console.log("")}catch(t){if(h(t)){const{response:{data:o}={}}=t;console.log(e.red(`error code ${o?.error_code} : ${o?.error_msg}`))}}}),J=new t("fetch").description("offline download").argument("[url]","url",w).argument("[remote]","remote path",".").option("-t --token <token>","access token").action(async(t,o,r,{pcs:n})=>{try{await n.fetch(t,o)}catch(t){if(h(t)){const{response:{data:o}={}}=t;console.log(e.red(`error code ${o?.error_code} : ${o?.error_msg}`))}}}),M=new t("delete").alias("rm").description("delete remote file").argument("<remote>","remote path").option("-t --token <token>","access token").action(async(t,o,{pcs:r})=>{if(r.resolve(t)===r.resolve("/")){console.log(e.redBright("You are about to delete the root directory of the application, which will lose all data"));const{confirm:t}=await d({type:"confirm",name:"confirm",message:"Are you sure you want to continue?",initial:!1});if(!t)return}try{await r.delete(t)}catch(t){if(h(t)){const{response:{data:o}={}}=t;console.log(e.red(`error code ${o?.error_code} : ${o?.error_msg}`))}}}),W=new t("upload").description("upload local file").argument("[pattern]","glob pattern","*").argument("[remote]","remote path",w).option("-t --token [token]","access token").option("-b --bytes <number>","split upload bytes size",Number,1073741824).option("--thread","Thread").action(async(t,o,r,{pcs:s})=>{const a=await C(t,{nodir:!0}),{bytes:i}=r,c=k(u.tmpdir(),"pcs-cli");try{for(const t of a){const r=n(t);if(Number.isInteger(i)&&r.size>i){const e=await R(t,i,c),r=[];for(const t of e){const{md5:e}=await s.upload(t,"","overwrite","tmpfile");r.push(e)}const n={block_list:r};await s.createSuperFile(k(o,t),n)}console.log(`${e.blueBright("==>")} Uploading ${t}`),await s.upload(t,k(o,t))}}catch(t){if(h(t)){const{response:{data:o}={}}=t;console.log(e.red(`error code ${o?.error_code} : ${o?.error_msg}`))}}}),G=new t("refresh").description("refresh token").option("-k, --key <string>","app key","").option("-s, --secret <string>","app secret","").option("-t --refresh-token <refresh token>","refresh token").action(async t=>{const o=new L({client_id:t.key,client_secret:t.secret});try{const r=await o.refreshToken(t.refreshToken??"");await V(P,{...t,...r}),console.log(e.green("Successfully refreshed token"))}catch(t){if(h(t)){const{response:{data:o}={}}=t;console.log(e.red(`error code ${o?.error_code} : ${o?.error_msg}`))}}}),K=new t("download").alias("dl").description("download remote file").argument("[remote]","remote path",O).argument("[local]","local path",".").option("-t --token <token>","access token").action(async(t,o,r,{pcs:n})=>{try{const{list:r}=await n.list(t);for(const t of r){const r=B(o,n.normalize(t.path));if(1===t.isdir)return;console.log(`${e.blueBright("==>")} Downloading ${e.green(r)}`),await n.download(t.path,r)}}catch(t){if(h(t)){const{response:{data:o}={}}=t;console.log(e.red(`error code ${o?.error_code??o?.statusCode} : ${o?.error_msg??o?.statusMessage}`))}}}),Q=class{constructor(e,t){this.name=e,this.token=t,this.axios=m.create({timeout:1e4,baseURL:"https://pcs.baidu.com/rest/2.0"})}get rootdir(){return k("/apps",this.name)}axios;async userInfo(){const{data:e}=await m.get("https://pan.baidu.com/rest/2.0/xpan/nas",{params:{method:"uinfo",vip_version:2,access_token:this.token}});return e}async quota(){const{data:e}=await this.axios.get("/pcs/quota",{params:{method:"info",access_token:this.token}});return e}async meta(e){const{data:t}=await this.axios.get("/pcs/file",{params:{method:"meta",access_token:this.token,path:this.resolve(e)}});return t}async list(e){const{data:t}=await this.axios.get("/pcs/file",{params:{method:"list",access_token:this.token,path:this.resolve(e)}});return t}async delete(e){const{data:t}=await m.get("/pcs/file",{params:{method:"delete",access_token:this.token,path:this.resolve(e)}});return t}async fetch(e,t){const{data:o}=await m.get("/pcs/services/cloud_dl",{params:{method:"add_task",access_token:this.token,save_path:this.resolve(t),source_url:e}});return o}async download(e,t){o.mkdirSync(b(t),{recursive:!0});const r=o.createWriteStream(t),{data:n,headers:s}=await this.axios.get("/pcs/file",{params:{method:"download",access_token:this.token,path:this.resolve(e)},responseType:"stream"}),a=s["content-length"],i=new T(" downloading [:bar] :rate/bps :percent :etas",{complete:"=",incomplete:" ",width:40,total:parseInt(a,10)});return n.on("data",e=>{i.tick(e.length)}),n.pipe(r),new Promise((e,t)=>{r.on("finish",e),r.on("error",t)})}async upload(e,t,r="overwrite",n){let s=`/rest/2.0/pcs/file?method=upload&access_token=${this.token}&path=${encodeURIComponent(this.resolve(t))}&ondup=${r}`;Boolean(n)&&(s=`${s}&type=${n}`);const a=o.statSync(e),i=Math.random().toString(16),c=`--${i}\r\nContent-Type: text/plain\r\nContent-Disposition: form-data; name="file"; filename="${t}"\r\n\r\n`,p=`\r\n--${i}--`,l=Buffer.byteLength(c)+Buffer.byteLength(p)+a.size,d=new T("[:bar] :rate/bps :percent :etas",{complete:"=",incomplete:" ",width:40,total:a.size});return new Promise((t,r)=>{const n=E.request({hostname:"pcs.baidu.com",method:"POST",path:s},e=>{e.on("data",e=>{t(JSON.parse(e))}),e.on("end",()=>{}),e.on("error",e=>{r(e)})});n.setHeader("Content-Type",`multipart/form-data; boundary=${i}`),n.setHeader("Content-Length",l),n.write(c);const a=o.createReadStream(e);a.pipe(n,{end:!1}),a.on("end",()=>{n.end(p)}),a.on("data",e=>{d.tick(e.length)})})}async createSuperFile(e,t){const{data:o}=await m.get("/pcs/file",{params:{method:"createsuperfile",access_token:this.token,path:this.resolve(e),param:JSON.stringify(t)}});return o}resolve(e){return k(this.rootdir,e)}normalize(e){return v(this.rootdir,e)}},X=new t("pcs-cli").description(`${e.bold("Baidu Personal Cloud Storage Scaffold")}.\n\nYou can get app key by visit ${e.blue.underline(F(q,q))}.\nIf you have already created an app, you can visit ${e.blue.underline(F(I,I))} and get it in your app's info.`).version("1.7.1").hook("preAction",async(t,o)=>{const r=o.name();if("init"!==r)try{const e=await async function(e){try{const t=await p.readFile(e,"utf8");return JSON.parse(t)}catch(t){if(t instanceof Error&&"ENOENT"===t.code)throw new Error(`文件不存在: ${e}`,{cause:t});if(t instanceof SyntaxError)throw new Error(`JSON 格式错误: ${t.message}`,{cause:t});throw t}}(P);"refresh"===r?(o.setOptionValue("key",o.getOptionValue("key")??e.key),o.setOptionValue("secret",o.getOptionValue("secret")??e.secret),o.setOptionValue("refreshToken",o.getOptionValue("refreshToken")??e.refresh_token)):o.pcs=new Q(o.getOptionValue("name")??e.name,o.getOptionValue("token")??e.access_token)}catch(t){throw new Error(e.redBright('Please use the "pcs init" command to initialize'),{cause:t})}});X.addCommand(D).addCommand(H).addCommand(Y).addCommand(j).addCommand(J).addCommand(M).addCommand(W).addCommand(G).addCommand(K),X.exitOverride();try{await X.parseAsync(process.argv)}catch(e){}
|
package/package.json
CHANGED
|
@@ -1,46 +1,54 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pcs-cli",
|
|
3
|
-
"description": "Baidu Personal Cloud Storage's Command Line Interface.",
|
|
4
|
-
"version": "1.6.1",
|
|
5
3
|
"type": "module",
|
|
6
|
-
"
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"build": "tsup",
|
|
12
|
-
"test": "vitest",
|
|
13
|
-
"typecheck": "tsc --noEmit",
|
|
14
|
-
"prepublishOnly": "tsx scripts/prepublish.ts"
|
|
4
|
+
"version": "1.7.1",
|
|
5
|
+
"packageManager": "pnpm@10.30.2",
|
|
6
|
+
"description": "Baidu Personal Cloud Storage's Command Line Interface.",
|
|
7
|
+
"author": {
|
|
8
|
+
"name": "sobird"
|
|
15
9
|
},
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"licenses": [
|
|
12
|
+
{
|
|
13
|
+
"type": "MIT",
|
|
14
|
+
"url": "https://github.com/sobird/pcs-cli/blob/master/LICENSE"
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"readmeFilename": "README.md",
|
|
18
|
+
"homepage": "https://github.com/sobird/pcs-cli",
|
|
16
19
|
"repository": {
|
|
17
20
|
"type": "git",
|
|
18
21
|
"url": "git+https://github.com/sobird/pcs-cli.git"
|
|
19
22
|
},
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/sobird/pcs-cli/issues"
|
|
25
|
+
},
|
|
20
26
|
"keywords": [
|
|
21
27
|
"pcs-cli",
|
|
22
28
|
"pcs",
|
|
23
29
|
"baidu",
|
|
24
30
|
"baidu-pcs"
|
|
25
31
|
],
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
},
|
|
29
|
-
"author": {
|
|
30
|
-
"name": "sobird"
|
|
32
|
+
"bin": {
|
|
33
|
+
"pcs": "dist/cli.js"
|
|
31
34
|
},
|
|
32
|
-
"
|
|
33
|
-
|
|
34
|
-
{
|
|
35
|
-
"type": "MIT",
|
|
36
|
-
"url": "https://github.com/sobird/pcs-cli/blob/master/LICENSE"
|
|
37
|
-
}
|
|
35
|
+
"files": [
|
|
36
|
+
"dist"
|
|
38
37
|
],
|
|
39
|
-
"
|
|
40
|
-
|
|
38
|
+
"engines": {
|
|
39
|
+
"node": ">=18"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"dev": "tsx bin/dev.ts",
|
|
43
|
+
"build": "tsup",
|
|
44
|
+
"test": "vitest",
|
|
45
|
+
"lint": "eslint",
|
|
46
|
+
"lint:fix": "eslint --fix",
|
|
47
|
+
"typecheck": "tsc --noEmit",
|
|
48
|
+
"prepublishOnly": "tsx scripts/prepublish.ts"
|
|
49
|
+
},
|
|
41
50
|
"dependencies": {
|
|
42
|
-
"
|
|
43
|
-
"axios": "^1.13.2",
|
|
51
|
+
"axios": "^1.13.6",
|
|
44
52
|
"bytes": "^3.1.2",
|
|
45
53
|
"chalk": "^5.6.2",
|
|
46
54
|
"cliui": "^9.0.1",
|
|
@@ -52,22 +60,21 @@
|
|
|
52
60
|
"prompts": "^2.4.2"
|
|
53
61
|
},
|
|
54
62
|
"devDependencies": {
|
|
55
|
-
"@
|
|
56
|
-
"@commitlint/
|
|
57
|
-
"@
|
|
58
|
-
"@types/
|
|
63
|
+
"@commander-js/extra-typings": "^14.0.0",
|
|
64
|
+
"@commitlint/cli": "^20.4.3",
|
|
65
|
+
"@commitlint/config-conventional": "^20.4.3",
|
|
66
|
+
"@types/bytes": "^3.1.5",
|
|
67
|
+
"@types/node": "^25.3.3",
|
|
59
68
|
"@types/progress": "^2.0.7",
|
|
60
|
-
"@types/prompts": "^2.4.
|
|
61
|
-
"eslint
|
|
62
|
-
"
|
|
69
|
+
"@types/prompts": "^2.4.9",
|
|
70
|
+
"eslint": "^10.0.2",
|
|
71
|
+
"eslint-config-sobird": "^1.0.5",
|
|
72
|
+
"husky": "^9.1.7",
|
|
63
73
|
"tsup": "^8.5.1",
|
|
64
74
|
"tsx": "^4.21.0",
|
|
65
75
|
"typescript": "^5.9.3",
|
|
66
|
-
"vitest": "^4.0.
|
|
76
|
+
"vitest": "^4.0.18"
|
|
67
77
|
},
|
|
68
|
-
"files": [
|
|
69
|
-
"dist"
|
|
70
|
-
],
|
|
71
78
|
"publishConfig": {
|
|
72
79
|
"registry": "https://registry.npmjs.org/"
|
|
73
80
|
}
|