@dlient-open/dev-tools 0.1.7
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/LICENSE +21 -0
- package/README.md +58 -0
- package/README.zh-CN.md +58 -0
- package/assets/icon.svg +1 -0
- package/assets/index.en-US.md +35 -0
- package/assets/index.md +35 -0
- package/assets/index.zh-CN.md +35 -0
- package/dist/assets/style.css +1 -0
- package/dist/remoteEntry.js +35 -0
- package/dist/worker.js +11 -0
- package/package.json +126 -0
- package/script/build-clean.mjs +7 -0
- package/script/build-worker.mjs +51 -0
- package/script/make-dlient.mjs +203 -0
package/dist/worker.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createRequire as __dlientCreateRequire } from 'node:module';
|
|
2
|
+
import { fileURLToPath as __dlientFileURLToPath } from 'node:url';
|
|
3
|
+
import { dirname as __dlientDirname } from 'node:path';
|
|
4
|
+
const require = __dlientCreateRequire(import.meta.url);
|
|
5
|
+
const __filename = __dlientFileURLToPath(import.meta.url);
|
|
6
|
+
const __dirname = __dlientDirname(__filename);
|
|
7
|
+
|
|
8
|
+
import{EventEmitter as rt}from"node:events";var ie="CMD_NPM";function it(e,t=!1){let n=Buffer.from(JSON.stringify(e),"utf-8");if(t)return Buffer.concat([n,Buffer.from(`
|
|
9
|
+
`,"utf-8")]);let r=Buffer.alloc(4);return r.writeUInt32BE(n.length,0),Buffer.concat([r,n])}var ge=class{constructor(t,n){this.onMessage=n,this.buffer=Buffer.alloc(0),this.ndjson=t}push(t){let n=typeof t=="string"?Buffer.from(t,"utf8"):t;if(this.buffer=this.buffer.length===0?n:Buffer.concat([this.buffer,n]),this.ndjson){let r;for(;(r=this.buffer.indexOf(10))>=0;){let i=this.buffer.subarray(0,r).toString("utf-8").trim();this.buffer=this.buffer.subarray(r+1),i&&this.tryParse(i)}return}for(;this.buffer.length>=4;){let r=this.buffer.readUInt32BE(0);if(this.buffer.length<4+r)break;let i=this.buffer.subarray(4,4+r).toString("utf-8");this.buffer=this.buffer.subarray(4+r),this.tryParse(i)}}tryParse(t){try{let n=JSON.parse(t);this.onMessage(n)}catch{}}};function st(e,t){return{write:n=>{e.stdin?.writable&&e.stdin.write(n)},end:()=>{try{e.stdin?.end()}catch{}},onStdout:n=>e.stdout?.on("data",r=>n(r.toString())),onExit:n=>e.on("exit",r=>n(r)),onError:n=>e.on("error",r=>n(r)),kill:()=>{if(t)try{t()}catch{}else try{e.kill()}catch{}}}}function ot(e){return{write:t=>{let n=typeof t=="string"?t:t.toString("utf8");e.write(n).catch(()=>{})},end:()=>{e.end().catch(()=>{})},onStdout:t=>e.onStdout(t),onExit:t=>e.onExit(n=>{t(n.code)}),onError:t=>e.onError(t),kill:()=>e.kill()}}function at(e){let{transport:t,proc:n,dispose:r,timeoutMs:i=3e4,heartbeatTimeoutMs:a=33e3,ndjson:o=!1}=e,c=(()=>{let D=t??(n?st(n,r):void 0);if(!D)throw new Error("createNativeHostClient: transport or proc required");return D})(),g=new rt,v=[],f=new Map,p=0,I=!1,y=new ge(o,D=>{_=Date.now();let C=D;if(C&&typeof C=="object"&&"ping"in C){R({pong:C.ping});return}if(C&&typeof C=="object"&&"event"in C){let P=C;g.emit(P.event,P.data);return}if(C&&typeof C=="object"&&"id"in C){let P=C,$=f.get(P.id);if(!$)return;if($.isStream){if(P.ok&&P.more){g.emit(`stream:${P.id}:data`,P.result);return}f.delete(P.id),$.timer&&clearTimeout($.timer),P.ok?$.resolve(P.result):$.reject(new Error(P.error??`native-host RPC failed: ${P.id}`));return}f.delete(P.id),$.timer&&clearTimeout($.timer),P.ok?$.resolve(P.result):$.reject(new Error(P.error??`native-host RPC failed: ${P.id}`))}});c.onStdout(D=>y.push(D));let R=D=>{I||c.write(it(D,o))},N=(D,C,P)=>{for(let[,$]of f)$.timer&&clearTimeout($.timer),$.reject(P);f.clear();for(let $ of v)try{$({code:C,reason:D})}catch{}};c.onExit(D=>{I||N("exit",D,new Error(`\u539F\u751F\u6A21\u5757\u8FDB\u7A0B\u5F02\u5E38\u9000\u51FA\uFF08code=${D??"unknown"}\uFF09\uFF0C\u8BF7\u91CD\u542F\u63D2\u4EF6`))}),c.onError(D=>{I||N("exit",null,new Error(`\u539F\u751F\u6A21\u5757\u8FDB\u7A0B\u542F\u52A8\u5931\u8D25: ${D.message}`))});let _=Date.now(),E=setInterval(()=>{I||Date.now()-_>a&&N("timeout",null,new Error("\u539F\u751F\u6A21\u5757\u8FDB\u7A0B\u5FC3\u8DF3\u8D85\u65F6\uFF08\u5931\u8054\uFF09\uFF0C\u8BF7\u91CD\u542F\u63D2\u4EF6"))},5e3);function G(D,C,P,$){return new Promise((K,O)=>{if(I)return O(new Error("native-host client disposed"));let z=`rpc-${++p}`,F={id:z,resolve:ne=>K(ne),reject:O,isStream:!1},X=$?.timeoutMs??i;X>0&&(F.timer=setTimeout(()=>{f.delete(z),O(new Error(`native-host RPC \u8D85\u65F6\uFF08${D}.${C} > ${X/1e3}s\uFF09`))},X)),f.set(z,F),R({id:z,service:D,method:C,params:P})})}function te(D,C,P){let $=`stream-${++p}`,K=[],O=[],z=[],F=`stream:${$}:data`,X={onData:j=>K.push(j),onDone:j=>O.push(j),onError:j=>z.push(j),abort:()=>{f.delete($),g.removeAllListeners(F)}},ne={id:$,resolve:j=>{for(let Y of O)try{Y(j)}catch{}g.removeAllListeners(F)},reject:j=>{for(let Y of z)try{Y(j)}catch{}g.removeAllListeners(F)},isStream:!0};return f.set($,ne),g.on(F,j=>{for(let Y of K)try{Y(j)}catch{}}),R({id:$,service:D,method:C,params:P}),X}function U(){if(I)return;I=!0,clearInterval(E),N("disposed",null,new Error("native-host client disposed"));try{c.end?.()}catch{}let D=c.kill();D&&typeof D.then=="function"&&D.catch(()=>{})}return{call:G,stream:te,onEvent:(D,C)=>g.on(D,C),onExit:D=>v.push(D),dispose:U}}function ct(e){let{backoffMs:t=300,maxBackoffMs:n=8e3,maxRestarts:r=10}=e,i=null,a,o=null,c=!1,g=!1,v=0,f=[],p=N=>new Promise(_=>setTimeout(_,N)),I=N=>{for(let _ of f)try{_(N)}catch{}};async function y(){if(c||g)return!1;try{let N=await e.spawn();return c||g?(N.dispose?.(),!1):(e.onRestarted&&await e.onRestarted(N.client),i=N.client,a=N.dispose,v=0,N.client.onExit(_=>{i=null,a=void 0,!c&&I({code:_.code,reason:_.reason==="disposed"?"disposed":_.reason==="timeout"?"timeout":"exit"})}),!0)}catch{if(v+=1,c||v>r)return g=!0,I({code:null,reason:"dead"}),!1;let _=Math.min(t*2**Math.min(v-1,8),n);return await p(_),y()}}let R=async()=>{if(i)return i;if(c)throw new Error("native-host \u5DF2\u505C\u6B62");if(g)throw new Error("native-host \u591A\u6B21\u542F\u52A8\u5931\u8D25\uFF0C\u8BF7\u91CD\u542F\u63D2\u4EF6");o||(o=y());let N=await o;if(o=null,!N||!i)throw new Error("native-host \u4E0D\u53EF\u7528");return i};return{call:async(N,_,E,G)=>(await R()).call(N,_,E,G),onExit:N=>f.push(N),stop:async()=>{c=!0;let N=a,_=i;i=null,a=void 0;try{_?.dispose()}catch{}if(N&&N!==_?.dispose)try{await N()}catch{}}}}var dt=["app.getVersion","app.getPath","app.isActive","app.isHidden","app.getName","app.getLocale","app.getLocaleCountryCode","app.getSystemLocale","app.getPreferredSystemLanguages","app.notify","app.setAutoLaunch","app.event","app.createNativeHost","app.createNativeClient","app.disposeNativeClient","app.window.close","app.window.focus","app.window.blur","app.window.show","app.window.hide","app.window.maximize","app.window.unmaximize","app.window.minimize","app.window.restore","app.window.isMaximized","app.window.setFullScreen","app.data.read","app.data.write","app.crypt.encrypt","app.crypt.decrypt","app.shortcut.register","app.shortcut.unregister","app.menu.popup",...["read","write","clear","has","availableFormats"].map(e=>`clipboard.${e}`),...["Text","HTML","RTF","Bookmark","FindText","Image","Buffer"].flatMap(e=>[`clipboard.read${e}`,`clipboard.write${e}`]),"dialog.showMessageBox","dialog.showOpenDialog","dialog.showSaveDialog","fs.read","fs.stat","fs.listDir","fs.watch","fs.unwatch","fs.mkdir","fs.write","fs.append","fs.delete","fs.copyDir","fs.lock","fs.unlock","fs.withLock","i18n.getLocale","net.isOnline","net.fetch","net.request","net.getFreePort","net.probePort","nodejs.checkLocal","nodejs.checkBundled","nodejs.resolveRuntime","nodejs.install","notification.isSupported","notification.send","notification.remove","notification.removeGroup","notification.subscribe","notification.unsubscribe","log.write","os.openExternal","os.showItemInFolder","permission.plugin.list","permission.revoke","permission.request","permission.list","plugin.invoke","plugin.requestGrant","plugin.capabilities","plugin.registry.report","plugin.start","plugin.stop","plugin.system","plugin.scanInstalled","plugin.runtimeList","plugin.installLocal","plugin.install","plugin.isRunning","plugin.cleanupUninstall","plugin.setActive","plugin.dev.selectDirectory","plugin.dev.getDirInfo","plugin.dev.sync","plugin.dev.startWatcher","plugin.dev.stopWatcher","plugin.dev.startDevWorker","plugin.dev.stopDevWorker","plugin.dev.isPortReady","plugin.dev.readLogs","plugin.dev.clearLogs","plugin.dev.list","plugin.dev.remove","plugin.logs.subscribe","plugin.logs.unsubscribe","powerSaveBlocker.start","powerSaveBlocker.stop","powerSaveBlocker.isStarted","screen.getCursorScreenPoint","screen.getPrimaryDisplay","screen.getAllDisplays","screen.getDisplayNearestPoint","screen.getDisplayMatching","screen.screenToDipPoint","screen.dipToScreenPoint","screen.screenToDipRect","screen.dipToScreenRect","system.getIdleState","system.listenNativeTheme","webview.create","webview.update","webview.destroy","webview.setVisible","webview.webContentsCall","webview.showWebviewByPlugin","webview.hideWebviewByPlugin"];function lt(e,t,n){let r=t.split("."),i=r.pop(),a=e;for(let o of r)(typeof a[o]!="object"||a[o]===null)&&(a[o]={}),a=a[o];a[i]=(...o)=>n(t,o)}function ut(e){let t={};for(let n of dt)lt(t,n,e);return t}var ft="__dlientPoolCtx",pt="__dlientRpcTable",J={OK:0,METHOD_NOT_REGISTERED:-2101,INTERNAL:-2200,THROW_ERROR:-2201};var m=class extends Error{constructor(t,n){super(typeof n=="string"?n:n?.zhCN??n?.enUS??`plugin error ${t}`),this.name="PluginError",this.code=t,this.msg=n}};function pe(e){if(e&&typeof e=="object"&&typeof e.code=="number")return e.code}function gt(e){if(e instanceof m&&e.msg!==void 0)return e.msg;if(e&&typeof e=="object"&&"msg"in e){let t=e.msg;if(typeof t=="string"||t&&typeof t=="object"&&typeof t.enUS=="string")return t}return e instanceof Error?e.message:String(e)}function wt(e){return!!e&&typeof e=="object"&&typeof e.code=="number"&&typeof e.from=="string"}function ht(e,t){let n=new Error(t);return n.code=e,n}function _e(e){let t=new Map,n=new Set,r=0,i=new Map,a=null,o=new Map,c,g,v=[],f=[],p=[],I=!1,y=()=>{if(!I){I=!0;for(let s of f)try{s()}catch(l){console.error(`[plugin:${E}] onReady failed:`,l)}}},R=globalThis[ft],N=process.parentPort,_=R?.controlPort??(typeof process<"u"?N:void 0),E=R?.pluginId??e,G=new Map,te=new Map,U=s=>{_?.postMessage(s)},D=s=>({code:J.OK,data:s,from:E}),C=(s,l)=>({code:s,msg:l,data:null,from:E}),P=s=>{a&&a.postMessage(s)},$=s=>{let l=s.data;switch(l.type){case"request":ne(l,!1);break;case"listen":ne(l,!0);break;case"cancel":o.get(l.request_id)?.abort(),o.delete(l.request_id);break}},K=new Set,O,z=s=>{let l=!K.has(s);O&&a&&a!==s&&(a.off?.("message",O),O=void 0),a=s,l&&(K.add(s),O=u=>{a===s&&$(u)},s.on("message",O),s.start?.()),U({type:"direct-port-ready",pluginId:E}),y()},F=R?.directPort;F&&z(F);let X=globalThis[pt]??={};X[E]={setDirectPort:z};function ne(s,l){let u=t.get(s.method);if(!u){let S=`Method "${s.method}" not found`;P({type:"error",request_id:s.request_id,error:S,error_code:J.METHOD_NOT_REGISTERED,result:C(J.METHOD_NOT_REGISTERED,S)});return}let w=new AbortController;o.set(s.request_id,w);let b=(S,x)=>{P({type:"stream",request_id:s.request_id,chunk:S,data:x?.data})},h={from_plugin_id:s.from_plugin_id,request_id:s.request_id,data:s.data,signal:w.signal,emit:l?b:void 0};Promise.resolve().then(()=>u(s.args,h)).then(S=>{if(w.signal.aborted)return;let x=S?.data,q=x instanceof ArrayBuffer?x:void 0,re=q!==void 0?S.result:S,T=wt(re)?re:D(re);P(l?{type:"stream-done",request_id:s.request_id,result:T}:{type:"response",request_id:s.request_id,result:T,data:q})}).catch(S=>{if(w.signal.aborted)return;let x=S instanceof Error?S.message:String(S),q=pe(S)??J.THROW_ERROR,re=C(q,gt(S));P(l?{type:"stream-error",request_id:s.request_id,error:x,error_code:q}:{type:"error",request_id:s.request_id,error:x,error_code:q,result:re})}).finally(()=>{o.delete(s.request_id)})}if(_){let s=Date.now();_.on("message",l=>{let u=l,w=u.data;switch(s=Date.now(),w.type){case"init":{w.pluginId&&(E=w.pluginId),U({type:"ready",pluginId:E,capabilities:Array.from(t.keys())}),y();break}case"port-setup":{let[b]=u.ports??[];b&&z(b);break}case"rpc-call":j(w);break;case"host-api-response":Y(w);break;case"child-control-response":et(w);break;case"child-event":{let h=w.child,S=h?.handleId;if(!S)break;let x=G.get(S);if(!x)break;h.event==="stdout"?x.onStdout?.(h.data??""):h.event==="stderr"?x.onStderr?.(h.data??""):h.event==="exit"?x.onExit?.({code:h.code??null,signal:h.signal??void 0,reason:"exited"}):h.event==="error"&&x.onError?.(new Error(h.data??"spawn error"));break}case"notification-event":{let b=w,h=te.get(b.id);if(!h)break;let S=h.events.get(b.event);if(!S)break;for(let x of Array.from(S))try{x(b.payload)}catch(q){console.error(`[plugin:${E}] notification "${b.event}" handler error:`,q)}break}case"snapshot-request":{let b=w;c&&Promise.resolve().then(()=>c()).then(h=>{let S={type:"snapshot-response",pluginId:E,request_id:b.request_id,snapshot:h};U(S)}).catch(h=>{let S={type:"snapshot-response",pluginId:E,request_id:b.request_id,error:h instanceof Error?h.message:String(h)};U(S)});break}case"snapshot-restore":{let b=w;g&&Promise.resolve().then(()=>g(b.snapshot)).catch(h=>console.error(`[plugin:${e}] snapshot restore failed:`,h));break}case"heartbeat":break;case"dispose":{(async()=>{for(let h=p.length-1;h>=0;h--)try{await p[h]()}catch(S){console.error(`[plugin:${E}] onDispose "${h}" failed:`,S)}p.length=0;for(let h=v.length-1;h>=0;h--)try{await v[h]()}catch(S){console.error(`[plugin:${E}] dispose "${h}" failed:`,S)}v.length=0,U({type:"dispose-done",pluginId:E})})();break}}}),_.start?.(),setInterval(()=>{U({type:"heartbeat",pluginId:E,ts:Date.now()})},1e4),setInterval(()=>{if(Date.now()-s>3e4){let l=new Error("Main process lost (no message for 30s)");for(let{reject:u}of i.values())u(l);i.clear()}},5e3)}let j=s=>{let l=t.get(s.method),u={type:"rpc-response",id:s.id,pluginId:E};if(!l)u.error=`Method "${s.method}" not found`,u.error_code=J.METHOD_NOT_REGISTERED;else{let w={from_plugin_id:s.from_plugin_id??"",request_id:s.id,signal:new AbortController().signal};try{let b=l(s.args,w);if(b instanceof Promise){b.then(h=>{u.result=h,U(u)},h=>{u.error=h.message,u.error_code=pe(h)??J.INTERNAL,U(u)});return}u.result=b}catch(b){u.error=b instanceof Error?b.message:String(b),u.error_code=pe(b)??J.INTERNAL}}U(u)},Y=s=>{let l=i.get(s.id);l&&(i.delete(s.id),s.error?l.reject(ht(s.error_code??J.INTERNAL,s.error)):l.resolve(s.result))},fe=new Map,Ze=0,et=s=>{let l=fe.get(s.id);l&&(fe.delete(s.id),s.ok?l.resolve():l.reject(new Error(s.reason??"child control failed")))},$e=s=>{U({type:"child-subscribe",pluginId:E,handleId:s})},ce=(s,l,u)=>{let w=`child-ctl-${E}-${++Ze}`,b={type:"child-control",id:w,pluginId:E,handleId:s,op:l,data:u};return new Promise((h,S)=>{fe.set(w,{resolve:h,reject:S}),U(b)})};return t.set("webview:create",([s])=>M("webview.create",[s])),t.set("webview:update",([s])=>M("webview.update",[s])),t.set("webview:destroy",([s])=>M("webview.destroy",[String(s??"")])),t.set("webview:setVisible",([s])=>M("webview.setVisible",[s])),t.set("webview:webContents:call",([s])=>M("webview.webContentsCall",[s])),t.set("onWebContentsEvent",([s])=>{let{viewId:l,name:u,args:w}=s??{};if(!l||!u)return;U({type:"push",pluginId:E,event:"webview:event",data:{viewId:l,name:u,args:w}})}),{...ut((s,l)=>M(s,l)),child:{spawn:async s=>{let l=await M("child.spawn",[{...s}]),u=String(l?.handleId??"");if(!u)throw new Error("child.spawn: handleId missing");return Ce(u,typeof l?.pid=="number"?l.pid:0)},execFile:s=>M("child.execFile",[{...s}])},notification:{isSupported:async()=>!!await M("notification.isSupported",[]),send:s=>tt(s),remove:async s=>{await M("notification.remove",[String(s)])},removeGroup:async()=>{await M("notification.removeGroup",[])},subscribe:async s=>{await M("notification.subscribe",[s])},unsubscribe:async s=>{await M("notification.unsubscribe",[s])}},getPluginId:()=>E,registerHandler:(s,l)=>{t.set(s,l),n.delete(s)},registerSnapshotHandler:s=>{c=s},registerRestoreHandler:s=>{g=s},effect:s=>{let l=!1,u;try{let w=s();w instanceof Promise?w.then(b=>{typeof b=="function"&&(u=b,v.push(u),l=!0)}).catch(b=>console.error(`[plugin:${E}] effect install failed:`,b)):typeof w=="function"&&(u=w,v.push(u),l=!0)}catch(w){console.error(`[plugin:${E}] effect install failed:`,w)}return l?u:void 0},onReady:s=>{if(f.push(s),I)try{s()}catch(l){console.error(`[plugin:${E}] onReady failed:`,l)}},onDispose:s=>{p.push(s)},registerStreamHandler:(s,l)=>{t.set(s,l),n.add(s)},push:(s,l)=>{U({type:"push",pluginId:E,event:s,data:l})},registerChildEvent:(s,l)=>{G.set(s,l)},unregisterChildEvent:s=>{G.delete(s)},childSubscribe:s=>$e(s),childControl:(s,l,u)=>ce(s,l,u),success:s=>D(s),error:(s,l)=>C(s,l),createNativeHost:async(s,l)=>{let u="";try{let T=await M("nodejs.resolveRuntime",[]);u=typeof T?.node=="string"?T.node:""}catch{}if(!u)return{host:null,client:null,error:"createNativeHost: nodejs runtime unavailable"};let w={};try{w=await M("app.createNativeHost",[{fileName:String(s),node:u}])}catch(T){return{host:null,client:null,error:T instanceof Error?T.message:String(T)}}let b=String(w?.handleId??"");if(!b)return{host:null,client:null,error:"createNativeHost: no handle"};let h=Ce(b,typeof w?.pid=="number"?w.pid:0),S;try{S=at({transport:ot(h)})}catch(T){return{host:null,client:null,error:T instanceof Error?T.message:String(T)}}let x=[],q=ct({spawn:async()=>({client:S,dispose:()=>void h.kill()}),onRestarted:async()=>{for(let T of x)try{T()}catch{}},maxRestarts:l?.maxRestarts,backoffMs:l?.backoffMs,maxBackoffMs:l?.maxBackoffMs});return{host:Object.assign(q,{onRestarted:T=>void x.push(T)}),client:S,error:void 0}}};function Ce(s,l){let u={};return G.set(s,u),$e(s),{handleId:s,pid:l,kill:()=>(G.delete(s),ce(s,"kill")),onStdout:w=>{u.onStdout=w},onStderr:w=>{u.onStderr=w},onExit:w=>{u.onExit=w},onError:w=>{u.onError=w},write:w=>ce(s,"write",w),end:()=>ce(s,"end")}}async function tt(s){let l=await M("notification.send",[s]),u=String(l?.id??"");if(!u)throw new Error("notification.send: id missing");return nt(u)}function nt(s){let l=new Map;te.set(s,{events:l});let u={id:s,on(w,b){let h=l.get(w)??[];return h.push(b),l.set(w,h),u},close:async()=>{te.delete(s),await M("notification.remove",[s])}};return M("notification.subscribe",[{id:s}]).catch(()=>{}),u}function M(s,l){let u=`host-api-${E}-${++r}`,w={type:"host-api-call",id:u,method:s,args:l,pluginId:E};return new Promise((b,h)=>{i.set(u,{resolve:b,reject:h}),U(w)})}}import{dirname as vt,join as L,normalize as Me}from"node:path";import{fileURLToPath as yt}from"node:url";var k={INVALID_ARG:-3001,PACKAGE_INVALID:-3002,BUILD_FAIL:-3003,NOT_FOUND:-3004,NODEJS_MISSING:-3010,DUPLICATE:-3011,PERMISSION_DENIED:-3012,CANCELED:-3013};import{basename as mt,join as Re}from"node:path";var we=10*60*1e3;function Ae(e){let{rpc:t}=e;async function n(o,c){let g=await e.readText(Re(o,"package.json"));if(g==null)throw new m(k.NOT_FOUND,{enUS:`Cannot read package.json in ${o}`,zhCN:`\u65E0\u6CD5\u8BFB\u53D6\u63D2\u4EF6\u76EE\u5F55\u4E0B\u7684 package.json\uFF1A${o}`});let v=JSON.parse(g),f=String(v?.dlient?.id??"");if(f!==c)throw new m(k.INVALID_ARG,{enUS:`Manifest id mismatch (expected "${c}", got "${f}")`,zhCN:`\u63D2\u4EF6 manifest \u7684 id \u4E0E\u8BF7\u6C42\u4E0D\u4E00\u81F4\uFF08\u671F\u671B\u300C${c}\u300D\uFF0C\u5B9E\u9645\u300C${f}\u300D\uFF09`});let p=String(v?.version??"").trim();if(!/^\d+\.\d+\.\d+([-+][0-9A-Za-z.-]+)?$/.test(p))throw new m(k.INVALID_ARG,{enUS:`Invalid or missing package.json version: "${p}"`,zhCN:`package.json \u7684 version \u7F3A\u5931\u6216\u4E0D\u5408\u6CD5\uFF1A\u300C${p}\u300D`});if(!v?.scripts?.pack)throw new m(k.BUILD_FAIL,{enUS:'This plugin has no "pack" script, cannot produce a .dlient package. Add e.g. "pack": "npm run build && node script/make-dlient.mjs"',zhCN:'\u8BE5\u63D2\u4EF6\u672A\u58F0\u660E pack \u811A\u672C\uFF0C\u65E0\u6CD5\u4EA7\u51FA .dlient \u5305\u3002\u8BF7\u53C2\u8003\u6A21\u677F\u52A0\u5165 "pack": "npm run build && node script/make-dlient.mjs"'});return{version:p}}async function r(o,c){await e.ensureNode();let g=await t.child.spawn({cmd:ie,args:["run","pack"],cwd:o}),v="";g.onStdout(p=>v+=p),g.onStderr(p=>v+=p);let f=await e.awaitChild(g,we);if(f.reason==="timeout")throw e.log("error","packDlient timeout",{pluginId:c}),new m(k.BUILD_FAIL,{enUS:`Packaging timed out after ${Math.round(we/6e4)} minutes${v.trim()?`: ${v.trim().slice(-300)}`:""}`,zhCN:`\u6253\u5305\u8D85\u65F6\uFF08${Math.round(we/6e4)} \u5206\u949F\uFF09${v.trim()?`\uFF1A${v.trim().slice(-300)}`:""}`});if(f.code!==0){let p=v.trim().slice(-500);throw e.log("error","packDlient failed",{pluginId:c,code:f.code}),new m(k.BUILD_FAIL,{enUS:`npm run pack failed (exit ${f.code??"unknown"})${p?`: ${p}`:""}`,zhCN:`npm run pack \u5931\u8D25\uFF08exit ${f.code??"unknown"}\uFF09${p?`\uFF1A${p}`:""}`})}}async function i(o,c){let f=(await t.fs.listDir(o).catch(()=>null)??[]).filter(p=>p.isFile!==!1&&/\.dlient$/i.test(String(p.name))&&Number(p.mtimeMs??0)>=c).sort((p,I)=>Number(I.mtimeMs??0)-Number(p.mtimeMs??0))[0];if(!f)throw new m(k.BUILD_FAIL,{enUS:`Packaging finished but no .dlient file was produced in ${o}`,zhCN:`\u6253\u5305\u5DF2\u7ED3\u675F\uFF0C\u4F46\u5728 ${o} \u672A\u627E\u5230\u65B0\u4EA7\u51FA\u7684 .dlient \u6587\u4EF6`});return Re(o,f.name)}return{async run(o,c){let g=String(c.pluginId??"");await n(o,g);let v=Date.now();e.log("info","packDlient start",{pluginId:g}),await r(o,g);let f=await i(o,v),p=mt(f);if(e.log("info","packDlient produced",{pluginId:g,name:p}),c.savePath)return await a(f,c.savePath),{ok:!0,name:p,savePath:c.savePath};let I=await t.dialog.showSaveDialog({defaultPath:f,filters:[{name:"dlient",extensions:["dlient"]}]}).catch(()=>null),y=String(I?.filePath??"");return y?(await a(f,y),{ok:!0,name:p,savePath:y}):{ok:!0,name:p,savePath:f}}};async function a(o,c){let g=String(await t.fs.read(o,{base64:!0}));await t.fs.write(c,{base64:g})}}var d=_e("dev-tools"),A={debug:(e,t)=>void d.log.write("debug",e,t),info:(e,t)=>void d.log.write("info",e,t),warn:(e,t)=>void d.log.write("warn",e,t),error:(e,t)=>void d.log.write("error",e,t)};async function ve(e){try{let t=await d.fs.stat(e);return{exists:!0,size:t?.size??0,mtimeMs:t?.mtimeMs??0}}catch{return null}}function ye(e){return ve(e).then(t=>t?.exists===!0)}async function Ue(e){try{return String(await d.fs.read(e))}catch{return null}}async function kt(e){try{return String(await d.fs.read(e,{base64:!0}))}catch{return null}}async function Le(e){await d.fs.mkdir(e)}async function je(e,t){await d.fs.write(e,t)}async function bt(e,t){await d.fs.write(e,{base64:t})}function St(e){return typeof e=="string"?e:void 0}var he=Promise.resolve();function W(){return d.app.data.read("plugins.json").then(e=>Array.isArray(e)?e:[])}function se(e){return he=he.then(()=>d.app.data.write("plugins.json",e)),he}var He=Promise.resolve();function It(e){He=e.then(()=>{},()=>{})}function Dt(){return He}async function be(e,t){try{let n=await d.permission.request([{type:"fs",path:e,mode:["read","write"]}],t);return!(n?.denied&&n.denied.length>0)}catch(n){return A.warn("ensureDirAccess failed",{dir:e,error:n instanceof Error?n.message:String(n)}),!1}}async function Nt(){let e=await W(),t=e.map(o=>o.path).filter(o=>!!o);if(t.length===0)return[];let n=await d.permission.request(t.map(o=>({type:"fs",path:o,mode:["read","write"]})),"dev \u63D2\u4EF6\u76EE\u5F55\u8BFB\u5199\uFF08\u540C\u610F\u540E\u7EB3\u5165 dev-tools \u7BA1\u7406\uFF1B\u62D2\u7EDD\u7684\u63D2\u4EF6\u5C06\u4ECE\u5217\u8868\u79FB\u9664\uFF09").catch(()=>null),r=new Set(n?.denied??[]);if(r.size===0)return[];let i=[],a=e.filter(o=>!o.path||!r.has(o.path)?!0:(i.push(o.id),!1));if(i.length>0){await se(a);for(let o of i)await Se(o);await ee(a),A.warn("dev dirs denied, removed from plugins.json",{removed:i})}return i}async function xe(e){try{let t=await W(),n=Me(e),r=t.find(a=>Me(String(a.path??""))===n);if(!r)return;let i=t.filter(a=>a!==r);if(i.length===t.length)return;await se(i),await Se(r.id),await ee(i),A.warn("dev entry removed (dir read failed)",{id:r.id,dir:e})}catch(t){A.error("dropEntryIfRegistered failed",{dir:e,error:t instanceof Error?t.message:String(t)})}}async function ee(e){await d.plugin.dev.sync(e).catch(()=>{})}async function Se(e){Oe(e),await Ee(e)}function Et(e){return JSON.stringify({permissions:e.permissions,dependencies:e.dependencies,fsDirs:e.fsDirs,spawnCmds:e.spawnCmds,expose:e.expose})}async function V(e){if(!await be(e,"dev \u63D2\u4EF6\u76EE\u5F55\u8BFB\u5199\uFF08\u540C\u610F\u540E\u7EB3\u5165 dev-tools \u7BA1\u7406\uFF09"))return A.warn("readManifest denied dir access",{dir:e}),xe(e),null;try{await Dt();let t=await d.fs.read(`${e}/package.json`),n=JSON.parse(String(t)),r=n.dlient??{};return{id:typeof r.id=="string"?r.id:void 0,name:typeof r.name=="string"?r.name:r.name?.default,dist:typeof r.dist=="string"?r.dist:void 0,version:typeof n.version=="string"?n.version:void 0,type:typeof r.type=="string"?r.type:void 0,system:r.system===!0,icon:St(r.icon),source:typeof r.source=="string"?r.source:void 0,nodeVersion:typeof r.nodeVersion=="string"?r.nodeVersion:void 0,authHash:Et(r),dependencies:Array.isArray(r.dependencies)?r.dependencies.map(i=>String(i)):r.dependencies&&typeof r.dependencies=="object"?Object.keys(r.dependencies):void 0}}catch(t){return A.warn("readManifest failed",{dir:e,error:t instanceof Error?t.message:String(t)}),xe(e),null}}function Ie(e){let t=e??"ui";return{hasUI:t!=="worker",needsWorker:t!=="ui"}}var le=new Map,ae=new Map,ke=new Map,Pt=1500;function Oe(e){let t=le.get(e);t&&(le.delete(e),d.fs.unwatch(t).catch(()=>{}));let n=ae.get(e);n&&(clearTimeout(n),ae.delete(e))}async function ze(e){if(Oe(e.id),!!await ye(L(e.path,"package.json")))try{let t=String(await d.fs.watch(e.path));le.set(e.id,t)}catch{}}d.registerHandler("dev-tools.fs-watch-event",([e,t])=>{if(String(t??"").replace(/\\/g,"/")!=="package.json")return;let n=Array.from(le.entries()).find(([,i])=>i===e)?.[0];if(!n)return;let r=ae.get(n);r&&clearTimeout(r),ae.set(n,setTimeout(async()=>{ae.delete(n);let i=ke.get(n);if(i&&Date.now()-i<Pt){ke.delete(n);return}try{let a=await W(),o=a.find(f=>f.id===n);if(!o)return;let c=await V(o.path);if(!c)return;let g={...o,name:c.name??o.name,version:c.version,type:c.type,dist:c.dist??o.dist,system:c.system===!0,icon:c.icon,authHash:c.authHash},v=a.map(f=>f.id===n?g:f);await se(v),await ee(v)}catch(a){A.error("sync package.json failed",{pluginId:n,error:a instanceof Error?a.message:String(a)})}},500))});d.registerHandler("dev-tools.list",async()=>(await W()).sort((t,n)=>t.addedAt-n.addedAt));async function Be(){return(await d.dialog.showOpenDialog(["fs.read","fs.write"],{properties:["openDirectory"]}).catch(()=>null))?.filePaths?.[0]??null}d.registerHandler("dev-tools.selectDirectory",()=>Be());d.registerHandler("dev-tools.checkImportable",async([e])=>{let t=String(e??"").trim();if(!t)return{ok:!1,error:{enUS:"Plugin directory cannot be empty",zhCN:"\u63D2\u4EF6\u76EE\u5F55\u4E0D\u80FD\u4E3A\u7A7A"}};if(!await be(t,"\u5BFC\u5165\u63D2\u4EF6\u76EE\u5F55"))return{ok:!1,error:{enUS:"Canceled: no access permission granted to this directory",zhCN:"\u5DF2\u53D6\u6D88\uFF1A\u672A\u6388\u4E88\u8BE5\u76EE\u5F55\u7684\u8BBF\u95EE\u6743\u9650"}};let n=await V(t);return n?.id?/^[a-z0-9-]+$/.test(n.id)?{ok:!0,needsDevChange:n.source!=="dev",id:n.id,name:n.name??n.id}:{ok:!1,error:{enUS:`Invalid plugin ID (only lowercase letters, digits and hyphens allowed, current: ${n.id})`,zhCN:`\u63D2\u4EF6 ID \u4E0D\u5408\u6CD5\uFF08\u4EC5\u5141\u8BB8\u5C0F\u5199\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u8FDE\u5B57\u7B26\uFF0C\u5F53\u524D\uFF1A${n.id}\uFF09`}}:{ok:!1,error:{enUS:"No valid plugin manifest found in the directory (dlient.id in package.json)",zhCN:"\u76EE\u5F55\u4E2D\u672A\u627E\u5230\u6709\u6548\u7684\u63D2\u4EF6 manifest\uFF08package.json \u7684 dlient.id\uFF09"}}});d.registerHandler("dev-tools.changeToDev",async([e])=>{let t=String(e??"").trim();if(!t)throw new m(k.INVALID_ARG,{enUS:"Plugin directory cannot be empty",zhCN:"\u63D2\u4EF6\u76EE\u5F55\u4E0D\u80FD\u4E3A\u7A7A"});if(!await be(t,"\u5199\u56DE\u63D2\u4EF6 manifest\uFF08dlient.source=dev\uFF09"))throw new m(k.PERMISSION_DENIED,{enUS:"Canceled: no access permission granted to this directory",zhCN:"\u5DF2\u53D6\u6D88\uFF1A\u672A\u6388\u4E88\u8BE5\u76EE\u5F55\u7684\u8BBF\u95EE\u6743\u9650"});let n=String(await d.fs.read(`${t}/package.json`).catch(()=>""));if(!n)throw new m(k.PACKAGE_INVALID,{enUS:"No package.json found in the directory",zhCN:"\u76EE\u5F55\u4E2D\u672A\u627E\u5230 package.json"});let r=JSON.parse(n),i={...r.dlient??{}};return i.source==="dev"?{ok:!0,changed:!1}:(i.source="dev",await d.fs.write(`${t}/package.json`,`${JSON.stringify({...r,dlient:i},null,Ve(n))}
|
|
10
|
+
`),{ok:!0,changed:!0})});async function We(e){let t=String(e??"").trim();if(!t)throw new m(k.INVALID_ARG,{enUS:"Plugin directory cannot be empty",zhCN:"\u63D2\u4EF6\u76EE\u5F55\u4E0D\u80FD\u4E3A\u7A7A"});let n=await V(t);if(!n?.id)throw new m(k.PACKAGE_INVALID,{enUS:"No valid plugin manifest found in the directory (dlient.id in package.json)",zhCN:"\u76EE\u5F55\u4E2D\u672A\u627E\u5230\u6709\u6548\u7684\u63D2\u4EF6 manifest\uFF08package.json \u7684 dlient.id\uFF09"});if(n.source!=="dev")throw new m(k.PACKAGE_INVALID,{enUS:`This plugin is not a dev plugin (dlient.source="${n.source??"unset"}"). Change dlient.source in package.json to "dev" and add it again.`,zhCN:`\u8BE5\u63D2\u4EF6\u4E0D\u662F dev \u63D2\u4EF6\uFF08dlient.source="${n.source??"\u672A\u58F0\u660E"}"\uFF09\u3002\u8BF7\u5C06 package.json \u7684 dlient.source \u6539\u4E3A "dev" \u540E\u91CD\u65B0\u6DFB\u52A0\u3002`});if(!/^[a-z0-9-]+$/.test(n.id))throw new m(k.INVALID_ARG,{enUS:`Invalid plugin ID (only lowercase letters, digits and hyphens allowed, current: ${n.id}). Edit dlient.id in package.json`,zhCN:`\u63D2\u4EF6 ID \u4E0D\u5408\u6CD5\uFF08\u4EC5\u5141\u8BB8\u5C0F\u5199\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u8FDE\u5B57\u7B26\uFF0C\u5F53\u524D\uFF1A${n.id}\uFF09\uFF0C\u8BF7\u4FEE\u6539 package.json \u7684 dlient.id`});let r=await W(),i=r.find(c=>c.id===n.id),a={id:n.id,name:n.name??n.id,path:t,dist:n.dist??"dist",version:n.version,type:n.type,system:n.system===!0,icon:n.icon,authHash:n.authHash,addedAt:i?.addedAt??Date.now()},o=i?r.map(c=>c.id===a.id?a:c):[...r,a];return await se(o),await ze(a),await ee(o),a}d.registerHandler("dev-tools.register",([e])=>We(String(e)));d.registerHandler("dev-tools.remove",async([e])=>{let t=String(e),n=await W(),r=n.filter(i=>i.id!==t);return r.length===n.length?{ok:!1,error:{enUS:`Dev plugin not registered: ${t}`,zhCN:`dev \u63D2\u4EF6\u672A\u6CE8\u518C: ${t}`}}:(await se(r),await Se(t),await ee(r),{ok:!0})});async function H(e){let n=(await W()).find(r=>r.id===e);return n?{dir:n.path,dist:n.dist}:await d.plugin.dev.getDirInfo(e)}d.registerHandler("dev-tools.getDirInfo",([e])=>H(String(e)));var Fe=["name","description","type","platforms","icon","tags"];async function qe(e){let t=await H(e);if(!t)throw new m(k.NOT_FOUND,{enUS:`Dev plugin does not exist: ${e}`,zhCN:`dev \u63D2\u4EF6\u4E0D\u5B58\u5728: ${e}`});let n=String(await d.fs.read(`${t.dir}/package.json`));return{dir:t.dir,raw:n}}function Ve(e){let t=/\n([ \t]+)"/.exec(e);return t?t[1].includes(" ")?" ":t[1].length:2}d.registerHandler("dev-tools.readSettings",async([e])=>{let{dir:t,raw:n}=await qe(String(e)),r=JSON.parse(n),i=r.dlient??{},a={};for(let o of Fe)i[o]!==void 0&&(a[o]=i[o]);return{dir:t,version:typeof r.version=="string"?r.version:void 0,id:typeof i.id=="string"?i.id:void 0,dlient:a}});d.registerHandler("dev-tools.writeSettings",async([e,t])=>{let n=String(e),r=t??{},{dir:i,raw:a}=await qe(n),o=JSON.parse(a),c={...o.dlient??{}},g=!1;for(let p of Fe){if(!(p in r))continue;let I=r[p];if(I==null){p in c&&(delete c[p],g=!0);continue}JSON.stringify(c[p])!==JSON.stringify(I)&&(c[p]=I,g=!0)}if(!g)return{ok:!0,changed:!1};let v=JSON.stringify({...o,dlient:c},null,Ve(a));ke.set(n,Date.now()),await d.fs.write(`${i}/package.json`,`${v}
|
|
11
|
+
`);let f=await V(i);if(f){let p=await W(),I=p.find(y=>y.id===n);if(I){let y={...I,name:f.name??I.name,version:f.version,type:f.type,dist:f.dist??I.dist,system:f.system===!0,icon:f.icon,authHash:f.authHash},R=p.map(N=>N.id===n?y:N);await se(R),await ee(R)}}return{ok:!0,changed:!0}});d.registerHandler("dev-tools.writeAsset",async([e,t,n])=>{let r=String(e),i=String(t??"");if(!/^icon[\w-]*\.(svg|png)$/.test(i))throw new m(k.INVALID_ARG,{enUS:`Invalid icon file name: ${i}`,zhCN:`\u975E\u6CD5\u7684\u56FE\u6807\u6587\u4EF6\u540D: ${i}`});let a=await H(r);if(!a)throw new m(k.NOT_FOUND,{enUS:`Dev plugin does not exist: ${r}`,zhCN:`dev \u63D2\u4EF6\u4E0D\u5B58\u5728: ${r}`});let o=Buffer.from(String(n??""),"base64");if(o.length===0||o.length>200*1024)throw new m(k.INVALID_ARG,{enUS:"Icon content is invalid or exceeds 200KB",zhCN:"\u56FE\u6807\u5185\u5BB9\u975E\u6CD5\u6216\u8D85\u8FC7 200KB"});let c=L(a.dir,"assets");return await Le(c),await bt(L(c,i),String(n??"")),A.info(`writeAsset ${r} ${i} ${o.length}B`),{ok:!0,path:`assets/${i}`}});d.registerHandler("dev-tools.readAsset",async([e,t])=>{let n=String(e),r=String(t??"");if(!/^icon[\w-]*\.(svg|png)$/.test(r))throw new m(k.INVALID_ARG,{enUS:`Invalid icon file name: ${r}`,zhCN:`\u975E\u6CD5\u7684\u56FE\u6807\u6587\u4EF6\u540D: ${r}`});let i=await H(n);if(!i)throw new m(k.NOT_FOUND,{enUS:`Dev plugin does not exist: ${n}`,zhCN:`dev \u63D2\u4EF6\u4E0D\u5B58\u5728: ${n}`});let a=L(i.dir,"assets",r),o=await kt(a);if(o==null||o.length===0)return{ok:!0,data:"",mime:"",exists:!1};let c=r.endsWith(".png")?"image/png":"image/svg+xml";return{ok:!0,data:o,mime:c,exists:!0}});var Ge=["index.md","index.zh-CN.md","index.en-US.md","SKILL.md"];d.registerHandler("dev-tools.readMarkdown",async([e,t])=>{let n=String(e),r=String(t??"");if(!Ge.includes(r))throw new m(k.INVALID_ARG,{enUS:`Invalid markdown file name: ${r}`,zhCN:`\u975E\u6CD5\u7684 markdown \u6587\u4EF6\u540D: ${r}`});let i=await H(n);if(!i)throw new m(k.NOT_FOUND,{enUS:`Dev plugin does not exist: ${n}`,zhCN:`dev \u63D2\u4EF6\u4E0D\u5B58\u5728: ${n}`});let a=L(i.dir,"assets",r);try{return{ok:!0,content:String(await d.fs.read(a)),exists:!0}}catch{return{ok:!0,content:"",exists:!1}}});d.registerHandler("dev-tools.writeMarkdown",async([e,t,n])=>{let r=String(e),i=String(t??"");if(!Ge.includes(i))throw new m(k.INVALID_ARG,{enUS:`Invalid markdown file name: ${i}`,zhCN:`\u975E\u6CD5\u7684 markdown \u6587\u4EF6\u540D: ${i}`});let a=await H(r);if(!a)throw new m(k.NOT_FOUND,{enUS:`Dev plugin does not exist: ${r}`,zhCN:`dev \u63D2\u4EF6\u4E0D\u5B58\u5728: ${r}`});let o=L(a.dir,"assets");return await Le(o),await je(L(o,i),String(n??"")),A.info(`writeMarkdown ${r} ${i} ${String(n??"").length}B`),{ok:!0}});async function De(e){let t=await d.nodejs.resolveRuntime(e?{version:e}:void 0).catch(()=>null);return!t||t.source==="none"||t.source===void 0?{ok:!1,reason:"runtime-missing"}:{ok:!0}}function $t(e){let t=/^v?(\d+)\.?(\d+)?\.?(\d+)?/.exec(e.trim().replace(/^>=?/,""));return t?[Number(t[1]),Number(t[2]??0),Number(t[3]??0)]:null}async function Ct(){try{let e=L(vt(yt(import.meta.url)),"..","package.json"),t=await d.fs.read(e),r=JSON.parse(String(t)).dlient??{};return{nodeVersion:typeof r.nodeVersion=="string"?r.nodeVersion:void 0}}catch{return null}}async function _t(){let t=[(await Ct())?.nodeVersion],n=await W();for(let a of n){let o=await V(a.path);o?.nodeVersion&&t.push(o.nodeVersion)}let r,i=null;for(let a of t){if(!a)continue;let o=$t(a);o&&(!i||o[0]>i[0]||o[0]===i[0]&&o[1]>i[1]||o[0]===i[0]&&o[1]===i[1]&&o[2]>i[2])&&(r=a,i=o)}return r}function Je(){return{enUS:"No usable Node.js runtime. Install system Node.js, or let the host download the built-in LTS runtime and retry.",zhCN:"\u672A\u68C0\u6D4B\u5230\u53EF\u7528\u7684 Node.js \u8FD0\u884C\u65F6\uFF1B\u8BF7\u5B89\u88C5\u7CFB\u7EDF Node.js\uFF0C\u6216\u7531\u5BBF\u4E3B\u4E0B\u8F7D\u5185\u7F6E LTS \u8FD0\u884C\u65F6\u540E\u91CD\u8BD5"}}d.registerHandler("dev-tools.checkNode",async()=>{await Nt();let e=await _t(),t=await De(e);if(!t.ok)return{ready:!1,reason:t.reason,requiredVersion:e};let n=await d.nodejs.resolveRuntime(e?{version:e}:void 0).catch(()=>null);return{ready:!0,version:n&&n.source!=="none"?n.version:void 0,requiredVersion:e}});d.registerHandler("dev-tools.installNode",async([e])=>{let t=typeof e=="string"&&e.trim()?e.trim():void 0,n=await d.nodejs.install(t).catch(r=>({ok:!1,error:r instanceof Error?r.message:String(r)}));if(n?.ok)return{ok:!0};throw new m(k.NODEJS_MISSING,{enUS:`Failed to install the built-in Node.js runtime: ${n?.error??"unknown error"}`,zhCN:`\u5B89\u88C5\u5185\u7F6E Node.js \u8FD0\u884C\u65F6\u5931\u8D25\uFF1A${n?.error??"\u672A\u77E5\u9519\u8BEF"}`})});async function Rt(e,t){let n=L(e,"package.json"),r=await Ue(n);if(r==null)return;let i=JSON.parse(r);i.dlient={...i.dlient??{},name:t},await je(n,JSON.stringify(i,null,2))}var At=18e4;async function Mt(e,t,n=At){let r;try{await ue();let c=await Ut();r=await d.child.spawn({cmd:ie,args:["exec","--yes",...c,...e],cwd:t})}catch(c){return{ok:!1,detail:String(c instanceof Error?c.message:c).slice(0,300)}}let i="";r.onStdout(c=>i+=c),r.onStderr(c=>i+=c);let a=await Ne(r,n);if(a.reason==="timeout")return{ok:!1,detail:`npm exec \u8D85\u65F6\uFF08${Math.round(n/1e3)}s\uFF09${i.trim()?`\uFF1A${i.trim().slice(0,300)}`:""}`};let o=i.trim();return a.code!==0?{ok:!1,detail:(o||`exit ${a.code}`).slice(0,500)}:{ok:!0}}d.registerHandler("dev-tools.devCreatePlugin",async([e,t])=>{let n=String(e??"").trim(),r=String(t??"").trim();if(!/^[a-z][a-z0-9-]*$/.test(n))throw new m(k.INVALID_ARG,{enUS:"Invalid plugin ID (must start with a lowercase letter; lowercase letters/digits/hyphens only)",zhCN:"\u63D2\u4EF6 ID \u4E0D\u5408\u6CD5\uFF08\u987B\u4EE5\u5C0F\u5199\u5B57\u6BCD\u5F00\u5934\uFF0C\u4EC5\u542B\u5C0F\u5199\u5B57\u6BCD/\u6570\u5B57/\u8FDE\u5B57\u7B26\uFF09"});if(!r)throw new m(k.INVALID_ARG,{enUS:"Plugin name cannot be empty",zhCN:"\u63D2\u4EF6\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A"});let[i,a]=await Promise.all([W(),d.plugin.scanInstalled().catch(()=>[])]);if([...i,...a??[]].some(v=>v.id===n))throw new m(k.DUPLICATE,{enUS:`A plugin with the same ID "${n}" already exists locally and cannot be created`,zhCN:`\u672C\u5730\u5DF2\u5B58\u5728\u540C\u540D\u63D2\u4EF6\u300C${n}\u300D\uFF0C\u65E0\u6CD5\u521B\u5EFA`});let c=await Be();if(!c)return null;let g=await Mt(["@dlient-open/create-plugin","--",n,"--dir",c,"--skip-install","--no-git"],c);if(!g.ok)throw new m(k.BUILD_FAIL,{enUS:`npx @dlient-open/create-plugin failed: ${g.detail}`,zhCN:`npx @dlient-open/create-plugin \u521B\u5EFA\u5931\u8D25\uFF1A${g.detail}`});return await Rt(L(c,n),r),We(L(c,n))});d.registerHandler("dev-tools.runtimeStates",()=>d.plugin.runtimeList());d.registerHandler("dev-tools.previewInfo",async([e])=>{let t=String(e),n=await H(t),r=n?await V(n.dir):null,i=(r?.type??"ui")!=="worker";return{pluginId:`${t}@dev`,remoteEntryUrl:`dlientOpen://plugin/${t}@dev/dist/remoteEntry.js`,hasUI:i,manifest:r?.nodeVersion||r?.dependencies?.length?{nodeVersion:r.nodeVersion,dependencies:r.dependencies}:void 0}});var Q=new Map,B=new Map,Z=new Map,oe=new Map,Te=5e3;function me(e,t,n){if(!n)return;let r=Z.get(e)??[];r.push({ts:Date.now(),stream:t,data:n}),r.length>Te&&r.splice(0,r.length-Te),Z.set(e,r)}var xt=["zh"],de;async function Ke(){if(de===void 0)try{de=String(await d.app.getLocale())}catch{de="en-US"}return xt.some(e=>de.startsWith(e))}async function Tt(e){let t=await Ke()?"https://registry.npmmirror.com":void 0;return!t||e.some(n=>n.startsWith("--registry"))?e:[...e,`--registry=${t}`]}async function Ut(){let e=await Ke()?"https://registry.npmmirror.com":void 0;return e?[`--registry=${e}`]:[]}async function ue(){if(!(await De()).ok)throw new m(k.NODEJS_MISSING,Je())}async function Ne(e,t){return new Promise(n=>{let r=!1,i=o=>{r||(r=!0,clearTimeout(a),n(o))};e.onExit(o=>i({code:o.code,reason:o.reason==="killed"?"killed":"exited"})),e.onError(o=>i({code:-1,reason:"error",message:o.message}));let a=setTimeout(()=>{e.kill().catch(()=>{}),i({code:null,reason:"timeout"})},t)})}async function Xe(e){let t=B.get(e);if(t&&t.status==="running")return{ok:!0,procId:t.procId,reused:!0};let n=await H(e);if(!n)throw new m(k.NOT_FOUND,{enUS:`Dev plugin does not exist: ${e}`,zhCN:`dev \u63D2\u4EF6\u4E0D\u5B58\u5728: ${e}`});try{await ue();let r=await d.child.spawn({cmd:ie,args:["run","dev"],cwd:n.dir});return Q.set(e,r),B.set(e,{procId:r.handleId,caller:"dev-tools",kind:"dev",pid:r.pid,status:"running",cwd:n.dir,startedAt:Date.now()}),Z.delete(e),oe.set(e,Date.now()),r.onStdout(i=>me(e,"stdout",i)),r.onStderr(i=>me(e,"stderr",i)),r.onExit(()=>{let i=B.get(e);i&&(i.status="exited")}),r.onError(i=>{let a=B.get(e);a&&(a.status="error"),me(e,"stderr",`spawn error: ${i.message}`)}),{ok:!0,procId:r.handleId}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}d.registerHandler("dev-tools.buildDev",([e])=>Xe(String(e)));d.registerHandler("dev-tools.stop",async([e])=>{let t=String(e),n=Array.from(B.entries()).find(([,r])=>r.procId===t)?.[0];if(n){let r=Q.get(n);r&&await r.kill().catch(()=>{}),Q.delete(n),B.delete(n),Z.delete(n),oe.delete(n)}return{ok:!0}});d.registerHandler("dev-tools.listBuilds",()=>Array.from(B.values()));d.registerHandler("dev-tools.log",([e,t])=>{let n=Array.from(B.entries()).find(([,i])=>i.procId===String(e))?.[0];if(!n)return[];let r=typeof t=="number"&&t>0?t:0;return(Z.get(n)??[]).filter(i=>i.ts>=r)});async function Ee(e){let t=Q.get(e);t&&(await t.kill().catch(()=>{}),Q.delete(e),B.delete(e),Z.delete(e),oe.delete(e))}async function Pe(e,t){try{await ue();let n=await Tt(t),r=await d.child.spawn({cmd:ie,args:n,cwd:e}),i="";r.onStderr(o=>i+=o);let a=await Ne(r,10*60*1e3);return a.reason==="timeout"?{ok:!1,detail:`npm ${t.join(" ")} \u8D85\u65F6\uFF0810min\uFF09`}:a.reason==="error"?{ok:!1,detail:`npm ${t.join(" ")} \u542F\u52A8\u5931\u8D25\uFF1A${a.message??""}`.slice(0,300)}:a.code!==0?{ok:!1,detail:(i.trim()||`npm ${t.join(" ")} \u8FD4\u56DE\u5931\u8D25`).slice(0,300)}:{ok:!0}}catch(n){return{ok:!1,detail:`\u6267\u884C npm \u5931\u8D25\uFF1A${n instanceof Error?n.message:String(n)}`.slice(0,300)}}}function Ye(e){return Pe(e,["install","--no-audit","--no-fund"])}d.registerHandler("dev-tools.ensureDeps",async([e])=>{let t=String(e),n=await H(t);if(!n)throw new m(k.NOT_FOUND,{enUS:`Dev plugin does not exist: ${t}`,zhCN:`dev \u63D2\u4EF6\u4E0D\u5B58\u5728: ${t}`});if(!(await De()).ok)throw new m(k.NODEJS_MISSING,Je());let{needsWorker:i}=Ie((await V(n.dir))?.type),a=!1;if(!await ye(L(n.dir,"node_modules"))){let o=await Ye(n.dir);if(!o.ok)throw new m(k.BUILD_FAIL,{enUS:`npm install failed: ${o.detail}`,zhCN:`npm install \u5931\u8D25\uFF1A${o.detail}`});a=!0}if(i&&!await ye(L(n.dir,n.dist,"worker.js"))){let o=await Pe(n.dir,["run","build:worker"]);if(!o.ok)throw new m(k.BUILD_FAIL,{enUS:`build:worker failed: ${o.detail}`,zhCN:`build:worker \u5931\u8D25\uFF1A${o.detail}`})}return{ok:!0,installed:a}});d.registerHandler("dev-tools.checkManifest",async([e])=>{let t=String(e??"").trim();if(!t)return{ok:!1,error:{enUS:"Plugin ID cannot be empty",zhCN:"\u63D2\u4EF6 ID \u4E0D\u80FD\u4E3A\u7A7A"}};let n=await H(t);if(!n)return{ok:!1,error:{enUS:`Dev plugin does not exist: ${t}`,zhCN:`dev \u63D2\u4EF6\u4E0D\u5B58\u5728: ${t}`}};let r=await V(n.dir);return r?.id?r.id!==t?{ok:!1,error:{enUS:`The dlient.id in the manifest (${r.id}) does not match the registered ID (${t})`,zhCN:`manifest \u7684 dlient.id\uFF08${r.id}\uFF09\u4E0E\u6CE8\u518C ID\uFF08${t}\uFF09\u4E0D\u4E00\u81F4`}}:/^[a-z0-9-]+$/.test(r.id)?r.source!=="dev"?{ok:!1,error:{enUS:`This plugin is not a dev plugin (dlient.source="${r.source??"unset"}"). Change dlient.source in package.json to "dev"`,zhCN:`\u8BE5\u63D2\u4EF6\u4E0D\u662F dev \u63D2\u4EF6\uFF08dlient.source="${r.source??"\u672A\u58F0\u660E"}"\uFF09\uFF0C\u8BF7\u5C06 package.json \u7684 dlient.source \u6539\u4E3A "dev"`}}:{ok:!0,id:r.id,name:r.name}:{ok:!1,error:{enUS:`Invalid plugin ID (only lowercase letters, digits and hyphens allowed, current: ${r.id})`,zhCN:`\u63D2\u4EF6 ID \u4E0D\u5408\u6CD5\uFF08\u4EC5\u5141\u8BB8\u5C0F\u5199\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u8FDE\u5B57\u7B26\uFF0C\u5F53\u524D\uFF1A${r.id}\uFF09`}}:{ok:!1,error:{enUS:"No valid plugin manifest found in the directory (dlient.id in package.json)",zhCN:"\u76EE\u5F55\u4E2D\u672A\u627E\u5230\u6709\u6548\u7684\u63D2\u4EF6 manifest\uFF08package.json \u7684 dlient.id\uFF09"}}});d.registerHandler("dev-tools.ensureEnv",async([e])=>{let t=String(e??"").trim();if(!t)return{ok:!1,error:{enUS:"Plugin ID cannot be empty",zhCN:"\u63D2\u4EF6 ID \u4E0D\u80FD\u4E3A\u7A7A"}};let n=await H(t);if(!n)return{ok:!1,error:{enUS:`Dev plugin does not exist: ${t}`,zhCN:`dev \u63D2\u4EF6\u4E0D\u5B58\u5728: ${t}`}};let{hasUI:r,needsWorker:i}=Ie((await V(n.dir))?.type),a=oe.get(t)??0;return await Qe(n,r,i,a,3e4)?(await d.plugin.dev.startWatcher(t).catch(c=>A.error(`ensureEnv ${t} startWatcher`,{error:c instanceof Error?c.message:String(c)})),{ok:!0}):{ok:!1,error:{enUS:`${i?"worker.js":"remoteEntry.js"} initial build not complete (npm run dev did not produce new artifacts)`,zhCN:`${i?"worker.js":"remoteEntry.js"} \u521D\u59CB\u6784\u5EFA\u672A\u5B8C\u6210\uFF08npm run dev \u672A\u4EA7\u51FA\u65B0\u4EA7\u7269\uFF09`}}});d.registerHandler("dev-tools.closePlugin",async([e])=>{let t=String(e??"").trim();return t?(await d.plugin.dev.stopWatcher(t).catch(n=>A.error("closePlugin stopWatcher",{pluginId:t,error:n instanceof Error?n.message:String(n)})),await d.plugin.dev.stopDevWorker(t).catch(n=>A.error("closePlugin stopDevWorker",{pluginId:t,error:n instanceof Error?n.message:String(n)})),await Ee(t),{ok:!0}):{ok:!1,error:{enUS:"Plugin ID cannot be empty",zhCN:"\u63D2\u4EF6 ID \u4E0D\u80FD\u4E3A\u7A7A"}}});function Lt(e){return new Promise(t=>setTimeout(t,e))}async function Qe(e,t,n,r,i,a){let o=[];t&&o.push(L(e.dir,e.dist,"remoteEntry.js")),n&&o.push(L(e.dir,e.dist,"worker.js"));let c=Date.now()+i;for(;Date.now()<c;){if((await Promise.all(o.map(p=>ve(p)))).every(p=>p?.exists===!0&&p.mtimeMs>=r))return!0;if(a){let p=Array.from(B.values()).find(I=>I.procId===a);if(p&&p.status!=="running")return!1}await Lt(500)}return(await Promise.all(o.map(v=>ve(v)))).every(v=>v?.exists===!0)}d.registerStreamHandler("dev-tools.refresh",async([e],t)=>{let n=String(e??"");if(!t?.emit)return{ok:!1,error:{enUS:"refresh requires a streaming call (api.listen)",zhCN:"refresh \u9700\u8981\u6D41\u5F0F\u8C03\u7528\uFF08api.listen\uFF09"}};let r=t.emit,i=(a,o,c)=>{let g={step:a,status:o};c?.detail!==void 0&&(g.detail=c.detail),c?.procId!==void 0&&(g.procId=c.procId),r(g)};try{let a=await H(n);if(!a)throw new m(k.NOT_FOUND,{enUS:`Dev plugin does not exist: ${n}`,zhCN:`dev \u63D2\u4EF6\u4E0D\u5B58\u5728: ${n}`});let{hasUI:o,needsWorker:c}=Ie((await V(a.dir))?.type);i(0,"running"),await d.plugin.dev.stopDevWorker(n).catch(y=>A.error("refresh stopDevWorker",{pluginId:n,error:y instanceof Error?y.message:String(y)})),i(0,"done"),await d.plugin.dev.stopWatcher(n).catch(y=>A.error("refresh stopWatcher",{pluginId:n,error:y instanceof Error?y.message:String(y)})),i(1,"running"),await Ee(n),i(1,"done"),i(2,"running");let g=await Ye(a.dir);if(!g.ok){let y={enUS:`npm install failed: ${g.detail}`,zhCN:`npm install \u5931\u8D25\uFF1A${g.detail}`};return i(2,"error",{detail:y}),i(5,"error",{detail:y}),{ok:!1,error:y}}i(2,"done"),i(3,"running");let v=Date.now();if(c){let y=await Pe(a.dir,["run","build:worker"]);if(!y.ok){let R={enUS:`build:worker failed: ${y.detail}`,zhCN:`build:worker \u5931\u8D25\uFF1A${y.detail}`};return i(3,"error",{detail:R}),i(5,"error",{detail:R}),{ok:!1,error:R}}}let f=await Xe(n);if(!f?.ok){let y=f?.error??"unknown",R={enUS:`Failed to start build: ${y}`,zhCN:`\u6784\u5EFA\u542F\u52A8\u5931\u8D25\uFF1A${y}`};return i(3,"error",{detail:R}),i(5,"error",{detail:R}),{ok:!1,error:R}}let p=oe.get(n)??v;if(!await Qe(a,o,c,p,12e4,f.procId)){let y={enUS:`${a.dist}/${c?"worker.js":"remoteEntry.js"} was not produced (build failed or timed out)`,zhCN:`${a.dist}/${c?"worker.js":"remoteEntry.js"} \u672A\u4EA7\u51FA\uFF08\u6784\u5EFA\u5931\u8D25\u6216\u8D85\u65F6\uFF09`};return i(3,"error",{detail:y}),i(5,"error",{detail:y}),{ok:!1,error:y}}return i(3,"done"),i(4,"running"),await d.plugin.dev.startWatcher(n).catch(y=>A.error("refresh startWatcher",{pluginId:n,error:y instanceof Error?y.message:String(y)})),i(4,"done"),i(5,"done",{procId:f.procId??null}),{ok:!0,procId:f.procId??null}}catch(a){let o=a instanceof m&&a.msg!==void 0?a.msg:a instanceof Error?a.message:String(a);return A.error("refresh failed",{pluginId:n,error:typeof o=="string"?o:o.zhCN}),i(5,"error",{detail:o}),{ok:!1,error:o}}});d.onReady(()=>{A.info("worker ready",{pid:process.pid}),It(W().then(async e=>{for(let t of e)await ze(t);await ee(e)}).catch(e=>A.error("initial sync failed",{error:e instanceof Error?e.message:String(e)})))});d.onDispose(async()=>{for(let e of Q.values())await e.kill().catch(()=>{});Q.clear(),B.clear(),Z.clear(),oe.clear()});var jt=Ae({rpc:d,ensureNode:ue,awaitChild:Ne,readText:Ue,log:(e,t,n)=>A[e](t,n)});d.registerHandler("dev-tools.packDlient",async([e])=>{let t=e??{},n=String(t.pluginId??"").trim();if(!n)throw new m(k.INVALID_ARG,{enUS:"pluginId required",zhCN:"\u7F3A\u5C11 pluginId \u53C2\u6570"});let r=await H(n);if(!r)throw new m(k.NOT_FOUND,{enUS:`Dev plugin does not exist: ${n}`,zhCN:`dev \u63D2\u4EF6\u4E0D\u5B58\u5728: ${n}`});return jt.run(r.dir,{pluginId:n,savePath:t.savePath?String(t.savePath):void 0})});d.registerHandler("dev-tools.revealPath",async([e])=>{let t=String(e??"").trim();if(!t)throw new m(k.INVALID_ARG,{enUS:"path required",zhCN:"\u7F3A\u5C11 path \u53C2\u6570"});return await d.os.showItemInFolder(t),{ok:!0}});
|
package/package.json
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dlient-open/dev-tools",
|
|
3
|
+
"version": "0.1.7",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist",
|
|
8
|
+
"assets",
|
|
9
|
+
"script"
|
|
10
|
+
],
|
|
11
|
+
"dlient": {
|
|
12
|
+
"id": "dev-tools",
|
|
13
|
+
"name": {
|
|
14
|
+
"default": "Dev Tools",
|
|
15
|
+
"zh-CN": "插件开发工具箱",
|
|
16
|
+
"en-US": "Dev Tools"
|
|
17
|
+
},
|
|
18
|
+
"description": {
|
|
19
|
+
"default": "Manage and preview dev plugins (create / build / preview / pack / logs)",
|
|
20
|
+
"zh-CN": "开发插件管理与预览:模板创建 / 添加目录 / 自动构建 / 实例预览 / 打包 / 日志",
|
|
21
|
+
"en-US": "Manage and preview dev plugins: template create / add dir / auto build / instance preview / pack / logs"
|
|
22
|
+
},
|
|
23
|
+
"type": "app",
|
|
24
|
+
"source": "market",
|
|
25
|
+
"system": false,
|
|
26
|
+
"icon": "assets/icon.svg",
|
|
27
|
+
"platforms": [
|
|
28
|
+
"win32.x64",
|
|
29
|
+
"win32.arm64",
|
|
30
|
+
"darwin.x64",
|
|
31
|
+
"darwin.arm64",
|
|
32
|
+
"linux.x64",
|
|
33
|
+
"linux.arm64"
|
|
34
|
+
],
|
|
35
|
+
"nodeVersion": "22",
|
|
36
|
+
"spawnCmds": [
|
|
37
|
+
"CMD_NODE",
|
|
38
|
+
"CMD_NPM"
|
|
39
|
+
],
|
|
40
|
+
"permissions": [
|
|
41
|
+
"log",
|
|
42
|
+
"fs.read",
|
|
43
|
+
"fs.write",
|
|
44
|
+
"fs.delete",
|
|
45
|
+
"fs.listDir",
|
|
46
|
+
"fs.watch",
|
|
47
|
+
"fs.stat",
|
|
48
|
+
"fs.mkdir",
|
|
49
|
+
"fs.unwatch",
|
|
50
|
+
"dialog.showOpenDialog",
|
|
51
|
+
"dialog.showSaveDialog",
|
|
52
|
+
"app.getLocale",
|
|
53
|
+
"app.data.read",
|
|
54
|
+
"app.data.write",
|
|
55
|
+
"child.spawn",
|
|
56
|
+
"nodejs.resolveRuntime",
|
|
57
|
+
"nodejs.install",
|
|
58
|
+
"permission.request",
|
|
59
|
+
"os.showItemInFolder",
|
|
60
|
+
"plugin.dev.sync",
|
|
61
|
+
"plugin.dev.getDirInfo",
|
|
62
|
+
"plugin.dev.startWatcher",
|
|
63
|
+
"plugin.dev.stopWatcher",
|
|
64
|
+
"plugin.dev.stopDevWorker",
|
|
65
|
+
"plugin.scanInstalled",
|
|
66
|
+
"plugin.runtimeList"
|
|
67
|
+
],
|
|
68
|
+
"fsDirs": {
|
|
69
|
+
"read": [
|
|
70
|
+
"PLUGINS"
|
|
71
|
+
],
|
|
72
|
+
"write": [
|
|
73
|
+
"PLUGINS"
|
|
74
|
+
]
|
|
75
|
+
},
|
|
76
|
+
"expose": {
|
|
77
|
+
"dev-tools.fs-watch-event": {
|
|
78
|
+
"description": "宿主 fs.watch 事件回调:目录内文件变更时宿主主动推送(watchId + filename),用于同步 dev 插件 package.json 变更",
|
|
79
|
+
"access": "default",
|
|
80
|
+
"paramsSchema": {
|
|
81
|
+
"type": "array",
|
|
82
|
+
"description": "参数(按位置)",
|
|
83
|
+
"items": [
|
|
84
|
+
{
|
|
85
|
+
"name": "watchId",
|
|
86
|
+
"type": "string",
|
|
87
|
+
"description": "宿主 fs.watch 返回的监听 ID(watchPackageJson 登记保存)"
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"name": "filename",
|
|
91
|
+
"type": "string",
|
|
92
|
+
"description": "变更文件名(仅处理顶层 package.json)"
|
|
93
|
+
}
|
|
94
|
+
]
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
"scripts": {
|
|
100
|
+
"build": "node script/build-clean.mjs && npm run build:ui && npm run build:worker",
|
|
101
|
+
"build:ui": "vite build",
|
|
102
|
+
"build:worker": "node script/build-worker.mjs",
|
|
103
|
+
"pack": "npm run build && node script/make-dlient.mjs",
|
|
104
|
+
"dev:watch": "vite build --watch",
|
|
105
|
+
"dev:watch:worker": "node script/build-worker.mjs --watch",
|
|
106
|
+
"dev": "concurrently -k \"npm:dev:watch\" \"npm:dev:watch:worker\""
|
|
107
|
+
},
|
|
108
|
+
"devDependencies": {
|
|
109
|
+
"@atomic-editor/editor": "^0.6.2",
|
|
110
|
+
"@dlient-open/api-bridge": "^0.1.0",
|
|
111
|
+
"@dlient-open/create-plugin": "^0.1.7",
|
|
112
|
+
"@dlient-open/i18n": "^0.1.0",
|
|
113
|
+
"@dlient-open/plugin-sdk": "^0.1.4",
|
|
114
|
+
"@dlient-open/ui": "^0.3.1",
|
|
115
|
+
"@types/node": "^26.2.0",
|
|
116
|
+
"@types/react": "^18.2.48",
|
|
117
|
+
"@types/react-dom": "^18.2.18",
|
|
118
|
+
"@vitejs/plugin-react": "^4.2.1",
|
|
119
|
+
"concurrently": "^9.1.0",
|
|
120
|
+
"esbuild": "^0.20.0",
|
|
121
|
+
"react": "18.3.1",
|
|
122
|
+
"react-dom": "18.3.1",
|
|
123
|
+
"typescript": "^5.2.2",
|
|
124
|
+
"vite": "^5.1.6"
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { rmSync } from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { fileURLToPath } from 'node:url'
|
|
4
|
+
|
|
5
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
6
|
+
rmSync(path.join(__dirname, '..', 'dist'), { recursive: true, force: true })
|
|
7
|
+
console.log('[clean] dist removed')
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// 构建 worker(esbuild):打包 src/main/index.ts → dist/worker.js(utilityProcess 入口)
|
|
2
|
+
// 用法:node build-worker.mjs 单次构建(输出到默认 dist)
|
|
3
|
+
// node build-worker.mjs --outdir <dir> 指定输出目录(npm run pack 复用构建链,构建到 pack/<version>/dist)
|
|
4
|
+
// node build-worker.mjs --watch 监听源码变化自动重建(dev 插件热重载用)
|
|
5
|
+
import { build, context } from 'esbuild'
|
|
6
|
+
import path from 'node:path'
|
|
7
|
+
import { fileURLToPath } from 'node:url'
|
|
8
|
+
|
|
9
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
10
|
+
|
|
11
|
+
const watchMode = process.argv.includes('--watch')
|
|
12
|
+
// --outdir:pack 场景把 worker 产物构建到 pack/<version>/dist(不改动开发期 dist)
|
|
13
|
+
const outdirIdx = process.argv.indexOf('--outdir')
|
|
14
|
+
const distDir = outdirIdx !== -1 && process.argv[outdirIdx + 1]
|
|
15
|
+
? path.resolve(__dirname, '..', process.argv[outdirIdx + 1])
|
|
16
|
+
: path.join(__dirname, '..', 'dist')
|
|
17
|
+
|
|
18
|
+
// ESM bundle 顶层注入 require / __filename / __dirname(createRequire + fileURLToPath):
|
|
19
|
+
// 库代码对 node 内建模块的动态 require 与 __filename/__dirname 用法在 esbuild ESM 输出中需运行时
|
|
20
|
+
// 提供;banner 定义后模块作用域可用(防 Node ESM 下 "Dynamic require of path is not supported")。
|
|
21
|
+
const ESM_REQUIRE_BANNER =
|
|
22
|
+
`import { createRequire as __dlientCreateRequire } from 'node:module';\n` +
|
|
23
|
+
`import { fileURLToPath as __dlientFileURLToPath } from 'node:url';\n` +
|
|
24
|
+
`import { dirname as __dlientDirname } from 'node:path';\n` +
|
|
25
|
+
`const require = __dlientCreateRequire(import.meta.url);\n` +
|
|
26
|
+
`const __filename = __dlientFileURLToPath(import.meta.url);\n` +
|
|
27
|
+
`const __dirname = __dlientDirname(__filename);\n`
|
|
28
|
+
|
|
29
|
+
const outfile = path.join(distDir, 'worker.js')
|
|
30
|
+
|
|
31
|
+
const options = {
|
|
32
|
+
entryPoints: [path.join(__dirname, '..', 'src/main/index.ts')],
|
|
33
|
+
bundle: true,
|
|
34
|
+
platform: 'node',
|
|
35
|
+
format: 'esm',
|
|
36
|
+
|
|
37
|
+
banner: { js: ESM_REQUIRE_BANNER },
|
|
38
|
+
target: 'node20',
|
|
39
|
+
outfile,
|
|
40
|
+
external: ['electron'],
|
|
41
|
+
minify: !watchMode,
|
|
42
|
+
logLevel: 'info',
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (watchMode) {
|
|
46
|
+
const ctx = await context(options)
|
|
47
|
+
await ctx.watch()
|
|
48
|
+
console.log('[worker:watch] watching src/main -> dist/worker.js (Ctrl+C to stop)')
|
|
49
|
+
} else {
|
|
50
|
+
await build(options)
|
|
51
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
// make-dlient.mjs - 开源版 .dlient 打包器(无需签名 / 无服务端依赖)
|
|
2
|
+
//
|
|
3
|
+
// 背景:原版 .dlient 需要服务端平台签名(signature.json 由 dev-tools 提交 hash 到
|
|
4
|
+
// /api/plugins/package 换取)。开源版宿主导入不做任何验签——导入端只要求:
|
|
5
|
+
// .dlient = 普通 zip,内含 package.json(dlient.id 合法)+ assets/ + skills/ + <dist>/
|
|
6
|
+
// 本脚本把「打包即 .dlient」一步完成(store 压缩 zip,纯 Node 无第三方依赖)。
|
|
7
|
+
//
|
|
8
|
+
// 用法(在插件目录执行,插件须已完成 npm run build 产出 <dist>):
|
|
9
|
+
// node script/make-dlient.mjs # 产出 <id>-<version>.dlient
|
|
10
|
+
// node script/make-dlient.mjs --version 1.2.3 # 覆盖版本(同步改写包内 version)
|
|
11
|
+
// node script/make-dlient.mjs --name my.dlient
|
|
12
|
+
// node script/make-dlient.mjs --build # 先执行插件自身 npm run build
|
|
13
|
+
//
|
|
14
|
+
// 说明:
|
|
15
|
+
// - package.json 落包时被改写:dlient.source='local'、dlient.system=false(与导入端一致);
|
|
16
|
+
// - dist/ 内的 node_modules(原生模块)不进包——原生依赖在导入时按 dlient.nativeModules 重装;
|
|
17
|
+
// - 中间产物放 .make-dlient-* 临时目录,结束自动清理;产物写到插件根目录。
|
|
18
|
+
import { spawnSync } from 'node:child_process'
|
|
19
|
+
import { cpSync, existsSync, mkdtempSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from 'node:fs'
|
|
20
|
+
import { join, sep } from 'node:path'
|
|
21
|
+
import { tmpdir } from 'node:os'
|
|
22
|
+
|
|
23
|
+
// ---- 参数 ----
|
|
24
|
+
function parseArgv() {
|
|
25
|
+
const out = { version: '', name: '', build: false }
|
|
26
|
+
const args = process.argv.slice(2)
|
|
27
|
+
for (let i = 0; i < args.length; i++) {
|
|
28
|
+
if (args[i] === '--version') out.version = String(args[++i] ?? '').trim()
|
|
29
|
+
else if (args[i] === '--name') out.name = String(args[++i] ?? '').trim()
|
|
30
|
+
else if (args[i] === '--build') out.build = true
|
|
31
|
+
else if (args[i].startsWith('-')) {
|
|
32
|
+
console.error(`未知参数: ${args[i]}`)
|
|
33
|
+
process.exit(1)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return out
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const ROOT = process.cwd()
|
|
40
|
+
const pkgPath = join(ROOT, 'package.json')
|
|
41
|
+
if (!existsSync(pkgPath)) {
|
|
42
|
+
console.error('[make-dlient] 当前目录不是插件工程(无 package.json)。请在插件目录内执行本脚本。')
|
|
43
|
+
process.exit(1)
|
|
44
|
+
}
|
|
45
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'))
|
|
46
|
+
const dlient = pkg?.dlient ?? {}
|
|
47
|
+
const id = typeof dlient.id === 'string' ? dlient.id : ''
|
|
48
|
+
if (!id || !/^[a-z0-9-]+$/.test(id)) {
|
|
49
|
+
console.error(`[make-dlient] package.json 缺少合法的 dlient.id(当前: ${id || '(空)'})。`)
|
|
50
|
+
process.exit(1)
|
|
51
|
+
}
|
|
52
|
+
if (dlient.system === true) {
|
|
53
|
+
console.error('[make-dlient] system 插件不允许本地打包 .dlient。')
|
|
54
|
+
process.exit(1)
|
|
55
|
+
}
|
|
56
|
+
const distRel = typeof dlient.dist === 'string' && dlient.dist ? dlient.dist : 'dist'
|
|
57
|
+
const argv = parseArgv()
|
|
58
|
+
const version = argv.version || String(pkg.version || '').trim()
|
|
59
|
+
if (!version) {
|
|
60
|
+
console.error('[make-dlient] 缺少版本号:package.json version 为空时请用 --version <v> 指定。')
|
|
61
|
+
process.exit(1)
|
|
62
|
+
}
|
|
63
|
+
if (!/^\d+\.\d+\.\d+([-+][0-9A-Za-z.-]+)?$/.test(version)) {
|
|
64
|
+
console.error(`[make-dlient] 版本号不合法(示例:1.0.0),当前: ${version}`)
|
|
65
|
+
process.exit(1)
|
|
66
|
+
}
|
|
67
|
+
const srcDist = join(ROOT, distRel)
|
|
68
|
+
const name = argv.name || `${id}-${version}.dlient`
|
|
69
|
+
if (!name.endsWith('.dlient')) {
|
|
70
|
+
console.error('[make-dlient] 输出文件名需以 .dlient 结尾。')
|
|
71
|
+
process.exit(1)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ---- 可选:先跑插件自身构建(--build)----
|
|
75
|
+
if (argv.build) {
|
|
76
|
+
console.log(`[make-dlient] npm run build`)
|
|
77
|
+
const npmCli = process.env.npm_execpath && existsSync(process.env.npm_execpath)
|
|
78
|
+
? process.env.npm_execpath
|
|
79
|
+
: join(ROOT, 'node_modules', 'npm', 'bin', 'npm-cli.js')
|
|
80
|
+
const cli = existsSync(npmCli) ? npmCli : 'npm'
|
|
81
|
+
const args = existsSync(npmCli) ? [npmCli, 'run', 'build'] : ['run', 'build']
|
|
82
|
+
const res = spawnSync(process.execPath ?? process.argv[0], args, { cwd: ROOT, stdio: 'inherit' })
|
|
83
|
+
if (res.error || res.status !== 0) {
|
|
84
|
+
console.error(`[make-dlient] npm run build 失败(exit ${res.status ?? res.error?.message ?? '?'})`)
|
|
85
|
+
process.exit(res.status ?? 1)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (!existsSync(srcDist)) {
|
|
89
|
+
console.error(`[make-dlient] 未找到构建产物 ${distRel}/。请先执行 npm run build(或加 --build),再打包。`)
|
|
90
|
+
process.exit(1)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ---- 暂存目录:package.json(改写 source/system)+ assets + skills + <dist>(排除 node_modules)----
|
|
94
|
+
const stage = mkdtempSync(join(tmpdir(), 'make-dlient-'))
|
|
95
|
+
try {
|
|
96
|
+
const patchedDlient = { ...dlient, source: 'local', system: false }
|
|
97
|
+
writeFileSync(join(stage, 'package.json'), `${JSON.stringify({ ...pkg, version, dlient: patchedDlient }, null, 2)}\n`)
|
|
98
|
+
|
|
99
|
+
// 注意:dist 目录名按 manifest(dlient.dist)保留在包内——导入端协议重写依赖该结构
|
|
100
|
+
cpSync(srcDist, join(stage, distRel), {
|
|
101
|
+
recursive: true,
|
|
102
|
+
filter: (src) => !src.split(sep).includes('node_modules'),
|
|
103
|
+
})
|
|
104
|
+
for (const sub of ['assets', 'skills']) {
|
|
105
|
+
if (!existsSync(join(ROOT, sub))) continue
|
|
106
|
+
cpSync(join(ROOT, sub), join(stage, sub), { recursive: true })
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ---- 递归收集 stage 文件 ----
|
|
110
|
+
const files = []
|
|
111
|
+
const walk = (dir, base) => {
|
|
112
|
+
for (const ent of readdirSync(dir, { withFileTypes: true })) {
|
|
113
|
+
const full = join(dir, ent.name)
|
|
114
|
+
const rel = base ? `${base}/${ent.name}` : ent.name
|
|
115
|
+
if (ent.isDirectory()) walk(full, rel)
|
|
116
|
+
else if (ent.isFile()) files.push({ name: rel, data: readFileSync(full) })
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
walk(stage, '')
|
|
120
|
+
|
|
121
|
+
const out = join(ROOT, name)
|
|
122
|
+
writeFileSync(out, makeZip(files))
|
|
123
|
+
console.log(`[make-dlient] 完成 → ${out}`)
|
|
124
|
+
console.log(`[make-dlient] 共 ${files.length} 个文件,.dlient 大小 ${(statSync(out).size / 1024).toFixed(1)} KB(无需签名,可直接导入开源版)`)
|
|
125
|
+
} finally {
|
|
126
|
+
rmSync(stage, { recursive: true, force: true })
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// ---- 极简 zip 写入(method=0 store;兼容开源版导入端 extractZip)----
|
|
130
|
+
// var 提升:函数在模块底部、顶层调用在前,用 var 避免 TDZ(首轮 !CRC_TABLE 为真即建表)
|
|
131
|
+
var CRC_TABLE = null
|
|
132
|
+
function crc32(buf) {
|
|
133
|
+
if (!CRC_TABLE) {
|
|
134
|
+
CRC_TABLE = new Int32Array(256)
|
|
135
|
+
for (let n = 0; n < 256; n++) {
|
|
136
|
+
let c = n
|
|
137
|
+
for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1
|
|
138
|
+
CRC_TABLE[n] = c
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
let c = 0xffffffff
|
|
142
|
+
for (let i = 0; i < buf.length; i++) c = CRC_TABLE[(c ^ buf[i]) & 0xff] ^ (c >>> 8)
|
|
143
|
+
return (c ^ 0xffffffff) >>> 0
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function makeZip(entries) {
|
|
147
|
+
const local = []
|
|
148
|
+
const central = []
|
|
149
|
+
let offset = 0
|
|
150
|
+
let centralSize = 0
|
|
151
|
+
for (const f of entries) {
|
|
152
|
+
const nameBuf = Buffer.from(f.name, 'utf-8')
|
|
153
|
+
const crc = crc32(f.data)
|
|
154
|
+
const size = f.data.length
|
|
155
|
+
|
|
156
|
+
const lh = Buffer.alloc(30)
|
|
157
|
+
lh.writeUInt32LE(0x04034b50, 0)
|
|
158
|
+
lh.writeUInt16LE(20, 4) // version needed
|
|
159
|
+
lh.writeUInt16LE(0x0800, 6) // flags: UTF-8 文件名
|
|
160
|
+
lh.writeUInt16LE(0, 8) // method = store
|
|
161
|
+
lh.writeUInt16LE(0, 10) // mod time
|
|
162
|
+
lh.writeUInt16LE(0x0021, 12) // mod date 1980-01-01
|
|
163
|
+
lh.writeUInt32LE(crc, 14)
|
|
164
|
+
lh.writeUInt32LE(size, 18)
|
|
165
|
+
lh.writeUInt32LE(size, 22)
|
|
166
|
+
lh.writeUInt16LE(nameBuf.length, 26)
|
|
167
|
+
lh.writeUInt16LE(0, 28)
|
|
168
|
+
local.push(lh, nameBuf, f.data)
|
|
169
|
+
|
|
170
|
+
const ch = Buffer.alloc(46)
|
|
171
|
+
ch.writeUInt32LE(0x02014b50, 0)
|
|
172
|
+
ch.writeUInt16LE(20, 4) // version made by
|
|
173
|
+
ch.writeUInt16LE(20, 6) // version needed
|
|
174
|
+
ch.writeUInt16LE(0x0800, 8)
|
|
175
|
+
ch.writeUInt16LE(0, 10) // method
|
|
176
|
+
ch.writeUInt16LE(0, 12)
|
|
177
|
+
ch.writeUInt16LE(0x0021, 14)
|
|
178
|
+
ch.writeUInt32LE(crc, 16)
|
|
179
|
+
ch.writeUInt32LE(size, 20)
|
|
180
|
+
ch.writeUInt32LE(size, 24)
|
|
181
|
+
ch.writeUInt16LE(nameBuf.length, 28)
|
|
182
|
+
ch.writeUInt16LE(0, 30)
|
|
183
|
+
ch.writeUInt16LE(0, 32)
|
|
184
|
+
ch.writeUInt16LE(0, 34)
|
|
185
|
+
ch.writeUInt16LE(0, 36)
|
|
186
|
+
ch.writeUInt32LE(0, 38)
|
|
187
|
+
ch.writeUInt32LE(offset, 42)
|
|
188
|
+
central.push(ch, nameBuf)
|
|
189
|
+
centralSize += 46 + nameBuf.length
|
|
190
|
+
offset += 30 + nameBuf.length + size
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const eocd = Buffer.alloc(22)
|
|
194
|
+
eocd.writeUInt32LE(0x06054b50, 0)
|
|
195
|
+
eocd.writeUInt16LE(0, 4)
|
|
196
|
+
eocd.writeUInt16LE(0, 6)
|
|
197
|
+
eocd.writeUInt16LE(entries.length, 8)
|
|
198
|
+
eocd.writeUInt16LE(entries.length, 10)
|
|
199
|
+
eocd.writeUInt32LE(centralSize, 12)
|
|
200
|
+
eocd.writeUInt32LE(offset, 16)
|
|
201
|
+
eocd.writeUInt16LE(0, 20)
|
|
202
|
+
return Buffer.concat([...local, ...central, eocd])
|
|
203
|
+
}
|