@litingwei621/test-deveco-cli-common 1.3.7 → 1.3.10-Test.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 +58 -4
- package/dist/cli.js +156 -140
- package/dist/internal/telemetry-upload-background.js +5 -5
- package/package.json +4 -2
- package/skills/deveco-cli/SKILL.md +23 -4
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import Vt from"path";import*as
|
|
2
|
-
`;if(this.mode==="file"&&this.currentLogFile){if(this.fd===null&&this.openLogFile(),this.fd!==null)try{let p=Buffer.from(l);a.writeSync(this.fd,p),this.checkRotation(p.byteLength)}catch{this.closeLogFile(),this.openLogFile()}}else this.mode==="console"&&process.stderr.write(l)}debug(t,...e){this.write("debug",t,...e)}info(t,...e){this.write("info",t,...e)}warn(t,...e){this.write("warn",t,...e)}error(t,...e){this.write("error",t,...e)}dispose(){this.closeLogFile()}flush(){if(!(this.mode!=="file"||this.fd===null))try{a.fsyncSync(this.fd)}catch{}}getLogFilePath(){return this.currentLogFile}getLogDirectory(){return this.logDir}},E=null;function Ot(n=!1){E&&E.dispose(),E=new j(n)}function F(){return E||Ot(!1),E}var c={debug:(n,...t)=>F().debug(n,...t),info:(n,...t)=>F().info(n,...t),warn:(n,...t)=>F().warn(n,...t),error:(n,...t)=>F().error(n,...t)};import at from"fs";import Pt from"path";var k=300*1e3,O=3600*1e3,lt=10080*60*1e3,v=8*1024*1024,ct=3600*1e3,st=".pending",Rt=/\.reclaim-\d+-\d+$/;function P(){let n=process.env.DEVECO_CLI_DISABLE_TELEMETRY;return n==="1"||n==="true"}var It="upload-state.json";function ut(n){return Pt.join(n,It)}function Nt(){return{firstEventAt:null,lastUploadAt:null,lastRetryAt:null}}function U(n){try{let t=at.readFileSync(ut(n),"utf8"),e=JSON.parse(t);return{firstEventAt:typeof e.firstEventAt=="number"?e.firstEventAt:null,lastUploadAt:typeof e.lastUploadAt=="number"?e.lastUploadAt:null,lastRetryAt:typeof e.lastRetryAt=="number"?e.lastRetryAt:null}}catch{return Nt()}}function V(n,t){try{at.writeFileSync(ut(n),JSON.stringify(t),"utf8")}catch{}}function pt(n){let t=U(n);t.firstEventAt===null&&(t.firstEventAt=Date.now(),V(n,t))}function ft(n){let t=U(n);t.lastUploadAt=Date.now(),t.firstEventAt=null,V(n,t)}function dt(n){let t=U(n);t.lastRetryAt=Date.now(),V(n,t)}function mt(n){let t=n.match(/^telemetry-(\d{4})-(\d{2})-(\d{2})\.txt$/);if(!t)return null;let e=Date.parse(`${t[1]}-${t[2]}-${t[3]}T00:00:00`);return Number.isFinite(e)?e:null}function z(n){if(!n.startsWith("telemetry-"))return null;let t=Rt.exec(n),e=t?n.slice(0,t.index):n;if(!e.endsWith(st))return null;let r=e.slice(0,-st.length);return r.endsWith(".txt")?r:null}function gt(n){return z(n)!==null}import $t from"crypto";import R from"fs";import Mt from"path";var Bt="install-id";function jt(n){return Mt.join(n,Bt)}function ht(n){try{let t=R.readFileSync(n,"utf8").trim();return Ut(t)?t:null}catch{return null}}function Ut(n){return/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(n)}function yt(n){let t=jt(n),e=ht(t);if(e)return e;let r=$t.randomUUID();try{R.mkdirSync(n,{recursive:!0});try{R.writeFileSync(t,r,{flag:"wx"})}catch{let i=ht(t);if(i)return i;R.writeFileSync(t,r)}}catch{}return r}var
|
|
1
|
+
import Vt from"path";import*as b from"fs";import*as Y from"path";import{homedir as St}from"os";import*as H from"fs";import*as g from"path";function W(n){let t=g.resolve(n),e=[],r=t;for(;;)try{let i=H.realpathSync(r);return e.length===0?i:g.join(i,...e.reverse())}catch(i){if(i.code!=="ENOENT")throw i;let o=g.dirname(r);if(o===r)return t;e.push(g.basename(r)),r=o}}function N(n){let t=n.trim();return(t.startsWith('"')&&t.endsWith('"')||t.startsWith("'")&&t.endsWith("'"))&&(t=t.slice(1,-1).trim()),t}function K(n){let t=N(n);if(!t)throw new Error("Path must not be empty.");return W(t)}var Et="deveco-cli",J,$=class extends Error{constructor(t){super(t),this.name="CliDataDirError"}};function xt(){let n=process.env.DEVECO_CLI_DATA_DIR,e=(n===void 0||n===""?"":N(n))||Y.join(St(),".local","share",Et);try{return K(e)}catch(r){throw new $(r instanceof Error?r.message:String(r))}}function X(){if(J!==void 0)return J;let n=xt();try{if(b.existsSync(n))return b.realpathSync(n)}catch{}return n}import vt from"crypto";import u from"fs";import I from"os";import m from"path";import wt from"https";var G={devecoApiTraceUpload:"https://cn.devecostudio.huawei.com/codeGenie/cli/trace/upload"};var D=class n{static ENDPOINT=G.devecoApiTraceUpload;static SENDER="deveco-cli";async upload(t,e){let r={"Content-Type":"application/json",sender:n.SENDER,deviceid:e.replace(/-/g,""),"Content-Length":String(Buffer.byteLength(t,"utf8"))};try{let{body:i}=await this.httpPost(n.ENDPOINT,r,Buffer.from(t,"utf8"),6e4);return n.isSuccess(i)}catch{return!1}}static isSuccess(t){try{let e=JSON.parse(t),r=e?.code??e?.errorCode;return String(r)==="200"}catch{return!1}}async httpPost(t,e,r,i){let o=new URL(t);return new Promise((s,l)=>{let p=wt.request({protocol:o.protocol,hostname:o.hostname,port:o.port||(o.protocol==="https:"?443:80),path:o.pathname+o.search,method:"POST",headers:e,timeout:i},h=>{let w=[];h.on("data",bt=>w.push(bt)),h.on("end",()=>{s({code:h.statusCode??0,body:Buffer.concat(w).toString("utf8")})})});p.on("timeout",()=>{p.destroy(new Error("request timeout"))}),p.on("error",l),p.write(r),p.end()})}};var q={identity:n=>n,camelCase:n=>n,snakeCase:n=>n.replace(/([a-z0-9])([A-Z])/g,"$1_$2").replace(/([A-Z]+)([A-Z][a-z])/g,"$1_$2").toLowerCase()},M=class{constructor(t={}){this.config=t}serialize(t){let e=this.transform(t);return this.config.pretty?JSON.stringify(e,null,2):JSON.stringify(e)}toObject(t){return this.transform(t)}transform(t){if(t==null)return t;if(Array.isArray(t))return t.map(o=>this.transform(o));if(typeof t!="object")return t;let e=this.config.namingStrategy??q.identity,r=this.config.fieldNames??{},i={};for(let[o,s]of Object.entries(t)){let l=r[o]??e(o);i[l]=this.transform(s)}return i}},Z=new M({namingStrategy:q.snakeCase,fieldNames:{}});import S from"crypto";import C from"fs";import Dt from"path";var _="aes-256-gcm",Ct=12,_t="deveco-cli-trace-file",Lt="trace-file-secret",tt=32;function Q(n){try{let t=C.readFileSync(n,"utf8").trim();return t&&Buffer.from(t,"base64").length===tt?t:null}catch{return null}}function et(n){let t=Dt.join(n,Lt),e=Q(t);if(e)return B(e);let r=S.randomBytes(tt).toString("base64");try{C.mkdirSync(n,{recursive:!0});try{C.writeFileSync(t,r,{flag:"wx"})}catch{let i=Q(t);if(i)return B(i);C.writeFileSync(t,r)}}catch{}return B(r)}function B(n){return S.createHash("sha256").update(_t).update(n).digest()}function rt(n,t){let e=S.randomBytes(Ct),r=S.createCipheriv(_,t,e),i=Buffer.concat([r.update(n,"utf8"),r.final()]),o={version:1,algorithm:_,ciphertext:i.toString("base64"),iv:e.toString("base64"),authTag:r.getAuthTag().toString("base64")};return JSON.stringify(o)}function nt(n,t){let e;try{e=JSON.parse(n)}catch{return null}if(!Ft(e))return n;try{let r=S.createDecipheriv(_,t,Buffer.from(e.iv,"base64"));return r.setAuthTag(Buffer.from(e.authTag,"base64")),Buffer.concat([r.update(Buffer.from(e.ciphertext,"base64")),r.final()]).toString("utf8")}catch{return null}}function Ft(n){if(!n||typeof n!="object")return!1;let t=n;return t.version===1&&t.algorithm===_&&typeof t.ciphertext=="string"&&typeof t.iv=="string"&&typeof t.authTag=="string"}import*as a from"fs";import*as A from"path";import*as L from"os";import*as y from"path";import ge from"json5";var ve=64*1024;function it(){if(process.platform==="win32"){let n=process.env.LOCALAPPDATA??y.join(L.homedir(),"AppData","Local");return y.join(n,"devecocli-mcp-server","logs")}return process.platform==="darwin"?y.join(L.homedir(),"Library","Logs","devecocli-mcp-server"):y.join(L.homedir(),".local","share","devecocli-mcp-server","logs")}var ot="mcp-server.log",At="mcp-server",kt={maxSize:10*1024*1024,maxFiles:4},j=class n{fd=null;logDir=null;currentLogFile=null;mode;rotationOptions;currentFileSize=0;currentDate="";isRotating=!1;minLevel;static LEVEL_ORDER={debug:0,info:1,warn:2,error:3};constructor(t,e){this.rotationOptions={...kt,...e},this.minLevel=t?"debug":"info",t?(this.mode="console",this.logDir=null,this.currentLogFile=null):(this.mode="file",this.logDir=it(),this.currentLogFile=A.join(this.logDir,ot),a.existsSync(this.logDir)||a.mkdirSync(this.logDir,{recursive:!0}),this.cleanupOrphanLogFiles(),this.openLogFile())}getCurrentDateString(){let t=new Date,e=t.getFullYear(),r=String(t.getMonth()+1).padStart(2,"0"),i=String(t.getDate()).padStart(2,"0");return`${e}-${r}-${i}`}getRotatedFileName(t,e){return A.join(this.logDir,`${At}-${t}.log.${e}`)}fileExists(t){try{return a.accessSync(t,a.constants.F_OK),!0}catch{return!1}}cleanupOrphanLogFiles(){if(this.logDir)try{let t=a.readdirSync(this.logDir),e=[];for(let i of t)if(i===ot||/^mcp-server-\d{4}-\d{2}-\d{2}\.log\.\d+$/.test(i)){let o=A.join(this.logDir,i),s=a.statSync(o);e.push({name:i,mtime:s.mtime,path:o})}e.sort((i,o)=>o.mtime.getTime()-i.mtime.getTime());let r=1+this.rotationOptions.maxFiles;for(let i=r;i<e.length;i++)try{a.unlinkSync(e[i].path)}catch{}}catch{}}rotateLog(){if(!(this.isRotating||!this.logDir||!this.currentLogFile)){this.isRotating=!0;try{if(this.closeLogFile(),!this.fileExists(this.currentLogFile)){this.isRotating=!1,this.openLogFile();return}let t=this.getCurrentDateString(),e=this.getRotatedFileName(t,this.rotationOptions.maxFiles);this.fileExists(e)&&a.unlinkSync(e);for(let i=this.rotationOptions.maxFiles-1;i>=1;i--){let o=this.getRotatedFileName(t,i),s=this.getRotatedFileName(t,i+1);this.fileExists(o)&&a.renameSync(o,s)}let r=this.getRotatedFileName(t,1);a.renameSync(this.currentLogFile,r),this.cleanupOrphanLogFiles(),this.openLogFile()}catch{this.openLogFile()}finally{this.isRotating=!1}}}openLogFile(){if(this.currentLogFile){this.currentDate=this.getCurrentDateString();try{if(this.fileExists(this.currentLogFile)){let t=a.statSync(this.currentLogFile);this.currentFileSize=t.size}else this.currentFileSize=0}catch{this.currentFileSize=0}try{this.fd=a.openSync(this.currentLogFile,"a")}catch{this.fd=null}}}closeLogFile(){if(this.fd!==null){try{a.closeSync(this.fd)}catch{}this.fd=null}}checkRotation(t){let e=this.getCurrentDateString();this.currentDate&&this.currentDate!==e&&(this.rotateLog(),this.currentDate=e),this.currentFileSize+=t,this.currentFileSize>=this.rotationOptions.maxSize&&this.rotateLog()}write(t,e,...r){if(this.mode==="silent"||n.LEVEL_ORDER[t]<n.LEVEL_ORDER[this.minLevel])return;let i=r.map(p=>p instanceof Error?`${p.name}: ${p.message}`:typeof p=="object"?JSON.stringify(p,null,2):String(p)).join(" "),o=i?`${e} ${i}`:e,l=`[${new Date().toLocaleString("sv-SE",{timeZone:"Asia/Shanghai",hour12:!1})+"."+String(new Date().getMilliseconds()).padStart(3,"0")}] [MCP/${t.toUpperCase()}] [pid=${process.pid}] ${o}
|
|
2
|
+
`;if(this.mode==="file"&&this.currentLogFile){if(this.fd===null&&this.openLogFile(),this.fd!==null)try{let p=Buffer.from(l);a.writeSync(this.fd,p),this.checkRotation(p.byteLength)}catch{this.closeLogFile(),this.openLogFile()}}else this.mode==="console"&&process.stderr.write(l)}debug(t,...e){this.write("debug",t,...e)}info(t,...e){this.write("info",t,...e)}warn(t,...e){this.write("warn",t,...e)}error(t,...e){this.write("error",t,...e)}dispose(){this.closeLogFile()}flush(){if(!(this.mode!=="file"||this.fd===null))try{a.fsyncSync(this.fd)}catch{}}getLogFilePath(){return this.currentLogFile}getLogDirectory(){return this.logDir}},E=null;function Ot(n=!1){E&&E.dispose(),E=new j(n)}function F(){return E||Ot(!1),E}var c={debug:(n,...t)=>F().debug(n,...t),info:(n,...t)=>F().info(n,...t),warn:(n,...t)=>F().warn(n,...t),error:(n,...t)=>F().error(n,...t)};import at from"fs";import Pt from"path";var k=300*1e3,O=3600*1e3,lt=10080*60*1e3,v=8*1024*1024,ct=3600*1e3,st=".pending",Rt=/\.reclaim-\d+-\d+$/;function P(){let n=process.env.DEVECO_CLI_DISABLE_TELEMETRY;return n==="1"||n==="true"}var It="upload-state.json";function ut(n){return Pt.join(n,It)}function Nt(){return{firstEventAt:null,lastUploadAt:null,lastRetryAt:null}}function U(n){try{let t=at.readFileSync(ut(n),"utf8"),e=JSON.parse(t);return{firstEventAt:typeof e.firstEventAt=="number"?e.firstEventAt:null,lastUploadAt:typeof e.lastUploadAt=="number"?e.lastUploadAt:null,lastRetryAt:typeof e.lastRetryAt=="number"?e.lastRetryAt:null}}catch{return Nt()}}function V(n,t){try{at.writeFileSync(ut(n),JSON.stringify(t),"utf8")}catch{}}function pt(n){let t=U(n);t.firstEventAt===null&&(t.firstEventAt=Date.now(),V(n,t))}function ft(n){let t=U(n);t.lastUploadAt=Date.now(),t.firstEventAt=null,V(n,t)}function dt(n){let t=U(n);t.lastRetryAt=Date.now(),V(n,t)}function mt(n){let t=n.match(/^telemetry-(\d{4})-(\d{2})-(\d{2})\.txt$/);if(!t)return null;let e=Date.parse(`${t[1]}-${t[2]}-${t[3]}T00:00:00`);return Number.isFinite(e)?e:null}function z(n){if(!n.startsWith("telemetry-"))return null;let t=Rt.exec(n),e=t?n.slice(0,t.index):n;if(!e.endsWith(st))return null;let r=e.slice(0,-st.length);return r.endsWith(".txt")?r:null}function gt(n){return z(n)!==null}import $t from"crypto";import R from"fs";import Mt from"path";var Bt="install-id";function jt(n){return Mt.join(n,Bt)}function ht(n){try{let t=R.readFileSync(n,"utf8").trim();return Ut(t)?t:null}catch{return null}}function Ut(n){return/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(n)}function yt(n){let t=jt(n),e=ht(t);if(e)return e;let r=$t.randomUUID();try{R.mkdirSync(n,{recursive:!0});try{R.writeFileSync(t,r,{flag:"wx"})}catch{let i=ht(t);if(i)return i;R.writeFileSync(t,r)}}catch{}return r}var x=class n{static MAX_BATCH=200;static MAX_PAYLOAD_BYTES=Math.floor(1.7*1024*1024);static TRACE_OS_VERSION="1.0";static COUNTRY_CODE="CN";static EVENT_PREFIX="devecocli_";uploader=new D;storageDir="";failedDir="";installId="";disabled=P();traceFileKey=Buffer.alloc(0);sessionId=vt.randomUUID();cliVersion="";devecoStudioVersion=null;sourceType="";cltVersion=null;nodeVersion="";initialTimer=null;intervalTimer=null;retryInitialTimer=null;retryTimer=null;flushChain=Promise.resolve();constructor(){c.debug(`[telemetry] instance created, sessionId=${this.sessionId}`)}init(t){this.storageDir=t,this.failedDir=m.join(t,"failed"),this.cliVersion="1.3.10-Test.1",this.nodeVersion=process.version,this.installId=yt(t).replace(/-/g,""),this.traceFileKey=et(t),u.mkdirSync(t,{recursive:!0}),u.mkdirSync(this.failedDir,{recursive:!0}),c.debug(`[telemetry] init: dir=${t}, failedDir=${this.failedDir}, cliVersion=${this.cliVersion}, os=${this.resolveOsName()}, arch=${this.resolveOsArch()}, node=${this.nodeVersion}`)}setStudioVersion(t){this.devecoStudioVersion=t,c.debug(`[telemetry] studio version set: ${t}`)}setSourceType(t){this.sourceType=t,c.debug(`[telemetry] sourceType set: ${t}`)}setCltVersion(t){this.cltVersion=t,c.debug(`[telemetry] CLT version set: ${t}`)}async track(t,e){if(this.disabled)return typeof e=="function"?e():void 0;if(!this.storageDir)throw new Error("Telemetry not initialized. Call init() first.");if(!e){await this.record(t,0,!0,null);return}if(typeof e!="function"){let o=e;await this.record(t,o.duration_ms,o.success,o.error_code);return}let r=e,i=Date.now();try{let o=await r(),s=Date.now()-i;return await this.record(t,s,!0,null),o}catch(o){let s=Date.now()-i,l=this.toErrorCode(o);throw await this.record(t,s,!1,l),o}}async record(t,e,r,i){let o=this.buildTraceEvent(t,e,r,i);try{let s=rt(JSON.stringify(o),this.traceFileKey);await u.promises.appendFile(this.currentFile(),s+`
|
|
3
3
|
`,"utf8"),pt(this.storageDir)}catch{}}buildTraceEvent(t,e,r,i){let o=`${n.EVENT_PREFIX}${t.event}`,s=Z.toObject(t);return s&&typeof s=="object"&&delete s.event,{countryCode:n.COUNTRY_CODE,event:o,eventtime:String(Date.now()),properties:{uid:this.installId,trace_uuid:vt.randomUUID(),trace_os_version:n.TRACE_OS_VERSION,os_arch:this.resolveOsArch(),action:o,trace_os_name:this.resolveOsName(),version:`${this.cliVersion}_${this.resolveOsSuffix()}`,deveco_studio_version:this.formatVersion(this.devecoStudioVersion),command_line_version:this.formatVersion(this.cltVersion),source_type:this.sourceType,session_id:this.sessionId,node_version:this.nodeVersion,ram:this.resolveTotalRam(),duration_ms:e,success:r,error_code:i,event_detail:s}}}toErrorCode(t){return t instanceof Error?t.code??t.name:"UnknownError"}resolveOsArch(){let t=I.arch();return t==="x64"?"amd64":t}resolveOsName(){switch(process.platform){case"win32":{let t=I.release().split(".");return(parseInt(t[t.length-1],10)||0)>=22e3?"Windows 11":"Windows 10"}case"darwin":return"macOS";case"linux":return"Linux";default:return process.platform}}resolveOsSuffix(){switch(process.platform){case"win32":return"windows";case"darwin":return I.arch()==="arm64"?"mac_arm":"mac";default:return"linux"}}resolveTotalRam(){return`${Math.round(I.totalmem()/1024)}KB`}formatVersion(t){return t?`${t}_${this.resolveOsSuffix()}`:""}dateString(){let t=new Date,e=String(t.getMonth()+1).padStart(2,"0"),r=String(t.getDate()).padStart(2,"0");return`${t.getFullYear()}-${e}-${r}`}currentFile(){return m.join(this.storageDir,`telemetry-${this.dateString()}.txt`)}startScheduler(){if(this.stopScheduler(),this.disabled){c.debug("[telemetry] scheduler not started (telemetry disabled)");return}c.debug(`[telemetry] scheduler started (flush every ${k/1e3}s, retry failed every ${O/1e3}s)`),this.initialTimer=setTimeout(()=>{this.flush().catch(()=>{}),this.intervalTimer=setInterval(()=>{this.flush().catch(()=>{})},k)},k),this.retryInitialTimer=setTimeout(()=>{this.retryFailed().catch(()=>{}),this.retryTimer=setInterval(()=>{this.retryFailed().catch(()=>{})},O)},O)}stopScheduler(){this.initialTimer&&(clearTimeout(this.initialTimer),this.initialTimer=null),this.intervalTimer&&(clearInterval(this.intervalTimer),this.intervalTimer=null),this.retryInitialTimer&&(clearTimeout(this.retryInitialTimer),this.retryInitialTimer=null),this.retryTimer&&(clearInterval(this.retryTimer),this.retryTimer=null),c.debug("[telemetry] scheduler stopped")}async flush(){return this.disabled?!0:this.runExclusive(()=>this.performFlush())}async performFlush(){if(!this.storageDir)throw new Error("Telemetry not initialized. Call init() first.");let t=await this.listPendingFiles(),e=!0;for(let r of t)await this.flushFile(r)||(e=!1);return ft(this.storageDir),e}async retryFailed(){return this.disabled?!0:this.runExclusive(()=>this.performRetryFailed())}async performRetryFailed(){if(!this.storageDir)throw new Error("Telemetry not initialized. Call init() first.");await this.reclaimStalePending();let t=Date.now(),e;try{e=await u.promises.readdir(this.failedDir)}catch{return!0}let r=e.filter(o=>o.startsWith("telemetry-")&&o.endsWith(".txt")).map(o=>m.join(this.failedDir,o)),i=!0;for(let o of r){let s=m.basename(o),l=mt(s);if(l!==null){if(t-l>lt){try{await u.promises.unlink(o)}catch{}continue}await this.flushFile(o)||(i=!1)}}return i||c.warn("[telemetry] retry failed"),dt(this.storageDir),i}async reclaimStalePending(){let t=Date.now();for(let e of[this.storageDir,this.failedDir]){let r;try{r=await u.promises.readdir(e)}catch{continue}for(let i of r)gt(i)&&await this.reclaimOrphan(m.join(e,i),t)}}async reclaimOrphan(t,e){let r;try{let h=await u.promises.stat(t);if(e-h.mtimeMs<ct)return;r=h.size}catch{return}let i=m.dirname(t),o=z(m.basename(t));if(o===null)return;let s=await this.claimOrphan(t,i,o);if(s===null)return;let l=m.join(i,o),p=v-await this.fileSize(l);if(r>p){try{await u.promises.unlink(s)}catch{}c.warn(`[telemetry] dropped stale orphan pending: ${s} (${r} bytes > budget ${p})`);return}if(await this.mergeOrphanInto(s,l)){c.warn(`[telemetry] reclaimed orphan pending: ${s} (${r} bytes)`);return}c.warn(`[telemetry] merge failed, kept orphan pending for retry: ${s} (${r} bytes)`)}async claimOrphan(t,e,r){let i=m.join(e,`${r}.pending.reclaim-${process.pid}-${Date.now()}`);try{await u.promises.rename(t,i)}catch{return null}return await this.touch(i),i}async mergeOrphanInto(t,e){try{await u.promises.appendFile(e,await u.promises.readFile(t))}catch(r){return c.warn(`[telemetry] merge orphan pending failed: ${t} -> ${e}: ${r instanceof Error?r.message:String(r)}`),!1}try{await u.promises.unlink(t)}catch(r){c.warn(`[telemetry] merged but failed to remove orphan pending: ${t}: ${r instanceof Error?r.message:String(r)}`)}return!0}async fileSize(t){try{return(await u.promises.stat(t)).size}catch{return 0}}async touch(t){let e=new Date;try{await u.promises.utimes(t,e,e)}catch{}}runExclusive(t){let e=()=>t(),r=this.flushChain.then(e,e);return this.flushChain=r.then(()=>{},()=>{}),r}async listPendingFiles(){let t;try{t=await u.promises.readdir(this.storageDir)}catch{return[]}return t.filter(e=>e.startsWith("telemetry-")&&e.endsWith(".txt")).map(e=>m.join(this.storageDir,e)).sort()}async flushFile(t){let e=await this.fileSize(t);if(e>v){c.warn(`[telemetry] ${m.basename(t)} is ${e} bytes (> ${v}), dropped without uploading`);try{await u.promises.unlink(t)}catch{}return!0}let r=t+".pending";try{await u.promises.rename(t,r)}catch{return!1}await this.touch(r);let i;try{i=await u.promises.readFile(r,"utf8")}catch{return!1}let o=i.split(`
|
|
4
|
-
`).map(s=>s.trim()).filter(Boolean);if(o.length===0){try{await u.promises.unlink(r)}catch{}return!0}return this.uploadInBatches(r,o)}collectDecryptBatch(t,e){let r=[],i=16,o=e;for(;o<t.length&&r.length<n.MAX_BATCH;){let s=nt(t[o],this.traceFileKey);if(s===null){o++;continue}let l=Buffer.byteLength(s,"utf8")+1;if(r.length>0&&i+l>n.MAX_PAYLOAD_BYTES)break;r.push(s),i+=l,o++}return{batch:r,next:o}}async uploadInBatches(t,e){let r=0;for(;r<e.length;){let i=r,{batch:o,next:s}=this.collectDecryptBatch(e,r);r=s;let l=this.buildPayloadItems(o);if(l.length===0)continue;let p=JSON.stringify(l),h=!1;try{h=await this.uploader.upload(p,this.installId)}catch{}if(!h){c.warn("[telemetry] upload failed");let
|
|
4
|
+
`).map(s=>s.trim()).filter(Boolean);if(o.length===0){try{await u.promises.unlink(r)}catch{}return!0}return this.uploadInBatches(r,o)}collectDecryptBatch(t,e){let r=[],i=16,o=e;for(;o<t.length&&r.length<n.MAX_BATCH;){let s=nt(t[o],this.traceFileKey);if(s===null){o++;continue}let l=Buffer.byteLength(s,"utf8")+1;if(r.length>0&&i+l>n.MAX_PAYLOAD_BYTES)break;r.push(s),i+=l,o++}return{batch:r,next:o}}async uploadInBatches(t,e){let r=0;for(;r<e.length;){let i=r,{batch:o,next:s}=this.collectDecryptBatch(e,r);r=s;let l=this.buildPayloadItems(o);if(l.length===0)continue;let p=JSON.stringify(l),h=!1;try{h=await this.uploader.upload(p,this.installId)}catch{}if(!h){c.warn("[telemetry] upload failed");let w=e.slice(i).join(`
|
|
5
5
|
`)+`
|
|
6
|
-
`;return await this.moveToFailed(t,
|
|
6
|
+
`;return await this.moveToFailed(t,w),!1}}try{await u.promises.unlink(t)}catch{}return!0}buildPayloadItems(t){let e=[];for(let r of t)try{let i=JSON.parse(r);e.push({action:i.event,detail:JSON.stringify(i.properties),timestamp:Number(i.eventtime)||Date.now()})}catch{}return e}async moveToFailed(t,e){let r=m.basename(t,".pending"),i=m.join(this.failedDir,r),o=e.split(`
|
|
7
7
|
`).filter(Boolean),s=v-await this.fileSize(i),l=this.takeNewestWithinBudget(o,s);l.length<o.length&&c.warn(`[telemetry] failed file capped at ${v} bytes, dropped ${o.length-l.length} older events`);try{l.length>0&&await u.promises.appendFile(i,l.join(`
|
|
8
8
|
`)+`
|
|
9
|
-
`,"utf8")}catch{return}try{await u.promises.unlink(t)}catch{}}takeNewestWithinBudget(t,e){if(e<=0)return[];let r=[],i=0;for(let o=t.length-1;o>=0;o--){let s=Buffer.byteLength(t[o],"utf8")+1;if(i+s>e)break;i+=s,r.push(t[o])}return r.reverse(),r}};var
|
|
9
|
+
`,"utf8")}catch{return}try{await u.promises.unlink(t)}catch{}}takeNewestWithinBudget(t,e){if(e<=0)return[];let r=[],i=0;for(let o=t.length-1;o>=0;o--){let s=Buffer.byteLength(t[o],"utf8")+1;if(i+s>e)break;i+=s,r.push(t[o])}return r.reverse(),r}};var T=new x;import{spawn as Ge}from"child_process";import{existsSync as Ze}from"fs";import{dirname as tr,join as er}from"path";import{fileURLToPath as nr}from"url";var zt=Vt.join(X(),"TraceLogData");T.init(zt);(async()=>{try{await T.flush(),await T.retryFailed()}catch(n){c.warn("[telemetry] background upload error:",n),process.exit(1)}})();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@litingwei621/test-deveco-cli-common",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.10-Test.1",
|
|
4
4
|
"description": "HarmonyOS application development command line tool",
|
|
5
5
|
"supportedDocsApi": 1,
|
|
6
6
|
"license": "MIT",
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"skills"
|
|
14
14
|
],
|
|
15
15
|
"scripts": {
|
|
16
|
+
"prebuild": "node ../../scripts/sync-cli-docs.mjs",
|
|
16
17
|
"build": "tsup",
|
|
17
18
|
"dev": "tsx watch src/cli.ts",
|
|
18
19
|
"start": "tsx src/cli.ts",
|
|
@@ -20,6 +21,7 @@
|
|
|
20
21
|
"lint": "eslint \"src/**/*.ts\"",
|
|
21
22
|
"lint:fix": "eslint src --fix",
|
|
22
23
|
"format": "prettier --write src",
|
|
24
|
+
"prepack": "node ../../scripts/sync-cli-docs.mjs",
|
|
23
25
|
"prepublishOnly": "npm run lint && npm run build"
|
|
24
26
|
},
|
|
25
27
|
"engines": { "node": ">=22" },
|
|
@@ -34,7 +36,7 @@
|
|
|
34
36
|
],
|
|
35
37
|
"keywords": ["harmonyos", "deveco", "cli"],
|
|
36
38
|
"dependencies": {
|
|
37
|
-
"@deveco/deveco-cli-knowledgebase": "1.3.
|
|
39
|
+
"@deveco/deveco-cli-knowledgebase": "1.3.9",
|
|
38
40
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
39
41
|
"@xmldom/xmldom": "^0.8.15",
|
|
40
42
|
"adm-zip": "0.6.1",
|
|
@@ -89,12 +89,14 @@ Inspect UI on a connected physical device or running emulator.
|
|
|
89
89
|
*Ex*: `devecocli ui screenrecord start --device Phone --path ./recordings/` → `devecocli ui screenrecord stop --device Phone`
|
|
90
90
|
|
|
91
91
|
### `devecocli docs`
|
|
92
|
-
Search,
|
|
92
|
+
Search HarmonyOS developer documentation in the local HarmonyOS Knowledge Base — returns articles with examples and guidance on HarmonyOS APIs, ArkTS, and libraries. Typical use cases: finding API migration guides, examples for HarmonyOS APIs/ArkTS, up-to-date API references, and best practices for HarmonyOS development.
|
|
93
93
|
- `search <keywords...>`: Match any keyword. Opts: `--catalog <name>`, `--format <default|json>`, `--limit <n>`.
|
|
94
94
|
- `read <documentId>`: Read full content by ID (e.g. `devecocli docs read 开发指南/冷启动_Launch分析/Launch模板基本操作/ide-insight-session-launch`).
|
|
95
95
|
- `catalog`: List available catalogs.
|
|
96
96
|
- `update`: Update docs to the latest compatible version — downloads, sha512-verifies, and installs the docs-data package with atomic version switching; falls back to the bundled baseline if missing/corrupted. Opts: `--force`, `--check`.
|
|
97
97
|
|
|
98
|
+
Always use this tool to get the most up-to-date information about HarmonyOS concepts.
|
|
99
|
+
|
|
98
100
|
### `devecocli device`
|
|
99
101
|
- `list`: Show active real devices and running emulators. Opt: `--format <table|json>` (default: `table`).
|
|
100
102
|
- `view`: Detailed info. Req `-t <name|serial>` on multi-device hosts. Opt: `--format <table|json>` (default: `table`).
|
|
@@ -146,7 +148,12 @@ Fetch hilog or crash logs. Req `--device <name|serial>` on multi-device hosts.
|
|
|
146
148
|
- `--bundle-name` / `--keyword`: Filter output.
|
|
147
149
|
- `--from <start>` / `--to <end>`: Relative offsets (`30s`, `5m`).
|
|
148
150
|
- `--tail <num>` / `--follow`: Keep last N lines / stream real-time (no `--to`).
|
|
149
|
-
|
|
151
|
+
- `--path <path>` + `--crash`: Analyze a local fault log file or directory (cpp_crash / js_error / app_freeze) instead of pulling from device; picks the most relevant candidate when given a directory.
|
|
152
|
+
- `--stack` + `--path <file>`: Analyze a local sample stack file (enhanced log); mutually exclusive with `--crash`.
|
|
153
|
+
- `--format default|json|report|hilog`: Output format for local analysis (`hilog` extracts the HiLog section, `--crash` only).
|
|
154
|
+
- `--limit <num>`: Max hotspots shown by `--stack` (default 20).
|
|
155
|
+
- `--path` cannot be combined with device log options (`--device`, `--level`, `--bundle-name`, `--keyword`, `--tail`, `--from`, `--to`, `--follow`).
|
|
156
|
+
*Ex*: `devecocli log --crash --bundle-name com.example.app`, `devecocli log --level E --from 5m --tail 200`, `devecocli log --crash --path ./faultlog --format report`, `devecocli log --stack --path sample_stacks.txt --limit 20`
|
|
150
157
|
|
|
151
158
|
### `devecocli verify`
|
|
152
159
|
Run UI verification on a connected device. Based on natural-language functional steps, the tool launches the app, uses screenshots to judge the current UI state, performs UI operations, and verifies whether the functionality completes correctly. This is the standard tool for UI testing and issue reproduction.
|
|
@@ -261,11 +268,12 @@ Show the current logged-in user.
|
|
|
261
268
|
List team accounts the current user has joined.
|
|
262
269
|
|
|
263
270
|
### `devecocli skills`
|
|
264
|
-
|
|
271
|
+
The skills command manages HarmonyOS skills for AI agents and projects, supporting listing, keyword search, installation and uninstallation. Use this tool when you need to view, search, add or remove HarmonyOS skills bound to AI agents or projects.
|
|
265
272
|
- `list [-l|--long]` / `find <keyword>`: List or search skills.
|
|
266
273
|
- `add (--all | --skill <name>) [--agent <a,b…>] [--project <path>] [--path <path>] [-f]`: Install.
|
|
267
274
|
- `remove --skill <name> [...]`: Uninstall.
|
|
268
275
|
- Ordinary project paths such as `skills/` and `data/` require an explicit `--agent` for installation or removal; automatic selection ignores them.
|
|
276
|
+
A minimum set: the ArkTS red-line rules (mandatory review before writing ArkTS) plus `hmos-arkui-develop-skill` (fatal pitfalls + accurate API entry), `hmos-one-sdk-skill` (searchable official-doc knowledge base), and `hmos-runtime-fix-skill` (anchor-based minimal fix) — covering pre-coding constraints, API accuracy, doc lookup, and crash diagnosis.
|
|
269
277
|
|
|
270
278
|
**Existing Skill paths are skipped by default. Use `-f` / `--force` to replace them, deleting old content and local edits. To convert copied agent installations to symlinks, rerun `devecocli init -f` for `deveco-cli` or `devecocli skills add --skill <name> -f` for other Skills.** This flag also allows missing target directories to be created. Correct links are kept; failed linking falls back to copying. Batch installation continues after failures, prints a summary and exits nonzero if any Skill fails.
|
|
271
279
|
|
|
@@ -273,6 +281,17 @@ Extracted skills are stored in <CLI data directory>/skills/store/<name> globally
|
|
|
273
281
|
|
|
274
282
|
*Ex*: `devecocli skills add --skill <name> --agent opencode --project ./MyApp`
|
|
275
283
|
|
|
284
|
+
### `devecocli plugin`
|
|
285
|
+
Manage whitelisted npm-packaged AI agent plugins (official registry only; the whitelist `npmPackage` coordinate may carry a `@tag` suffix to select a dist-tag, e.g. `@scope/pkg@cli` — defaults to `latest`).
|
|
286
|
+
- `list [-l|--long]`: List whitelisted plugins and locally downloaded versions in a `Name / Version / Status` table (one row per cached version, ascending; `not installed` when none); `--long` appends `NpmPackage` and `Desc` columns.
|
|
287
|
+
- `install <name> [--agent <a,b…>] [--project <path>] [-f]`: Download the package into the CLI data directory, then run the plugin's `install` hook per target agent (detected agents ∩ plugin `supportedAgents`; a plugin omitting `supportedAgents` defaults to all known agents, while an explicitly empty/unknown list fails contract validation). `--force` runs the previous version's `uninstall` hook first. With an explicit `--agent` plus `--project`, missing target skills dirs (including the `.<agent>` root) are created before hooks run, matching `init --project --agent`.
|
|
288
|
+
- `update <name> [--agent <a,b…>] [--project <path>]`: Fetch the latest version and run the `update` hook (`from`/`to` provided; `from` is the highest pre-download store version — `from === to` means no local version or latest already cached, treat as repair/first install). With an explicit `--agent` plus `--project`, missing target skills dirs (including the `.<agent>` root) are created before hooks run, same as `install`.
|
|
289
|
+
- `uninstall <name> [--agent <a,b…>] [--project <path>]`: Run the `uninstall` hook of the highest loadable local version. Never deletes cached packages (`<dataDir>/plugins/store/<name>/`) or plugin data (`<dataDir>/plugins/data/<name>/`); remove those manually if needed.
|
|
290
|
+
- The CLI keeps no install state: package caches in `<dataDir>/plugins/store/<name>/<version>/` are the only state; install/update always re-run hooks (idempotency is the plugin's responsibility). Names outside the whitelist are rejected. Summary counts `Success / Skipped / Failed` per target — `skipped` = the agent is not supported by the plugin (no hook run; an explicit unsupported `--agent` errors, consistently across install/update/uninstall), or, with `--project` and no explicit `--agent`, the project lacks the agent's `.<agent>` root dir (matches `init --project`: only agents whose entry is the shared `.agents/skills` go through without a pre-existing root; reason `agent-project-root-missing`, no hook run, no `.<agent>` dir created). Exit code is nonzero when any target fails; restart your agent afterwards to load the plugin.
|
|
291
|
+
- Hook execution is capped at 120s (override with `DEVECO_CLI_PLUGIN_HOOK_TIMEOUT_MS`).
|
|
292
|
+
|
|
293
|
+
*Ex*: `devecocli plugin install example-plugin --agent opencode --project ./MyApp`
|
|
294
|
+
|
|
276
295
|
### `devecocli check compat` `[Outside sandbox]`
|
|
277
296
|
Scan source code for breaking API changes between two SDK versions. Built on top of DevEco Studio's `arkanalyzer-apiscan` plugin.
|
|
278
297
|
- `versions`: List available target SDK versions. Opts: `--format <default|json>` (default: `default`).
|
|
@@ -304,7 +323,7 @@ Validation order: `files` + `--modules` mutually exclusive → `--source-version
|
|
|
304
323
|
- **Fresh checkout to emulator**:
|
|
305
324
|
`devecocli build` -> `devecocli emulator list` -> `devecocli emulator start "Name"` -> `devecocli run`
|
|
306
325
|
- **Diagnose crash**:
|
|
307
|
-
`devecocli log --crash --bundle-name <bundle>`
|
|
326
|
+
`devecocli log --crash --bundle-name <bundle>` (device), or `devecocli log --crash --path <faultlog file|dir> --format report` (local analysis), or `devecocli log --stack --path <sample_stacks file>` (freeze hotspot analysis)
|
|
308
327
|
- **Release build**:
|
|
309
328
|
`devecocli build --product oversea --build-mode release`
|
|
310
329
|
- **First-time signing setup** (login authorized; user completes browser authentication):
|