ezllm 0.0.38 → 0.0.40
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 +7 -7
- package/dist/cli.js +22 -22
- package/dist/daemon.js +4 -4
- package/dist/plugin-host.js +3 -3
- package/dist/server.js +65 -65
- package/dist/web-ui/assets/{DashboardView-STI4Bkxw.js → DashboardView-C0baZH6c.js} +1 -1
- package/dist/web-ui/assets/{LayoutView-DHXc1jcz.js → LayoutView-BMNKjFyW.js} +1 -1
- package/dist/web-ui/assets/{LoginView-CwH6eISd.js → LoginView-C3CW0urr.js} +1 -1
- package/dist/web-ui/assets/{LogsView-CGgzVbGZ.js → LogsView-C70vgFde.js} +1 -1
- package/dist/web-ui/assets/{ManagedServicesView-BvzeGhcb.js → ManagedServicesView-dC-MR3OW.js} +1 -1
- package/dist/web-ui/assets/PluginsView-B7CeBD8Q.js +2 -0
- package/dist/web-ui/assets/PluginsView-ggYmqCJ8.css +1 -0
- package/dist/web-ui/assets/ProvidersView-B8l3DcZY.js +14 -0
- package/dist/web-ui/assets/{ProvidersView-BpBOF4eX.css → ProvidersView-BSFjmX9y.css} +1 -1
- package/dist/web-ui/assets/{ServiceStatusView-BvuFCgT-.js → ServiceStatusView-DpGDJHh9.js} +1 -1
- package/dist/web-ui/assets/{SettingsView-W9bjQ9ly.js → SettingsView-CqUrHj3w.js} +1 -1
- package/dist/web-ui/assets/{SetupView-D5KYolFQ.js → SetupView-B_hE_Uhm.js} +1 -1
- package/dist/web-ui/assets/{config-CX31TfJ4.js → config-IC8ZbooF.js} +1 -1
- package/dist/web-ui/assets/{index-D6ydfwRk.js → index-D2JhqxaS.js} +1 -1
- package/dist/web-ui/assets/{message-B022-tc2.js → message-Cp_GAwQs.js} +1 -1
- package/dist/web-ui/assets/{providers-C6QKdfzy.js → providers-BwuaO9o9.js} +1 -1
- package/dist/web-ui/assets/{router-Daxct41S.js → router-BzfmTCjF.js} +2 -2
- package/dist/web-ui/assets/{useCopyToast-0dz9T4-H.js → useCopyToast-BpTqycuM.js} +1 -1
- package/dist/web-ui/assets/{useManagerApi-DdgACgsq.js → useManagerApi-lFkQleJX.js} +1 -1
- package/dist/web-ui/assets/{useQuickRangeFilter-D8oTOMUp.js → useQuickRangeFilter-DmQyTw4A.js} +1 -1
- package/dist/web-ui/assets/useToast-D3xB24vj.js +1 -0
- package/dist/web-ui/index.html +3 -3
- package/package.json +1 -1
- package/dist/web-ui/assets/PluginsView-CNDVeVj8.js +0 -2
- package/dist/web-ui/assets/PluginsView-zyTILSF1.css +0 -1
- package/dist/web-ui/assets/ProvidersView-DTIx2VSi.js +0 -14
- package/dist/web-ui/assets/useToast-nQZ8k6t7.js +0 -1
package/dist/daemon.js
CHANGED
|
@@ -9,7 +9,7 @@ import{createRequire as e}from"node:module";import{copyFileSync as t,createWrite
|
|
|
9
9
|
`));return i}function an(e){return tn(e)!==null&&nn(e,`npm`).some(e=>r(e))}var on=[`.cmd`,`.bat`,`.exe`];function sn(e,t){if(t.includes(`/`)||t.includes(`\\`))return null;let n=Zt?on.map(n=>m(e,`${t}${n}`)):[m(e,t)];return n.push(m(e,`node_modules`,`.bin`,t)),n.find(e=>r(e))??null}function cn(e){return(Zt?[e,m(e,`node_modules`,`.bin`)]:[m(e,`bin`),e]).join(f)+f}var ln=q(((e,t)=>{t.exports=n;function n(){this.pending=0,this.max=1/0,this.listeners=[],this.waiting=[],this.error=null}n.prototype.go=function(e){this.pending<this.max?i(this,e):this.waiting.push(e)},n.prototype.wait=function(e){this.pending===0?e(this.error):this.listeners.push(e)},n.prototype.hold=function(){return r(this)};function r(e){e.pending+=1;var t=!1;return n;function n(n){if(t)throw Error(`callback called twice`);if(t=!0,e.error=e.error||n,--e.pending,e.waiting.length>0&&e.pending<e.max)i(e,e.waiting.shift());else if(e.pending===0){var a=e.listeners;e.listeners=[],a.forEach(r)}}function r(t){t(e.error)}}function i(e,t){t(r(e))}})),un=q((e=>{var t=Y(`fs`),n=Y(`util`),r=Y(`stream`),i=r.Readable,a=r.PassThrough,o=ln(),s=Y(`events`).EventEmitter;e.BufferSlicer=u,e.FdSlicer=c,n.inherits(c,s);function c(e){s.call(this),this.fd=e,this.pend=new o,this.pend.max=1,this.refCount=0}c.prototype.read=function(e,n,r,i,a){var o=this;o.pend.go(function(s){t.read(o.fd,e,n,r,i,function(e,t,n){s(),a(e,t,n)})})},c.prototype.createReadStream=function(e){return new l(this,e)},c.prototype.ref=function(){this.refCount+=1},c.prototype.unref=function(){var e=this;if(--e.refCount,e.refCount<0)throw Error(`invalid unref`);if(e.refCount>0)return;t.close(e.fd,n);function n(t){t?e.emit(`error`,t):e.emit(`close`)}},n.inherits(l,i);function l(e,t){t||={},i.call(this,t),this.context=e,this.context.ref(),this.start=t.start||0,this.endOffset=t.end,this.pos=this.start}l.prototype._read=function(e){var n=this,r=Math.min(n._readableState.highWaterMark,e);n.endOffset!=null&&(r=Math.min(r,n.endOffset-n.pos)),r<=0?(n.push(null),this._cleanup()):n.context.pend.go(function(e){var i=Buffer.allocUnsafe(r);t.read(n.context.fd,i,0,r,n.pos,function(t,r){t?n.destroy(t):r===0?(n.push(null),n._cleanup()):(n.pos+=r,n.push(i.slice(0,r))),e()})})},l.prototype._destroy=function(e,t){this._cleanup(),t(e)},l.prototype._cleanup=function(){this.context!=null&&(this.context.unref(),this.context=null)},n.inherits(u,s);function u(e){s.call(this),this.refCount=0,this.buffer=e}u.prototype.read=function(e,t,n,r,i){if(!(0<=t&&t<=e.length))throw RangeError(`offset outside buffer: 0 <= `+t+` <= `+e.length);if(r<0)throw RangeError(`position is negative: `+r);t+n>e.length&&(n=e.length-t),r+n>this.buffer.length&&(n=this.buffer.length-r),n<=0?setImmediate(function(){i(null,0)}):(this.buffer.copy(e,t,r,r+n),setImmediate(function(){i(null,n)}))},u.prototype.createReadStream=function(e){e||={};var t=new a(e);t.start=e.start||0,t.endOffset=e.end,t.pos=t.endOffset||this.buffer.length;for(var n=this.buffer.slice(t.start,t.pos),r=65536,i=0;;){var o=i+r;if(o>=n.length){i<n.length&&t.write(n.slice(i,n.length));break}t.write(n.slice(i,o)),i=o}return t.end(),t},u.prototype.ref=function(){this.refCount+=1},u.prototype.unref=function(){if(--this.refCount,this.refCount<0)throw Error(`invalid unref`)}})),dn=q(((e,t)=>{var n=new Int32Array([0,1996959894,3993919788,2567524794,124634137,1886057615,3915621685,2657392035,249268274,2044508324,3772115230,2547177864,162941995,2125561021,3887607047,2428444049,498536548,1789927666,4089016648,2227061214,450548861,1843258603,4107580753,2211677639,325883990,1684777152,4251122042,2321926636,335633487,1661365465,4195302755,2366115317,997073096,1281953886,3579855332,2724688242,1006888145,1258607687,3524101629,2768942443,901097722,1119000684,3686517206,2898065728,853044451,1172266101,3705015759,2882616665,651767980,1373503546,3369554304,3218104598,565507253,1454621731,3485111705,3099436303,671266974,1594198024,3322730930,2970347812,795835527,1483230225,3244367275,3060149565,1994146192,31158534,2563907772,4023717930,1907459465,112637215,2680153253,3904427059,2013776290,251722036,2517215374,3775830040,2137656763,141376813,2439277719,3865271297,1802195444,476864866,2238001368,4066508878,1812370925,453092731,2181625025,4111451223,1706088902,314042704,2344532202,4240017532,1658658271,366619977,2362670323,4224994405,1303535960,984961486,2747007092,3569037538,1256170817,1037604311,2765210733,3554079995,1131014506,879679996,2909243462,3663771856,1141124467,855842277,2852801631,3708648649,1342533948,654459306,3188396048,3373015174,1466479909,544179635,3110523913,3462522015,1591671054,702138776,2966460450,3352799412,1504918807,783551873,3082640443,3233442989,3988292384,2596254646,62317068,1957810842,3939845945,2647816111,81470997,1943803523,3814918930,2489596804,225274430,2053790376,3826175755,2466906013,167816743,2097651377,4027552580,2265490386,503444072,1762050814,4150417245,2154129355,426522225,1852507879,4275313526,2312317920,282753626,1742555852,4189708143,2394877945,397917763,1622183637,3604390888,2714866558,953729732,1340076626,3518719985,2797360999,1068828381,1219638859,3624741850,2936675148,906185462,1090812512,3747672003,2825379669,829329135,1181335161,3412177804,3160834842,628085408,1382605366,3423369109,3138078467,570562233,1426400815,3317316542,2998733608,733239954,1555261956,3268935591,3050360625,752459403,1541320221,2607071920,3965973030,1969922972,40735498,2617837225,3943577151,1913087877,83908371,2512341634,3803740692,2075208622,213261112,2463272603,3855990285,2094854071,198958881,2262029012,4057260610,1759359992,534414190,2176718541,4139329115,1873836001,414664567,2282248934,4279200368,1711684554,285281116,2405801727,4167216745,1634467795,376229701,2685067896,3608007406,1308918612,956543938,2808555105,3495958263,1231636301,1047427035,2932959818,3654703836,1088359270,936918e3,2847714899,3736837829,1202900863,817233897,3183342108,3401237130,1404277552,615818150,3134207493,3453421203,1423857449,601450431,3009837614,3294710456,1567103746,711928724,3020668471,3272380065,1510334235,755167117]);function r(e){let t=-1;for(let r of e)t=n[(t^r)&255]^t>>>8;return(t^-1)>>>0}t.exports=r})),fn=q((e=>{var t=Y(`fs`),n=Y(`zlib`),r=un(),i=Y(`util`),a=Y(`events`).EventEmitter,o=Y(`stream`).Transform,s=Y(`stream`).PassThrough,c=Y(`stream`).Writable,l=typeof n.crc32==`function`?n.crc32:dn();e.openPromise=u;function u(e,t){return new Promise((n,r)=>{d(e,{...t,lazyEntries:!0},function(e,t){if(e)return r(e);n(t)})})}function d(e,n,r){typeof n==`function`&&(r=n,n=null),n??={},n.autoClose??(n.autoClose=!0),n.lazyEntries??(n.lazyEntries=!1),n.decodeStrings??(n.decodeStrings=!0),n.validateEntrySizes??(n.validateEntrySizes=!0),n.strictFileNames??(n.strictFileNames=!1),r??=M,t.open(e,`r`,function(e,i){if(e)return r(e);f(i,n,function(e,n){e&&t.close(i,M),r(e,n)})})}function f(e,n,i){typeof n==`function`&&(i=n,n=null),n??={},n.autoClose??(n.autoClose=!1),n.lazyEntries??(n.lazyEntries=!1),n.decodeStrings??(n.decodeStrings=!0),n.validateEntrySizes??(n.validateEntrySizes=!0),n.strictFileNames??(n.strictFileNames=!1),i??=M,t.fstat(e,function(t,a){if(t)return i(t);p(new r.FdSlicer(e),a.size,n,i)})}function p(e,t,n,r){typeof n==`function`&&(r=n,n=null),n??={},n.autoClose??(n.autoClose=!0),n.lazyEntries??(n.lazyEntries=!1),n.decodeStrings??(n.decodeStrings=!0);var i=!!n.decodeStrings;if(n.validateEntrySizes??(n.validateEntrySizes=!0),n.strictFileNames??(n.strictFileNames=!1),r??=M,typeof t!=`number`)throw Error(`expected totalSize parameter to be a number`);if(t>2**53-1)throw Error(`zip file too large. only file sizes up to 2^52 are supported due to JavaScript's Number type being an IEEE 754 double.`);e.ref();var a=22,o=20,s=Math.min(o+a+65535,t),c=A(s);C(e,c,0,s,t-c.length,function(l){if(l)return r(l);for(var u=s-a;u>=0;--u)if(c.readUInt32LE(u)===101010256){var d=c.subarray(u),f=d.readUInt16LE(4),p=d.readUInt16LE(10),h=d.readUInt32LE(16),g=d.readUInt16LE(20),_=d.length-a;if(g!==_)return r(Error(`Invalid comment length. Expected: `+_+`. Found: `+g+". Are there extra bytes at the end of the file? Or is the end of central dir signature `PK☺☻` in the comment?"));var v=i?O(d.subarray(22),!1):d.subarray(22);if(u-o>=0&&c.readUInt32LE(u-o)===117853008){var y=k(c.subarray(u-o,u-o+o),8),b=A(56);return C(e,b,0,b.length,y,function(a){return a?r(a):b.readUInt32LE(0)===101075792?(f=b.readUInt32LE(16),f===0?(p=k(b,32),h=k(b,48),r(null,new m(e,h,t,p,v,n.autoClose,n.lazyEntries,i,n.validateEntrySizes,n.strictFileNames))):r(Error(`multi-disk zip files are not supported: found disk number: `+f))):r(Error(`invalid zip64 end of central directory record signature`))})}return f===0?r(null,new m(e,h,t,p,v,n.autoClose,n.lazyEntries,i,n.validateEntrySizes,n.strictFileNames)):r(Error(`multi-disk zip files are not supported: found disk number: `+f))}r(Error(`End of central directory record signature not found. Either not a zip file, or file is truncated.`))})}i.inherits(m,a);function m(e,t,n,r,i,o,s,c,l,u){var d=this;a.call(d),d.reader=e,d.reader.on(`error`,function(e){g(d,e)}),d.reader.once(`close`,function(){d.emit(`close`)}),d.readEntryCursor=t,d.fileSize=n,d.entryCount=r,d.comment=i,d.entriesRead=0,d.autoClose=!!o,d.lazyEntries=!!s,d.decodeStrings=!!c,d.validateEntrySizes=!!l,d.strictFileNames=!!u,d.isOpen=!0,d.emittedError=!1,d.hasEachEntryBeenCalled=!1,d.lazyEntries||d._readEntry()}m.prototype.close=function(){this.isOpen&&(this.isOpen=!1,this.reader.unref())};function h(e,t){e.autoClose&&e.close(),g(e,t)}function g(e,t){e.emittedError||(e.emittedError=!0,e.emit(`error`,t))}m.prototype.readEntry=function(){if(!this.lazyEntries)throw Error(`readEntry() called without lazyEntries:true`);this._readEntry()},m.prototype._readEntry=function(){var e=this;if(e.entryCount===e.entriesRead)setImmediate(function(){e.autoClose&&e.close(),!e.emittedError&&e.emit(`end`)});else if(!e.emittedError){var t=A(46);C(e.reader,t,0,t.length,e.readEntryCursor,function(n){if(n)return h(e,n);if(!e.emittedError){var r=new _,i=t.readUInt32LE(0);if(i!==33639248)return h(e,Error(`invalid central directory file header signature: 0x`+i.toString(16)));if(r.versionMadeBy=t.readUInt16LE(4),r.versionNeededToExtract=t.readUInt16LE(6),r.generalPurposeBitFlag=t.readUInt16LE(8),r.compressionMethod=t.readUInt16LE(10),r.lastModFileTime=t.readUInt16LE(12),r.lastModFileDate=t.readUInt16LE(14),r.crc32=t.readUInt32LE(16),r.compressedSize=t.readUInt32LE(20),r.uncompressedSize=t.readUInt32LE(24),r.fileNameLength=t.readUInt16LE(28),r.extraFieldLength=t.readUInt16LE(30),r.fileCommentLength=t.readUInt16LE(32),r.internalFileAttributes=t.readUInt16LE(36),r.externalFileAttributes=t.readUInt32LE(38),r.relativeOffsetOfLocalHeader=t.readUInt32LE(42),r.generalPurposeBitFlag&64)return h(e,Error(`strong encryption is not supported`));e.readEntryCursor+=46,t=A(r.fileNameLength+r.extraFieldLength+r.fileCommentLength),C(e.reader,t,0,t.length,e.readEntryCursor,function(n){if(n)return h(e,n);if(!e.emittedError){r.fileNameRaw=t.subarray(0,r.fileNameLength);var i=r.fileNameLength+r.extraFieldLength;r.extraFieldRaw=t.subarray(r.fileNameLength,i),r.fileCommentRaw=t.subarray(i,i+r.fileCommentLength);try{r.extraFields=S(r.extraFieldRaw)}catch(t){return h(e,t)}if(e.decodeStrings){var a=!!(r.generalPurposeBitFlag&2048);r.fileComment=O(r.fileCommentRaw,a),r.fileName=b(r.generalPurposeBitFlag,r.fileNameRaw,r.extraFields,e.strictFileNames);var o=x(r.fileName);if(o!=null)return h(e,Error(o))}else r.fileComment=r.fileCommentRaw,r.fileName=r.fileNameRaw;r.comment=r.fileComment,e.readEntryCursor+=t.length,e.entriesRead+=1;for(var s=0;s<r.extraFields.length;s++){var c=r.extraFields[s];if(c.id===1){var l=c.data,u=0;if(r.uncompressedSize===4294967295){if(u+8>l.length)return h(e,Error(`zip64 extended information extra field does not include uncompressed size`));r.uncompressedSize=k(l,u),u+=8}if(r.compressedSize===4294967295){if(u+8>l.length)return h(e,Error(`zip64 extended information extra field does not include compressed size`));r.compressedSize=k(l,u),u+=8}if(r.relativeOffsetOfLocalHeader===4294967295){if(u+8>l.length)return h(e,Error(`zip64 extended information extra field does not include relative header offset`));r.relativeOffsetOfLocalHeader=k(l,u),u+=8}break}}if(e.validateEntrySizes&&r.compressionMethod===0){var d=r.uncompressedSize;if(r.isEncrypted()&&(d+=12),r.compressedSize!==d){var f=`compressed/uncompressed size mismatch for stored file: `+r.compressedSize+` != `+r.uncompressedSize;return h(e,Error(f))}}e.emit(`entry`,r),e.lazyEntries||e._readEntry()}})}})}},m.prototype.eachEntry=function(){let e=this;if(!e.lazyEntries)throw Error(`eachEntry() requires lazyEntries: true`);if(e.hasEachEntryBeenCalled)throw Error(`eachEntry() must only be called once per ZipFile`);e.hasEachEntryBeenCalled=!0;let t=null;e.on(`entry`,r),e.on(`end`,i),e.on(`error`,a);function n(){e.removeListener(`entry`,r),e.removeListener(`end`,i),e.removeListener(`error`,a),e.autoClose&&e.close()}function r(e){let{resolve:n}=t;t=null,n({value:e})}function i(){let{resolve:e}=t;t=null,n(),e({done:!0})}function a(e){let{reject:r}=t;t=null,n(),r(e)}return{[Symbol.asyncIterator](){return this},next(){let n=new Promise((e,n)=>{if(t!=null)throw Error(`next() called before previous Promise was resolved.`);t={resolve:e,reject:n}});return e.readEntry(),n},return(e){return n(),Promise.resolve({done:!0,value:e})},throw(e){return n(),Promise.reject(e)}}},m.prototype.openReadStream=function(e,t,n){var r=this,i=0,a=e.compressedSize;if(n??(n=t,t=null),t==null)t={};else{if(t.decodeFileData===!1){if(t.decrypt!=null)throw Error(`cannot use options.decrypt when options.decodeFileData === false`);if(t.decompress!=null)throw Error(`cannot use options.decompress when options.decodeFileData === false`)}else{if(t.decrypt!=null){if(!e.isEncrypted())throw Error(`options.decrypt can only be specified for encrypted entries. See also option decodeFileData.`);if(t.decrypt!==!1)throw Error(`invalid options.decrypt value: `+t.decrypt);if(e.isCompressed()&&t.decompress!==!1)throw Error(`entry is encrypted and compressed, and options.decompress !== false. See also option decodeFileData.`)}if(t.decompress!=null){if(!e.isCompressed())throw Error(`options.decompress can only be specified for compressed entries. See also option decodeFileData.`);if(t.decompress!==!1&&t.decompress!==!0)throw Error(`invalid options.decompress value: `+t.decompress);s=t.decompress}}if(t.start!=null){if(i=t.start,i<0)throw Error(`options.start < 0`);if(i>e.compressedSize)throw Error(`options.start > entry.compressedSize`)}if(t.end!=null){if(a=t.end,a<0)throw Error(`options.end < 0`);if(a>e.compressedSize)throw Error(`options.end > entry.compressedSize`);if(a<i)throw Error(`options.end < options.start`)}}var o=t.decodeFileData===!1||(e.compressionMethod===0||e.compressionMethod===8&&t.decompress===!1)&&(!e.isEncrypted()||t.decrypt===!1);if((t.start!=null||t.end!=null)&&!o)throw Error(`start/end range require options.decodeFileData === false for non-trivial encoded entries.`);if(!r.isOpen)return n(Error(`closed`));if(e.isEncrypted()&&!o&&t.decrypt!==!1)return n(Error(`entry is encrypted, and options.decodeFileData !== false`));var s;if(o)s=!1;else if(e.compressionMethod===8)s=t.decodeFileData!==!0;else return n(Error(`unsupported compression method: `+e.compressionMethod));r.readLocalFileHeader(e,{minimal:!0},function(t,o){if(t)return n(t);r.openReadStreamLowLevel(o.fileDataStart,e.compressedSize,i,a,s,e.uncompressedSize,n)})},m.prototype.openReadStreamLowLevel=function(e,t,r,i,a,o,s){var c=this;e+t;var l=c.reader.createReadStream({start:e+r,end:e+i}),u=l;if(a){var d=!1,f=n.createInflateRaw();l.on(`error`,function(e){setImmediate(function(){d||f.emit(`error`,e)})}),l.pipe(f),c.validateEntrySizes?(u=new w(o),f.on(`error`,function(e){setImmediate(function(){d||u.emit(`error`,e)})}),f.pipe(u)):u=f,j(u,function(){d=!0,f!==u&&f.unpipe(u),l.unpipe(f),l.destroy()})}s(null,u)},m.prototype.readLocalFileHeader=function(e,t,n){var r=this;n??(n=t,t=null),t??={},r.reader.ref();var i=A(30);C(r.reader,i,0,i.length,e.relativeOffsetOfLocalHeader,function(a){try{if(a)return n(a);var o=i.readUInt32LE(0);if(o!==67324752)return n(Error(`invalid local file header signature: 0x`+o.toString(16)));var s=i.readUInt16LE(26),c=i.readUInt16LE(28),l=e.relativeOffsetOfLocalHeader+30+s+c;if(l+e.compressedSize>r.fileSize)return n(Error(`file data overflows file bounds: `+l+` + `+e.compressedSize+` > `+r.fileSize));if(t.minimal)return n(null,{fileDataStart:l});var u=new v;u.fileDataStart=l,u.versionNeededToExtract=i.readUInt16LE(4),u.generalPurposeBitFlag=i.readUInt16LE(6),u.compressionMethod=i.readUInt16LE(8),u.lastModFileTime=i.readUInt16LE(10),u.lastModFileDate=i.readUInt16LE(12),u.crc32=i.readUInt32LE(14),u.compressedSize=i.readUInt32LE(18),u.uncompressedSize=i.readUInt32LE(22),u.fileNameLength=s,u.extraFieldLength=c,i=A(s+c),r.reader.ref(),C(r.reader,i,0,i.length,e.relativeOffsetOfLocalHeader+30,function(e){try{return e?n(e):(u.fileName=i.subarray(0,s),u.extraField=i.subarray(s),n(null,u))}finally{r.reader.unref()}})}finally{r.reader.unref()}})},m.prototype.openReadStreamPromise=function(e,t){return new Promise((n,r)=>{this.openReadStream(e,t,function(e,t){if(e)return r(e);n(t)})})},m.prototype.openReadStreamLowLevelPromise=function(e,t,n,r,i,a){return new Promise((o,s)=>{this.openReadStream(e,t,n,r,i,a,function(e,t){if(e)return s(e);o(t)})})},m.prototype.readLocalFileHeaderPromise=function(e,t){return new Promise((n,r)=>{this.readLocalFileHeader(e,t,function(e,t){if(e)return r(e);n(t)})})};function _(){}_.prototype.getLastModDate=function(e){if(e??={},!e.forceDosFormat)for(var t=0;t<this.extraFields.length;t++){var n=this.extraFields[t];if(n.id===21589){var r=n.data;if(r.length<5||!(r[0]&1))continue;var i=r.readInt32LE(1);return new Date(i*1e3)}if(n.id===10){var r=n.data;if(r.length!==32||r.readUInt16LE(4)!==1||r.readUInt16LE(6)!==24)continue;var a=(r.readUInt32LE(8)+4294967296*r.readInt32LE(12))/1e4-0xa9730b66800;return new Date(a)}}return y(this.lastModFileDate,this.lastModFileTime,e.timezone)},_.prototype.canDecodeFileData=function(){return!this.isEncrypted()&&(this.compressionMethod===0||this.compressionMethod===8)},_.prototype.isEncrypted=function(){return!!(this.generalPurposeBitFlag&1)},_.prototype.isCompressed=function(){return this.compressionMethod===8};function v(){}function y(e,t,n){var r=e&31,i=(e>>5&15)-1,a=(e>>9&127)+1980,o=0,s=(t&31)*2,c=t>>5&63,l=t>>11&31;if(n==null||n===`local`)return new Date(a,i,r,l,c,s,o);if(n===`UTC`)return new Date(Date.UTC(a,i,r,l,c,s,o));throw Error(`unrecognized options.timezone: `+options.timezone)}function b(e,t,n,r){for(var i=null,a=0;a<n.length;a++){var o=n[a];if(o.id===28789){if(o.data.length<6||o.data.readUInt8(0)!==1)continue;var s=o.data.readUInt32LE(1);if(l(t)!==s)continue;i=O(o.data.subarray(5),!0);break}}return i??=O(t,!!(e&2048)),r||(i=i.replace(/\\/g,`/`)),i}function x(e){return e.indexOf(`\\`)===-1?/^[a-zA-Z]:/.test(e)||/^\//.test(e)?`absolute path: `+e:e.split(`/`).indexOf(`..`)===-1?null:`invalid relative path: `+e:`invalid characters in fileName: `+e}function S(e){for(var t=[],n=0;n<e.length-3;){var r=e.readUInt16LE(n+0),i=e.readUInt16LE(n+2),a=n+4,o=a+i;if(o>e.length)throw Error(`extra field length exceeds extra field buffer size`);var s=e.subarray(a,o);t.push({id:r,data:s}),n=o}return t}function C(e,t,n,r,i,a){if(r===0)return setImmediate(function(){a(null,A(0))});e.read(t,n,r,i,function(e,t){if(e)return a(e);if(t<r)return a(Error(`unexpected EOF`));a()})}i.inherits(w,o);function w(e){o.call(this),this.actualByteCount=0,this.expectedByteCount=e}w.prototype._transform=function(e,t,n){if(this.actualByteCount+=e.length,this.actualByteCount>this.expectedByteCount){var r=`too many bytes in the stream. expected `+this.expectedByteCount+`. got at least `+this.actualByteCount;return n(Error(r))}n(null,e)},w.prototype._flush=function(e){if(this.actualByteCount<this.expectedByteCount){var t=`not enough bytes in the stream. expected `+this.expectedByteCount+`. got only `+this.actualByteCount;return e(Error(t))}e()},i.inherits(T,a);function T(){a.call(this),this.refCount=0}T.prototype.ref=function(){this.refCount+=1},T.prototype.unref=function(){var e=this;if(--e.refCount,e.refCount>0)return;if(e.refCount<0)throw Error(`invalid unref`);e.close(t);function t(t){if(t)return e.emit(`error`,t);e.emit(`close`)}},T.prototype.createReadStream=function(e){e??={};var t=e.start,n=e.end;if(t===n){var r=new s;return setImmediate(function(){r.end()}),r}var i=this._readStreamForRange(t,n),a=!1,o=new E(this);i.on(`error`,function(e){setImmediate(function(){a||o.emit(`error`,e)})}),j(o,function(){i.unpipe(o),o.unref(),i.destroy()});var c=new w(n-t);return o.on(`error`,function(e){setImmediate(function(){a||c.emit(`error`,e)})}),j(c,function(){a=!0,o.unpipe(c),o.destroy()}),i.pipe(o).pipe(c)},T.prototype._readStreamForRange=function(e,t){throw Error(`not implemented`)},T.prototype.read=function(e,t,n,r,i){var a=this.createReadStream({start:r,end:r+n}),o=new c,s=0;o._write=function(n,r,i){n.copy(e,t+s,0,n.length),s+=n.length,i()},o.on(`finish`,i),a.on(`error`,function(e){i(e)}),a.pipe(o)},T.prototype.close=function(e){setImmediate(e)},i.inherits(E,s);function E(e){s.call(this),this.context=e,this.context.ref(),this.unreffedYet=!1}E.prototype._flush=function(e){this.unref(),e()},E.prototype.unref=function(e){this.unreffedYet||(this.unreffedYet=!0,this.context.unref())};var D=`\0☺☻♥♦♣♠•◘○◙♂♀♪♫☼►◄↕‼¶§▬↨↑↓→←∟↔▲▼ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_\`abcdefghijklmnopqrstuvwxyz{|}~⌂ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜ¢£¥₧ƒáíóúñѪº¿⌐¬½¼¡«»░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀αßΓπΣσµτΦΘΩδ∞φε∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■\xA0`;function O(e,t){if(t)return e.toString(`utf8`);for(var n=``,r=0;r<e.length;r++)n+=D[e[r]];return n}function k(e,t){var n=e.readUInt32LE(t);return e.readUInt32LE(t+4)*4294967296+n}var A=typeof Buffer.allocUnsafe==`function`?function(e){return Buffer.allocUnsafe(e)}:function(e){return new Buffer(e)};function j(e,t){typeof e.destroy==`function`?e._destroy=function(e,n){t(),n?.(e)}:e.destroy=t}function M(e){if(e)throw e}}))(),pn=16384,mn=16;function hn(e,t){let n=g(e,t);if(!n.startsWith(e+_))throw Error(`zip 条目越出目标目录:${t}`);if(t.split(`/`).some(e=>e.includes(`:`)))throw Error(`zip 条目名含非法字符「:」(NTFS 数据流形态):${t}`);return n}function gn(e){return(e.externalFileAttributes>>>16&61440)===pn||e.fileName.endsWith(`/`)?!0:!(e.versionMadeBy>>>8)&&e.externalFileAttributes===mn}async function _n(e,t,r){let a=g(t);i(a,{recursive:!0});let o=0,s=0,c=null,l=(e=0)=>{r?.({entries:o,bytes:s+e,current:c})},u=await(0,fn.openPromise)(e);try{for await(let e of u.eachEntry()){if(e.fileName.startsWith(`__MACOSX/`))continue;let t=hn(a,e.fileName);if(gn(e)){i(t,{recursive:!0}),o+=1,l();continue}i(p(t),{recursive:!0}),c=e.fileName,l();let r=0,d=await u.openReadStreamPromise(e);d.on(`data`,e=>{r+=e.length,l(r)}),await w(d,n(t)),s+=r,o+=1,c=null,l()}}finally{u.isOpen&&u.close()}}var vn={"win32-x64":{dir:`win-x64`,ext:`zip`},"win32-arm64":{dir:`win-arm64`,ext:`zip`},"darwin-arm64":{dir:`darwin-arm64`,ext:`tar.gz`},"darwin-x64":{dir:`darwin-x64`,ext:`tar.gz`},"linux-x64":{dir:`linux-x64`,ext:`tar.gz`},"linux-arm64":{dir:`linux-arm64`,ext:`tar.gz`}},yn=process.platform===`win32`?[[`node_modules`]]:[[`lib`,`node_modules`]],bn=5e3,xn=3e3;function Sn(e,t){let n=e.bytes===null?``:`,${Math.round(e.bytes/1048576)} MB`,r=e.current===null?``:`(正在 ${e.current})`;return`[ezn] 已解压 ${e.entries} 项${n}${r}(已用 ${t} 秒) ...`}function Cn(e,t){let n=setInterval(()=>console.error(t()),e);return()=>clearInterval(n)}function wn(e){return Math.round((Date.now()-e)/1e3)}var Tn=new Set([`EPERM`,`EACCES`,`EBUSY`,`ENOSPC`,`ENAMETOOLONG`,`EROFS`,`EMFILE`]);function En(e){let t=String(e?.code??``);return Tn.has(t)?`local`:`network`}function Dn(e){let t={local:`本机(文件/权限/安全软件)`,network:`网络`,unknown:`未识别`},n=e.mirrors.map((n,r)=>{let i=e.failures[r],a=i?.kind??`unknown`,o=i?i.error instanceof Error?i.error.message:String(i.error):`(无记录)`;return` ${r+1}. [${t[a]}] ${n}\n ${o}`}).join(`
|
|
10
10
|
`),r=new Set(e.failures.map(e=>e.kind)),i=r.size>0&&[...r].every(e=>e===`network`),a=[`Node 运行时下载失败。`,i?`所有源的失败都是网络类(不可达 / 超时 / TLS 失败)。`:`其中至少一个源是本机类失败(文件被占用、权限、磁盘空间,或被安全软件拦下)——这种情况下换源不会好转。`,`各源失败原因:`,n,``,`可尝试的恢复方式:`];return i?(a.push(` 1. 检查网络 / 代理后重试(本下载不读 http_proxy 等环境变量,需要能直连上面这些源);`),a.push(` 2. 换一个镜像源重试(**推荐入口是环境变量**,不必改任何文件):`,` Windows: set ELLM_NODE_MIRROR=https://your-mirror.example/node-dist`,` POSIX: export ELLM_NODE_MIRROR=https://your-mirror.example/node-dist`,` 目录结构需同 nodejs.org/dist(即 <mirror>/<版本>/<文件名>);`)):(a.push(` 1. 先按上面的「本机」条目处理:确认磁盘空间、把应用目录加入安全软件(360/电脑管家/Defender)信任区、`,` 或关闭其实时防护后重试;被拦下的文件也可能已被隔离/删除,删掉解压残留再试:`,` ${e.extractDir}`),a.push(` 2. 排除本机原因后若仍是网络问题,再换镜像源(**推荐入口是环境变量**):`,` Windows: set ELLM_NODE_MIRROR=https://your-mirror.example/node-dist`,` POSIX: export ELLM_NODE_MIRROR=https://your-mirror.example/node-dist`)),a.push(` 3. 手动放置:下载 ${e.filename}(见 https://nodejs.org/dist/${e.nodeVersion}/),`,` 解压到 ${e.target}(如带顶层版本目录请把其内容上移一层),`,` 确保可执行文件位于 ${e.nodePath}。`),a.join(`
|
|
11
11
|
`)}async function On(e,t,r){let a=`node-${e}-${r.platform.dir}.${r.platform.ext}`,o=m(t,`node.download.tmp`),c=m(t,`node.extract.tmp`),d=()=>{l(o,{force:!0}),l(c,{recursive:!0,force:!0})},f=[];r.mirror&&f.push(r.mirror.replace(/\/+$/,``)),f.push(`https://registry.npmmirror.com/-/binary/node`,`https://nodejs.org/dist`),l(o,{force:!0}),l(c,{recursive:!0,force:!0}),i(c,{recursive:!0});let p=[];for(let t=0;t<f.length;t++){let h=f[t],g=`${h}/${e}/${a}`;console.error(`[ezn] (${t+1}/${f.length}) 正在下载 ${a}\n[ezn] 来源:${h}`);try{let e=Date.now(),t=0,i=Jt.default.stream(g,{decompress:!1,timeout:{connect:3e4,response:6e4,socket:6e4}});i.on(`downloadProgress`,e=>{t=e.transferred});let a=Cn(bn,()=>`[ezn] 已下载 ${Math.round(t/1048576)} MB(已用 ${wn(e)} 秒) ...`);try{await w(i,n(o))}finally{a()}console.error(`[ezn] 下载完成(${Math.round(u(o).size/1048576)} MB,耗时 ${wn(e)} 秒),正在解压 ...`);let f=Date.now(),p={entries:0,bytes:null,current:null},h=Cn(xn,()=>Sn(p,wn(f)));try{r.platform.ext===`zip`?await _n(o,c,e=>{p=e}):await Yt.x({file:o,cwd:c,onentry:e=>{p={entries:p.entries+1,bytes:null,current:e.path}}})}finally{h()}console.error(`[ezn] 解压完成(${p.entries} 项,耗时 ${wn(f)} 秒)`);let _=s(c),v=_.length===1&&_[0]!==void 0&&u(m(c,_[0])).isDirectory()?m(c,_[0]):c;return l(o,{force:!0}),{src:v,cleanup:d}}catch(e){p.push({error:e,kind:En(e)}),console.error(`[ezn] 该源失败(${En(e)}):${e instanceof Error?e.message:String(e)}`),l(o,{force:!0}),l(c,{recursive:!0,force:!0}),i(c,{recursive:!0})}}throw l(c,{recursive:!0,force:!0}),Error(Dn({mirrors:f,failures:p,filename:a,nodeVersion:e,target:r.target,nodePath:r.nodePath,extractDir:c}))}function kn(e,t,n,i,a){if(i.some(e=>n.length<=e.length&&n.every((t,n)=>e[n]===t))&&r(t)&&u(e).isDirectory()&&u(t).isDirectory()){for(let r of s(e))kn(m(e,r),m(t,r),[...n,r],i,a);l(e,{recursive:!0,force:!0})}else{if(r(t)){let e=`${t}.old-${a}`;c(t,e),console.error(`[ezn] 同名项已备份:${e}`)}c(e,t)}}async function An(e,t,n={}){let r=(e,t)=>{let n=Date.now();for(let r of s(e))kn(m(e,r),m(t,r),[r],yn,n)},a=g(e),o=Qt(t),c=$t(a),l=en(),u=vn[l];if(!u)throw Error(`暂不支持的平台:${l}。请手动放置 Node ${o} 到 ${a},确保可执行文件位于 ${c}(Windows 为 node.exe,Unix 为 bin/node)。`);i(a,{recursive:!0});let{src:d,cleanup:f}=await On(o,p(a),{target:a,nodePath:c,platform:u,mirror:n.mirror??null});try{console.error(`[ezn] 正在落位运行时文件 ...`),r(d,a)}finally{f()}console.error(`[ezn] Node ${o} 已就绪:${a}`)}function jn(e){return e!==``&&!/[\s"^&|<>()%!]/.test(e)?e:`"${e.replace(/"/g,`\\"`)}"`}function Mn(e,t,n){return n?[[e,...t].map(jn).join(` `),[]]:[e,[...t]]}function Nn(e,t,n={}){return new Promise((r,i)=>{let a=b(e,t,{windowsHide:!0,...n}),o=null,s=null;a.stdout&&(o=``,a.stdout.on(`data`,e=>o=(o??``)+e)),a.stderr&&(s=``,a.stderr.on(`data`,e=>s=(s??``)+e)),a.on(`error`,i),a.on(`close`,(e,t)=>r({status:e,signal:t,stdout:o,stderr:s}))})}var Pn=process.platform===`win32`,Fn=15e3;function In(e){return typeof e==`string`?e.trim().split(/\s+/).filter(e=>e.length>0):[...e]}var Ln=class e{constructor(e,t){this.path=e,this.rt=t}get npmCliPath(){return rn(this.rt,`npm`)}get npxCliPath(){return rn(this.rt,`npx`)}get major(){return e.probeMajor(this.path)}resolveArgs(e,t){let n=In(e);if(n.length===0)throw Error(`命令为空`);let r=n[0],i=n.slice(1);return r===`node`?i:r===`npm`?[this.npmCliPath,...i]:r===`npx`?[this.npxCliPath,...i]:t===null?n:[t,...n]}resolveExec(e,t){let n=In(e);if(n.length===0)throw Error(`命令为空`);let r=n[0],i=n.slice(1),a=this.childEnv(t.env);if(r===`node`)return{file:this.path,argv:i,shell:!1,env:a};if(r===`npm`)return{file:this.path,argv:[this.npmCliPath,...i],shell:!1,env:a};if(r===`npx`)return{file:this.path,argv:[this.npxCliPath,...i],shell:!1,env:a};let o=sn(this.rt,r);if(o!==null){let e=Pn&&/\.(cmd|bat)$/i.test(o),[t,n]=Mn(o,i,e);return{file:t,argv:n,shell:e,env:a}}return{file:this.path,argv:n,shell:!1,env:a}}childEnv(e){let t=e??process.env;return{...t,PATH:cn(this.rt)+(t.PATH??t.Path??``)}}execSync(e,t){let{file:n,argv:r,shell:i,env:a}=this.resolveExec(e,t??{});return x(n,r,{windowsHide:!0,encoding:`utf8`,...t,shell:i,env:a})}exec(e,t){let{file:n,argv:r,shell:i,env:a}=this.resolveExec(e,t??{});return Nn(n,r,{...t,shell:i,env:a})}npmSync(e,t){return x(this.path,this.resolveArgs(e,this.npmCliPath),{windowsHide:!0,encoding:`utf8`,...t})}npm(e,t){return Nn(this.path,this.resolveArgs(e,this.npmCliPath),t)}npxSync(e,t){return x(this.path,this.resolveArgs(e,this.npxCliPath),{windowsHide:!0,encoding:`utf8`,...t})}npx(e,t){return Nn(this.path,this.resolveArgs(e,this.npxCliPath),t)}static probeMajor(t){return e.probe(t).major}static probe(e){try{let t=y(e,[`-v`],{encoding:`utf8`,timeout:Fn,windowsHide:!0}),n=String(t).trim().match(/^v(\d+)\./);return{major:n?Number(n[1]):null,reason:n?`ok`:`unexecutable`}}catch(e){let t=e;return{major:null,reason:t.code===`ETIMEDOUT`||t.signal===`SIGTERM`?`timeout`:`unexecutable`}}}static async ensure(t,n,i={}){let a=g(t),o=Qt(n),s=Number(o.replace(/^v/,``).split(`.`)[0]),c=m(a,`node`),l=$t(c),u=i.probe??(t=>e.probe(t)),d=i.nodeBin??null;if(d){if(!r(d))throw Error(`ezn.nodeBin 指定的 Node 不存在:${d}`);let t=e.probeMajor(d);if(t===null)throw Error(`ezn.nodeBin 指定的 Node 无法执行:${d}`);return t<s&&console.error(`[ezn] 警告:ezn.nodeBin 的 Node 主版本为 ${t},低于建议值 ${s},按用户指定继续。`),console.error(`[ezn] Node 运行时(ezn.nodeBin):${d}`),new e(d,c)}if(r(l)){let{major:t,reason:n}=u(l);if(t!==null&&t>=s&&an(c))return console.error(`[ezn] Node 运行时就绪:${l}`),new e(l,c);if(n===`timeout`)throw Error([`已有 Node 运行时探测超时(${Fn/1e3} 秒内未返回),已中止——不重装。`,` 探测对象:${l}`,` 常见原因:安全软件(360 / 电脑管家 / Windows Defender 实时防护)拦下了它的执行,`,` 或正在扫描这个刚下载的可执行文件;重新下载通常同样会被拦,故这里不重试。`,` 可尝试的恢复方式:`,` 1. 把应用目录(托管运行时所在目录)加入安全软件信任区后重试;`,` 2. 临时关闭安全软件的主动防御/实时防护后重试;`," 3. 手工确认该文件可执行:在该目录执行 `node -v`,若同样超时则确认为拦截。"].join(`
|
|
12
|
-
`));console.error(t===null?`[ezn] 已有 Node 运行时无法执行(疑似损坏),准备重新安装 ...`:t<s?`[ezn] 已有 Node 运行时主版本为 ${t},低于要求 ${s},准备重装 ...`:`[ezn] 已有 Node 运行时缺少自带 npm(上次落位可能中断),准备补齐 ...`)}return await An(c,n,{mirror:i.mirror??null}),new e(l,c)}};function Rn(e){if(e===void 0)return;let t=e.trim();return t===``?void 0:t}function zn(e){let t=`--${e}`,n=`${t}=`,r=process.argv;for(let e=0;e<r.length;e+=1){let i=r[e]??``;if(i.startsWith(n))return Rn(i.slice(n.length));if(i===t){let t=r[e+1]??``;return t.startsWith(`-`)?void 0:Rn(t)}}}function Bn(e,t){return zn(e)??Rn(process.env[t])}var Vn=65535;function Hn(e){let t=Rn(e);if(t===void 0)return;let n=Number(t);return Number.isInteger(n)&&n>0&&n<=Vn?n:void 0}var Un=(()=>{let e=Bn(`app-dir`,`ELLM_APP_DIR`),t=e===void 0?void 0:g(T(),e),n=zn(`port`),r=Hn(n??Rn(process.env.ELLM_PORT));n!==void 0&&r===void 0&&M(`--port=${n} 无效(需为 1-65535 的整数),已忽略:本次不生效、也不会写入配置文件。`);let i=Bn(`node-mirror`,`ELLM_NODE_MIRROR`),a=Bn(`node-bin`,`ELLM_NODE_BIN`);return t!==void 0&&(process.env.ELLM_APP_DIR=t),i!==void 0&&(process.env.ELLM_NODE_MIRROR=i),a!==void 0&&(process.env.ELLM_NODE_BIN=a),{appDir:t,port:r,nodeMirror:i,nodeBin:a}})(),Wn=p(E(import.meta.url)),Gn;function Kn(){return m(S(),`.ezllm`)}function Z(){return Gn===void 0&&(Gn=Un.appDir??Kn()),Gn}function qn(){return g(Z(),`data`)}function Jn(){return g(Z(),`ezllm.config.json`)}function Yn(){return g(Z(),`logs`)}function Xn(){return g(qn(),`updater.json`)}function Zn(){return g(qn(),`plugin-host.json`)}function Qn(){return g(Z(),`autostart.opt-out`)}function $n(){return g(Z(),`plugins`)}function er(){return g(Z(),`services.json`)}var
|
|
13
|
-
`,`utf-8`),c(n,e)}finally{try{l(n,{force:!0})}catch{}}}function
|
|
14
|
-
`);return t<0?null:{line:e.slice(0,t),rest:e.slice(t+1)}}var Pr=class{appDir;endpoint;constructor(e){this.appDir=e,this.endpoint=this.#e()}#e(){return C()===`win32`?`\\\\.\\pipe\\daemon-${D(`sha1`).update(this.appDir.toLowerCase()).digest(`hex`).slice(0,12)}`:m(this.appDir,`daemon.sock`)}request(e,t={},n={}){let r=n.timeoutMs??3e4,i={id:1,cmd:e,args:t};return new Promise((t,n)=>{let a=k(this.endpoint),o=``,s=!1,c=e=>{s||(s=!0,clearTimeout(l),a.destroy(),e())},l=setTimeout(()=>c(()=>n(Error(`控制通道超时(${e},${String(r)}ms)`))),r);a.setEncoding(`utf-8`),a.on(`connect`,()=>a.write(`${JSON.stringify(i)}\n`)),a.on(`data`,r=>{o+=r;let i=Nr(o);if(i===null)return;let a;try{a=JSON.parse(i.line)}catch{c(()=>n(Error(`控制通道响应不是合法 JSON(${e})`)));return}c(()=>a.ok?t(a.data):n(Error(a.error??`${e} 失败`)))}),a.on(`error`,e=>c(()=>n(e))),a.on(`close`,()=>c(()=>n(Error(`控制通道已关闭(${e} 未收到响应)`))))})}async isReachable(e={}){try{return await this.request(Q.ping,{},{timeoutMs:e.timeoutMs??1e3}),!0}catch{return!1}}async serve(e){let t=A(t=>{let n=``;t.setEncoding(`utf-8`),t.on(`error`,()=>t.destroy()),t.on(`data`,r=>{n+=r;let i=Nr(n);i!==null&&(n=i.rest,(async()=>{let n;try{n=JSON.parse(i.line)}catch{t.end();return}try{let r=await e(n.cmd,n.args??{});t.end(`${JSON.stringify({id:n.id,ok:!0,data:r})}\n`)}catch(e){let r=Er(e);t.end(`${JSON.stringify({id:n.id,ok:!1,error:r})}\n`)}})())})});try{await this.#t(t)}catch(e){if(C()!==`win32`&&e.code===`EADDRINUSE`)l(this.endpoint,{force:!0}),await this.#t(t);else throw e}return{endpoint:this.endpoint,close:()=>new Promise(e=>{t.close(()=>e())})}}#t(e){return new Promise((t,n)=>{let r=t=>{e.removeListener(`listening`,i),n(t)},i=()=>{e.removeListener(`error`,r),t()};e.once(`error`,r),e.once(`listening`,i),e.listen(this.endpoint)})}get infoPath(){return m(this.appDir,`daemon.json`)}readInfo(){try{let e=JSON.parse(o(this.infoPath,`utf-8`));return typeof e?.pid==`number`?e:null}catch{return null}}liveChildPids(e){let t=Object.values(this.readInfo()?.children??{}).map(e=>e?.pid).filter(t=>typeof t==`number`&&t!==e);return[...new Set(t)].filter(e=>P(e))}writeInfo(e){i(this.appDir,{recursive:!0}),hr(this.infoPath,e)}clearInfo(e){let t=this.readInfo();if(!(t&&t.pid!==e))try{l(this.infoPath,{force:!0})}catch{}}},Fr=class{name;#e;#t;#n;#r=null;#i=null;#a=null;#o=!1;constructor(e,t,n){this.#e=e,this.#t=t,this.#n=n,this.name=t.name}get snapshot(){return this.#i?{...this.#i}:null}get alive(){return this.#i!==null&&P(this.#i.pid)}async start(){return this.alive?this.snapshot:(this.#a||=this.#c().finally(()=>{this.#a=null}),this.#a)}async stop(e){let t=this.#r,n=this.#i?.pid??t?.pid??null;if(!t||n===null)return this.#s(),{stopped:!0,pid:null};this.#o=!0,this.#n.log(`正在停止 ${this.name}(pid ${String(n)},${e})...`),t.kill();let r=await W(n);return r||this.#n.log(`警告:${this.name} 进程(pid ${String(n)})终止未确认,可能仍占用端口`),this.#s(),{stopped:r,pid:n}}send(e){try{return this.#r?.send?.(e),!0}catch{return!1}}#s(){this.#r=null,this.#i=null,this.#n.onSnapshotChange()}async#c(){this.#o=!1;let e=this.#e.appDir;await this.#t.prepare?.(e);let t=this.#t.env?.(e),n=this.#r=await this.#l(e,t);if(n.on(`error`,e=>this.#n.log(`${this.name} 进程错误:${e.message}`)),n.on(`message`,e=>this.#n.onChildMessage(e)),n.once(`exit`,(e,t)=>this.#d(e,t)),!n.pid)throw this.#o=!0,this.#s(),Error(`无法启动 ${this.name}(未获得进程号)`);return this.#i={pid:n.pid,startedAt:Date.now()},this.#n.onSnapshotChange(),this.#n.log(`${this.name} 进程已启动(pid ${String(n.pid)})`),{...this.#i}}async#l(e,t){let n=this.#t.entry,r=this.#t.spawn;if(n===void 0==(r===void 0))throw Error(`子进程 ${this.name} 的规格必须且只能声明 entry 或 spawn 之一`);if(n!==void 0){let r=await this.#e.interpreter(),i=n(e);return this.#n.log(`正在启动 ${this.name}(${i})...`),R({entryJs:i,appDir:e,env:t,interpreter:r,logFiles:this.#u()})}let i=r(e),a=i.command!==void 0&&i.command.trim()!==``?i.command:process.execPath;return this.#n.log(`正在启动 ${this.name}(${a}${i.cwd===void 0?``:`,cwd ${i.cwd}`})...`),U({command:a,args:i.args??[],...i.cwd===void 0?{}:{cwd:i.cwd},appDir:e,env:t,logFiles:this.#u()})}#u(){return{out:`out-${this.name}.log`,err:`err-${this.name}.log`}}#d(e,t){this.#s();let n=`退出码 ${String(e??`?`)}${t?`,信号 ${t}`:``}`;if(this.#o){this.#n.log(`${this.name} 已停止(${n})`);return}this.#n.log(`${this.name} 异常退出(${n})`),this.#n.onUnexpectedExit(this,n)}},Ir=class{#e;#t;#n=null;constructor(e,t){this.#e=e,this.#t=t}start(){if(this.#e.length===0){this.#t(`未注入任何定时任务(依赖定时任务的能力——如自动更新——将不可用)`);return}let e=this.#e.map(e=>({name:e.name,everyMs:e.everyMs,retryMs:e.retryMs,...e.firstDelayMs===void 0?{}:{firstDelayMs:e.firstDelayMs},run:t=>e.run(t)}));this.#n=Sr({jobs:e,log:this.#t,onError:(e,t,n)=>{this.#t(`定时任务 ${e.name} 失败(${String(Math.round(n/6e4))} 分钟后重试):${t.message}`)}});for(let e of this.#e)this.#t(`定时任务已注册:${e.name}`)}stop(){this.#n?.stop(),this.#n=null}},Lr=`daemon`,Rr=6e4,zr=10,Br=class{#e;#t=null;#n=Lr;#r=[];#i=new Map;#a=[];#o=null;#s=null;#c={};#l=Date.now();#u=!1;#d=!1;#f=()=>``;#p=null;#m=e=>process.exit(e);constructor(e){this.#e=e}configure(e){this.#t=e.daemonJs??null,this.#n=e.logPrefix??Lr,this.#r=e.children??[],this.#a=e.tasks??[],this.#c=e.commands??{},this.#f=e.version??(()=>``),this.#p=e.onStartup??null,this.#m=e.exit??(e=>process.exit(e))}async run(){if(this.#u){this.#h(`本进程已在运行,忽略重复启动`);return}for(let e of Object.keys(this.#c))if(Object.values(Q).includes(e))throw Error(`注入命令与内置命令重名:${e}`);for(let e of this.#r){if(this.#e.child(e.name))throw Error(`注入子进程规格重名:${e.name}`);this.#v(e)}if(await this.#e.control.isReachable()){this.#h(`主进程已在运行(端点 ${this.#e.control.endpoint}),本次启动忽略`);return}let e=this.#O();this.#o=await this.#e.control.serve((e,t)=>this.#b(e,t)),this.#u=!0,this.#C();let t=this.#f();this.#h(`主进程已启动(pid ${String(process.pid)}${t===``?``:`,版本 ${t}`},端点 ${this.#o.endpoint})`);for(let e of[`SIGINT`,`SIGTERM`])process.on(e,()=>{this.#k(`收到 ${e}`)});process.on(`uncaughtException`,e=>{N(`[${this.#n}] 主进程未捕获异常:`,e?.stack??String(e)),N(`[${this.#n}] 主进程即将退出;托管 server 会在 IPC 断开后自行退出`),this.#m(1)});let n=this.#t,r=this.#p;if(n&&r)try{let e=await this.#e.interpreter();await r({daemonJs:n,appDir:this.#e.appDir,interpreter:e})}catch(e){this.#h(`启动钩子失败(不阻塞启动):${Er(e)}`)}this.#s=new Ir(this.#a,this.#g),this.#s.start();for(let t of e)this.#h(`发现无主的子进程(pid ${String(t)})继续运行:本次将重启它以恢复纳管(服务会出现秒级中断)`),await W(t);for(let e of this.#e.children())this.#i.get(e.name)?.autostart!==!1&&await e.start()}#h(e){j(`[${this.#n}] ${e}`)}#g=e=>this.#h(e);#_={log:this.#g,children:{register:e=>{this.#v(e),this.#h(`已注册子进程:${e.name}`)},unregister:async e=>{let t=this.#e.child(e);t&&(await t.stop(`被注销`),this.#e.removeChild(e),this.#i.delete(e),this.#C(),this.#h(`已注销子进程:${e}`))},start:async e=>await this.#x({name:e}).start(),stop:async e=>await this.#x({name:e}).stop(`收到停止指令`),status:()=>this.#y()}};#v(e){if(this.#i.has(e.name)||this.#e.child(e.name))throw Error(`子进程名已被占用:${e.name}`);this.#i.set(e.name,e),this.#e.addChild(new Fr(this.#e,e,this.#w(e.name)))}#y(){let e={};for(let t of this.#e.children())e[t.name]=t.snapshot;return e}async#b(e,t){switch(e){case Q.ping:return{pid:process.pid,version:this.#f()};case Q.status:return this.#S();case Q.childStart:return await this.#x(t).start();case Q.childStop:return await this.#x(t).stop(`收到停止指令`);case Q.childRestart:{let e=this.#x(t),n=await e.stop(`收到重启指令`);return n.stopped?await e.start():{stopped:!1,pid:n.pid}}case Q.daemonStop:return setTimeout(()=>void this.#k(`收到停止指令`),150),{stopping:!0};default:{let n=this.#c[e];if(!n)throw Error(`未知控制命令:${e}`);return await n(t,this.#_)}}}#x(e){let t=typeof e.name==`string`?e.name:``,n=this.#e.child(t);if(!n)throw Error(`未知子进程:${t||`(未指定名称)`}(可用:${this.#e.childNames().join(`、`)})`);return n}#S(){return{daemonPid:process.pid,version:this.#f()||null,startedAt:this.#l,endpoint:this.#o?.endpoint??this.#e.control.endpoint,children:this.#y()}}#C(){this.#e.control.writeInfo({pid:process.pid,endpoint:this.#o?.endpoint??this.#e.control.endpoint,startedAt:this.#l,version:this.#f()||null,children:this.#S().children})}#w(e){return{log:this.#g,onSnapshotChange:()=>this.#C(),onUnexpectedExit:e=>this.#T(e),onChildMessage:t=>this.#D(e,t)}}#T(e){if(this.#d)return;let{allowed:t,count:n}=this.#E(e.name);if(!t){this.#h(`1 分钟内已重启 ${String(e.name)} ${String(n)} 次(上限 ${String(zr)}),停止自动重启;请排查日志后手工 ${this.#n} start`);return}setTimeout(()=>{e.start().catch(e=>this.#h(`自动重启失败:${e.message}`))},500)}#E(e){let t=m(this.#e.appDir,`daemon-restarts.json`),n={};try{let e=JSON.parse(o(t,`utf-8`));typeof e==`object`&&e&&(n=e)}catch{}let r=Date.now(),i={...n[e]??{windowStart:0,count:0}},a=Number.isFinite(i.windowStart)?i.windowStart:r;r-a>Rr?(i.windowStart=r,i.count=0):i.windowStart=a,i.count=(Number.isFinite(i.count)?i.count:0)+1,n[e]=i;try{d(t,JSON.stringify(n),`utf-8`)}catch{}return{allowed:i.count<=zr,count:i.count}}#D(e,t){let n=t;if(!n?.type||typeof n.id!=`number`)return;let r=t=>{this.#e.child(e)?.send({type:`${String(n.type)}.result`,id:n.id,...t})};if(n.type===Q.daemonStop){r({error:`该命令不可经 IPC 委派(仅支持经由 CLI 的控制通道)`});return}this.#b(n.type,n.args??{}).then(e=>r({data:e}),e=>r({error:e.message}))}#O(){let e=this.#e.control.readInfo();return!e||e.pid===process.pid?[]:this.#e.control.liveChildPids(process.pid)}async#k(e){if(!this.#d){this.#d=!0,this.#h(`主进程准备退出(${e})`),this.#s?.stop();for(let t of this.#e.children())await t.stop(e);this.#e.control.clearInfo(process.pid);try{await this.#o?.close()}catch{}this.#h(`主进程已退出(服务随之停止)`),this.#m(0)}}},Vr=class e{static#e=new Map;appDir;control;client;#t;#n=new Map;#r=null;static of(t){let n=e.#e.get(t);return n||(n=new e(t),e.#e.set(t,n)),n}constructor(e){this.appDir=e,this.control=new Pr(e),this.client=new Mr(this),this.#t=new Br(this)}configure(e={}){this.#r=e.interpreter??null,this.client.configure(e),this.#t.configure(e)}interpreter(){let e=this.#r;return e===null?Promise.resolve(process.execPath):e(this.appDir)}get daemon(){return this.#t}addChild(e){this.#n.set(e.name,e)}removeChild(e){this.#n.delete(e)}child(e){return this.#n.get(e)}children(){return[...this.#n.values()]}childNames(){return[...this.#n.keys()]}},Hr=class{#e;constructor(e){this.#e=Vr.of(e)}configure(e={}){return this.#e.configure(e),this}run(){return this.#e.daemon.run()}isRunning(){return this.#e.client.isRunning()}status(){return this.#e.client.status()}childStatus(e){return this.#e.client.childStatus(e)}call(e,t={},n){return this.#e.client.call(e,t,n)}startChild(e){return this.#e.client.startChild(e)}stopChild(e){return this.#e.client.stopChild(e)}restartChild(e){return this.#e.client.restartChild(e)}shutdown(){return this.#e.client.shutdown()}restartDaemon(e={}){return this.#e.client.restartDaemon(e)}},Ur=`server`,$=`plugin-host`,Wr=`ezllm`;`${Wr}`,`${Wr}`;var Gr=/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/,Kr=[Ur,$];function qr(){let e=e=>{if(typeof e!=`object`||!e||Array.isArray(e))throw Error(`服务条目必须是对象`);let t=e,n=typeof t.name==`string`?t.name:``,r=typeof t.cwd==`string`?t.cwd:``,i=typeof t.exec==`string`?t.exec.trim():``,a=t.args;return{name:n,cwd:r,exec:i,args:Array.isArray(a)?a.map(e=>String(e)):[],autostart:t.autostart!==!1}},t=er();if(!r(t))return[];let n;try{n=JSON.parse(o(t,`utf-8`))}catch(e){throw Error(`托管服务清单解析失败:${t}(${e.message})`)}let i=n?.services;if(!Array.isArray(i))throw Error(`托管服务清单格式错误:${t}(顶层需为 { services: [...] })`);return i.map(t=>e(t))}function Jr(e){return{name:e.name,spawn:()=>({...e.exec===``?{}:{command:e.exec},args:e.args,cwd:e.cwd}),autostart:e.autostart}}function Yr(){let e=(e,t)=>Gr.test(e.name)?Kr.includes(e.name)?`服务名 ${e.name} 为系统预留`:t?.has(e.name)?`服务名 ${e.name} 已存在`:e.cwd.trim()===``?`工作目录不能为空`:r(e.cwd)?u(e.cwd).isDirectory()?null:`工作目录不是目录:${e.cwd}`:`工作目录不存在:${e.cwd}`:`服务名只能用字母、数字、点、下划线、短横线(字母或数字开头,不超过 64 个字符)`,t=e=>{hr(er(),{services:[...e]})},n=(e,t)=>{let n=e=>{let t=Yn();return{out:m(t,`out-${e}.log`),err:m(t,`err-${e}.log`)}},r=t.status()[e.name]??null;return{...e,pid:r?.pid??null,startedAt:r?.startedAt??null,logFiles:n(e.name)}},i=(e,t)=>{let n=(e,t,n)=>{let r=e[t];return typeof r==`string`?r:n},r=(e,t,n)=>typeof e[t]==`boolean`?e[t]:n,i=e.args;return{name:n(e,`name`,t?.name??``),cwd:n(e,`cwd`,t?.cwd??``),exec:n(e,`exec`,t?.exec??``).trim(),args:Array.isArray(i)?i.map(e=>String(e)):t?.args??[],autostart:r(e,`autostart`,t?.autostart??!0),start:e.start===!0}},a=()=>qr();return{"svc.list":async(e,t)=>{let r;try{r=a()}catch(e){return{services:[],configError:e.message}}return{services:r.map(e=>n(e,t.children))}},"svc.create":async(r,o)=>{let s=a(),c=i(r,null),l=e(c,new Set(s.map(e=>e.name)));if(l!==null)throw Error(l);let u={name:c.name,cwd:c.cwd,exec:c.exec,args:c.args,autostart:c.autostart};return t([...s,u]),o.children.register(Jr(u)),o.log(`已新增托管服务:${u.name}(${u.exec===``?`主进程 node`:u.exec})`),(c.start||u.autostart)&&await o.children.start(u.name),{service:n(u,o.children)}},"svc.update":async(r,o)=>{let s=a(),c=typeof r.name==`string`?r.name:``,l=s.findIndex(e=>e.name===c);if(l<0)throw Error(`托管服务不存在:${c}`);let u=s[l],d=i(r,u),f=e(d);if(f!==null)throw Error(f);let p={name:d.name,cwd:d.cwd,exec:d.exec,args:d.args,autostart:d.autostart},m=[...s];m[l]=p,t(m);let h=(o.children.status()[p.name]??null)!==null;return await o.children.unregister(p.name),o.children.register(Jr(p)),h&&o.log(`托管服务 ${p.name} 配置已更新,按新配置重启`),(h||d.start)&&await o.children.start(p.name),{service:n(p,o.children)}},"svc.remove":async(e,n)=>{let r=a(),i=typeof e.name==`string`?e.name:``;if(r.findIndex(e=>e.name===i)<0)throw Error(`托管服务不存在:${i}`);return await n.children.unregister(i),t(r.filter(e=>e.name!==i)),n.log(`已删除托管服务:${i}`),{removed:i}}}}function Xr(){try{return{specs:qr().map(e=>Jr(e)),error:null}}catch(e){return{specs:[],error:e.message}}}[h(Jn()).base,h(er()).base,...tr,h(nr()).base,h(Qn()).base],[h(Jn()).base,h(er()).base,...tr,h(nr()).base];var Zr=`ezllm`;function Qr(){try{let e=o(m(Z(),`package.json`),`utf-8`).replace(/^\uFEFF/,``);return JSON.parse(e)}catch{return null}}var $r=`https://registry.npmjs.org/`,ei=/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;function ti(e){return ei.test(e)}function ni(e,t){let n=e=>String(e||``).split(`-`)[0].split(`.`).map(e=>Number(e)||0),r=n(e),i=n(t);for(let e=0;e<3;e++)if((r[e]??0)!==(i[e]??0))return(r[e]??0)>(i[e]??0);return!1}async function ri(e){let t=await e.npm([`view`,Zr,`version`,`--registry`,$r]);if(t.status!==0)throw Error(`查询最新版本失败(npm view 退出码 ${String(t.status)},源 ${$r})。请检查网络与 registry 可达性。`);let n=`${t.stdout??``}`.split(`
|
|
15
|
-
`).map(e=>e.trim()).filter(e=>e!==``),r
|
|
12
|
+
`));console.error(t===null?`[ezn] 已有 Node 运行时无法执行(疑似损坏),准备重新安装 ...`:t<s?`[ezn] 已有 Node 运行时主版本为 ${t},低于要求 ${s},准备重装 ...`:`[ezn] 已有 Node 运行时缺少自带 npm(上次落位可能中断),准备补齐 ...`)}return await An(c,n,{mirror:i.mirror??null}),new e(l,c)}};function Rn(e){if(e===void 0)return;let t=e.trim();return t===``?void 0:t}function zn(e){let t=`--${e}`,n=`${t}=`,r=process.argv;for(let e=0;e<r.length;e+=1){let i=r[e]??``;if(i.startsWith(n))return Rn(i.slice(n.length));if(i===t){let t=r[e+1]??``;return t.startsWith(`-`)?void 0:Rn(t)}}}function Bn(e,t){return zn(e)??Rn(process.env[t])}var Vn=65535;function Hn(e){let t=Rn(e);if(t===void 0)return;let n=Number(t);return Number.isInteger(n)&&n>0&&n<=Vn?n:void 0}var Un=(()=>{let e=Bn(`app-dir`,`ELLM_APP_DIR`),t=e===void 0?void 0:g(T(),e),n=zn(`port`),r=Hn(n??Rn(process.env.ELLM_PORT));n!==void 0&&r===void 0&&M(`--port=${n} 无效(需为 1-65535 的整数),已忽略:本次不生效、也不会写入配置文件。`);let i=Bn(`node-mirror`,`ELLM_NODE_MIRROR`),a=Bn(`node-bin`,`ELLM_NODE_BIN`);return t!==void 0&&(process.env.ELLM_APP_DIR=t),i!==void 0&&(process.env.ELLM_NODE_MIRROR=i),a!==void 0&&(process.env.ELLM_NODE_BIN=a),{appDir:t,port:r,nodeMirror:i,nodeBin:a}})(),Wn=p(E(import.meta.url)),Gn;function Kn(){return m(S(),`.ezllm`)}function Z(){return Gn===void 0&&(Gn=Un.appDir??Kn()),Gn}function qn(){return g(Z(),`data`)}function Jn(){return g(Z(),`ezllm.config.json`)}function Yn(){return g(Z(),`logs`)}function Xn(){return g(qn(),`updater.json`)}function Zn(){return g(qn(),`plugin-host.json`)}function Qn(){return g(Z(),`autostart.opt-out`)}function $n(){return g(Z(),`plugins`)}function er(){return g(Z(),`plugin-auth.json`)}function tr(){return g(Z(),`services.json`)}var nr=[`hook.cjs`,`hook.mjs`,`hook.js`];function rr(){return g(Z(),`node`)}function ir(e){return e.find(e=>r(e))}function ar(e){try{let t=o(m(e,`package.json`),`utf-8`).replace(/^\uFEFF/,``);return JSON.parse(t)}catch{return null}}function or(e,t){let n=Wn;for(let r=0;r<t;r++){if(ar(n)?.name===e)return n;n=p(n)}return null}var sr=or(`ezllm`,5)??g(Wn,`..`),cr=or(`ellm`,6);function lr(){let e=ar(sr);return e?.name===`ezllm`?e.version??`0.0.0`:`0.0.0`}function ur(){let e=qn();r(e)||i(e,{recursive:!0})}function dr(e){return ir([g(Z(),`dist`,e),m(sr,`dist`,e),...cr?[m(cr,`app`,`cli`,`dist`,e)]:[]])??m(sr,`dist`,e)}function fr(){return dr(`server.js`)}function pr(){return dr(`daemon.js`)}function mr(){return dr(`plugin-host.js`)}function hr(){let e=Jn();if(r(e))return e;let n=[m(sr,`ezllm.config.example.json`),...cr?[m(cr,`app`,`server`,`ezllm.config.example.json`)]:[]],a=ir(n);if(!a)throw Error(`缺少示例配置 ${String(n[0])},无法自动创建配置文件`);return i(Z(),{recursive:!0}),t(a,e),j(`已创建配置文件: ${e}`),j(`可直接编辑该文件(填入供应商 apiKey),或经管理台在线修改(实时热更新)。`),e}function gr(e,t){let n=`${e}.${`${process.pid}.${O(6).toString(`hex`)}`}.tmp`;try{d(n,JSON.stringify(t,null,2)+`
|
|
13
|
+
`,`utf-8`),c(n,e)}finally{try{l(n,{force:!0})}catch{}}}function _r(){try{let e=JSON.parse(o(Xn(),`utf-8`));return typeof e!=`object`||!e||Array.isArray(e)?{}:e}catch{return{}}}function vr(e){try{ur(),gr(Xn(),{..._r(),...e})}catch{}}function yr(){try{let e=JSON.parse(o(Zn(),`utf-8`));if(typeof e!=`object`||!e||Array.isArray(e))return{enabled:!1,autoUpdate:!0};let t=e;return{enabled:t.enabled===!0,autoUpdate:t.autoUpdate!==!1}}catch{return{enabled:!1,autoUpdate:!0}}}function br(e){ur(),gr(Zn(),{enabled:e.enabled,autoUpdate:e.autoUpdate})}var xr=29770;function Sr(e){return e+2}function Cr(e){let t=e.now??Date.now,n=e.log??j,r=e.maxArmMs??6e4,i=e.onError??((e,t,r)=>{n(`[scheduler] 作业 ${e.name} 失败(${String(Math.round(r/6e4))} 分钟后重试):${t.message}`)}),a=e=>typeof e==`number`&&Number.isFinite(e)&&e>0?e:null,o=(e,t)=>a(typeof e==`function`?e():e)??t,s=e=>typeof e==`object`&&!!e&&typeof e.then==`function`,c=e.jobs.map(e=>({job:e,nextAt:t()+(e.firstDelayMs??o(e.everyMs,r)),running:!1})),l=null,u=!1;function d(){if(u||l!==null)return;let e=t(),n=1/0;for(let t of c){if(t.running)continue;let r=Math.max(0,t.nextAt-e);r<n&&(n=r)}Number.isFinite(n)&&(l=setTimeout(f,Math.min(n,r)),l.unref())}function f(){if(l=null,u)return;let e=t();for(let t of c)!t.running&&e>=t.nextAt&&m(t);d()}function p(e,n,a){e.running=!1;let s=o(e.job.retryMs??e.job.everyMs,r),c=n??o(e.job.everyMs,r);e.nextAt=t()+(a===null?c:s),a!==null&&i(e.job,a,s),d()}function m(e){e.running=!0;let t;try{t=e.job.run({log:n})}catch(t){p(e,null,t instanceof Error?t:Error(String(t)));return}if(!s(t)){p(e,a(t),null);return}t.then(t=>{p(e,a(t),null)},t=>{p(e,null,t instanceof Error?t:Error(String(t)))})}return d(),{stop(){u=!0,l!==null&&(clearTimeout(l),l=null)}}}var wr=`https://registry.npmmirror.com/`,Tr=`https://registry.npmjs.org/`;function Er(){return[wr,Tr]}async function Dr(e,t=Er()){let n;for(let r of t)try{return{value:await e(r),registry:r}}catch(e){n=e}throw n??Error(`没有任何可用的 npm registry`)}var Or=`24`;function kr(e=Un){let t={};return e.nodeMirror&&(t.mirror=e.nodeMirror),e.nodeBin&&(t.nodeBin=e.nodeBin),t}async function Ar(e){return(await Ln.ensure(e,Or,kr())).path}var Q={ping:`ping`,status:`status`,childStart:`child.start`,childStop:`child.stop`,childRestart:`child.restart`,daemonStop:`daemon.stop`};function jr(e){return e instanceof Error?e.message:String(e)}var Mr=1e4,Nr=2e4,Pr=3e5,Fr=`daemon`,Ir=class extends Error{stage;pid;constructor(e,t,n){super(t),this.name=`DaemonRestartError`,this.stage=e,this.pid=n}},Lr=class{#e;#t;#n=null;#r={};#i=null;#a=Fr;constructor(e){this.#e=e,this.#t=e.appDir}configure(e){this.#n=e.daemonJs??null,this.#r=e.env??{},this.#i=e.logFiles??null,this.#a=e.logPrefix??Fr}#o(e=1e3){return this.#e.control.isReachable({timeoutMs:e})}#s(e,t={},n){return this.#e.control.request(e,t,n===void 0?{}:{timeoutMs:n})}#c(){return this.#i??{out:`${this.#a}.out.log`,err:`${this.#a}.err.log`}}#l(){return m(this.#t,`logs`,this.#c().err)}async isRunning(){return this.#o()}async status(){return await this.#o()?this.#s(Q.status):null}async childStatus(e){return(await this.status())?.children[e]??null}async call(e,t={},n){return this.#s(e,t,n||void 0)}async startChild(e){await this.#g();let t=await this.status();return{...await this.#s(Q.childStart,{name:e},Pr),alreadyRunning:t?.children[e]!=null}}async stopChild(e){await this.#g();let t=await this.#s(Q.childStop,{name:e});return this.#d(t)}async restartChild(e){await this.#g();let t=await this.#s(Q.childRestart,{name:e},Pr);if(`stopped`in t)throw Error(`停止旧服务失败(pid ${String(t.pid??`?`)} 可能仍存活),已中止重启。请检查该进程后重试。`);return t}async shutdown(){if(await this.#o()){let e=await this.#u(Mr);if(e?.kind===`command`)throw e.error;return e?.kind===`kill`?{stopped:!1,reason:`kill-failed`,pid:e.pid}:{stopped:!0}}let e=this.#e.control.readInfo(),t=this.#e.control.liveChildPids();if(t.length>0){for(let e of t)if(!await W(e))return{stopped:!1,reason:`kill-failed`,pid:e};return e&&this.#e.control.clearInfo(e.pid),{stopped:!0}}return e&&this.#e.control.clearInfo(e.pid),{stopped:!1,reason:`not-running`}}async restartDaemon(e={}){let t=e.stopTimeoutMs??Mr,n=e.startTimeoutMs??Nr;if(await this.#o()){let e=await this.#u(t);if(e?.kind===`command`)throw new Ir(`stop`,jr(e.error));if(e?.kind===`kill`)throw new Ir(`stop`,`停止旧主进程失败(pid ${String(e.pid)} 可能仍存活),已中止。新版本可能已装好,请手工结束该进程后重试。`,e.pid)}else await this.#p();let r=await this.#h(n);if(r!==null)throw new Ir(`spawn`,r);let i=Date.now()+Pr;for(;Date.now()<i;){try{let t=await this.status(),n=t!==null&&Object.values(t.children).some(e=>e!==null);if(t!==null&&n&&(!e.expectVersion||t.version===e.expectVersion))return t}catch{}await this.#_(100)}throw new Ir(`spawn`,`新主进程已启动,但未在时限内接管子进程或版本不匹配`)}async#u(e){let t=null;try{let e=await this.#s(Q.ping);t=typeof e?.pid==`number`?e.pid:null}catch{}try{await this.#s(Q.daemonStop)}catch(e){return t===null||P(t)?{kind:`command`,error:e}:null}return t!==null&&!await this.#f(t,e)&&!await W(t)?{kind:`kill`,pid:t}:null}#d(e){return e.stopped?{stopped:!0}:e.pid===null?{stopped:!1,reason:`not-running`}:{stopped:!1,reason:`kill-failed`,pid:e.pid}}async#f(e,t=Mr){let n=Date.now()+t;for(;Date.now()<n;){if(!P(e))return!0;await this.#_(100)}return!P(e)}async#p(){let e=this.#e.control.readInfo(),t=this.#e.control.liveChildPids();for(let e of t)await W(e);e&&t.length>0&&this.#e.control.clearInfo(e.pid)}async#m(){let e=this.#n;if(!e)throw Error(`主进程入口未配置:请经 new Daemon(appDir).configure({ daemonJs }) 注入本包 daemon 产物路径`);if(!r(e))throw Error(`未找到主进程入口:${e}(安装可能不完整,可执行 ${this.#a} install 修复)`);let t=await this.#e.interpreter();return L({entryJs:e,appDir:this.#t,env:this.#r,interpreter:t,logFiles:this.#c()})}async#h(e){if(await this.#m()===0)return`拉起主进程失败(未获得进程号)。请查看日志:${this.#l()}`;let t=Date.now()+e;for(;Date.now()<t;){if(await this.#o(500))return null;await this.#_(200)}return`主进程在 ${String(Math.round(e/1e3))}s 内未启动。请查看日志:${this.#l()}`}async#g(){if(await this.#o())return;let e=await this.#h(Nr);if(e!==null)throw Error(e)}#_(e){return new Promise(t=>setTimeout(t,e))}};function Rr(e){let t=e.indexOf(`
|
|
14
|
+
`);return t<0?null:{line:e.slice(0,t),rest:e.slice(t+1)}}var zr=class{appDir;endpoint;constructor(e){this.appDir=e,this.endpoint=this.#e()}#e(){return C()===`win32`?`\\\\.\\pipe\\daemon-${D(`sha1`).update(this.appDir.toLowerCase()).digest(`hex`).slice(0,12)}`:m(this.appDir,`daemon.sock`)}request(e,t={},n={}){let r=n.timeoutMs??3e4,i={id:1,cmd:e,args:t};return new Promise((t,n)=>{let a=k(this.endpoint),o=``,s=!1,c=e=>{s||(s=!0,clearTimeout(l),a.destroy(),e())},l=setTimeout(()=>c(()=>n(Error(`控制通道超时(${e},${String(r)}ms)`))),r);a.setEncoding(`utf-8`),a.on(`connect`,()=>a.write(`${JSON.stringify(i)}\n`)),a.on(`data`,r=>{o+=r;let i=Rr(o);if(i===null)return;let a;try{a=JSON.parse(i.line)}catch{c(()=>n(Error(`控制通道响应不是合法 JSON(${e})`)));return}c(()=>a.ok?t(a.data):n(Error(a.error??`${e} 失败`)))}),a.on(`error`,e=>c(()=>n(e))),a.on(`close`,()=>c(()=>n(Error(`控制通道已关闭(${e} 未收到响应)`))))})}async isReachable(e={}){try{return await this.request(Q.ping,{},{timeoutMs:e.timeoutMs??1e3}),!0}catch{return!1}}async serve(e){let t=A(t=>{let n=``;t.setEncoding(`utf-8`),t.on(`error`,()=>t.destroy()),t.on(`data`,r=>{n+=r;let i=Rr(n);i!==null&&(n=i.rest,(async()=>{let n;try{n=JSON.parse(i.line)}catch{t.end();return}try{let r=await e(n.cmd,n.args??{});t.end(`${JSON.stringify({id:n.id,ok:!0,data:r})}\n`)}catch(e){let r=jr(e);t.end(`${JSON.stringify({id:n.id,ok:!1,error:r})}\n`)}})())})});try{await this.#t(t)}catch(e){if(C()!==`win32`&&e.code===`EADDRINUSE`)l(this.endpoint,{force:!0}),await this.#t(t);else throw e}return{endpoint:this.endpoint,close:()=>new Promise(e=>{t.close(()=>e())})}}#t(e){return new Promise((t,n)=>{let r=t=>{e.removeListener(`listening`,i),n(t)},i=()=>{e.removeListener(`error`,r),t()};e.once(`error`,r),e.once(`listening`,i),e.listen(this.endpoint)})}get infoPath(){return m(this.appDir,`daemon.json`)}readInfo(){try{let e=JSON.parse(o(this.infoPath,`utf-8`));return typeof e?.pid==`number`?e:null}catch{return null}}liveChildPids(e){let t=Object.values(this.readInfo()?.children??{}).map(e=>e?.pid).filter(t=>typeof t==`number`&&t!==e);return[...new Set(t)].filter(e=>P(e))}writeInfo(e){i(this.appDir,{recursive:!0}),gr(this.infoPath,e)}clearInfo(e){let t=this.readInfo();if(!(t&&t.pid!==e))try{l(this.infoPath,{force:!0})}catch{}}},Br=3e3,Vr=class{name;#e;#t;#n;#r=null;#i=null;#a=null;#o=!1;constructor(e,t,n){this.#e=e,this.#t=t,this.#n=n,this.name=t.name}get snapshot(){return this.#i?{...this.#i}:null}get alive(){return this.#i!==null&&P(this.#i.pid)}async start(){return this.alive?this.snapshot:(this.#a||=this.#c().finally(()=>{this.#a=null}),this.#a)}async stop(e){let t=this.#r,n=this.#i?.pid??t?.pid??null;if(!t||n===null)return this.#s(),{stopped:!0,pid:null};this.#o=!0,this.#n.log(`正在停止 ${this.name}(pid ${String(n)},${e})...`);let r=new Promise(e=>{t.once(`exit`,()=>e())});t.kill();let i=await W(n);i||this.#n.log(`警告:${this.name} 进程(pid ${String(n)})终止未确认,可能仍占用端口`);let a,o=await Promise.race([r.then(()=>!0),new Promise(e=>{a=setTimeout(()=>e(!1),Br)})]);return clearTimeout(a),o||(this.#n.log(`警告:${this.name}(pid ${String(n)})退出事件未在 ${String(Br)}ms 内确认`),this.#s()),{stopped:i&&o,pid:n}}send(e){try{return this.#r?.send?.(e),!0}catch{return!1}}#s(){this.#r=null,this.#i=null,this.#n.onSnapshotChange()}async#c(){this.#o=!1;let e=this.#e.appDir;await this.#t.prepare?.(e);let t=this.#t.env?.(e),n=this.#r=await this.#l(e,t);if(n.on(`error`,e=>this.#n.log(`${this.name} 进程错误:${e.message}`)),n.on(`message`,e=>this.#n.onChildMessage(e)),n.once(`exit`,(e,t)=>{this.#r===n&&this.#d(e,t)}),!n.pid)throw this.#o=!0,this.#s(),Error(`无法启动 ${this.name}(未获得进程号)`);return this.#i={pid:n.pid,startedAt:Date.now()},this.#n.onSnapshotChange(),this.#n.log(`${this.name} 进程已启动(pid ${String(n.pid)})`),{...this.#i}}async#l(e,t){let n=this.#t.entry,r=this.#t.spawn;if(n===void 0==(r===void 0))throw Error(`子进程 ${this.name} 的规格必须且只能声明 entry 或 spawn 之一`);if(n!==void 0){let r=await this.#e.interpreter(),i=n(e);return this.#n.log(`正在启动 ${this.name}(${i})...`),R({entryJs:i,appDir:e,env:t,interpreter:r,logFiles:this.#u()})}let i=r(e),a=i.command!==void 0&&i.command.trim()!==``?i.command:process.execPath;return this.#n.log(`正在启动 ${this.name}(${a}${i.cwd===void 0?``:`,cwd ${i.cwd}`})...`),U({command:a,args:i.args??[],...i.cwd===void 0?{}:{cwd:i.cwd},appDir:e,env:t,logFiles:this.#u()})}#u(){return{out:`out-${this.name}.log`,err:`err-${this.name}.log`}}#d(e,t){this.#s();let n=`退出码 ${String(e??`?`)}${t?`,信号 ${t}`:``}`;if(this.#o){this.#n.log(`${this.name} 已停止(${n})`);return}this.#n.log(`${this.name} 异常退出(${n})`),this.#n.onUnexpectedExit(this,n)}},Hr=class{#e;#t;#n;#r=null;constructor(e,t,n){this.#e=e,this.#t=t,this.#n=n}start(){if(this.#e.length===0){this.#t(`未注入任何定时任务(依赖定时任务的能力——如自动更新——将不可用)`);return}let e=this.#e.map(e=>({name:e.name,everyMs:e.everyMs,retryMs:e.retryMs,...e.firstDelayMs===void 0?{}:{firstDelayMs:e.firstDelayMs},run:t=>e.run({log:t.log,restartChild:this.#n})}));this.#r=Cr({jobs:e,log:this.#t,onError:(e,t,n)=>{this.#t(`定时任务 ${e.name} 失败(${String(Math.round(n/6e4))} 分钟后重试):${t.message}`)}});for(let e of this.#e)this.#t(`定时任务已注册:${e.name}`)}stop(){this.#r?.stop(),this.#r=null}},Ur=`daemon`,Wr=6e4,Gr=10,Kr=class{#e;#t=null;#n=Ur;#r=[];#i=new Map;#a=[];#o=null;#s=null;#c={};#l=Date.now();#u=!1;#d=!1;#f=()=>``;#p=null;#m=e=>process.exit(e);constructor(e){this.#e=e}configure(e){this.#t=e.daemonJs??null,this.#n=e.logPrefix??Ur,this.#r=e.children??[],this.#a=e.tasks??[],this.#c=e.commands??{},this.#f=e.version??(()=>``),this.#p=e.onStartup??null,this.#m=e.exit??(e=>process.exit(e))}async run(){if(this.#u){this.#h(`本进程已在运行,忽略重复启动`);return}for(let e of Object.keys(this.#c))if(Object.values(Q).includes(e))throw Error(`注入命令与内置命令重名:${e}`);for(let e of this.#r){if(this.#e.child(e.name))throw Error(`注入子进程规格重名:${e.name}`);this.#v(e)}if(await this.#e.control.isReachable()){this.#h(`主进程已在运行(端点 ${this.#e.control.endpoint}),本次启动忽略`);return}let e=this.#k();this.#o=await this.#e.control.serve((e,t)=>this.#b(e,t)),this.#u=!0,this.#w();let t=this.#f();this.#h(`主进程已启动(pid ${String(process.pid)}${t===``?``:`,版本 ${t}`},端点 ${this.#o.endpoint})`);for(let e of[`SIGINT`,`SIGTERM`])process.on(e,()=>{this.#A(`收到 ${e}`)});process.on(`uncaughtException`,e=>{N(`[${this.#n}] 主进程未捕获异常:`,e?.stack??String(e)),N(`[${this.#n}] 主进程即将退出;托管 server 会在 IPC 断开后自行退出`),this.#m(1)});let n=this.#t,r=this.#p;if(n&&r)try{let e=await this.#e.interpreter();await r({daemonJs:n,appDir:this.#e.appDir,interpreter:e})}catch(e){this.#h(`启动钩子失败(不阻塞启动):${jr(e)}`)}this.#s=new Hr(this.#a,this.#g,e=>this.#S(e)),this.#s.start();for(let t of e)this.#h(`发现无主的子进程(pid ${String(t)})继续运行:本次将重启它以恢复纳管(服务会出现秒级中断)`),await W(t);for(let e of this.#e.children())this.#i.get(e.name)?.autostart!==!1&&await e.start()}#h(e){j(`[${this.#n}] ${e}`)}#g=e=>this.#h(e);#_={log:this.#g,children:{register:e=>{this.#v(e),this.#h(`已注册子进程:${e.name}`)},unregister:async e=>{let t=this.#e.child(e);t&&(await t.stop(`被注销`),this.#e.removeChild(e),this.#i.delete(e),this.#w(),this.#h(`已注销子进程:${e}`))},start:async e=>await this.#x({name:e}).start(),stop:async e=>await this.#x({name:e}).stop(`收到停止指令`),status:()=>this.#y()}};#v(e){if(this.#i.has(e.name)||this.#e.child(e.name))throw Error(`子进程名已被占用:${e.name}`);this.#i.set(e.name,e),this.#e.addChild(new Vr(this.#e,e,this.#T(e.name)))}#y(){let e={};for(let t of this.#e.children())e[t.name]=t.snapshot;return e}async#b(e,t){switch(e){case Q.ping:return{pid:process.pid,version:this.#f()};case Q.status:return this.#C();case Q.childStart:return await this.#x(t).start();case Q.childStop:return await this.#x(t).stop(`收到停止指令`);case Q.childRestart:{let e=this.#x(t),n=await e.stop(`收到重启指令`);return n.stopped?await e.start():{stopped:!1,pid:n.pid}}case Q.daemonStop:return setTimeout(()=>void this.#A(`收到停止指令`),150),{stopping:!0};default:{let n=this.#c[e];if(!n)throw Error(`未知控制命令:${e}`);return await n(t,this.#_)}}}#x(e){let t=typeof e.name==`string`?e.name:``,n=this.#e.child(t);if(!n)throw Error(`未知子进程:${t||`(未指定名称)`}(可用:${this.#e.childNames().join(`、`)})`);return n}async#S(e){let t=this.#x({name:e}),n=await t.stop(`收到重启指令`);if(!n.stopped)throw Error(`无法停止子进程 ${e}(pid ${String(n.pid)}),重启中止`);await t.start()}#C(){return{daemonPid:process.pid,version:this.#f()||null,startedAt:this.#l,endpoint:this.#o?.endpoint??this.#e.control.endpoint,children:this.#y()}}#w(){this.#e.control.writeInfo({pid:process.pid,endpoint:this.#o?.endpoint??this.#e.control.endpoint,startedAt:this.#l,version:this.#f()||null,children:this.#C().children})}#T(e){return{log:this.#g,onSnapshotChange:()=>this.#w(),onUnexpectedExit:e=>this.#E(e),onChildMessage:t=>this.#O(e,t)}}#E(e){if(this.#d)return;let{allowed:t,count:n}=this.#D(e.name);if(!t){this.#h(`1 分钟内已重启 ${String(e.name)} ${String(n)} 次(上限 ${String(Gr)}),停止自动重启;请排查日志后手工 ${this.#n} start`);return}setTimeout(()=>{e.start().catch(e=>this.#h(`自动重启失败:${e.message}`))},500)}#D(e){let t=m(this.#e.appDir,`daemon-restarts.json`),n={};try{let e=JSON.parse(o(t,`utf-8`));typeof e==`object`&&e&&(n=e)}catch{}let r=Date.now(),i={...n[e]??{windowStart:0,count:0}},a=Number.isFinite(i.windowStart)?i.windowStart:r;r-a>Wr?(i.windowStart=r,i.count=0):i.windowStart=a,i.count=(Number.isFinite(i.count)?i.count:0)+1,n[e]=i;try{d(t,JSON.stringify(n),`utf-8`)}catch{}return{allowed:i.count<=Gr,count:i.count}}#O(e,t){let n=t;if(!n?.type||typeof n.id!=`number`)return;let r=t=>{this.#e.child(e)?.send({type:`${String(n.type)}.result`,id:n.id,...t})};if(n.type===Q.daemonStop){r({error:`该命令不可经 IPC 委派(仅支持经由 CLI 的控制通道)`});return}this.#b(n.type,n.args??{}).then(e=>r({data:e}),e=>r({error:e.message}))}#k(){let e=this.#e.control.readInfo();return!e||e.pid===process.pid?[]:this.#e.control.liveChildPids(process.pid)}async#A(e){if(!this.#d){this.#d=!0,this.#h(`主进程准备退出(${e})`),this.#s?.stop();for(let t of this.#e.children())await t.stop(e);this.#e.control.clearInfo(process.pid);try{await this.#o?.close()}catch{}this.#h(`主进程已退出(服务随之停止)`),this.#m(0)}}},qr=class e{static#e=new Map;appDir;control;client;#t;#n=new Map;#r=null;static of(t){let n=e.#e.get(t);return n||(n=new e(t),e.#e.set(t,n)),n}constructor(e){this.appDir=e,this.control=new zr(e),this.client=new Lr(this),this.#t=new Kr(this)}configure(e={}){this.#r=e.interpreter??null,this.client.configure(e),this.#t.configure(e)}interpreter(){let e=this.#r;return e===null?Promise.resolve(process.execPath):e(this.appDir)}get daemon(){return this.#t}addChild(e){this.#n.set(e.name,e)}removeChild(e){this.#n.delete(e)}child(e){return this.#n.get(e)}children(){return[...this.#n.values()]}childNames(){return[...this.#n.keys()]}},Jr=class{#e;constructor(e){this.#e=qr.of(e)}configure(e={}){return this.#e.configure(e),this}run(){return this.#e.daemon.run()}isRunning(){return this.#e.client.isRunning()}status(){return this.#e.client.status()}childStatus(e){return this.#e.client.childStatus(e)}call(e,t={},n){return this.#e.client.call(e,t,n)}startChild(e){return this.#e.client.startChild(e)}stopChild(e){return this.#e.client.stopChild(e)}restartChild(e){return this.#e.client.restartChild(e)}shutdown(){return this.#e.client.shutdown()}restartDaemon(e={}){return this.#e.client.restartDaemon(e)}},Yr=`server`,$=`plugin-host`,Xr=`ezllm`;`${Xr}`,`${Xr}`;var Zr=/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/,Qr=[Yr,$];function $r(){let e=e=>{if(typeof e!=`object`||!e||Array.isArray(e))throw Error(`服务条目必须是对象`);let t=e,n=typeof t.name==`string`?t.name:``,r=typeof t.cwd==`string`?t.cwd:``,i=typeof t.exec==`string`?t.exec.trim():``,a=t.args;return{name:n,cwd:r,exec:i,args:Array.isArray(a)?a.map(e=>String(e)):[],autostart:t.autostart!==!1}},t=tr();if(!r(t))return[];let n;try{n=JSON.parse(o(t,`utf-8`))}catch(e){throw Error(`托管服务清单解析失败:${t}(${e.message})`)}let i=n?.services;if(!Array.isArray(i))throw Error(`托管服务清单格式错误:${t}(顶层需为 { services: [...] })`);return i.map(t=>e(t))}function ei(e){return{name:e.name,spawn:()=>({...e.exec===``?{}:{command:e.exec},args:e.args,cwd:e.cwd}),autostart:e.autostart}}function ti(){let e=(e,t)=>Zr.test(e.name)?Qr.includes(e.name)?`服务名 ${e.name} 为系统预留`:t?.has(e.name)?`服务名 ${e.name} 已存在`:e.cwd.trim()===``?`工作目录不能为空`:r(e.cwd)?u(e.cwd).isDirectory()?null:`工作目录不是目录:${e.cwd}`:`工作目录不存在:${e.cwd}`:`服务名只能用字母、数字、点、下划线、短横线(字母或数字开头,不超过 64 个字符)`,t=e=>{gr(tr(),{services:[...e]})},n=(e,t)=>{let n=e=>{let t=Yn();return{out:m(t,`out-${e}.log`),err:m(t,`err-${e}.log`)}},r=t.status()[e.name]??null;return{...e,pid:r?.pid??null,startedAt:r?.startedAt??null,logFiles:n(e.name)}},i=(e,t)=>{let n=(e,t,n)=>{let r=e[t];return typeof r==`string`?r:n},r=(e,t,n)=>typeof e[t]==`boolean`?e[t]:n,i=e.args;return{name:n(e,`name`,t?.name??``),cwd:n(e,`cwd`,t?.cwd??``),exec:n(e,`exec`,t?.exec??``).trim(),args:Array.isArray(i)?i.map(e=>String(e)):t?.args??[],autostart:r(e,`autostart`,t?.autostart??!0),start:e.start===!0}},a=()=>$r();return{"svc.list":async(e,t)=>{let r;try{r=a()}catch(e){return{services:[],configError:e.message}}return{services:r.map(e=>n(e,t.children))}},"svc.create":async(r,o)=>{let s=a(),c=i(r,null),l=e(c,new Set(s.map(e=>e.name)));if(l!==null)throw Error(l);let u={name:c.name,cwd:c.cwd,exec:c.exec,args:c.args,autostart:c.autostart};return t([...s,u]),o.children.register(ei(u)),o.log(`已新增托管服务:${u.name}(${u.exec===``?`主进程 node`:u.exec})`),(c.start||u.autostart)&&await o.children.start(u.name),{service:n(u,o.children)}},"svc.update":async(r,o)=>{let s=a(),c=typeof r.name==`string`?r.name:``,l=s.findIndex(e=>e.name===c);if(l<0)throw Error(`托管服务不存在:${c}`);let u=s[l],d=i(r,u),f=e(d);if(f!==null)throw Error(f);let p={name:d.name,cwd:d.cwd,exec:d.exec,args:d.args,autostart:d.autostart},m=[...s];m[l]=p,t(m);let h=(o.children.status()[p.name]??null)!==null;return await o.children.unregister(p.name),o.children.register(ei(p)),h&&o.log(`托管服务 ${p.name} 配置已更新,按新配置重启`),(h||d.start)&&await o.children.start(p.name),{service:n(p,o.children)}},"svc.remove":async(e,n)=>{let r=a(),i=typeof e.name==`string`?e.name:``;if(r.findIndex(e=>e.name===i)<0)throw Error(`托管服务不存在:${i}`);return await n.children.unregister(i),t(r.filter(e=>e.name!==i)),n.log(`已删除托管服务:${i}`),{removed:i}}}}function ni(){try{return{specs:$r().map(e=>ei(e)),error:null}}catch(e){return{specs:[],error:e.message}}}[h(Jn()).base,h(tr()).base,...nr,h(rr()).base,h(Qn()).base],h(qn()).base,h(Yn()).base,h(er()).base,h($n()).base,[h(Jn()).base,h(tr()).base,...nr,h(rr()).base];var ri=`ezllm`;function ii(){try{let e=o(m(Z(),`package.json`),`utf-8`).replace(/^\uFEFF/,``);return JSON.parse(e)}catch{return null}}var ai=/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;function oi(e){return ai.test(e)}function si(e,t){let n=e=>String(e||``).split(`-`)[0].split(`.`).map(e=>Number(e)||0),r=n(e),i=n(t);for(let e=0;e<3;e++)if((r[e]??0)!==(i[e]??0))return(r[e]??0)>(i[e]??0);return!1}async function ci(e,t){let n=await e.npm([`view`,ri,`version`,`--registry`,t]);if(n.status!==0)throw Error(`查询最新版本失败(npm view 退出码 ${String(n.status)},源 ${t})。请检查网络与 registry 可达性。`);let r=`${n.stdout??``}`.split(`
|
|
15
|
+
`).map(e=>e.trim()).filter(e=>e!==``),i=r[r.length-1]??``;if(!oi(i))throw Error(`查询到的最新版本串不合法(${i||`空`}),拒绝用它执行安装。`);return i}async function li(e){let{value:t}=await ui(e);return t}async function ui(e){return Dr(t=>ci(e,t))}function di(){let e=ii();return e?.name===`ezllm`&&typeof e.version==`string`&&e.version!==``?e.version:lr()}async function fi(e={}){let t=await(e.ensureNode??((e,t)=>Ln.ensure(e,t,kr())))(Z(),`24`),n=await(e.fetchLatest??li)(t),r=(e.version??di)(),i={hasUpdate:si(n,r),currentVersion:r,latestVersion:n,checkedAt:Date.now()};return vr({lastCheck:i}),i}function pi(){try{let e=JSON.parse(o(Jn(),`utf-8`));return typeof e?.autoUpdate!=`boolean`||e.autoUpdate}catch{return!0}}var mi={name:`update-check`,everyMs:36e5,retryMs:6e5,firstDelayMs:3e4,run:async({log:e})=>{if(!pi())return;let t=await fi();if(!t.hasUpdate){e(`自动检查完成:已是最新版本 ${t.currentVersion}(下次约 1 小时后)`);return}e(`发现新版本 ${t.latestVersion}(当前 ${t.currentVersion}):在终端执行 npx ezllm update 更新`)}};async function hi(e){let t=await fi();return t.hasUpdate?(e.log(`管理台检查更新:发现 ${t.latestVersion}(当前 ${t.currentVersion})——请在终端执行 npx ezllm update`),{...t,updated:!1}):(e.log(`管理台检查更新:已是最新版本 ${t.currentVersion}`),{...t,updated:!1})}var gi={"update.check":async()=>await fi(),"update.apply":async(e,t)=>await hi(t)};function _i(){try{let e=JSON.parse(o(Jn(),`utf-8`))?.port;if(typeof e==`number`&&Number.isInteger(e)&&e>=1&&e<=65535)return e}catch{}return xr}function vi(){return{ELLM_APP_DIR:Z(),ELLM_PORT:``,PLUGIN_DIR:$n(),PORT:String(Sr(_i())),NODE_DIR:m(Z(),`node`),MAIN_PORT:String(_i()),HOST:`127.0.0.1`,PLUGIN_HOST_KEY:``,PLUGIN_HOST_LOG:``}}function yi(){return{name:$,entry:()=>mr(),env:()=>vi(),autostart:yr().enabled}}function bi(e){let t=yr(),n=e.children.status()[`plugin-host`]??null,r=Yn();return{enabled:t.enabled,autoUpdate:t.autoUpdate,running:n!==null,pid:n?.pid??null,startedAt:n?.startedAt??null,port:Sr(_i()),pluginDir:$n(),logFiles:{out:m(r,`out-${$}.log`),err:m(r,`err-${$}.log`)}}}function xi(){return{"plugin.enable":async(e,t)=>(br({...yr(),enabled:!0}),`plugin-host`in t.children.status()||t.children.register(yi()),await t.children.start($),t.log(`插件服务已开启(端口 ${String(Sr(_i()))})`),bi(t)),"plugin.disable":async(e,t)=>(br({...yr(),enabled:!1}),`plugin-host`in t.children.status()&&await t.children.stop($),t.log(`插件服务已关闭(已装插件保留,插件模型的请求将不可用)`),bi(t)),"plugin.restart":async(e,t)=>{if(!(`plugin-host`in t.children.status()))throw Error(`插件服务未开启,无需重启`);let n=await t.children.stop($);if(!n.stopped)throw Error(`无法停止插件服务(pid ${String(n.pid)}),重启中止`);return await t.children.start($),t.log(`插件服务已重启(端口 ${String(Sr(_i()))})`),bi(t)},"plugin.auto-update":async(e,t)=>{let n=e.enabled;if(typeof n!=`boolean`)throw Error(`args.enabled 需为布尔值(开启传 true、关闭传 false)`);return br({...yr(),autoUpdate:n}),t.log(n?`插件自动更新已开启(每小时检查一次)`:`插件自动更新已关闭`),bi(t)},"plugin.status":async(e,t)=>bi(t)}}var Si=3e4,Ci=6e5;function wi(e){return e instanceof Error?e.message:String(e)}function Ti(e){if(Array.isArray(e))return e;if(typeof e==`object`&&e){let t=e.packages;if(Array.isArray(t))return t}return null}function Ei(e){let t=Ti(e);if(t===null)return[];let n=[];for(let e of t){if(typeof e!=`object`||!e||Array.isArray(e))continue;let t=e;if(t.updateAvailable!==!0)continue;let r=t.package,i=t.latest;typeof r==`string`&&r!==``&&typeof i==`string`&&i!==``&&n.push({package:r,current:typeof t.current==`string`?t.current:``,latest:i})}return n}var Di={name:`plugin-update`,everyMs:36e5,retryMs:6e5,firstDelayMs:3e4,run:async e=>{let t=yr();if(!t.enabled||!t.autoUpdate)return;let n=`http://127.0.0.1:${String(Sr(_i()))}`,r;try{let e=await fetch(`${n}/packages/updates`,{signal:AbortSignal.timeout(Si)});if(!e.ok)throw Error(`HTTP ${String(e.status)}`);r=await e.json()}catch(t){e.log(`插件更新检查失败:${wi(t)}`);return}let i=Ti(r);if(i===null){e.log(`插件更新检查失败:响应形状不符合预期(既非数组,也无 packages 数组)`);return}let a=Ei(i);if(a.length===0){e.log(`插件均已是最新(${String(i.length)} 个)`);return}let o=[],s=0;for(let t of a)try{let e=await fetch(`${n}/packages`,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({package:t.package,version:t.latest}),signal:AbortSignal.timeout(Ci)});if(!e.ok)throw Error(`HTTP ${String(e.status)}`);o.push(`${t.package} ${t.current}→${t.latest}`)}catch(n){s+=1,e.log(`插件更新失败:${t.package} ${t.current}→${t.latest}(${wi(n)}),继续处理其余插件`)}if(o.length===0){e.log(`本轮插件更新未成功(${String(s)} 个失败),未重启插件服务`);return}await e.restartChild($);let c=s===0?``:`(另有 ${String(s)} 个失败)`;e.log(`插件已更新并重启生效:${o.join(`、`)}${c}`)}},Oi={name:Yr,entry:()=>fr(),env:e=>({ELLM_APP_DIR:e,ELLM_PORT:``}),prepare:async()=>{hr()}},ki=ni();ki.error===null?ki.specs.length>0&&j(`[${Xr}] 已加载 ${String(ki.specs.length)} 个托管服务:`+ki.specs.map(e=>e.name).join(`、`)):M(`[${Xr}] 托管服务清单不可用,本次不加载任何托管服务:${ki.error}`);var Ai=yi();Ai.autostart===!0&&j(`[${Xr}] 插件服务已开启,随宿主拉起(${Ai.name})`),await new Jr(Z()).configure({children:[Oi,...ki.specs,Ai],tasks:[mi,Di],commands:{...gi,...ti(),...xi()},daemonJs:pr(),logPrefix:Xr,version:lr,interpreter:e=>Ar(e)}).run();export{};
|
package/dist/plugin-host.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{dirname as e,join as t,resolve as n}from"node:path";import{fileURLToPath as r,pathToFileURL as i}from"node:url";import{cwd as a}from"node:process";import{existsSync as o,mkdirSync as s,readFileSync as c,readdirSync as l,renameSync as u,rmSync as d,statSync as f,writeFileSync as p}from"node:fs";import{homedir as
|
|
3
|
-
`,`utf-8`),u(n,e)}finally{try{d(n,{force:!0})}catch{}}}function C(e){try{let t=JSON.parse(c(e,`utf-8`));if(typeof t!=`object`||!t||Array.isArray(t))return{};let n={};for(let[e,r]of Object.entries(t))typeof r==`object`&&r&&!Array.isArray(r)&&(n[e]=r);return n}catch{return{}}}function w(e,t){pe(e,t)}var me=class{#e;#t;constructor(e){this.#e=e,this.#t=C(e)}reload(){this.#t=C(this.#e)}get(e){let t=this.#t[e];return t===void 0?void 0:{...t}}has(e){return e in this.#t}providers(){return Object.keys(this.#t)}save(e,t){let n={...this.#t,[e]:{...t}};w(this.#e,n),this.#t=n}remove(e){if(!(e in this.#t))return;let t={...this.#t};delete t[e],w(this.#e,t),this.#t=t}},he=/^(@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*(@[a-zA-Z0-9.^~<>=*|xX -]+)?$/,T=3e5,ge=65536,_e=`npm_config_`;function E(e){let t=e.trim();if(t===``||!he.test(t))return null;let n=t.lastIndexOf(`@`);return n<=0?{name:t,version:``}:{name:t.slice(0,n),version:t.slice(n+1)}}function ve(e,t=e=>e){let n=e.trim();if(n===``)return null;if(n.startsWith(`./`)||n.startsWith(`../`)||n.startsWith(`file:`)||n.startsWith(`/`)||/^[a-zA-Z]:[\\/]/.test(n)||n.startsWith(`\\\\`)){let e=n.startsWith(`file:`)?n.slice(5):n;return e.trim()===``?null:{kind:`path`,path:t(e.trim())}}let r=E(n);return r===null?null:{kind:`npm`,name:r.name,version:r.version}}function ye(e){return o(e)?f(e).isDirectory()?o(t(e,`package.json`))?null:`目录里没有 package.json:${e}`:`不是目录:${e}`:`目录不存在:${e}`}function D(e){return E(e)?.name??null}function be(e,n){return t(e,`node_modules`,...n.split(`/`))}function xe(n,r){let i=e=>e.endsWith(`npm-cli.js`)&&o(e),a=e=>{for(let n of[[`node_modules`,`npm`,`bin`,`npm-cli.js`],[`lib`,`node_modules`,`npm`,`bin`,`npm-cli.js`],[`bin`,`npm-cli.js`]]){let r=t(e,...n);if(i(r))return r}return null};if(n!==``){let e=a(n);if(e!==null)return e;if(i(n))return n;let r=t(n,`npm`,`bin`,`npm-cli.js`);if(i(r))return r}let s=t(e(process.execPath),`node_modules`,`npm`,`bin`,`npm-cli.js`);if(i(s))return s;let c=t(e(process.execPath),`..`,`lib`,`node_modules`,`npm`,`bin`,`npm-cli.js`);if(i(c))return c;if(r!==null){let e=t(r,`node`),n=a(e);if(n!==null)return n;try{for(let n of l(e)){let r=a(t(e,n));if(r!==null)return r}}catch{}}return null}function O(e,t,n){return new Promise(r=>{let i=re(e,[...t],{shell:n.shell,cwd:n.cwd,windowsHide:!0,...n.env===void 0?{}:{env:n.env}}),a=``,o=e=>{a.length<ge&&(a+=e.toString(`utf8`))};i.stdout.on(`data`,o),i.stderr.on(`data`,o);let s=setTimeout(()=>{a+=`\n[npm 超时 ${String(T/1e3)}s,已终止]`,i.kill()},T);i.on(`error`,e=>{clearTimeout(s),r({code:null,output:`${a}\n${e.message}`})}),i.on(`close`,e=>{clearTimeout(s),r({code:e,output:a})})})}function Se(e){let t={};for(let[n,r]of Object.entries(e))r!==void 0&&(n.toLowerCase().startsWith(_e)||(t[n]=r));return t}async function k(e,t,n,r,i){let a={...r??{}};i!==void 0&&(a.NPM_CONFIG_USERCONFIG=i,a.NPM_CONFIG_GLOBALCONFIG=`${i}.global-empty`);let o={...Se(process.env),...a};return e===null?O(process.platform===`win32`?`npm.cmd`:`npm`,t,{shell:process.platform===`win32`,cwd:n,env:o}):O(process.execPath,[e,...t],{shell:!1,cwd:n,env:o})}function Ce(e){s(e,{recursive:!0});let n=t(e,`package.json`);o(n)||p(n,`${JSON.stringify({name:`ezllm-plugins`,private:!0,version:`0.0.0`},null,2)}\n`,`utf8`)}async function A(e,t){let r=ve(t,e=>n(e));if(r===null)return{ok:!1,message:`不是合法的 npm 包规格,也不是本机目录:${t}`};if(r.kind===`path`){let e=ye(r.path);if(e!==null)return{ok:!1,message:e}}Ce(e.pluginsDir);let i=r.kind===`npm`?r.version===``?r.name:`${r.name}@${r.version}`:`file:${r.path}`,a=[`install`,`--no-audit`,`--no-fund`,`--save`,i];e.log(`[plugin-host] npm install ${i}`);let o=await k(e.npmCli,a,e.pluginsDir,e.env,e.userconfig);return o.code===0?{ok:!0,output:o.output.trim()}:{ok:!1,message:`npm install 失败(退出码 ${String(o.code)}):\n${o.output.trim().slice(-2e3)}`}}async function we(e,t){let n=D(t);if(n===null)return{ok:!1,message:`不是合法的 npm 包名:${t}`};let r=be(e.pluginsDir,n);if(!o(r))return{ok:!1,message:`未安装:${n}`};e.log(`[plugin-host] npm uninstall ${n}`);let i=await k(e.npmCli,[`uninstall`,`--no-audit`,`--no-fund`,n],e.pluginsDir,e.env,e.userconfig);if(o(r))try{d(r,{recursive:!0,force:!0}),e.log(i.code===0?`[plugin-host] npm 未清理 ${n} 的目录,已手动删除`:`[plugin-host] npm uninstall 退出码 ${String(i.code)},已手动删除 ${n} 的目录`)}catch(e){return{ok:!1,message:`卸载 ${n} 未完成:npm 退出码 ${String(i.code)},且目录残留删除失败(${e instanceof Error?e.message:String(e)})\n${i.output.trim().slice(-1500)}`}}return i.code!==0&&!o(r)?{ok:!0,output:`npm 退出码 ${String(i.code)}(目录已删除)\n${i.output.trim().slice(-1500)}`}:i.code===0?{ok:!0,output:i.output.trim()}:{ok:!1,message:`npm uninstall 失败(退出码 ${String(i.code)}):\n${i.output.trim().slice(-2e3)}`}}function Te(e){for(let n of[`README.md`,`readme.md`,`README.MD`,`README`]){let r=t(e,n);if(o(r))try{return c(r,`utf8`).slice(0,204800)}catch{return null}}return null}var Ee=[{npm:`@ai-sdk/openai-compatible`,protocol:`openai`,path:`chat/completions`},{npm:`@ai-sdk/anthropic`,protocol:`anthropic`,path:`messages`},{npm:`@ai-sdk/openai`,protocol:`openai-responses`,path:`responses`}];function De(e){return typeof e!=`string`||e===``?null:Ee.find(t=>t.npm===e)??null}function j(e){return new Proxy(async()=>({data:void 0}),{get(t,n){if(n!==`then`)return j(`${e}.${String(n)}`)},apply(){return Promise.resolve({data:void 0})}})}function Oe(e){let t={auth:{set:async t=>{let n=t?.path?.id??t?.id??t?.providerID,r=typeof n==`string`&&n!==``?n:e.scope?.key,i=t?.body??t?.auth;return typeof r==`string`&&r!==``&&typeof i==`object`&&i&&await e.saveAuth(r,i),{data:!0}},remove:async()=>({data:!0})},tui:{showToast:async()=>({data:!0})},app:{log:async()=>({data:!0})},config:{get:async()=>({data:{provider:{}}})}},n=(e,t)=>new Proxy(e,{get(e,r){if(typeof r==`symbol`)return Reflect.get(e,r);if(r in e){let i=e[r];return typeof i==`object`&&i?n(i,`${t}.${r}`):i}if(r!==`then`)return j(`${t}.${r}`)}});return n(t,`client`)}function ke(e){return{client:Oe(e),directory:e.directory,worktree:e.directory,project:{id:`ezllm`,worktree:e.directory},serverUrl:new URL(`http://127.0.0.1:4096`),$:()=>{throw Error("插件请求了 shell(`$`),但 ezllm 的插件宿主不提供:当前插件生态没有任何插件使用它;若某个插件真的需要,请提交 issue 说明用途(提供 shell 等于给第三方代码任意命令执行能力)")}}}var M=`@magpie-community`;function N(e){let n=t(e,`node_modules`,M);if(!o(n))return[];let r=[];for(let e of l(n)){let i=t(n,e),a=t(i,`package.json`);if(o(a))try{let t=JSON.parse(c(a,`utf-8`)),n=typeof t.name==`string`&&t.name!==``?t.name:`${M}/${e}`,o=t.exports,s=typeof o==`string`?o:typeof o==`object`&&o?o[`.`]:void 0,l=typeof t.main==`string`?t.main:s;if(typeof l!=`string`||l===``)continue;let u=t.repository,d=typeof u==`string`?u:typeof u==`object`&&u&&typeof u.url==`string`?String(u.url):``;r.push({name:n,version:typeof t.version==`string`?t.version:`0.0.0`,main:l,description:typeof t.description==`string`?t.description:``,license:typeof t.license==`string`?t.license:``,repository:d,dir:i})}catch{}}return r}function Ae(e,n){let r=n.name.startsWith(`${M}/`)?n.name.slice(18):n.name;return t(e,`node_modules`,M,r,n.main)}function P(e,t){return N(e).find(e=>e.name===t)}function je(e){let t=[];for(let[n,r]of Object.entries(e))n==="default"||n.startsWith(`_`)||typeof r==`function`&&t.push([n,r]);return t}function Me(e,t,n){let r=[],i=e=>{let t=n.find(t=>t.auth?.provider===e)??n.find(t=>t.provider?.id===e);return t===void 0||t.auth===void 0?null:{hooks:t,auth:t.auth}};for(let[n,a]of Object.entries(t.provider??{})){let t=a??{},o=i(n),s=De(typeof t.npm==`string`?t.npm:void 0);r.push({id:n,name:typeof t.name==`string`&&t.name!==``?t.name:n,source:{package:e.name,version:e.version},protocol:s?.protocol??null,path:s?.path??``,models:Object.keys(t.models??{}),config:t,hooks:o,capabilities:{models:typeof o?.hooks.provider?.models==`function`,usage:typeof o?.auth.usage==`function`,checkin:typeof o?.auth.checkin==`function`,refresh:typeof o?.auth.refresh==`function`,chatHeaders:typeof o?.hooks[`chat.headers`]==`function`}})}return r}async function Ne(e,t){let n=[],r=[];for(let a of N(e)){let s=Ae(e,a);if(!o(s)){r.push(`${a.name}: 入口不存在(${s})`);continue}let c;try{c=await import(i(s).href)}catch(e){r.push(`${a.name}: 导入失败(${e instanceof Error?e.message:String(e)})`);continue}let l=je(c);if(l.length===0){r.push(`${a.name}: 没有导出插件工厂函数`);continue}let u=[],d={provider:{}};for(let[e,n]of l)try{let e=await n(ke(t));u.push(e),typeof e.config==`function`&&await e.config(d)}catch(t){r.push(`${a.name}#${e}: 工厂/config 失败(${t instanceof Error?t.message:String(t)})`)}if(u.length===0)continue;let f=Me(a,d,u);n.push({package:a.name,version:a.version,providers:f,hooks:u})}return{plugins:n,errors:r}}function F(e){let t=new Set,n=[],r=[...e.errors];for(let i of e.plugins)for(let e of i.providers){if(t.has(e.id)){r.push(`${e.id}: provider id 重复(${e.source.package}),已忽略后一个`);continue}t.add(e.id),n.push(e)}return{providers:n,errors:r}}function Pe(e,t=process.env){let n={host:`127.0.0.1`,port:Number(t.PORT||29772),pluginsDir:t.PLUGIN_DIR||``,authFile:``,apiKey:t.PLUGIN_HOST_KEY||``,nodeDir:t.NODE_DIR||``,skipCheck:!1,help:!1};for(let t=0;t<e.length;t+=1){let r=e[t]??``,i=r.indexOf(`=`),a=r.startsWith(`--`)&&i>=0?r.slice(0,i):r,o=r.startsWith(`--`)&&i>=0?r.slice(i+1):void 0,s=()=>o===void 0?e[++t]:o;switch(a){case`--host`:n.host=s()||n.host;break;case`--port`:n.port=Number(s());break;case`--plugins-dir`:n.pluginsDir=s()||n.pluginsDir;break;case`--auth-file`:n.authFile=s()||n.authFile;break;case`--api-key`:n.apiKey=s()||``;break;case`--node-dir`:n.nodeDir=s()||``;break;case`--skip-check`:n.skipCheck=!0;break;case`--help`:case`-h`:n.help=!0}}return{help:n.help,options:{host:n.host,port:n.port,pluginsDir:n.pluginsDir,authFile:n.authFile,apiKey:n.apiKey,nodeDir:n.nodeDir,skipCheck:n.skipCheck}}}function Fe(){return[`plugin-host(OpenCode 插件服务:加载订阅插件并暴露成 OpenAI 兼容端点)`,``,`用法: plugin-host [选项]`,` --host HOST 监听地址(默认 127.0.0.1)`,` --port PORT 监听端口(默认 29772;托管形态由 daemon 注入 PORT = 主端口+2)`,` --plugins-dir DIR 插件安装目录(默认 <APP_DIR>/plugins;托管形态由 daemon 注入 PLUGIN_DIR)`,` --auth-file PATH 凭据文件(默认 <APP_DIR>/plugin-auth.json)`,` --api-key KEY 要求客户端携带的 API key(默认不校验;安装面是写操作,建议设)`,` --node-dir DIR 托管 Node 目录(安装面用其中的 node+npm;默认自动探测)`,` --skip-check 跳过启动预检`,``,`端点: GET /health · GET /plugins · GET / · GET /packages · GET /packages/<name>`,` POST /packages({package, version?})· DELETE /packages/<name>`,` GET /p/<id>/v1/models · POST /p/<id>/v1/{chat/completions|messages|responses}`,` GET /login/<id>(登录方式与状态)· POST /login/<id>(起登录,{method?})`,``,`环境变量(托管形态注入): PLUGIN_DIR · PORT · NODE_DIR`,``].join(`
|
|
2
|
+
import{dirname as e,join as t,resolve as n}from"node:path";import{fileURLToPath as r,pathToFileURL as i}from"node:url";import{cwd as a}from"node:process";import{existsSync as o,mkdirSync as s,readFileSync as c,readdirSync as l,renameSync as u,rmSync as d,statSync as f,writeFileSync as p}from"node:fs";import{homedir as m}from"node:os";import{createHash as h,randomBytes as g}from"node:crypto";import{spawn as ee}from"node:child_process";import{createServer as te}from"node:http";function _(e,...t){console.log(e,...t)}function v(e,...t){console.warn(e,...t)}function ne(e,...t){console.error(e,...t)}function y(e){if(e===void 0)return;let t=e.trim();return t===``?void 0:t}function b(e){let t=`--${e}`,n=`${t}=`,r=process.argv;for(let e=0;e<r.length;e+=1){let i=r[e]??``;if(i.startsWith(n))return y(i.slice(n.length));if(i===t){let t=r[e+1]??``;return t.startsWith(`-`)?void 0:y(t)}}}function x(e,t){return b(e)??y(process.env[t])}var re=65535;function ie(e){let t=y(e);if(t===void 0)return;let n=Number(t);return Number.isInteger(n)&&n>0&&n<=re?n:void 0}var ae=(()=>{let e=x(`app-dir`,`ELLM_APP_DIR`),t=e===void 0?void 0:n(a(),e),r=b(`port`),i=ie(r??y(process.env.ELLM_PORT));r!==void 0&&i===void 0&&v(`--port=${r} 无效(需为 1-65535 的整数),已忽略:本次不生效、也不会写入配置文件。`);let o=x(`node-mirror`,`ELLM_NODE_MIRROR`),s=x(`node-bin`,`ELLM_NODE_BIN`);return t!==void 0&&(process.env.ELLM_APP_DIR=t),o!==void 0&&(process.env.ELLM_NODE_MIRROR=o),s!==void 0&&(process.env.ELLM_NODE_BIN=s),{appDir:t,port:i,nodeMirror:o,nodeBin:s}})(),S=e(r(import.meta.url)),C;function oe(){return t(m(),`.ezllm`)}function w(){return C===void 0&&(C=ae.appDir??oe()),C}function se(){return n(w(),`plugins`)}function ce(){return n(w(),`plugin-auth.json`)}function le(e){try{let n=c(t(e,`package.json`),`utf-8`).replace(/^\uFEFF/,``);return JSON.parse(n)}catch{return null}}function ue(t,n){let r=S;for(let i=0;i<n;i++){if(le(r)?.name===t)return r;r=e(r)}return null}ue(`ezllm`,5)??n(S,`..`),ue(`ellm`,6);function de(e,t){let n=`${e}.${`${process.pid}.${g(6).toString(`hex`)}`}.tmp`;try{p(n,JSON.stringify(t,null,2)+`
|
|
3
|
+
`,`utf-8`),u(n,e)}finally{try{d(n,{force:!0})}catch{}}}var fe=`https://registry.npmmirror.com/`,pe=`https://registry.npmjs.org/`;function me(){return[fe,pe]}async function he(e,t=me()){let n;for(let r of t)try{return{value:await e(r),registry:r}}catch(e){n=e}throw n??Error(`没有任何可用的 npm registry`)}function T(e){try{let t=JSON.parse(c(e,`utf-8`));if(typeof t!=`object`||!t||Array.isArray(t))return{};let n={};for(let[e,r]of Object.entries(t))typeof r==`object`&&r&&!Array.isArray(r)&&(n[e]=r);return n}catch{return{}}}function E(e,t){de(e,t)}var ge=class{#e;#t;constructor(e){this.#e=e,this.#t=T(e)}reload(){this.#t=T(this.#e)}get(e){let t=this.#t[e];return t===void 0?void 0:{...t}}has(e){return e in this.#t}providers(){return Object.keys(this.#t)}save(e,t){let n={...this.#t,[e]:{...t}};E(this.#e,n),this.#t=n}remove(e){if(!(e in this.#t))return;let t={...this.#t};delete t[e],E(this.#e,t),this.#t=t}},_e=/^(@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*(@[a-zA-Z0-9.^~<>=*|xX -]+)?$/,D=3e5,ve=65536,ye=`npm_config_`;function O(e){let t=e.trim();if(t===``||!_e.test(t))return null;let n=t.lastIndexOf(`@`);return n<=0?{name:t,version:``}:{name:t.slice(0,n),version:t.slice(n+1)}}function be(e,t=e=>e){let n=e.trim();if(n===``)return null;if(n.startsWith(`./`)||n.startsWith(`../`)||n.startsWith(`file:`)||n.startsWith(`/`)||/^[a-zA-Z]:[\\/]/.test(n)||n.startsWith(`\\\\`)){let e=n.startsWith(`file:`)?n.slice(5):n;return e.trim()===``?null:{kind:`path`,path:t(e.trim())}}let r=O(n);return r===null?null:{kind:`npm`,name:r.name,version:r.version}}function xe(e){return o(e)?f(e).isDirectory()?o(t(e,`package.json`))?null:`目录里没有 package.json:${e}`:`不是目录:${e}`:`目录不存在:${e}`}function k(e){return O(e)?.name??null}function Se(e,n){return t(e,`node_modules`,...n.split(`/`))}function Ce(n,r){let i=e=>e.endsWith(`npm-cli.js`)&&o(e),a=e=>{for(let n of[[`node_modules`,`npm`,`bin`,`npm-cli.js`],[`lib`,`node_modules`,`npm`,`bin`,`npm-cli.js`],[`bin`,`npm-cli.js`]]){let r=t(e,...n);if(i(r))return r}return null};if(n!==``){let e=a(n);if(e!==null)return e;if(i(n))return n;let r=t(n,`npm`,`bin`,`npm-cli.js`);if(i(r))return r}let s=t(e(process.execPath),`node_modules`,`npm`,`bin`,`npm-cli.js`);if(i(s))return s;let c=t(e(process.execPath),`..`,`lib`,`node_modules`,`npm`,`bin`,`npm-cli.js`);if(i(c))return c;if(r!==null){let e=t(r,`node`),n=a(e);if(n!==null)return n;try{for(let n of l(e)){let r=a(t(e,n));if(r!==null)return r}}catch{}}return null}function A(e,t,n){return new Promise(r=>{let i=ee(e,[...t],{shell:n.shell,cwd:n.cwd,windowsHide:!0,...n.env===void 0?{}:{env:n.env}}),a=``,o=e=>{a.length<ve&&(a+=e.toString(`utf8`))};i.stdout.on(`data`,o),i.stderr.on(`data`,o);let s=setTimeout(()=>{a+=`\n[npm 超时 ${String(D/1e3)}s,已终止]`,i.kill()},D);i.on(`error`,e=>{clearTimeout(s),r({code:null,output:`${a}\n${e.message}`})}),i.on(`close`,e=>{clearTimeout(s),r({code:e,output:a})})})}function we(e){let t={};for(let[n,r]of Object.entries(e))r!==void 0&&(n.toLowerCase().startsWith(ye)||(t[n]=r));return t}async function j(e,t,n,r,i){let a={...r??{}};i!==void 0&&(a.NPM_CONFIG_USERCONFIG=i,a.NPM_CONFIG_GLOBALCONFIG=`${i}.global-empty`);let o={...we(process.env),...a};return e===null?A(process.platform===`win32`?`npm.cmd`:`npm`,t,{shell:process.platform===`win32`,cwd:n,env:o}):A(process.execPath,[e,...t],{shell:!1,cwd:n,env:o})}async function Te(e,t){let n=await he(async n=>{let r=await j(e.npmCli,[`view`,t,`version`,`--json`,`--registry`,n],e.pluginsDir,e.env,e.userconfig),i=r.code===0?Ee(r.output):null;if(i===null)throw Error(`npm view 未能取到 ${t} 的版本(源 ${n})`);return i}).catch(()=>null);return n===null?null:n.value}function Ee(e){let t=e=>{try{let t=JSON.parse(e);return typeof t==`string`&&t!==``?t:null}catch{return null}},n=e.trim(),r=t(n);if(r!==null)return r;let i=n.split(/\r?\n/);for(let e=i.length-1;e>=0;e--){let n=(i[e]??``).trim();if(n===``)continue;let r=t(n);if(r!==null)return r}return null}function De(e){s(e,{recursive:!0});let n=t(e,`package.json`);o(n)||p(n,`${JSON.stringify({name:`ezllm-plugins`,private:!0,version:`0.0.0`},null,2)}\n`,`utf8`)}async function Oe(e,t){let r=be(t,e=>n(e));if(r===null)return{ok:!1,message:`不是合法的 npm 包规格,也不是本机目录:${t}`};if(r.kind===`path`){let e=xe(r.path);if(e!==null)return{ok:!1,message:e}}De(e.pluginsDir);let i=r.kind===`npm`?r.version===``?r.name:`${r.name}@${r.version}`:`file:${r.path}`,a=[`install`,`--no-audit`,`--no-fund`,`--save`,i];return e.log(`[plugin-host] npm install ${i}`),await he(async t=>{let n=await j(e.npmCli,[...a,`--registry`,t],e.pluginsDir,e.env,e.userconfig);if(n.code!==0)throw Error(`npm install 失败(退出码 ${String(n.code)},源 ${t}):\n${n.output.trim().slice(-2e3)}`);return{ok:!0,output:n.output.trim()}}).then(({value:e})=>e).catch(e=>({ok:!1,message:e instanceof Error?e.message:`npm install 失败:${String(e)}`}))}async function ke(e,t){let n=k(t);if(n===null)return{ok:!1,message:`不是合法的 npm 包名:${t}`};let r=Se(e.pluginsDir,n);if(!o(r))return{ok:!1,message:`未安装:${n}`};e.log(`[plugin-host] npm uninstall ${n}`);let i=await j(e.npmCli,[`uninstall`,`--no-audit`,`--no-fund`,n],e.pluginsDir,e.env,e.userconfig);if(o(r))try{d(r,{recursive:!0,force:!0}),e.log(i.code===0?`[plugin-host] npm 未清理 ${n} 的目录,已手动删除`:`[plugin-host] npm uninstall 退出码 ${String(i.code)},已手动删除 ${n} 的目录`)}catch(e){return{ok:!1,message:`卸载 ${n} 未完成:npm 退出码 ${String(i.code)},且目录残留删除失败(${e instanceof Error?e.message:String(e)})\n${i.output.trim().slice(-1500)}`}}return i.code!==0&&!o(r)?{ok:!0,output:`npm 退出码 ${String(i.code)}(目录已删除)\n${i.output.trim().slice(-1500)}`}:i.code===0?{ok:!0,output:i.output.trim()}:{ok:!1,message:`npm uninstall 失败(退出码 ${String(i.code)}):\n${i.output.trim().slice(-2e3)}`}}function Ae(e){for(let n of[`README.md`,`readme.md`,`README.MD`,`README`]){let r=t(e,n);if(o(r))try{return c(r,`utf8`).slice(0,204800)}catch{return null}}return null}var je=[{npm:`@ai-sdk/openai-compatible`,protocol:`openai`,path:`chat/completions`},{npm:`@ai-sdk/anthropic`,protocol:`anthropic`,path:`messages`},{npm:`@ai-sdk/openai`,protocol:`openai-responses`,path:`responses`}];function Me(e){return typeof e!=`string`||e===``?null:je.find(t=>t.npm===e)??null}function M(e){return new Proxy(async()=>({data:void 0}),{get(t,n){if(n!==`then`)return M(`${e}.${String(n)}`)},apply(){return Promise.resolve({data:void 0})}})}function Ne(e){let t={auth:{set:async t=>{let n=t?.path?.id??t?.id??t?.providerID,r=typeof n==`string`&&n!==``?n:e.scope?.key,i=t?.body??t?.auth;return typeof r==`string`&&r!==``&&typeof i==`object`&&i&&await e.saveAuth(r,i),{data:!0}},remove:async()=>({data:!0})},tui:{showToast:async()=>({data:!0})},app:{log:async()=>({data:!0})},config:{get:async()=>({data:{provider:{}}})}},n=(e,t)=>new Proxy(e,{get(e,r){if(typeof r==`symbol`)return Reflect.get(e,r);if(r in e){let i=e[r];return typeof i==`object`&&i?n(i,`${t}.${r}`):i}if(r!==`then`)return M(`${t}.${r}`)}});return n(t,`client`)}function Pe(e){return{client:Ne(e),directory:e.directory,worktree:e.directory,project:{id:`ezllm`,worktree:e.directory},serverUrl:new URL(`http://127.0.0.1:4096`),$:()=>{throw Error("插件请求了 shell(`$`),但 ezllm 的插件宿主不提供:当前插件生态没有任何插件使用它;若某个插件真的需要,请提交 issue 说明用途(提供 shell 等于给第三方代码任意命令执行能力)")}}}var Fe=`@magpie-community`,Ie=`ezllmPlugin`;function Le(e){let t=e.exports,n=typeof t==`string`?t:typeof t==`object`&&t?t[`.`]:void 0,r=typeof e.main==`string`?e.main:n;return typeof r==`string`&&r!==``?r:void 0}function N(e,r){let i=t(e,`package.json`);if(!o(i))return null;let a;try{let e=JSON.parse(c(i,`utf-8`));if(typeof e!=`object`||!e||Array.isArray(e))return null;a=e}catch{return null}let s=typeof a.name==`string`&&a.name!==``?a.name:r;if(!s.startsWith(`${Fe}/`)&&a[Ie]!==!0)return null;let l=Le(a);if(l===void 0)return null;let u=a.repository,d=typeof u==`string`?u:typeof u==`object`&&u&&typeof u.url==`string`?String(u.url):``;return{name:s,version:typeof a.version==`string`?a.version:`0.0.0`,main:l,description:typeof a.description==`string`?a.description:``,license:typeof a.license==`string`?a.license:``,repository:d,dir:n(e)}}function P(e){let n=t(e,`node_modules`);if(!o(n))return[];let r;try{r=l(n,{withFileTypes:!0})}catch{return[]}let i=[];for(let e of r){if(e.name.startsWith(`.`))continue;if(!e.name.startsWith(`@`)){let r=N(t(n,e.name),e.name);r!==null&&i.push(r);continue}let r=t(n,e.name),a;try{a=l(r,{withFileTypes:!0})}catch{continue}for(let n of a){let a=N(t(r,n.name),`${e.name}/${n.name}`);a!==null&&i.push(a)}}return i}function Re(e){return t(e.dir,e.main)}function ze(e,t){return P(e).find(e=>e.name===t)}function Be(e){let t=[];for(let[n,r]of Object.entries(e))n==="default"||n.startsWith(`_`)||typeof r==`function`&&t.push([n,r]);return t}function Ve(e,t,n){let r=[],i=e=>{let t=n.find(t=>t.auth?.provider===e)??n.find(t=>t.provider?.id===e);return t===void 0||t.auth===void 0?null:{hooks:t,auth:t.auth}};for(let[n,a]of Object.entries(t.provider??{})){let t=a??{},o=i(n),s=Me(typeof t.npm==`string`?t.npm:void 0);r.push({id:n,name:typeof t.name==`string`&&t.name!==``?t.name:n,source:{package:e.name,version:e.version},protocol:s?.protocol??null,path:s?.path??``,models:Object.keys(t.models??{}),config:t,hooks:o,capabilities:{models:typeof o?.hooks.provider?.models==`function`,usage:typeof o?.auth.usage==`function`,checkin:typeof o?.auth.checkin==`function`,refresh:typeof o?.auth.refresh==`function`,chatHeaders:typeof o?.hooks[`chat.headers`]==`function`}})}return r}async function He(e,t){let n=[],r=[];for(let a of P(e)){let e=Re(a);if(!o(e)){r.push(`${a.name}: 入口不存在(${e})`);continue}let s;try{s=await import(i(e).href)}catch(e){r.push(`${a.name}: 导入失败(${e instanceof Error?e.message:String(e)})`);continue}let c=Be(s);if(c.length===0){r.push(`${a.name}: 没有导出插件工厂函数`);continue}let l=[],u={provider:{}};for(let[e,n]of c)try{let e=await n(Pe(t));l.push(e),typeof e.config==`function`&&await e.config(u)}catch(t){r.push(`${a.name}#${e}: 工厂/config 失败(${t instanceof Error?t.message:String(t)})`)}if(l.length===0)continue;let d=Ve(a,u,l);n.push({package:a.name,version:a.version,providers:d,hooks:l})}return{plugins:n,errors:r}}function F(e){let t=new Set,n=[],r=[...e.errors];for(let i of e.plugins)for(let e of i.providers){if(t.has(e.id)){r.push(`${e.id}: provider id 重复(${e.source.package}),已忽略后一个`);continue}t.add(e.id),n.push(e)}return{providers:n,errors:r}}function Ue(e,t=process.env){let n={host:`127.0.0.1`,port:Number(t.PORT||29772),pluginsDir:t.PLUGIN_DIR||``,authFile:``,apiKey:t.PLUGIN_HOST_KEY||``,nodeDir:t.NODE_DIR||``,mainPort:Number(t.MAIN_PORT||0),skipCheck:!1,help:!1};for(let t=0;t<e.length;t+=1){let r=e[t]??``,i=r.indexOf(`=`),a=r.startsWith(`--`)&&i>=0?r.slice(0,i):r,o=r.startsWith(`--`)&&i>=0?r.slice(i+1):void 0,s=()=>o===void 0?e[++t]:o;switch(a){case`--host`:n.host=s()||n.host;break;case`--port`:n.port=Number(s());break;case`--plugins-dir`:n.pluginsDir=s()||n.pluginsDir;break;case`--auth-file`:n.authFile=s()||n.authFile;break;case`--api-key`:n.apiKey=s()||``;break;case`--node-dir`:n.nodeDir=s()||``;break;case`--skip-check`:n.skipCheck=!0;break;case`--help`:case`-h`:n.help=!0}}return{help:n.help,options:{host:n.host,port:n.port,pluginsDir:n.pluginsDir,authFile:n.authFile,apiKey:n.apiKey,nodeDir:n.nodeDir,mainPort:n.mainPort,skipCheck:n.skipCheck}}}function We(){return[`plugin-host(OpenCode 插件服务:加载订阅插件并暴露成 OpenAI 兼容端点)`,``,`用法: plugin-host [选项]`,` --host HOST 监听地址(默认 127.0.0.1)`,` --port PORT 监听端口(默认 29772;托管形态由 daemon 注入 PORT = 主端口+2)`,` --plugins-dir DIR 插件安装目录(默认 <APP_DIR>/plugins;托管形态由 daemon 注入 PLUGIN_DIR)`,` --auth-file PATH 凭据文件(默认 <APP_DIR>/plugin-auth.json)`,` --api-key KEY 要求客户端携带的 API key(默认不校验;安装面是写操作,建议设)`,` --node-dir DIR 托管 Node 目录(安装面用其中的 node+npm;默认自动探测)`,` --skip-check 跳过启动预检`,``,`端点: GET /health · GET /plugins · GET / · GET /packages · GET /packages/<name>`,` POST /packages({package, version?})· DELETE /packages/<name>`,` GET /p/<id>/v1/models · POST /p/<id>/v1/{chat/completions|messages|responses}`,` GET /login/<id>(登录方式与状态)· POST /login/<id>(起登录,{method?})`,``,`环境变量(托管形态注入): PLUGIN_DIR · PORT · NODE_DIR`,``].join(`
|
|
4
4
|
`)+`
|
|
5
|
-
`}var Ie=3e5,Le=864e5,Re=15e3,ze=3e4,I=new class{renewing=new Map;failed=new Map};function Be(e){let t=`${String(e?.refresh??``)}\0${String(e?.access??``)}`;return te(`sha256`).update(t).digest(`hex`).slice(0,16)}function Ve(e){let t=e.refreshLead;return typeof t==`number`&&Number.isFinite(t)&&t>=0?Math.min(t,Le):Ie}function He(e,t){if(typeof e.refresh!=`function`||t.type!==`oauth`)return 0;let n=Number(t.expires);return Number.isFinite(n)&&n>0?Math.max(1,n-Ve(e)):0}var Ue=class{#e;#t;#n;constructor(e,t,n){this.#e=e,this.#t=t,this.#n=n}current(){return this.#n.store.get(this.#e)}getAuth=async()=>this.current();async ensureFresh(){let e=this.#n.now?.()??Date.now(),t=this.current();if(t===void 0)return;let n=He(this.#t,t);if(!n||e<n)return;let r=this.#n.registry??I,i=`${this.#e}\0${Be(t)}`,a=r.failed.get(i);if(a!==void 0&&e-a<ze)return;let o=r.renewing.get(i);o===void 0&&(o=this.#r(t,i).finally(()=>r.renewing.delete(i)),r.renewing.set(i,o));let s;await Promise.race([o,new Promise(e=>{s=setTimeout(e,Re)})]),clearTimeout(s)}async#r(e,t){let n=this.#n.registry??I,r=this.#t.refresh;if(r!==void 0)try{let i=await r({...e},this.#n.providerInfo);typeof i==`object`&&i&&this.#n.store.save(this.#e,{...e,...i}),n.failed.delete(t),this.#n.log?.(`[plugin-host] 凭据已续期:${this.#e}`)}catch(e){n.failed.set(t,this.#n.now?.()??Date.now()),this.#n.log?.(`[plugin-host] 凭据续期失败:${this.#e}(${e instanceof Error?e.message:String(e)})`)}}},We=class extends Error{status;detail;constructor(e,t,n){super(t),this.name=`HttpError`,this.status=e,this.detail=n}};function L(e,t){return{error:{message:e,type:t}}}function R(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function Ge(e){let t=e.search(/[?#]/);return t>=0?e.slice(0,t):e}function z(e,t,n){if(e.headersSent){try{e.end()}catch{}return}e.writeHead(t,{"Content-Type":`application/json`}),e.end(JSON.stringify(n))}function B(e,t,n){z(e,t,{detail:n})}function V(e=process.env){return Number(e.PLUGIN_HOST_MAX_BODY||33554432)}function H(e,t){return new Promise((n,r)=>{let i=[],a=0;e.on(`data`,n=>{if(a+=n.length,a>t){e.destroy(),r(new We(413,`request body too large`,L(`request body too large`,`invalid_request_error`)));return}i.push(n)}),e.on(`end`,()=>{n(Buffer.concat(i))}),e.on(`error`,r)})}async function*Ke(e){if(!e)return;let t=e.getReader();try{for(;;){let{done:e,value:n}=await t.read();if(e)break;n&&(yield Buffer.isBuffer(n)?n:Buffer.from(n))}}finally{try{t.releaseLock()}catch{}}}var qe=new Set([`host`,`connection`,`keep-alive`,`proxy-authenticate`,`proxy-authorization`,`te`,`trailer`,`transfer-encoding`,`upgrade`,`content-length`,`authorization`,`x-api-key`,`x-plugin-session`]),Je=`x-plugin-session`;function Ye(e,t){return t===void 0||t===``?{}:e===`anthropic`?{"x-api-key":t}:{authorization:`Bearer ${t}`}}function Xe(e){let t={};for(let[n,r]of Object.entries(e.headers)){let e=n.toLowerCase();qe.has(e)||r!==void 0&&(t[e]=Array.isArray(r)?r.join(`, `):r)}return t}function Ze(e,t){let n=new Headers;for(let[r,i]of Object.entries(Ye(e.provider.protocol,t.apiKey)))n.set(r,i);for(let[e,r]of Object.entries(t.headers??{}))n.set(e,String(r));for(let[t,r]of Object.entries(Xe(e.req)))n.set(t,r);return{headers:n,baseURL:typeof t.baseURL==`string`?t.baseURL:``,fetchFn:typeof t.fetch==`function`?t.fetch:fetch}}async function Qe(e,t,n){let r=t.provider.hooks?.hooks[`chat.headers`];if(typeof r!=`function`)return;let i={headers:{}};try{await r({sessionID:t.sessionId,agent:`build`,model:{id:t.model,providerID:t.provider.id,api:{id:t.model,npm:t.provider.config.npm}},provider:{source:`custom`,info:t.provider.config,options:t.provider.config.options??{}},message:{id:``,sessionID:t.sessionId,role:`user`,time:{created:Date.now()},agent:`build`,model:{providerID:t.provider.id,modelID:t.model}}},i)}catch(e){n(`[plugin-host] ${t.provider.id}: chat.headers 失败(${e instanceof Error?e.message:String(e)})`);return}for(let[t,n]of Object.entries(i.headers))e.set(t,String(n))}async function $e(e,t){return H(e,t)}async function et(e,t){let{provider:n,credentials:r}=e;if(n.hooks===null)return{kind:`error`,status:501,message:`插件 ${n.source.package} 声明了 provider「${n.id}」但没有实现 auth 钩子(无登录/转发能力)`};if(await r.ensureFresh(),r.current()===void 0)return{kind:`error`,status:401,message:`${n.id} 尚未登录:请在管理台完成登录后重试`};let i=n.hooks.auth.loader;if(i===void 0)return{kind:`error`,status:501,message:`插件 ${n.source.package} 未实现 auth.loader,无法转发`};let a;try{a=await i(r.getAuth,n.config)??{}}catch(e){return{kind:`error`,status:502,message:`插件 loader 失败:${e instanceof Error?e.message:String(e)}`}}if(a.baseURL===void 0||a.baseURL===``)return{kind:`error`,status:502,message:`插件 ${n.source.package} 未给出 baseURL(登录态可能已失效)`};let o=Ze(e,a);await Qe(o.headers,e,t);let s=`${o.baseURL.replace(/\/+$/,``)}/${e.path.replace(/^\/+/,``)}`;try{return{kind:`ok`,response:await o.fetchFn(s,{method:e.method,headers:o.headers,body:e.body.length>0?new Uint8Array(e.body):void 0})}}catch(e){return{kind:`error`,status:502,message:`上游请求失败:${e instanceof Error?e.message:String(e)}`}}}async function tt(e,t,n){let r={};e.headers.forEach((e,t)=>{let n=t.toLowerCase();n!==`content-encoding`&&n!==`content-length`&&n!==`transfer-encoding`&&(r[n]=e)}),t(e.status,r);for await(let t of Ke(e.body))n(t)}function nt(e){return(e.headers.get(`content-type`)??``).includes(`text/event-stream`)}function rt(e,t){try{let t=JSON.parse(e.toString(`utf8`));if(R(t)&&typeof t.model==`string`&&t.model!==``)return t.model}catch{}return t.models[0]??``}function it(e,t){let n=e.apiKey;if(!n)return;let r=t.headers.authorization,i=t.headers[`x-api-key`],a=``;if(r&&r.startsWith(`Bearer `)&&(a=r.slice(7).trim()),!a&&i&&(a=String(i)),a!==n)throw Error(`invalid api key`)}var at=[`127.0.0.1`,`localhost`,`[::1]`,`::1`],U=`拒绝的 `;function ot(e){let t=e.trim();if(t===``)return``;if(t.startsWith(`[`)){let e=t.indexOf(`]`);return e<0?t.toLowerCase():t.slice(0,e+1).toLowerCase()}let n=t.lastIndexOf(`:`);return(n<0?t:t.slice(0,n)).toLowerCase()}function st(e){return e===`0.0.0.0`||e===`::`||e===``}function ct(e,t){if(st(e.host))return;let n=[...at,e.host.trim().toLowerCase()],r=ot(String(t.headers.host??``));if(!n.includes(r))throw Error(`${U}Host:${r===``?`(空)`:r}(只接受本机回环地址)`);let i=t.headers.origin;if(typeof i==`string`&&i!==``&&i!==`null`){let e=(()=>{try{return new URL(i).hostname.toLowerCase()}catch{return i.toLowerCase()}})();if(!n.includes(e))throw Error(`拒绝的 Origin:${e}`)}}function W(e,t){return{id:e.id,name:e.name,package:e.source.package,version:e.source.version,protocol:e.protocol,basePath:e.protocol===null?null:`/${e.id}/v1`,models:e.models,signedIn:t,hooks:e.capabilities}}function lt(e,t){let{providers:n}=F(e.loadResult());z(t,200,{status:`ok`,platform:process.platform,node:process.version.replace(/^v/,``),mode:`plugin-host (forwarding + package install)`,port:e.options.port,pluginsDir:e.options.pluginsDir,authFile:e.options.authFile,providers:n.length})}function ut(e,t){let n=e.loadResult(),{providers:r,errors:i}=F(n);z(t,200,{status:`ok`,pluginsDir:e.options.pluginsDir,packages:n.plugins.map(e=>({package:e.package,version:e.version,providers:e.providers.length})),providers:r.map(t=>W(t,e.auth.has(t.id))),errors:i})}function G(e,t){return F(e.loadResult()).providers.filter(e=>e.source.package===t)}function dt(e,t){let n=N(e.options.pluginsDir).map(t=>{let n=G(e,t.name);return{package:t.name,version:t.version,description:t.description,license:t.license,repository:t.repository,loaded:n.length>0,providers:n.map(t=>({id:t.id,name:t.name,protocol:t.protocol,models:t.models.length,signedIn:e.auth.has(t.id)}))}});z(t,200,{status:`ok`,pluginsDir:e.options.pluginsDir,count:n.length,packages:n,errors:e.loadResult().errors,canInstall:e.install!==void 0&&e.reload!==void 0})}function ft(e,t,n){let r=P(e.options.pluginsDir,t);if(r===void 0)return B(n,404,L(`未安装:${t}`,`not_found`));let i=G(e,r.name);z(n,200,{status:`ok`,package:r.name,version:r.version,description:r.description,license:r.license,repository:r.repository,dir:r.dir,entry:r.main,readme:e.install===void 0?null:Te(r.dir),loaded:i.length>0,providers:i.map(t=>W(t,e.auth.has(t.id))),errors:e.loadResult().errors.filter(e=>e.includes(r.name))})}async function K(e){if(e.reload===void 0)return[];try{return await e.reload()}catch(t){return e.log(`[plugin-host] 重载失败:${t instanceof Error?t.message:String(t)}`),[`重载失败:${t instanceof Error?t.message:String(t)}`]}}async function pt(e,t,n){if(e.install===void 0||e.reload===void 0)return B(n,501,L(`本进程未启用安装能力(未注入 npm 依赖)`,`not_implemented`));let r;try{r=await H(t,V())}catch(e){return B(n,413,L(e instanceof Error?e.message:String(e),`invalid_request_error`))}let i;try{i=r.length===0?{}:JSON.parse(r.toString(`utf8`))}catch{return B(n,400,L(`body 不是合法 JSON`,`invalid_request_error`))}if(!R(i)||typeof i.package!=`string`||i.package.trim()===``)return B(n,400,L(`缺少 package(npm 包名,可带 @版本)`,`invalid_request_error`));let a=i.package.trim(),o=typeof i.version==`string`&&i.version.trim()!==``?i.version.trim():``,s=o===``?a:`${D(a)??a}@${o}`;if(E(s)===null)return B(n,400,L(`不是合法的 npm 包规格:${s}`,`invalid_request_error`));e.log(`[plugin-host] 安装 ${s}`);let c=await A(e.install,s);if(!c.ok)return e.log(`[plugin-host] ✗ 安装失败:${s}`),B(n,502,L(c.message,`install_error`));let l=await K(e),u=D(s)??s,d=P(e.options.pluginsDir,u);e.log(`[plugin-host] ✓ 已安装 ${u}${d?`@${d.version}`:``}`),z(n,200,{status:`ok`,package:u,version:d?.version??``,loaded:G(e,u).length>0,providers:G(e,u).map(e=>({id:e.id,protocol:e.protocol,models:e.models.length})),npmOutput:c.output.slice(-2e3),reloadErrors:l})}async function mt(e,t,n){if(e.install===void 0||e.reload===void 0)return B(n,501,L(`本进程未启用卸载能力(未注入 npm 依赖)`,`not_implemented`));e.log(`[plugin-host] 卸载 ${t}`);let r=await we(e.install,t);if(!r.ok)return B(n,r.message.startsWith(`未安装`)?404:502,L(r.message,r.message.startsWith(`未安装`)?`not_found`:`install_error`));let i=await K(e);e.log(`[plugin-host] ✓ 已卸载 ${t}`),z(n,200,{status:`ok`,package:t,npmOutput:r.output.slice(-2e3),reloadErrors:i})}function q(e){let t=e.hooks?.hooks.auth?.methods;return Array.isArray(t)?t:[]}function J(e,t){let n=e.signIns?.get(t.id);return{provider:t.id,signedIn:e.auth.has(t.id),methods:q(t).map(e=>({type:e.type,label:e.label??``})),state:n?.state??`idle`,...n===void 0?{}:{url:n.url,instructions:n.instructions,...n.error===void 0?{}:{error:n.error},...n.accountId===void 0?{}:{accountId:n.accountId}}}}function ht(e,t,n){z(n,200,J(e,t))}async function gt(e,t,n,r){let i=q(t);if(i.length===0)return B(r,400,L(`${t.id} 未声明登录方式(auth.methods),无法登录;该插件可能不需要登录`,`invalid_request_error`));let a={};try{let e=await H(n,V());if(e.length>0){let t=JSON.parse(e.toString(`utf-8`));a=R(t)?t:{}}}catch{return B(r,400,L(`请求体不是合法 JSON`,`invalid_request_error`))}let o=e.signIns??=new Map;if(o.get(t.id)?.state===`pending`)return z(r,200,J(e,t));let s=typeof a.method==`string`?a.method:``;if(s===``&&i.length>1)return z(r,200,{...J(e,t),needMethod:!0});let c=s===``?i[0]:i.find(e=>e.label===s);if(c===void 0)return B(r,400,L(`未知登录方式:${s}(可选:${i.map(e=>e.label??`(未命名)`).join(`、`)})`,`invalid_request_error`));let l=c.authorize;if(typeof l!=`function`)return B(r,400,L(`登录方式「${c.label??c.type}」缺少 authorize 实现(插件声明与实现不一致)`,`invalid_request_error`));let u;try{u=await l()}catch(e){return B(r,502,L(`起登录流程失败:${e instanceof Error?e.message:String(e)}`,`upstream_error`))}let d=u.callback;if(typeof d!=`function`)return B(r,400,L(`登录流程没有 callback(插件声明与实现不一致)`,`invalid_request_error`));let f={state:`pending`,url:typeof u.url==`string`?u.url:``,instructions:typeof u.instructions==`string`?u.instructions:``};o.set(t.id,f);let p=s===``?c.label??c.type:s;return e.log(`[plugin-host] ▶ ${t.id} 登录已发起(${p}${f.url===``?`,读取本机凭据`:`,等待浏览器完成`})`),(async()=>{try{let n=await d(),r=R(n)?n:{};if(r.type===`success`){e.auth.save(t.id,{...r,type:`oauth`});let n=typeof r.accountId==`string`?r.accountId:``;f.state=`success`,f.accountId=n,e.log(`[plugin-host] ✓ ${t.id} 登录完成${n===``?``:`(${n})`}`)}else{let n=typeof r.error==`string`&&r.error!==``?r.error:`登录未完成`;f.state=`failed`,f.error=n,e.log(`[plugin-host] ✗ ${t.id} 登录失败:${n}`)}}catch(n){f.state=`failed`,f.error=n instanceof Error?n.message:String(n),e.log(`[plugin-host] ✗ ${t.id} 登录异常:${f.error}`)}})(),z(r,200,J(e,t))}function Y(e,t){let{providers:n}=F(e.loadResult());z(t,200,{service:`ezllm-plugin-host`,endpoints:[`GET /health`,`GET /plugins`,`GET /p/<id>/v1/models`,`POST /p/<id>/v1/{chat/completions|messages|responses}`,`GET /packages`,`GET /packages/<name>`,`POST /packages`,`DELETE /packages/<name>`,`GET /login/<id>`,`POST /login/<id>`],providers:n.map(e=>({id:e.id,apiBase:e.protocol===null?null:`/p/${e.id}/v1`,protocol:e.protocol}))})}function _t(e){let t=/^\/p\/([^/]+)\/v1(?:\/(.*))?$/.exec(e);if(t===null)return null;let n=decodeURIComponent(t[1]??``);return n===``?null:{id:n,rest:(t[2]??``).replace(/\/+$/,``)}}function X(e,t){return F(e.loadResult()).providers.find(e=>e.id===t)}function Z(e,t){if(t.hooks===null)throw Error(`provider「${t.id}」没有 auth 钩子(插件声明与实现不一致)`);return new Ue(t.id,t.hooks.auth,{store:e.auth,providerInfo:t.config,log:e.log})}async function vt(e,t,n){let r=t.models,i=!1,a=t.hooks?.hooks.provider?.models;if(typeof a==`function`&&t.hooks!==null&&e.auth.has(t.id)){let n=Z(e,t);await n.ensureFresh();let o=n.current();if(o!==void 0)try{let e=await a(t.config,{auth:o}),n=Object.keys(e??{});n.length>0&&(r=n,i=!0)}catch(n){e.log(`[plugin-host] ${t.id}: provider.models 失败,回落声明表(${n instanceof Error?n.message:String(n)})`)}}z(n,200,{object:`list`,provider:t.id,fromHook:i,data:r.map(e=>({id:e,object:`model`,owned_by:t.id}))})}async function yt(e,t,n,r,i){if(t.protocol===null)return B(i,501,L(`${t.id} 的协议未知(插件未声明可识别的 npm),不可转发`,`not_implemented`));if(n!==t.path)return B(i,404,L(`${t.id} 只提供 /p/${t.id}/v1/${t.path}(插件声明的协议是 ${t.protocol})`,`not_found`));let a;try{a=await $e(r,V())}catch(e){return B(i,413,L(e instanceof Error?e.message:String(e),`invalid_request_error`))}let o=r.headers[Je],s=typeof o==`string`?o:``,c=rt(a,t),l=Date.now();e.log(`[plugin-host] ▶ ${t.id} ${n} model=${c} bytes=${String(a.length)}${s?` session=${s}`:``}`);let u=await et({provider:t,credentials:Z(e,t),model:c,path:n,method:`POST`,req:r,body:a,sessionId:s},e.log);if(u.kind===`error`)return e.log(`[plugin-host] ✗ ${t.id} ${n} ${String(u.status)} ${u.message}`),B(i,u.status,L(u.message,u.status===401?`auth_error`:`upstream_error`));let d=u.response,f=nt(d);e.log(`[plugin-host] ◀ ${t.id} ${n} status=${String(d.status)} ${f?`stream`:`json`} ${String(Date.now()-l)}ms`),await tt(d,(e,t)=>{i.headersSent||i.writeHead(e,t)},e=>{i.write(e)}),i.end()}async function bt(e,t,n){let r=Ge(t.url??`/`),i=(t.method??`GET`).toUpperCase();try{if(ct(e.options,t),it(e.options,t),i===`GET`&&r===`/health`)return lt(e,n);if(i===`GET`&&r===`/plugins`)return ut(e,n);if(i===`GET`&&r===`/`)return Y(e,n);if(r===`/packages`)return i===`GET`?dt(e,n):i===`POST`?await pt(e,t,n):B(n,405,L(`不支持的方法:${i} ${r}`,`invalid_request_error`));let a=/^\/packages\/(.+)$/.exec(r);if(a!==null){let t=decodeURIComponent(a[1]??``);return t===``?B(n,404,L(`未找到:${i} ${r}`,`not_found`)):i===`GET`?ft(e,t,n):i===`DELETE`?await mt(e,t,n):B(n,405,L(`不支持的方法:${i} ${r}`,`invalid_request_error`))}let o=/^\/login\/(.+)$/.exec(r);if(o!==null){let a=decodeURIComponent(o[1]??``),s=a===``?void 0:X(e,a);return s===void 0?B(n,404,L(`未加载 provider:${a}`,`not_found`)):i===`GET`?ht(e,s,n):i===`POST`?await gt(e,s,t,n):B(n,405,L(`不支持的方法:${i} ${r}`,`invalid_request_error`))}let s=_t(r);if(s!==null){let a=X(e,s.id);return a===void 0?B(n,404,L(`未加载 provider:${s.id}`,`not_found`)):i===`GET`&&s.rest===`models`?await vt(e,a,n):i===`POST`?await yt(e,a,s.rest,t,n):B(n,405,L(`不支持的方法:${i} ${r}`,`invalid_request_error`))}B(n,404,L(`未找到:${i} ${r}`,`not_found`))}catch(e){let t=e instanceof Error?e.message:String(e),r=t===`invalid api key`?401:t.startsWith(U)?403:500;B(n,r,L(t,r===500?`internal_error`:`invalid_request_error`))}}function xt(e){return ie((t,n)=>{bt(e,t,n).catch(t=>{e.log(`[fatal] handler error: ${t instanceof Error&&t.stack?t.stack:String(t)}`);try{n.headersSent||n.writeHead(500,{"Content-Type":`application/json`}),n.end(JSON.stringify({detail:L(String(t),`internal_error`)}))}catch{}})})}function St(e){return{...e,pluginsDir:e.pluginsDir===``?ue():e.pluginsDir,authFile:e.authFile===``?de():e.authFile}}function Ct(e,t,n){let{providers:r,errors:i}=F(t);m(`==== 插件宿主预检 ====`),m(`平台 : ${process.platform}`),m(`Node : ${process.version}`),m(`插件目录 : ${e.pluginsDir}`),m(`凭据文件 : ${e.authFile}`),m(`已装插件 : ${String(t.plugins.length)} 个(provider ${String(r.length)} 个)`);for(let e of r){let t=e.protocol??`未知协议(不可用)`,r=n(e.id)?`已登录`:`未登录`;m(` - ${e.id}(${e.name}): ${t} | ${String(e.models.length)} 模型 | ${r}`)}for(let e of i)h(` ! ${e}`);r.length===0&&h(" 未加载到任何 provider——用 `npm install <plugin> --prefix` 装进插件目录后重启"),m(`======================`)}async function Q(e=process.argv.slice(2),t=process.env){let{options:n,help:r}=Pe(e,t);if(r)return process.stdout.write(Fe()),null;let i=St(n),a=new me(i.authFile),o={plugins:[],errors:[]},s=()=>o,c={directory:i.pluginsDir,saveAuth:(e,t)=>{a.save(e,t),m(`[plugin-host] 凭据已保存:${e}`)}},l=async()=>{let e=await Ne(i.pluginsDir,c);return o=e,e.errors};try{await l()}catch(e){h(`[plugin-host] 加载插件失败:${e instanceof Error?e.message:String(e)}`)}i.skipCheck||Ct(i,s(),e=>a.has(e));let u=xe(i.nodeDir,x()),d={pluginsDir:i.pluginsDir,npmCli:u,log:e=>m(e)};m(`[plugin-host] ✅ 监听 http://${i.host}:${String(i.port)}`),m(`[plugin-host] GET /health`),m(`[plugin-host] GET /plugins`),m(`[plugin-host] GET /packages · POST /packages · DELETE /packages/<name>`),m(`[plugin-host] GET /login/<id> · POST /login/<id>`),m(`[plugin-host] 安装能力: ${u===null?`npm 未找到(仅可运行已装插件)`:`已启用(${u})`}`),i.apiKey&&m(`[plugin-host] 鉴权已启用(API key 已设置)`),m(`[plugin-host] 按 Ctrl+C 退出。`);let f=xt({options:i,auth:a,loadResult:s,reload:l,install:d,log:e=>m(e)});return await new Promise((e,t)=>{f.once(`error`,t),f.listen(i.port,i.host,()=>{f.off(`error`,t),e()})}),m(`[plugin-host] listening on ${i.host}:${String(i.port)}`),{server:f,options:i,auth:a,loadResult:s,reload:l}}var $=process.argv[1];$!==void 0&&import.meta.url===i(n($)).href&&Q().catch(e=>{ae(`[plugin-host] 启动失败:${e instanceof Error?e.message:String(e)}`),process.exitCode=1});export{Q as main};
|
|
5
|
+
`}var Ge=3e5,Ke=864e5,qe=15e3,Je=3e4,I=new class{renewing=new Map;failed=new Map};function Ye(e){let t=`${String(e?.refresh??``)}\0${String(e?.access??``)}`;return h(`sha256`).update(t).digest(`hex`).slice(0,16)}function Xe(e){let t=e.refreshLead;return typeof t==`number`&&Number.isFinite(t)&&t>=0?Math.min(t,Ke):Ge}function Ze(e,t){if(typeof e.refresh!=`function`||t.type!==`oauth`)return 0;let n=Number(t.expires);return Number.isFinite(n)&&n>0?Math.max(1,n-Xe(e)):0}var Qe=class{#e;#t;#n;constructor(e,t,n){this.#e=e,this.#t=t,this.#n=n}current(){return this.#n.store.get(this.#e)}getAuth=async()=>this.current();async ensureFresh(){let e=this.#n.now?.()??Date.now(),t=this.current();if(t===void 0)return;let n=Ze(this.#t,t);if(!n||e<n)return;let r=this.#n.registry??I,i=`${this.#e}\0${Ye(t)}`,a=r.failed.get(i);if(a!==void 0&&e-a<Je)return;let o=r.renewing.get(i);o===void 0&&(o=this.#r(t,i).finally(()=>r.renewing.delete(i)),r.renewing.set(i,o));let s;await Promise.race([o,new Promise(e=>{s=setTimeout(e,qe)})]),clearTimeout(s)}async#r(e,t){let n=this.#n.registry??I,r=this.#t.refresh;if(r!==void 0)try{let i=await r({...e},this.#n.providerInfo);typeof i==`object`&&i&&this.#n.store.save(this.#e,{...e,...i}),n.failed.delete(t),this.#n.log?.(`[plugin-host] 凭据已续期:${this.#e}`)}catch(e){n.failed.set(t,this.#n.now?.()??Date.now()),this.#n.log?.(`[plugin-host] 凭据续期失败:${this.#e}(${e instanceof Error?e.message:String(e)})`)}}},$e=class extends Error{status;detail;constructor(e,t,n){super(t),this.name=`HttpError`,this.status=e,this.detail=n}};function L(e,t){return{error:{message:e,type:t}}}function R(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function et(e){let t=e.search(/[?#]/);return t>=0?e.slice(0,t):e}function z(e,t,n){if(e.headersSent){try{e.end()}catch{}return}e.writeHead(t,{"Content-Type":`application/json`}),e.end(JSON.stringify(n))}function B(e,t,n){z(e,t,{detail:n})}function V(e=process.env){return Number(e.PLUGIN_HOST_MAX_BODY||33554432)}function H(e,t){return new Promise((n,r)=>{let i=[],a=0;e.on(`data`,n=>{if(a+=n.length,a>t){e.destroy(),r(new $e(413,`request body too large`,L(`request body too large`,`invalid_request_error`)));return}i.push(n)}),e.on(`end`,()=>{n(Buffer.concat(i))}),e.on(`error`,r)})}async function*tt(e){if(!e)return;let t=e.getReader();try{for(;;){let{done:e,value:n}=await t.read();if(e)break;n&&(yield Buffer.isBuffer(n)?n:Buffer.from(n))}}finally{try{t.releaseLock()}catch{}}}var nt=new Set([`host`,`connection`,`keep-alive`,`proxy-authenticate`,`proxy-authorization`,`te`,`trailer`,`transfer-encoding`,`upgrade`,`content-length`,`authorization`,`x-api-key`,`x-plugin-session`]),rt=`x-plugin-session`;function it(e,t){return t===void 0||t===``?{}:e===`anthropic`?{"x-api-key":t}:{authorization:`Bearer ${t}`}}function at(e){let t={};for(let[n,r]of Object.entries(e.headers)){let e=n.toLowerCase();nt.has(e)||r!==void 0&&(t[e]=Array.isArray(r)?r.join(`, `):r)}return t}function ot(e,t){let n=new Headers;for(let[r,i]of Object.entries(it(e.provider.protocol,t.apiKey)))n.set(r,i);for(let[e,r]of Object.entries(t.headers??{}))n.set(e,String(r));for(let[t,r]of Object.entries(at(e.req)))n.set(t,r);return{headers:n,baseURL:typeof t.baseURL==`string`?t.baseURL:``,fetchFn:typeof t.fetch==`function`?t.fetch:fetch}}async function st(e,t,n){let r=t.provider.hooks?.hooks[`chat.headers`];if(typeof r!=`function`)return;let i={headers:{}};try{await r({sessionID:t.sessionId,agent:`build`,model:{id:t.model,providerID:t.provider.id,api:{id:t.model,npm:t.provider.config.npm}},provider:{source:`custom`,info:t.provider.config,options:t.provider.config.options??{}},message:{id:``,sessionID:t.sessionId,role:`user`,time:{created:Date.now()},agent:`build`,model:{providerID:t.provider.id,modelID:t.model}}},i)}catch(e){n(`[plugin-host] ${t.provider.id}: chat.headers 失败(${e instanceof Error?e.message:String(e)})`);return}for(let[t,n]of Object.entries(i.headers))e.set(t,String(n))}async function ct(e,t){return H(e,t)}async function lt(e,t){let{provider:n,credentials:r}=e;if(n.hooks===null)return{kind:`error`,status:501,message:`插件 ${n.source.package} 声明了 provider「${n.id}」但没有实现 auth 钩子(无登录/转发能力)`};if(await r.ensureFresh(),r.current()===void 0)return{kind:`error`,status:401,message:`${n.id} 尚未登录:请在管理台完成登录后重试`};let i=n.hooks.auth.loader;if(i===void 0)return{kind:`error`,status:501,message:`插件 ${n.source.package} 未实现 auth.loader,无法转发`};let a;try{a=await i(r.getAuth,n.config)??{}}catch(e){return{kind:`error`,status:502,message:`插件 loader 失败:${e instanceof Error?e.message:String(e)}`}}if(a.baseURL===void 0||a.baseURL===``)return{kind:`error`,status:502,message:`插件 ${n.source.package} 未给出 baseURL(登录态可能已失效)`};let o=ot(e,a);await st(o.headers,e,t);let s=`${o.baseURL.replace(/\/+$/,``)}/${e.path.replace(/^\/+/,``)}`;try{return{kind:`ok`,response:await o.fetchFn(s,{method:e.method,headers:o.headers,body:e.body.length>0?new Uint8Array(e.body):void 0})}}catch(e){return{kind:`error`,status:502,message:`上游请求失败:${e instanceof Error?e.message:String(e)}`}}}async function ut(e,t,n){let r={};e.headers.forEach((e,t)=>{let n=t.toLowerCase();n!==`content-encoding`&&n!==`content-length`&&n!==`transfer-encoding`&&(r[n]=e)}),t(e.status,r);for await(let t of tt(e.body))n(t)}function dt(e){return(e.headers.get(`content-type`)??``).includes(`text/event-stream`)}function ft(e,t){try{let t=JSON.parse(e.toString(`utf8`));if(R(t)&&typeof t.model==`string`&&t.model!==``)return t.model}catch{}return t.models[0]??``}function pt(e,t){let n=e.apiKey;if(!n)return;let r=t.headers.authorization,i=t.headers[`x-api-key`],a=``;if(r&&r.startsWith(`Bearer `)&&(a=r.slice(7).trim()),!a&&i&&(a=String(i)),a!==n)throw Error(`invalid api key`)}var mt=[`127.0.0.1`,`localhost`,`[::1]`,`::1`],U=`拒绝的 `;function ht(e){let t=e.trim();if(t===``)return``;if(t.startsWith(`[`)){let e=t.indexOf(`]`);return e<0?t.toLowerCase():t.slice(0,e+1).toLowerCase()}let n=t.lastIndexOf(`:`);return(n<0?t:t.slice(0,n)).toLowerCase()}function gt(e){return e===`0.0.0.0`||e===`::`||e===``}function _t(e,t){if(gt(e.host))return;let n=[...mt,e.host.trim().toLowerCase()],r=ht(String(t.headers.host??``));if(!n.includes(r))throw Error(`${U}Host:${r===``?`(空)`:r}(只接受本机回环地址)`);let i=t.headers.origin;if(typeof i==`string`&&i!==``&&i!==`null`){let e=(()=>{try{return new URL(i).hostname.toLowerCase()}catch{return i.toLowerCase()}})();if(!n.includes(e))throw Error(`拒绝的 Origin:${e}`)}}function W(e,t){return{id:e.id,name:e.name,package:e.source.package,version:e.source.version,protocol:e.protocol,basePath:e.protocol===null?null:`/${e.id}/v1`,models:e.models,signedIn:t,hooks:e.capabilities}}function vt(e,t){let{providers:n}=F(e.loadResult());z(t,200,{status:`ok`,platform:process.platform,node:process.version.replace(/^v/,``),mode:`plugin-host (forwarding + package install)`,port:e.options.port,pluginsDir:e.options.pluginsDir,authFile:e.options.authFile,providers:n.length})}function yt(e,t){let n=e.loadResult(),{providers:r,errors:i}=F(n);z(t,200,{status:`ok`,pluginsDir:e.options.pluginsDir,packages:n.plugins.map(e=>({package:e.package,version:e.version,providers:e.providers.length})),providers:r.map(t=>W(t,e.auth.has(t.id))),errors:i})}function G(e,t){return F(e.loadResult()).providers.filter(e=>e.source.package===t)}function bt(e,t){let n=P(e.options.pluginsDir).map(t=>{let n=G(e,t.name);return{package:t.name,version:t.version,description:t.description,license:t.license,repository:t.repository,loaded:n.length>0,providers:n.map(t=>({id:t.id,name:t.name,protocol:t.protocol,models:t.models.length,signedIn:e.auth.has(t.id)}))}});z(t,200,{status:`ok`,pluginsDir:e.options.pluginsDir,count:n.length,packages:n,errors:e.loadResult().errors,canInstall:e.install!==void 0&&e.reload!==void 0})}function xt(e,t,n){let r=ze(e.options.pluginsDir,t);if(r===void 0)return B(n,404,L(`未安装:${t}`,`not_found`));let i=G(e,r.name);z(n,200,{status:`ok`,package:r.name,version:r.version,description:r.description,license:r.license,repository:r.repository,dir:r.dir,entry:r.main,readme:e.install===void 0?null:Ae(r.dir),loaded:i.length>0,providers:i.map(t=>W(t,e.auth.has(t.id))),errors:e.loadResult().errors.filter(e=>e.includes(r.name))})}async function K(e){if(e.reload===void 0)return[];try{return await e.reload()}catch(t){return e.log(`[plugin-host] 重载失败:${t instanceof Error?t.message:String(t)}`),[`重载失败:${t instanceof Error?t.message:String(t)}`]}}function q(e){let t=e.options.mainPort;if(!Number.isFinite(t)||t<=0){e.log(`[plugin-host] 未由 ezllm 托管(MAIN_PORT 未注入),装/卸后的重启需手动执行`);return}let n=`http://127.0.0.1:${String(t)}/api/service/plugins/restart`;fetch(n,{method:`POST`}).then(t=>{t.ok||e.log(`[plugin-host] 自请重启失败:HTTP ${String(t.status)}(插件已装,重启需手动)`)}).catch(t=>{e.log(`[plugin-host] 自请重启失败:${t instanceof Error?t.message:String(t)}(插件已装,重启需手动)`)})}var J=Promise.resolve();function Y(e){let t=J.then(e,e);return J=t.then(()=>void 0,()=>void 0),t}async function St(e,t,n){if(e.install===void 0||e.reload===void 0)return B(n,501,L(`本进程未启用安装能力(未注入 npm 依赖)`,`not_implemented`));let r;try{r=await H(t,V())}catch(e){return B(n,413,L(e instanceof Error?e.message:String(e),`invalid_request_error`))}let i;try{i=r.length===0?{}:JSON.parse(r.toString(`utf8`))}catch{return B(n,400,L(`body 不是合法 JSON`,`invalid_request_error`))}if(!R(i)||typeof i.package!=`string`||i.package.trim()===``)return B(n,400,L(`缺少 package(npm 包名,可带 @版本)`,`invalid_request_error`));let a=i.package.trim(),o=typeof i.version==`string`&&i.version.trim()!==``?i.version.trim():``,s=o===``?a:`${k(a)??a}@${o}`;if(O(s)===null)return B(n,400,L(`不是合法的 npm 包规格:${s}`,`invalid_request_error`));e.log(`[plugin-host] 安装 ${s}`);let c=await Oe(e.install,s);if(!c.ok)return e.log(`[plugin-host] ✗ 安装失败:${s}`),B(n,502,L(c.message,`install_error`));let l=await K(e),u=k(s)??s,d=ze(e.options.pluginsDir,u);e.log(`[plugin-host] ✓ 已安装 ${u}${d?`@${d.version}`:``}`),z(n,200,{status:`ok`,package:u,version:d?.version??``,loaded:G(e,u).length>0,providers:G(e,u).map(e=>({id:e.id,protocol:e.protocol,models:e.models.length})),npmOutput:c.output.slice(-2e3),reloadErrors:l,restartRequested:e.options.mainPort>0}),q(e)}async function Ct(e,t,n){if(e.install===void 0||e.reload===void 0)return B(n,501,L(`本进程未启用卸载能力(未注入 npm 依赖)`,`not_implemented`));e.log(`[plugin-host] 卸载 ${t}`);let r=await ke(e.install,t);if(!r.ok)return B(n,r.message.startsWith(`未安装`)?404:502,L(r.message,r.message.startsWith(`未安装`)?`not_found`:`install_error`));let i=await K(e);e.log(`[plugin-host] ✓ 已卸载 ${t}`),z(n,200,{status:`ok`,package:t,npmOutput:r.output.slice(-2e3),reloadErrors:i,restartRequested:e.options.mainPort>0}),q(e)}var wt=36e5;async function Tt(e,t){let n=e.versionCache??=new Map,r=Date.now(),i=[];for(let t of P(e.options.pluginsDir)){let a=null;if(e.install!==void 0){let i=n.get(t.name);i!==void 0&&r-i.at<wt?a=i.latest:(a=await Te(e.install,t.name),a!==null&&n.set(t.name,{latest:a,at:r}))}i.push({package:t.name,current:t.version,latest:a,updateAvailable:a!==null&&a!==t.version})}z(t,200,{status:`ok`,canCheck:e.install!==void 0,packages:i})}function X(e){let t=e.hooks?.hooks.auth?.methods;return Array.isArray(t)?t:[]}function Z(e,t){let n=e.signIns?.get(t.id);return{provider:t.id,signedIn:e.auth.has(t.id),methods:X(t).map(e=>({type:e.type,label:e.label??``})),state:n?.state??`idle`,...n===void 0?{}:{url:n.url,instructions:n.instructions,...n.error===void 0?{}:{error:n.error},...n.accountId===void 0?{}:{accountId:n.accountId}}}}function Et(e,t,n){z(n,200,Z(e,t))}function Dt(e,t){let n=e.when;if(n!==void 0){let e=typeof n.key==`string`?t[n.key]:void 0;if(e===void 0)return!1;let r=e===n.value;if(n.op===`eq`?!r:r)return!1}return!(typeof e.condition==`function`&&!e.condition(t))}function Ot(e,t){let n=[];for(let r of e.prompts??[])typeof r.key!=`string`||r.key===``||r.key in t||Dt(r,t)&&n.push(r);return n}function kt(e){return e.map(e=>({type:typeof e.type==`string`?e.type:`text`,key:typeof e.key==`string`?e.key:``,message:typeof e.message==`string`?e.message:``,placeholder:typeof e.placeholder==`string`?e.placeholder:``,options:Array.isArray(e.options)?e.options:[]}))}async function At(e,t,n,r,i,a){let o=e.signIns??=new Map,s=typeof n.placeholder==`string`?n.placeholder:``,c=n.label??n.type;if(i===``)return z(a,200,{...Z(e,t),needKey:!0,placeholder:s,label:c,inputs:r});let l=n.authorize;if(typeof l!=`function`){let n=Object.keys(r).length>0?{metadata:r}:{};return e.auth.save(t.id,{type:`api`,key:i,...n}),o.set(t.id,{state:`success`,url:``,instructions:``}),e.log(`[plugin-host] ✓ ${t.id} 登录完成(API key 已保存)`),z(a,200,Z(e,t))}let u;try{let e=await l(r);u=R(e)?e:{}}catch(e){return B(a,502,L(`起登录流程失败:${e instanceof Error?e.message:String(e)}`,`upstream_error`))}if(u.type!==`success`){let n=typeof u.error==`string`&&u.error!==``?u.error:`登录未完成`;return o.set(t.id,{state:`failed`,url:``,instructions:``,error:n}),e.log(`[plugin-host] ✗ ${t.id} 登录失败:${n}`),z(a,200,Z(e,t))}if(`refresh`in u){let n={...u};delete n.type,delete n.provider,e.auth.save(t.id,{...n,type:`oauth`})}else{let n=typeof u.key==`string`&&u.key!==``?u.key:i,a={...r,...R(u.metadata)?u.metadata:{}};e.auth.save(t.id,{type:`api`,key:n,...Object.keys(a).length>0?{metadata:a}:{}})}let d=typeof u.accountId==`string`?u.accountId:``;return o.set(t.id,{state:`success`,url:``,instructions:``,...d===``?{}:{accountId:d}}),e.log(`[plugin-host] ✓ ${t.id} 登录完成${d===``?``:`(${d})`}`),z(a,200,Z(e,t))}async function jt(e,t,n,r){let i=X(t);if(i.length===0)return B(r,400,L(`${t.id} 未声明登录方式(auth.methods),无法登录;该插件可能不需要登录`,`invalid_request_error`));let a={};try{let e=await H(n,V());if(e.length>0){let t=JSON.parse(e.toString(`utf-8`));a=R(t)?t:{}}}catch{return B(r,400,L(`请求体不是合法 JSON`,`invalid_request_error`))}let o=e.signIns??=new Map;if(o.get(t.id)?.state===`pending`)return z(r,200,Z(e,t));let s=typeof a.method==`string`?a.method:``;if(s===``&&i.length>1)return z(r,200,{...Z(e,t),needMethod:!0});let c=s===``?i[0]:i.find(e=>e.label===s);if(c===void 0)return B(r,400,L(`未知登录方式:${s}(可选:${i.map(e=>e.label??`(未命名)`).join(`、`)})`,`invalid_request_error`));let l={};if(R(a.inputs))for(let[e,t]of Object.entries(a.inputs))typeof t==`string`&&(l[e]=t);let u=typeof a.key==`string`?a.key.trim():``,d=Ot(c,l);if(d.length>0)return z(r,200,{...Z(e,t),needInputs:kt(d),inputs:l});if(c.type===`api`)return await At(e,t,c,l,u,r);let f=c.authorize;if(typeof f!=`function`)return B(r,400,L(`登录方式「${c.label??c.type}」缺少 authorize 实现(插件声明与实现不一致)`,`invalid_request_error`));let p;try{p=await f(c.prompts?.length?l:void 0)}catch(e){return B(r,502,L(`起登录流程失败:${e instanceof Error?e.message:String(e)}`,`upstream_error`))}let m=p.callback;if(typeof m!=`function`)return B(r,400,L(`登录流程没有 callback(插件声明与实现不一致)`,`invalid_request_error`));let h={state:`pending`,url:typeof p.url==`string`?p.url:``,instructions:typeof p.instructions==`string`?p.instructions:``};o.set(t.id,h);let g=s===``?c.label??c.type:s;return e.log(`[plugin-host] ▶ ${t.id} 登录已发起(${g}${h.url===``?`,读取本机凭据`:`,等待浏览器完成`})`),(async()=>{try{let n=await m(),r=R(n)?n:{};if(r.type===`success`){e.auth.save(t.id,{...r,type:`oauth`});let n=typeof r.accountId==`string`?r.accountId:``;h.state=`success`,h.accountId=n,e.log(`[plugin-host] ✓ ${t.id} 登录完成${n===``?``:`(${n})`}`)}else{let n=typeof r.error==`string`&&r.error!==``?r.error:`登录未完成`;h.state=`failed`,h.error=n,e.log(`[plugin-host] ✗ ${t.id} 登录失败:${n}`)}}catch(n){h.state=`failed`,h.error=n instanceof Error?n.message:String(n),e.log(`[plugin-host] ✗ ${t.id} 登录异常:${h.error}`)}})(),z(r,200,Z(e,t))}function Mt(e,t){let{providers:n}=F(e.loadResult());z(t,200,{service:`ezllm-plugin-host`,endpoints:[`GET /health`,`GET /plugins`,`GET /p/<id>/v1/models`,`GET /p/<id>/usage`,`POST /p/<id>/v1/{chat/completions|messages|responses}`,`GET /packages`,`GET /packages/updates`,`GET /packages/<name>`,`POST /packages`,`DELETE /packages/<name>`,`GET /login/<id>`,`POST /login/<id>`],providers:n.map(e=>({id:e.id,apiBase:e.protocol===null?null:`/p/${e.id}/v1`,protocol:e.protocol}))})}function Nt(e){let t=/^\/p\/([^/]+)\/v1(?:\/(.*))?$/.exec(e);if(t===null)return null;let n=decodeURIComponent(t[1]??``);return n===``?null:{id:n,rest:(t[2]??``).replace(/\/+$/,``)}}function Q(e,t){return F(e.loadResult()).providers.find(e=>e.id===t)}function $(e,t){if(t.hooks===null)throw Error(`provider「${t.id}」没有 auth 钩子(插件声明与实现不一致)`);return new Qe(t.id,t.hooks.auth,{store:e.auth,providerInfo:t.config,log:e.log})}async function Pt(e,t,n){let r=t.models,i=!1,a=t.hooks?.hooks.provider?.models;if(typeof a==`function`&&t.hooks!==null&&e.auth.has(t.id)){let n=$(e,t);await n.ensureFresh();let o=n.current();if(o!==void 0)try{let e=await a(t.config,{auth:o}),n=Object.keys(e??{});n.length>0&&(r=n,i=!0)}catch(n){e.log(`[plugin-host] ${t.id}: provider.models 失败,回落声明表(${n instanceof Error?n.message:String(n)})`)}}z(n,200,{object:`list`,provider:t.id,fromHook:i,data:r.map(e=>({id:e,object:`model`,owned_by:t.id}))})}async function Ft(e,t,n){let r=t.hooks?.auth.usage;if(t.hooks===null||t.capabilities.usage===!1||typeof r!=`function`)return B(n,400,L(`该插件未实现额度查询`,`invalid_request_error`));let i=$(e,t);await i.ensureFresh();let a;try{a=await r(i.getAuth,t.config)}catch(r){let i=r instanceof Error?r.message:String(r);return e.log(`[plugin-host] ✗ ${t.id} 额度查询失败:${i}`),B(n,502,L(`额度查询失败:${i}`,`upstream_error`))}z(n,200,a)}async function It(e,t,n,r,i){if(t.protocol===null)return B(i,501,L(`${t.id} 的协议未知(插件未声明可识别的 npm),不可转发`,`not_implemented`));if(n!==t.path)return B(i,404,L(`${t.id} 只提供 /p/${t.id}/v1/${t.path}(插件声明的协议是 ${t.protocol})`,`not_found`));let a;try{a=await ct(r,V())}catch(e){return B(i,413,L(e instanceof Error?e.message:String(e),`invalid_request_error`))}let o=r.headers[rt],s=typeof o==`string`?o:``,c=ft(a,t),l=Date.now();e.log(`[plugin-host] ▶ ${t.id} ${n} model=${c} bytes=${String(a.length)}${s?` session=${s}`:``}`);let u=await lt({provider:t,credentials:$(e,t),model:c,path:n,method:`POST`,req:r,body:a,sessionId:s},e.log);if(u.kind===`error`)return e.log(`[plugin-host] ✗ ${t.id} ${n} ${String(u.status)} ${u.message}`),B(i,u.status,L(u.message,u.status===401?`auth_error`:`upstream_error`));let d=u.response,f=dt(d);e.log(`[plugin-host] ◀ ${t.id} ${n} status=${String(d.status)} ${f?`stream`:`json`} ${String(Date.now()-l)}ms`),await ut(d,(e,t)=>{i.headersSent||i.writeHead(e,t)},e=>{i.write(e)}),i.end()}async function Lt(e,t,n){let r=et(t.url??`/`),i=(t.method??`GET`).toUpperCase();try{if(_t(e.options,t),pt(e.options,t),i===`GET`&&r===`/health`)return vt(e,n);if(i===`GET`&&r===`/plugins`)return yt(e,n);if(i===`GET`&&r===`/`)return Mt(e,n);if(r===`/packages`)return i===`GET`?bt(e,n):i===`POST`?await Y(()=>St(e,t,n)):B(n,405,L(`不支持的方法:${i} ${r}`,`invalid_request_error`));if(r===`/packages/updates`)return i===`GET`?await Tt(e,n):B(n,405,L(`不支持的方法:${i} ${r}`,`invalid_request_error`));let a=/^\/packages\/(.+)$/.exec(r);if(a!==null){let t=decodeURIComponent(a[1]??``);return t===``?B(n,404,L(`未找到:${i} ${r}`,`not_found`)):i===`GET`?xt(e,t,n):i===`DELETE`?await Y(()=>Ct(e,t,n)):B(n,405,L(`不支持的方法:${i} ${r}`,`invalid_request_error`))}let o=/^\/login\/(.+)$/.exec(r);if(o!==null){let a=decodeURIComponent(o[1]??``),s=a===``?void 0:Q(e,a);return s===void 0?B(n,404,L(`未加载 provider:${a}`,`not_found`)):i===`GET`?Et(e,s,n):i===`POST`?await jt(e,s,t,n):B(n,405,L(`不支持的方法:${i} ${r}`,`invalid_request_error`))}let s=/^\/p\/([^/]+)\/usage$/.exec(r);if(s!==null){let t=decodeURIComponent(s[1]??``),a=Q(e,t);return a===void 0?B(n,404,L(`未加载 provider:${t}`,`not_found`)):i===`GET`?await Ft(e,a,n):B(n,405,L(`不支持的方法:${i} ${r}`,`invalid_request_error`))}let c=Nt(r);if(c!==null){let a=Q(e,c.id);return a===void 0?B(n,404,L(`未加载 provider:${c.id}`,`not_found`)):i===`GET`&&c.rest===`models`?await Pt(e,a,n):i===`POST`?await It(e,a,c.rest,t,n):B(n,405,L(`不支持的方法:${i} ${r}`,`invalid_request_error`))}B(n,404,L(`未找到:${i} ${r}`,`not_found`))}catch(e){let t=e instanceof Error?e.message:String(e),r=t===`invalid api key`?401:t.startsWith(U)?403:500;B(n,r,L(t,r===500?`internal_error`:`invalid_request_error`))}}function Rt(e){return te((t,n)=>{Lt(e,t,n).catch(t=>{e.log(`[fatal] handler error: ${t instanceof Error&&t.stack?t.stack:String(t)}`);try{n.headersSent||n.writeHead(500,{"Content-Type":`application/json`}),n.end(JSON.stringify({detail:L(String(t),`internal_error`)}))}catch{}})})}function zt(e){return{...e,pluginsDir:e.pluginsDir===``?se():e.pluginsDir,authFile:e.authFile===``?ce():e.authFile}}function Bt(e,t,n){let{providers:r,errors:i}=F(t);_(`==== 插件宿主预检 ====`),_(`平台 : ${process.platform}`),_(`Node : ${process.version}`),_(`插件目录 : ${e.pluginsDir}`),_(`凭据文件 : ${e.authFile}`),_(`已装插件 : ${String(t.plugins.length)} 个(provider ${String(r.length)} 个)`);for(let e of r){let t=e.protocol??`未知协议(不可用)`,r=n(e.id)?`已登录`:`未登录`;_(` - ${e.id}(${e.name}): ${t} | ${String(e.models.length)} 模型 | ${r}`)}for(let e of i)v(` ! ${e}`);r.length===0&&v(" 未加载到任何 provider——用 `npm install <plugin> --prefix` 装进插件目录后重启"),_(`======================`)}async function Vt(e=process.argv.slice(2),t=process.env){let{options:n,help:r}=Ue(e,t);if(r)return process.stdout.write(We()),null;let i=zt(n),a=new ge(i.authFile),o={plugins:[],errors:[]},s=()=>o,c={directory:i.pluginsDir,saveAuth:(e,t)=>{a.save(e,t),_(`[plugin-host] 凭据已保存:${e}`)}},l=async()=>{let e=await He(i.pluginsDir,c);return o=e,e.errors};try{await l()}catch(e){v(`[plugin-host] 加载插件失败:${e instanceof Error?e.message:String(e)}`)}i.skipCheck||Bt(i,s(),e=>a.has(e));let u=Ce(i.nodeDir,w()),d={pluginsDir:i.pluginsDir,npmCli:u,log:e=>_(e)};_(`[plugin-host] ✅ 监听 http://${i.host}:${String(i.port)}`),_(`[plugin-host] GET /health`),_(`[plugin-host] GET /plugins`),_(`[plugin-host] GET /packages · POST /packages · DELETE /packages/<name>`),_(`[plugin-host] GET /login/<id> · POST /login/<id>`),_(`[plugin-host] 安装能力: ${u===null?`npm 未找到(仅可运行已装插件)`:`已启用(${u})`}`),i.apiKey&&_(`[plugin-host] 鉴权已启用(API key 已设置)`),_(`[plugin-host] 按 Ctrl+C 退出。`);let f=Rt({options:i,auth:a,loadResult:s,reload:l,install:d,log:e=>_(e)});return await new Promise((e,t)=>{f.once(`error`,t),f.listen(i.port,i.host,()=>{f.off(`error`,t),e()})}),_(`[plugin-host] listening on ${i.host}:${String(i.port)}`),{server:f,options:i,auth:a,loadResult:s,reload:l}}var Ht=process.argv[1];Ht!==void 0&&import.meta.url===i(n(Ht)).href&&Vt().catch(e=>{ne(`[plugin-host] 启动失败:${e instanceof Error?e.message:String(e)}`),process.exitCode=1});export{Vt as main};
|