@fastgpt-plugin/sdk-factory 0.0.1-alpha.1 → 0.0.1-alpha.2

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.
Files changed (2) hide show
  1. package/dist/index.js +1 -1
  2. package/package.json +3 -4
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import e from"zod";import{Readable as t}from"node:stream";import{randomUUID as n}from"node:crypto";import r,{Readable as i}from"stream";var a=class e{stream;closed=!1;constructor(e){this.stream=new t({objectMode:!0,read(){}}),e?.(this)}static create(t){return new e(t)}write(e){this.ensureWritable(),this.stream.push(e)}send(e){this.write(e)}close(){this.closed||(this.closed=!0,this.stream.push(null))}end(){this.close()}fail(e){this.closed=!0,this.stream.destroy(e)}toReadable(){return this.stream}onData(e){return this.stream.on(`data`,e),this}onEnd(e){return this.stream.on(`end`,e),this}onError(e){return this.stream.on(`error`,e),this}async consume(e){for await(let t of this.values())await e(t)}async*values(){for await(let e of this.stream)yield e}ensureWritable(){if(this.closed||this.stream.destroyed)throw Error(`StreamData is already closed`)}};const o=e=>(e=e.replace(/(?<=https?:\/\/)[^\s]+/g,`xxx`),e=e.replace(/ns-[\w-]+/g,`xxx`),e),s=(e,t=``)=>o(typeof e==`string`?e:e?.response?.data?.message||e?.response?.message||e?.message||e?.response?.data?.msg||e?.response?.msg||e?.msg||t),c=`application/javascript,application/json,application/yaml,application/zip,image/jpeg,image/png,image/gif,image/webp,image/svg+xml,application/pdf,text/plain,text/csv,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.openxmlformats-officedocument.presentationml.presentation,application/msword,application/vnd.ms-excel,application/vnd.ms-powerpoint,text/markdown,audio/mpeg,video/mp4,audio/wav,text/html,application/xml,application/gzip,application/octet-stream,multipart/form-data,text/event-stream`.split(`,`),l=e.enum(c).and(e.string().regex(/^[^/]+\/[^/]+$/)),u=e.object({fileKey:e.string(),fileName:e.string(),contentType:l,size:e.number(),etag:e.string(),createTime:e.date()}),d=e.object({fileKey:e.string().optional(),path:e.string().optional(),fileName:e.string().optional(),contentType:l.optional(),overwrite:e.boolean().optional(),file:e.union([e.instanceof(i,{error:`Stream cannot be empty`}),e.union([e.instanceof(Buffer,{error:`Buffer is required`}),e.instanceof(Uint8Array,{error:`Uint8Array is required`})]).transform(e=>e instanceof Uint8Array&&!(e instanceof Buffer)?Buffer.from(e):e)])});e.object({...d.omit({fileKey:!0,overwrite:!0,path:!0}).shape});const f=e.object({...u.omit({fileKey:!0}).shape,accessURL:e.string()}),p=e.enum([`uploadFile`]).enum,m=e=>[e,null];function h(e,t){return g(e)?[null,e]:[null,{reason:e,error:t}]}function g(e){return!!(e&&typeof e==`object`&&`reason`in e&&`error`in e)}const _=`__plugin_ipc_stream__`,v=`__pluginIpcDuplexReply__`;var y=class{pending=new Map;readyHandlers=new Set;errorHandlers=new Set;streamHandlers=new Map;incomingStreams=new Map;bufferedIncomingStreams=new Map;pendingStreamWaiters=new Map;requestHandler=null;eventHandler=null;endpoint;options;unsubscribeMessage;closed=!1;constructor(e,t={}){C(e)?(this.endpoint=e,this.options=t):(this.endpoint=process,this.options=e??{}),this.unsubscribeMessage=x(this.endpoint,e=>{this.dispatch(e).catch(e=>{this.emitError(e instanceof Error?e:Error(String(e)))})})}onReady(e){return this.readyHandlers.add(e),()=>{this.readyHandlers.delete(e)}}onError(e){return this.errorHandlers.add(e),()=>{this.errorHandlers.delete(e)}}setRequestHandler(e){this.requestHandler=e}setEventHandler(e){this.eventHandler=e}async requestDuplex(e,t,r){this.ensureOpen();let i=r?.requestId??n(),a=r?.timeoutMs??this.options.defaultTimeoutMs??3e4,o=Date.now(),s=r?.input===void 0?void 0:this.pipeStream(j(i),r.input,{traceId:r.traceId,streamId:r.inputStreamId,meta:r.inputMeta});s&&s.catch(e=>{this.emitError(e instanceof Error?e:Error(String(e)))});let c=await this.request(e,t,{timeoutMs:a,traceId:r?.traceId,requestId:i});if(!E(c))return{requestId:i,result:c,...s===void 0?{}:{inputDone:s}};let l;if(c.hasOutputStream){let e=Date.now()-o,t=Math.max(1,a-e);l=await this.waitForStream(M(i),{timeoutMs:t})}return{requestId:i,result:c.result,...l===void 0?{}:{output:l},...s===void 0?{}:{inputDone:s}}}replyDuplex(e,t,n){return{[v]:!0,...t===void 0?{}:{result:t},...n?.output===void 0?{}:{output:n.output},...n===void 0?{}:{options:{...n.traceId===void 0?{}:{traceId:n.traceId},...n.outputStreamId===void 0?{}:{outputStreamId:n.outputStreamId},...n.outputMeta===void 0?{}:{outputMeta:n.outputMeta}}}}}async waitForRequestInputStream(e,t){return this.waitForStream(j(e.id),t)}onStream(e,t){this.streamHandlers.has(e)||this.streamHandlers.set(e,new Set);let n=this.streamHandlers.get(e),r=t;return n.add(r),()=>{n.delete(r),n.size===0&&this.streamHandlers.delete(e)}}async waitForStream(e,t){this.ensureOpen();let n=this.bufferedIncomingStreams.get(e);if(n&&n.length>0){let t=n.shift();return n.length===0&&this.bufferedIncomingStreams.delete(e),t}return new Promise((n,r)=>{let i={resolve:n,reject:r};t?.timeoutMs!==void 0&&(i.timer=setTimeout(()=>{let t=this.pendingStreamWaiters.get(e);if(!t)return;let n=t.indexOf(i);n>=0&&t.splice(n,1),t.length===0&&this.pendingStreamWaiters.delete(e),r(Error(`Wait stream timeout: ${e}`))},t.timeoutMs)),this.pendingStreamWaiters.has(e)||this.pendingStreamWaiters.set(e,[]),this.pendingStreamWaiters.get(e).push(i)})}async createWritableStream(e,t){this.ensureOpen();let r=t?.streamId??n(),i=!1;return await this.sendStreamFrame({type:`start`,streamId:r,streamName:e,...t?.meta===void 0?{}:{meta:t.meta}},t?.traceId),{streamId:r,write:async e=>{if(i)throw Error(`Stream is already closed: ${r}`);await this.sendStreamFrame({type:`chunk`,streamId:r,chunk:e},t?.traceId)},end:async()=>{i||(i=!0,await this.sendStreamFrame({type:`end`,streamId:r},t?.traceId))},fail:async e=>{i||(i=!0,await this.sendStreamFrame({type:`error`,streamId:r,error:O(e)},t?.traceId))}}}async pipeStream(e,t,n){let r=await this.createWritableStream(e,n);try{for await(let e of A(t))await r.write(e);await r.end()}catch(e){throw await r.fail(e instanceof Error?e:Error(String(e))),e}}async request(e,t,r){this.ensureOpen();let i=r?.requestId??n(),a=r?.timeoutMs??this.options.defaultTimeoutMs??3e4;return new Promise((n,o)=>{let s=setTimeout(()=>{this.pending.delete(i),o(Object.assign(Error(`Request timeout: ${e}`),{code:`REQUEST_TIMEOUT`,requestId:i,method:e}))},a);this.pending.set(i,{resolve:n,reject:o,timer:s}),this.send({id:i,messageType:`request`,method:e,params:t,...r?.traceId===void 0?{}:{traceId:r.traceId},timestamp:Date.now()}).catch(e=>{clearTimeout(s),this.pending.delete(i),o(e)})})}async emit(e,t,r){this.ensureOpen(),await this.send({id:n(),messageType:`event`,method:e,params:t,...r===void 0?{}:{traceId:r},timestamp:Date.now()})}async sendReady(){this.ensureOpen(),await this.send({id:n(),messageType:`ready`,timestamp:Date.now()})}async reply(e,t){this.ensureOpen(),await this.send({id:e.id,messageType:`response`,...t===void 0?{}:{result:t},...e.traceId===void 0?{}:{traceId:e.traceId},timestamp:Date.now()})}async replyError(e,t){this.ensureOpen();let n=O(t);await this.send({id:e.id,messageType:`error`,error:n,...e.traceId===void 0?{}:{traceId:e.traceId},timestamp:Date.now()})}async close(e=Error(`IPC channel closed`)){this.closed||(this.closed=!0,this.unsubscribeMessage(),this.rejectAllPending(e),this.rejectAllPendingStreamWaiters(e),this.failAllIncomingStreams(e))}async dispatch(e){if(!this.closed){if(e.messageType===`response`||e.messageType===`error`){this.handleResponse(e);return}if(e.messageType===`ready`){this.readyHandlers.forEach(t=>t(e));return}if(e.messageType===`event`){if(T(e)){await this.handleStreamFrameMessage(e);return}await this.eventHandler?.(e);return}e.messageType===`request`&&await this.handleRequest(e)}}handleResponse(e){let t=this.pending.get(e.id);if(t){if(clearTimeout(t.timer),this.pending.delete(e.id),e.error){t.reject(k(e.error));return}t.resolve(e.result)}}async handleRequest(e){if(!e.method){await this.replyError(e,{code:`INVALID_REQUEST`,message:`Request method is required`});return}if(!this.requestHandler){await this.replyError(e,{code:`METHOD_NOT_FOUND`,message:`Method not found: ${e.method}`});return}try{let t=await this.requestHandler(e);if(D(t)){await this.handleDuplexReply(e,t);return}await this.reply(e,t)}catch(t){await this.replyError(e,t instanceof Error?t:Error(String(t)))}}rejectAllPending(e){this.pending.forEach((t,n)=>{clearTimeout(t.timer),t.reject(e),this.pending.delete(n)})}rejectAllPendingStreamWaiters(e){this.pendingStreamWaiters.forEach((t,n)=>{t.forEach(t=>{t.timer&&clearTimeout(t.timer),t.reject(e)}),this.pendingStreamWaiters.delete(n)})}failAllIncomingStreams(e){this.incomingStreams.forEach((t,n)=>{t.stream.fail(e),this.incomingStreams.delete(n)}),this.bufferedIncomingStreams.clear()}async sendStreamFrame(e,t){await this.emit(_,e,t)}async handleDuplexReply(e,t){let n=t.options?.traceId??e.traceId;t.output!==void 0&&this.pipeStream(M(e.id),t.output,{traceId:n,streamId:t.options?.outputStreamId,meta:t.options?.outputMeta}).catch(e=>{this.emitError(e instanceof Error?e:Error(String(e)))}),await this.reply(e,{[v]:!0,hasOutputStream:t.output!==void 0,...t.result===void 0?{}:{result:t.result}})}async handleStreamFrameMessage(e){let t=e.params;switch(t.type){case`start`:{let n={streamId:t.streamId,streamName:t.streamName,stream:a.create(),...e.traceId===void 0?{}:{traceId:e.traceId},...t.meta===void 0?{}:{meta:t.meta}};this.incomingStreams.set(t.streamId,n),this.dispatchIncomingStream(n);return}case`chunk`:{let e=this.incomingStreams.get(t.streamId);if(!e){this.emitError(Error(`Stream not found: ${t.streamId}`));return}e.stream.write(t.chunk);return}case`end`:{let e=this.incomingStreams.get(t.streamId);if(!e)return;e.stream.close(),this.incomingStreams.delete(t.streamId);return}case`error`:{let e=this.incomingStreams.get(t.streamId);if(!e)return;e.stream.fail(k(t.error)),this.incomingStreams.delete(t.streamId);return}}}dispatchIncomingStream(e){let t=!1,n=this.pendingStreamWaiters.get(e.streamName);if(n&&n.length>0){let r=n.shift();r.timer&&clearTimeout(r.timer),n.length===0&&this.pendingStreamWaiters.delete(e.streamName),r.resolve(e),t=!0}let r=this.streamHandlers.get(e.streamName);if(r&&r.size>0){r.forEach(t=>{Promise.resolve(t(e)).catch(e=>{this.emitError(e instanceof Error?e:Error(String(e)))})});return}t||(this.bufferedIncomingStreams.has(e.streamName)||this.bufferedIncomingStreams.set(e.streamName,[]),this.bufferedIncomingStreams.get(e.streamName).push(e))}emitError(e){this.errorHandlers.forEach(t=>t(e))}async send(e){await S(this.endpoint,e)}ensureOpen(){if(this.closed)throw Error(`IPC channel closed`)}};function b(e){return new y(process,e)}function x(e,t){let n=e,r=e=>{w(e)&&t(e)};return n.on(`message`,r),()=>n.off(`message`,r)}function S(e,t){let n=e;if(typeof n.send!=`function`)throw Error(`IPC channel not available`);return new Promise((e,r)=>{n.send(t,t=>{if(t){r(t);return}e()})})}function C(e){return!!(e&&typeof e==`object`&&`on`in e&&typeof e.on==`function`&&`off`in e&&typeof e.off==`function`)}function w(e){return!!(e&&typeof e==`object`&&`messageType`in e)}function T(e){return e.messageType===`event`&&e.method===_}function E(e){return!!(e&&typeof e==`object`&&v in e&&`hasOutputStream`in e)}function D(e){return!!(e&&typeof e==`object`&&v in e)}function O(e){return typeof e==`string`?{code:`HANDLER_ERROR`,message:e}:(e instanceof Error,{code:e.code??`HANDLER_ERROR`,message:e.message,...e.stack===void 0?{}:{stack:e.stack}})}function k(e){return Object.assign(Error(e.message),{code:e.code,stack:e.stack})}function A(e){return e instanceof a?e.values():e}function j(e){return`__plugin_ipc_request_input_stream__:${e}`}function M(e){return`__plugin_ipc_request_output_stream__:${e}`}var N=class{constructor(e,t={}){this.channel=e,this.options=t}async uploadFile(e){try{let t=await this.channel.requestDuplex(`__host_invoke__`,{method:p.uploadFile,args:{contentType:e.contentType,fileName:e.fileName}},{requestId:n(),traceId:this.options.invocationId,input:Buffer.isBuffer(e.file)?r.Readable.from(e.file):e.file});return m(f.parse(t.result))}catch(e){return h({en:`Failed to upload file`,"zh-CN":`上传文件失败`},e)}}};const P=e.enum([`en`,`zh-CN`,`zh-Hant`]).enum;e.object({[P.en]:e.string(),[P[`zh-CN`]]:e.string().optional(),[P[`zh-Hant`]]:e.string().optional()});const F=e.object({[P.en]:e.string(),[P[`zh-CN`]]:e.string(),[P[`zh-Hant`]]:e.string()});e.object({pluginId:e.string(),version:e.string(),etag:e.string()});const I=e.literal(`system`).or(e.string());e.array(e.record(e.string(),F));const L=e.enum([`localPool`,`serverless`]).enum;e.object({pluginId:e.string(),version:e.string().optional(),source:I.optional()});var R=class{channel;getChannel(){if(!this.channel)throw Error(`Channel is not initialized yet.`);return this.channel}mode;checkRuntimeMode(){process.env.RUNTIME_MODE==L.localPool&&(this.mode=L.localPool),process.env.RUNTIME_MODE===`dev`&&(this.mode=`dev`)}async init(){this.checkRuntimeMode(),this.mode===`localPool`&&(this.channel=b(),setImmediate(()=>this.getChannel().sendReady())),this.mode===`dev`&&(this.channel=z().pluginChannel,setImmediate(()=>this.getChannel().sendReady()))}constructor(){this.init()}};function z(){let e=globalThis.__FASTGPT_PLUGIN_LOCAL_DEBUG_RUNTIME__;if(!e)throw Error(`Local debug runtime is not initialized. Set it before importing the plugin.`);return e}function B(e){return e}var V=class t extends R{toolHandlers=new Map;childManifests=new Map;secretSchema=e.record(e.string(),e.unknown());constructor(e){super(),this.userToolManifest=e,this.mode&&this.getChannel().setRequestHandler(async e=>{if(e.method===`run`)try{let{input:t,systemVar:n,childId:r,secrets:i}=e.params,o=this.toolHandlers.get(r??`tool`);if(!o)throw Error(`No tool registered`);let s=a.create(),c=a.create();s.onData(e=>{c.send({type:`stream`,data:e})});let l=await o.handler(t,{systemVar:n,secrets:i,invoke:new N(this.getChannel(),{invocationId:e.traceId}),streamResponse:e=>{s.send(e)}});return c.send({type:`response`,data:l}),c.end(),this.getChannel().replyDuplex(e,void 0,{output:c})}catch(t){let n=a.create();return n.write({data:s(t),type:`error`}),n.end(),this.getChannel().replyDuplex(e,void 0,{output:n})}})}setSecretSchema(e){this.secretSchema=e}registerTool(e,t=`tool`,n){this.toolHandlers.set(t,e),n&&this.childManifests.set(t,n)}static getInstance(e){return new t(e)}getSecretSchema(){return this.secretSchema}getToolHandler(e=`tool`){return this.toolHandlers.get(e)}getUserToolManifest(){return this.userToolManifest}getChildManifests(){return[...this.childManifests.values()]}};const H=({manifest:t,handler:n})=>{let r=V.getInstance(t);return r.setSecretSchema(n.secretSchema??e.object()),r.registerTool(n),r},U=({manifest:t,children:n,secretSchema:r})=>{let i=V.getInstance(t);return n.forEach(e=>{i.registerTool(e.handler,e.id,{id:e.id,description:e.description,name:e.name,...e.icon===void 0?{}:{icon:e.icon},...e.toolDescription===void 0?{}:{toolDescription:e.toolDescription}})}),i.setSecretSchema(r??e.object()),i};export{B as createToolHandler,H as defineTool,U as defineToolSet};
1
+ import e from"zod";import{Readable as t}from"node:stream";import{randomUUID as n}from"node:crypto";import r,{Readable as i}from"stream";var a=class e{stream;closed=!1;constructor(e){this.stream=new t({objectMode:!0,read(){}}),e?.(this)}static create(t){return new e(t)}write(e){this.ensureWritable(),this.stream.push(e)}send(e){this.write(e)}close(){this.closed||(this.closed=!0,this.stream.push(null))}end(){this.close()}fail(e){this.closed=!0,this.stream.destroy(e)}toReadable(){return this.stream}onData(e){return this.stream.on(`data`,e),this}onEnd(e){return this.stream.on(`end`,e),this}onError(e){return this.stream.on(`error`,e),this}async consume(e){for await(let t of this.values())await e(t)}async*values(){for await(let e of this.stream)yield e}ensureWritable(){if(this.closed||this.stream.destroyed)throw Error(`StreamData is already closed`)}};const o=e=>(e=e.replace(/(?<=https?:\/\/)[^\s]+/g,`xxx`),e=e.replace(/ns-[\w-]+/g,`xxx`),e),s=(e,t=``)=>o(typeof e==`string`?e:e?.response?.data?.message||e?.response?.message||e?.message||e?.response?.data?.msg||e?.response?.msg||e?.msg||t),c=`application/javascript,application/json,application/yaml,application/zip,image/jpeg,image/png,image/gif,image/webp,image/svg+xml,application/pdf,text/plain,text/csv,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.openxmlformats-officedocument.presentationml.presentation,application/msword,application/vnd.ms-excel,application/vnd.ms-powerpoint,text/markdown,audio/mpeg,video/mp4,audio/wav,text/html,application/xml,application/gzip,application/octet-stream,multipart/form-data,text/event-stream`.split(`,`),l=e.enum(c).and(e.string().regex(/^[^/]+\/[^/]+$/)),u=e.object({fileKey:e.string(),fileName:e.string(),contentType:l,size:e.number(),etag:e.string(),createTime:e.date()}),d=e.object({fileKey:e.string().optional(),path:e.string().optional(),fileName:e.string().optional(),contentType:l.optional(),overwrite:e.boolean().optional(),file:e.union([e.instanceof(i,{error:`Stream cannot be empty`}),e.union([e.instanceof(Buffer,{error:`Buffer is required`}),e.instanceof(Uint8Array,{error:`Uint8Array is required`})]).transform(e=>e instanceof Uint8Array&&!(e instanceof Buffer)?Buffer.from(e):e)])});e.object({...d.omit({fileKey:!0,overwrite:!0,path:!0}).shape});const f=e.object({...u.omit({fileKey:!0}).shape,accessURL:e.string()}),p=e.enum([`uploadFile`]).enum,m=e=>[e,null];function h(e,t){return g(e)?[null,e]:[null,{reason:e,error:t}]}function g(e){return!!(e&&typeof e==`object`&&`reason`in e&&`error`in e)}const _=`__plugin_ipc_stream__`,v=`__pluginIpcDuplexReply__`;var y=class{pending=new Map;readyHandlers=new Set;errorHandlers=new Set;streamHandlers=new Map;incomingStreams=new Map;bufferedIncomingStreams=new Map;pendingStreamWaiters=new Map;requestHandler=null;eventHandler=null;endpoint;options;unsubscribeMessage;closed=!1;constructor(e,t={}){C(e)?(this.endpoint=e,this.options=t):(this.endpoint=process,this.options=e??{}),this.unsubscribeMessage=x(this.endpoint,e=>{this.dispatch(e).catch(e=>{this.emitError(e instanceof Error?e:Error(String(e)))})})}onReady(e){return this.readyHandlers.add(e),()=>{this.readyHandlers.delete(e)}}onError(e){return this.errorHandlers.add(e),()=>{this.errorHandlers.delete(e)}}setRequestHandler(e){this.requestHandler=e}setEventHandler(e){this.eventHandler=e}async requestDuplex(e,t,r){this.ensureOpen();let i=r?.requestId??n(),a=r?.timeoutMs??this.options.defaultTimeoutMs??3e4,o=Date.now(),s=r?.input===void 0?void 0:this.pipeStream(j(i),r.input,{traceId:r.traceId,streamId:r.inputStreamId,meta:r.inputMeta});s&&s.catch(e=>{this.emitError(e instanceof Error?e:Error(String(e)))});let c=await this.request(e,t,{timeoutMs:a,traceId:r?.traceId,requestId:i});if(!E(c))return{requestId:i,result:c,...s===void 0?{}:{inputDone:s}};let l;if(c.hasOutputStream){let e=Date.now()-o,t=Math.max(1,a-e);l=await this.waitForStream(M(i),{timeoutMs:t})}return{requestId:i,result:c.result,...l===void 0?{}:{output:l},...s===void 0?{}:{inputDone:s}}}replyDuplex(e,t,n){return{[v]:!0,...t===void 0?{}:{result:t},...n?.output===void 0?{}:{output:n.output},...n===void 0?{}:{options:{...n.traceId===void 0?{}:{traceId:n.traceId},...n.outputStreamId===void 0?{}:{outputStreamId:n.outputStreamId},...n.outputMeta===void 0?{}:{outputMeta:n.outputMeta}}}}}async waitForRequestInputStream(e,t){return this.waitForStream(j(e.id),t)}onStream(e,t){this.streamHandlers.has(e)||this.streamHandlers.set(e,new Set);let n=this.streamHandlers.get(e),r=t;return n.add(r),()=>{n.delete(r),n.size===0&&this.streamHandlers.delete(e)}}async waitForStream(e,t){this.ensureOpen();let n=this.bufferedIncomingStreams.get(e);if(n&&n.length>0){let t=n.shift();return n.length===0&&this.bufferedIncomingStreams.delete(e),t}return new Promise((n,r)=>{let i={resolve:n,reject:r};t?.timeoutMs!==void 0&&(i.timer=setTimeout(()=>{let t=this.pendingStreamWaiters.get(e);if(!t)return;let n=t.indexOf(i);n>=0&&t.splice(n,1),t.length===0&&this.pendingStreamWaiters.delete(e),r(Error(`Wait stream timeout: ${e}`))},t.timeoutMs)),this.pendingStreamWaiters.has(e)||this.pendingStreamWaiters.set(e,[]),this.pendingStreamWaiters.get(e).push(i)})}async createWritableStream(e,t){this.ensureOpen();let r=t?.streamId??n(),i=!1;return await this.sendStreamFrame({type:`start`,streamId:r,streamName:e,...t?.meta===void 0?{}:{meta:t.meta}},t?.traceId),{streamId:r,write:async e=>{if(i)throw Error(`Stream is already closed: ${r}`);await this.sendStreamFrame({type:`chunk`,streamId:r,chunk:e},t?.traceId)},end:async()=>{i||(i=!0,await this.sendStreamFrame({type:`end`,streamId:r},t?.traceId))},fail:async e=>{i||(i=!0,await this.sendStreamFrame({type:`error`,streamId:r,error:O(e)},t?.traceId))}}}async pipeStream(e,t,n){let r=await this.createWritableStream(e,n);try{for await(let e of A(t))await r.write(e);await r.end()}catch(e){throw await r.fail(e instanceof Error?e:Error(String(e))),e}}async request(e,t,r){this.ensureOpen();let i=r?.requestId??n(),a=r?.timeoutMs??this.options.defaultTimeoutMs??3e4;return new Promise((n,o)=>{let s=setTimeout(()=>{this.pending.delete(i),o(Object.assign(Error(`Request timeout: ${e}`),{code:`REQUEST_TIMEOUT`,requestId:i,method:e}))},a);this.pending.set(i,{resolve:n,reject:o,timer:s}),this.send({id:i,messageType:`request`,method:e,params:t,...r?.traceId===void 0?{}:{traceId:r.traceId},timestamp:Date.now()}).catch(e=>{clearTimeout(s),this.pending.delete(i),o(e)})})}async emit(e,t,r){this.ensureOpen(),await this.send({id:n(),messageType:`event`,method:e,params:t,...r===void 0?{}:{traceId:r},timestamp:Date.now()})}async sendReady(){this.ensureOpen(),await this.send({id:n(),messageType:`ready`,timestamp:Date.now()})}async reply(e,t){this.ensureOpen(),await this.send({id:e.id,messageType:`response`,...t===void 0?{}:{result:t},...e.traceId===void 0?{}:{traceId:e.traceId},timestamp:Date.now()})}async replyError(e,t){this.ensureOpen();let n=O(t);await this.send({id:e.id,messageType:`error`,error:n,...e.traceId===void 0?{}:{traceId:e.traceId},timestamp:Date.now()})}async close(e=Error(`IPC channel closed`)){this.closed||(this.closed=!0,this.unsubscribeMessage(),this.rejectAllPending(e),this.rejectAllPendingStreamWaiters(e),this.failAllIncomingStreams(e))}async dispatch(e){if(!this.closed){if(e.messageType===`response`||e.messageType===`error`){this.handleResponse(e);return}if(e.messageType===`ready`){this.readyHandlers.forEach(t=>t(e));return}if(e.messageType===`event`){if(T(e)){await this.handleStreamFrameMessage(e);return}await this.eventHandler?.(e);return}e.messageType===`request`&&await this.handleRequest(e)}}handleResponse(e){let t=this.pending.get(e.id);if(t){if(clearTimeout(t.timer),this.pending.delete(e.id),e.error){t.reject(k(e.error));return}t.resolve(e.result)}}async handleRequest(e){if(!e.method){await this.replyError(e,{code:`INVALID_REQUEST`,message:`Request method is required`});return}if(!this.requestHandler){await this.replyError(e,{code:`METHOD_NOT_FOUND`,message:`Method not found: ${e.method}`});return}try{let t=await this.requestHandler(e);if(D(t)){await this.handleDuplexReply(e,t);return}await this.reply(e,t)}catch(t){await this.replyError(e,t instanceof Error?t:Error(String(t)))}}rejectAllPending(e){this.pending.forEach((t,n)=>{clearTimeout(t.timer),t.reject(e),this.pending.delete(n)})}rejectAllPendingStreamWaiters(e){this.pendingStreamWaiters.forEach((t,n)=>{t.forEach(t=>{t.timer&&clearTimeout(t.timer),t.reject(e)}),this.pendingStreamWaiters.delete(n)})}failAllIncomingStreams(e){this.incomingStreams.forEach((t,n)=>{t.stream.fail(e),this.incomingStreams.delete(n)}),this.bufferedIncomingStreams.clear()}async sendStreamFrame(e,t){await this.emit(_,e,t)}async handleDuplexReply(e,t){let n=t.options?.traceId??e.traceId;t.output!==void 0&&this.pipeStream(M(e.id),t.output,{traceId:n,streamId:t.options?.outputStreamId,meta:t.options?.outputMeta}).catch(e=>{this.emitError(e instanceof Error?e:Error(String(e)))}),await this.reply(e,{[v]:!0,hasOutputStream:t.output!==void 0,...t.result===void 0?{}:{result:t.result}})}async handleStreamFrameMessage(e){let t=e.params;switch(t.type){case`start`:{let n={streamId:t.streamId,streamName:t.streamName,stream:a.create(),...e.traceId===void 0?{}:{traceId:e.traceId},...t.meta===void 0?{}:{meta:t.meta}};this.incomingStreams.set(t.streamId,n),this.dispatchIncomingStream(n);return}case`chunk`:{let e=this.incomingStreams.get(t.streamId);if(!e){this.emitError(Error(`Stream not found: ${t.streamId}`));return}e.stream.write(t.chunk);return}case`end`:{let e=this.incomingStreams.get(t.streamId);if(!e)return;e.stream.close(),this.incomingStreams.delete(t.streamId);return}case`error`:{let e=this.incomingStreams.get(t.streamId);if(!e)return;e.stream.fail(k(t.error)),this.incomingStreams.delete(t.streamId);return}}}dispatchIncomingStream(e){let t=!1,n=this.pendingStreamWaiters.get(e.streamName);if(n&&n.length>0){let r=n.shift();r.timer&&clearTimeout(r.timer),n.length===0&&this.pendingStreamWaiters.delete(e.streamName),r.resolve(e),t=!0}let r=this.streamHandlers.get(e.streamName);if(r&&r.size>0){r.forEach(t=>{Promise.resolve(t(e)).catch(e=>{this.emitError(e instanceof Error?e:Error(String(e)))})});return}t||(this.bufferedIncomingStreams.has(e.streamName)||this.bufferedIncomingStreams.set(e.streamName,[]),this.bufferedIncomingStreams.get(e.streamName).push(e))}emitError(e){this.errorHandlers.forEach(t=>t(e))}async send(e){await S(this.endpoint,e)}ensureOpen(){if(this.closed)throw Error(`IPC channel closed`)}};function b(e){return new y(process,e)}function x(e,t){let n=e,r=e=>{w(e)&&t(e)};return n.on(`message`,r),()=>n.off(`message`,r)}function S(e,t){let n=e;if(typeof n.send!=`function`)throw Error(`IPC channel not available`);return new Promise((e,r)=>{n.send(t,t=>{if(t){r(t);return}e()})})}function C(e){return!!(e&&typeof e==`object`&&`on`in e&&typeof e.on==`function`&&`off`in e&&typeof e.off==`function`)}function w(e){return!!(e&&typeof e==`object`&&`messageType`in e)}function T(e){return e.messageType===`event`&&e.method===_}function E(e){return!!(e&&typeof e==`object`&&v in e&&`hasOutputStream`in e)}function D(e){return!!(e&&typeof e==`object`&&v in e)}function O(e){return typeof e==`string`?{code:`HANDLER_ERROR`,message:e}:(e instanceof Error,{code:e.code??`HANDLER_ERROR`,message:e.message,...e.stack===void 0?{}:{stack:e.stack}})}function k(e){return Object.assign(Error(e.message),{code:e.code,stack:e.stack})}function A(e){return e instanceof a?e.values():e}function j(e){return`__plugin_ipc_request_input_stream__:${e}`}function M(e){return`__plugin_ipc_request_output_stream__:${e}`}var N=class{constructor(e,t={}){this.channel=e,this.options=t}async uploadFile(e){try{let t=await this.channel.requestDuplex(`__host_invoke__`,{method:p.uploadFile,args:{contentType:e.contentType,fileName:e.fileName}},{requestId:n(),traceId:this.options.invocationId,input:Buffer.isBuffer(e.file)?r.Readable.from(e.file):e.file});return m(f.parse(t.result))}catch(e){return h({en:`Failed to upload file`,"zh-CN":`上传文件失败`},e)}}};const P=e.enum([`en`,`zh-CN`,`zh-Hant`]).enum;e.object({[P.en]:e.string(),[P[`zh-CN`]]:e.string().optional(),[P[`zh-Hant`]]:e.string().optional()});const F=e.object({[P.en]:e.string(),[P[`zh-CN`]]:e.string(),[P[`zh-Hant`]]:e.string()});e.object({pluginId:e.string(),version:e.string(),etag:e.string()});const I=e.literal(`system`).or(e.string()),L=e.object({id:e.string(),name:F});e.array(L);const R=e.enum([`localPool`,`serverless`]).enum;e.object({pluginId:e.string(),version:e.string().optional(),source:I.optional()});var z=class{channel;getChannel(){if(!this.channel)throw Error(`Channel is not initialized yet.`);return this.channel}mode;checkRuntimeMode(){process.env.RUNTIME_MODE==R.localPool&&(this.mode=R.localPool),process.env.RUNTIME_MODE===`dev`&&(this.mode=`dev`)}async init(){this.checkRuntimeMode(),this.mode===`localPool`&&(this.channel=b(),setImmediate(()=>this.getChannel().sendReady())),this.mode===`dev`&&(this.channel=B().pluginChannel,setImmediate(()=>this.getChannel().sendReady()))}constructor(){this.init()}};function B(){let e=globalThis.__FASTGPT_PLUGIN_LOCAL_DEBUG_RUNTIME__;if(!e)throw Error(`Local debug runtime is not initialized. Set it before importing the plugin.`);return e}function V(e){return e}var H=class t extends z{toolHandlers=new Map;childManifests=new Map;secretSchema=e.record(e.string(),e.unknown());constructor(e){super(),this.userToolManifest=e,this.mode&&this.getChannel().setRequestHandler(async e=>{if(e.method===`run`)try{let{input:t,systemVar:n,childId:r,secrets:i}=e.params,o=this.toolHandlers.get(r??`tool`);if(!o)throw Error(`No tool registered`);let s=a.create(),c=a.create();s.onData(e=>{c.send({type:`stream`,data:e})});let l=await o.handler(t,{systemVar:n,secrets:i,invoke:new N(this.getChannel(),{invocationId:e.traceId}),streamResponse:e=>{s.send(e)}});return c.send({type:`response`,data:l}),c.end(),this.getChannel().replyDuplex(e,void 0,{output:c})}catch(t){let n=a.create();return n.write({data:s(t),type:`error`}),n.end(),this.getChannel().replyDuplex(e,void 0,{output:n})}})}setSecretSchema(e){this.secretSchema=e}registerTool(e,t=`tool`,n){this.toolHandlers.set(t,e),n&&this.childManifests.set(t,n)}static getInstance(e){return new t(e)}getSecretSchema(){return this.secretSchema}getToolHandler(e=`tool`){return this.toolHandlers.get(e)}getUserToolManifest(){return this.userToolManifest}getChildManifests(){return[...this.childManifests.values()]}};const U=({manifest:t,handler:n})=>{let r=H.getInstance(t);return r.setSecretSchema(n.secretSchema??e.object()),r.registerTool(n),r},W=({manifest:t,children:n,secretSchema:r})=>{let i=H.getInstance(t);return n.forEach(e=>{i.registerTool(e.handler,e.id,{id:e.id,description:e.description,name:e.name,...e.icon===void 0?{}:{icon:e.icon},...e.toolDescription===void 0?{}:{toolDescription:e.toolDescription}})}),i.setSecretSchema(r??e.object()),i};export{V as createToolHandler,U as defineTool,W as defineToolSet};
package/package.json CHANGED
@@ -1,9 +1,8 @@
1
1
  {
2
2
  "name": "@fastgpt-plugin/sdk-factory",
3
- "version": "0.0.1-alpha.1",
4
- "dependencies": {
5
- "@fastgpt-plugin/domain": "workspace:*",
6
- "@fastgpt-plugin/infrastructure": "workspace:*"
3
+ "version": "0.0.1-alpha.2",
4
+ "peerDependencies": {
5
+ "zod": "^4"
7
6
  },
8
7
  "scripts": {
9
8
  "build": "tsdown"