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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.en.md ADDED
@@ -0,0 +1,176 @@
1
+ # FastGPT Plugin SDK Factory
2
+
3
+ Language: [简体中文](./README.md) | [English](./README.en.md)
4
+
5
+ TypeScript SDK for building FastGPT Tool Plugins. It wraps plugin metadata, input/output validation, secret configuration, runtime communication, and streaming responses behind a small API surface so tool authors can focus on business logic.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ pnpm add @fastgpt-plugin/sdk-factory zod
11
+ ```
12
+
13
+ When developing inside this monorepo, use the workspace dependency directly.
14
+
15
+ ## Quick Start
16
+
17
+ The plugin entry file must default-export the instance returned by `defineTool()` or `defineToolSet()`.
18
+
19
+ ```ts
20
+ import {
21
+ createToolHandler,
22
+ defineTool,
23
+ type InputSchemaMetaType,
24
+ type OutputSchemaMetaType
25
+ } from '@fastgpt-plugin/sdk-factory';
26
+ import z from 'zod';
27
+
28
+ const handler = createToolHandler({
29
+ inputSchema: z.object({
30
+ text: z.string().meta({
31
+ title: 'Text',
32
+ isToolParams: true
33
+ } satisfies InputSchemaMetaType)
34
+ }),
35
+ outputSchema: z.object({
36
+ result: z.string().meta({
37
+ title: 'Result'
38
+ } satisfies OutputSchemaMetaType)
39
+ }),
40
+ handler: async (input) => {
41
+ return {
42
+ result: input.text.toUpperCase()
43
+ };
44
+ }
45
+ });
46
+
47
+ export default defineTool({
48
+ manifest: {
49
+ pluginId: 'uppercase',
50
+ version: '1.0.0',
51
+ name: {
52
+ en: 'Uppercase',
53
+ 'zh-CN': '转大写'
54
+ },
55
+ description: {
56
+ en: 'Convert text to uppercase',
57
+ 'zh-CN': '将文本转换为大写'
58
+ },
59
+ versionDescription: {
60
+ en: 'Initial version',
61
+ 'zh-CN': '初始版本'
62
+ },
63
+ tags: ['tools']
64
+ },
65
+ handler
66
+ });
67
+ ```
68
+
69
+ ## API
70
+
71
+ ### `createToolHandler(definition)`
72
+
73
+ Defines a tool handler and infers `input`, `output`, and `secrets` types from Zod schemas.
74
+
75
+ The handler context includes:
76
+
77
+ | Field | Description |
78
+ | --- | --- |
79
+ | `systemVar` | System variables injected by FastGPT. |
80
+ | `secrets` | Secret values validated by `secretSchema`. |
81
+ | `invoke` | Client for invoking host capabilities, such as file upload. |
82
+ | `streamResponse` | Sends streaming tool answers. |
83
+
84
+ ### `defineTool(options)`
85
+
86
+ Defines a single-tool plugin. `manifest` describes the plugin metadata, and `handler` contains the execution logic.
87
+
88
+ ```ts
89
+ export default defineTool({
90
+ manifest,
91
+ handler
92
+ });
93
+ ```
94
+
95
+ ### `defineToolSet(options)`
96
+
97
+ Defines a tool set with multiple child tools. All child tools share the top-level `manifest`; each child tool has its own `id`, name, description, and handler.
98
+
99
+ ## Manifest
100
+
101
+ `manifest` uses bilingual i18n fields. Common fields:
102
+
103
+ | Field | Required | Description |
104
+ | --- | --- | --- |
105
+ | `pluginId` | Yes | Unique plugin ID. |
106
+ | `version` | Yes | Plugin version. |
107
+ | `name` | Yes | Plugin name in `{ en, 'zh-CN' }` format. |
108
+ | `description` | Yes | Plugin description in `{ en, 'zh-CN' }` format. |
109
+ | `versionDescription` | No | Version description. |
110
+ | `author` | No | Author. |
111
+ | `repoUrl` | No | Repository URL. |
112
+ | `tutorialUrl` | No | Tutorial URL. |
113
+ | `tags` | No | Plugin tags. |
114
+ | `permission` | No | Plugin permission declarations. |
115
+ | `icon` | No | Plugin icon; the build pipeline can fill it automatically. |
116
+ | `toolDescription` | No | Tool description for the model; the build pipeline can fill it automatically. |
117
+
118
+ ## Secret Configuration
119
+
120
+ A single tool can declare `secretSchema` in its handler. A tool set can declare a shared `secretSchema` at the top level of `defineToolSet()`.
121
+
122
+ Schema field metadata is written into the built JSON Schema through Zod `.meta()`. Use `InputSchemaMetaType` for input fields and optionally set `isToolParams: true` for input parameters recommended to be managed by AI. Use `OutputSchemaMetaType` for output fields and `SecretSchemaMetaType` for secret fields. Every `secretSchema` field must include `isSecret`; set it to `true` for values that need encrypted storage.
123
+
124
+ ## Host Invocation
125
+
126
+ Use `ctx.invoke` to call FastGPT host capabilities. The SDK currently exposes file upload.
127
+
128
+ ```ts
129
+ const handler = createToolHandler({
130
+ inputSchema: z.object({
131
+ content: z.string().meta({
132
+ title: 'Content',
133
+ isToolParams: true
134
+ } satisfies InputSchemaMetaType)
135
+ }),
136
+ outputSchema: z.object({
137
+ accessURL: z.string().meta({
138
+ title: 'Access URL'
139
+ } satisfies OutputSchemaMetaType),
140
+ fileName: z.string().meta({
141
+ title: 'File Name'
142
+ } satisfies OutputSchemaMetaType),
143
+ size: z.number().meta({
144
+ title: 'Size'
145
+ } satisfies OutputSchemaMetaType)
146
+ }),
147
+ handler: async (input, { invoke }) => {
148
+ const [result, err] = await invoke.uploadFile({
149
+ fileName: 'result.txt',
150
+ contentType: 'text/plain',
151
+ file: Buffer.from(input.content, 'utf-8')
152
+ });
153
+
154
+ if (err) {
155
+ throw err;
156
+ }
157
+ if (!result) {
158
+ throw new Error('Failed to upload file');
159
+ }
160
+
161
+ return {
162
+ accessURL: result.accessURL,
163
+ fileName: result.fileName,
164
+ size: result.size
165
+ };
166
+ }
167
+ });
168
+ ```
169
+
170
+ ## Build
171
+
172
+ ```bash
173
+ pnpm --filter @fastgpt-plugin/sdk-factory build
174
+ ```
175
+
176
+ After build, the package entry is `dist/index.js` and type declarations are emitted to `dist/index.d.ts`.
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # FastGPT Plugin SDK Factory
2
2
 
3
+ 语言:[简体中文](./README.md) | [English](./README.en.md)
4
+
3
5
  用于构建 FastGPT Tool Plugin 的 TypeScript SDK。它把插件声明、输入输出校验、密钥配置、运行时通信和流式响应封装成少量 API,让工具开发者专注于业务逻辑。
4
6
 
5
7
  TypeScript SDK for building FastGPT Tool Plugins. It wraps plugin metadata, input/output validation, secret configuration, runtime communication, and streaming responses behind a small API surface so tool authors can focus on business logic.
@@ -32,7 +34,8 @@ import z from 'zod';
32
34
  const handler = createToolHandler({
33
35
  inputSchema: z.object({
34
36
  text: z.string().meta({
35
- title: 'Text'
37
+ title: 'Text',
38
+ isToolParams: true
36
39
  } satisfies InputSchemaMetaType)
37
40
  }),
38
41
  outputSchema: z.object({
@@ -81,7 +84,8 @@ Defines a tool handler and infers `input`, `output`, and `secrets` types from Zo
81
84
  const handler = createToolHandler({
82
85
  inputSchema: z.object({
83
86
  query: z.string().meta({
84
- title: 'Query'
87
+ title: 'Query',
88
+ isToolParams: true
85
89
  } satisfies InputSchemaMetaType)
86
90
  }),
87
91
  outputSchema: z.object({
@@ -142,7 +146,8 @@ Defines a tool set with multiple child tools. All child tools share the top-leve
142
146
  const searchHandler = createToolHandler({
143
147
  inputSchema: z.object({
144
148
  query: z.string().meta({
145
- title: 'Query'
149
+ title: 'Query',
150
+ isToolParams: true
146
151
  } satisfies InputSchemaMetaType)
147
152
  }),
148
153
  outputSchema: z.object({
@@ -156,7 +161,8 @@ const searchHandler = createToolHandler({
156
161
  const summaryHandler = createToolHandler({
157
162
  inputSchema: z.object({
158
163
  content: z.string().meta({
159
- title: 'Content'
164
+ title: 'Content',
165
+ isToolParams: true
160
166
  } satisfies InputSchemaMetaType)
161
167
  }),
162
168
  outputSchema: z.object({
@@ -238,9 +244,9 @@ export default defineToolSet({
238
244
 
239
245
  A single tool can declare `secretSchema` in its handler. A tool set can declare a shared `secretSchema` at the top level of `defineToolSet()`.
240
246
 
241
- Schema 字段元数据通过 Zod `.meta()` 写入构建后的 JSON Schema。输入字段使用 `InputSchemaMetaType`,输出字段使用 `OutputSchemaMetaType`,密钥字段使用 `SecretSchemaMetaType`。`secretSchema` 的每个字段都要包含 `isSecret`,需要加密存储时设为 `true`。
247
+ Schema 字段元数据通过 Zod `.meta()` 写入构建后的 JSON Schema。输入字段使用 `InputSchemaMetaType`,推荐由 AI 托管补充的输入参数可设置 `isToolParams: true`。输出字段使用 `OutputSchemaMetaType`,密钥字段使用 `SecretSchemaMetaType`。`secretSchema` 的每个字段都要包含 `isSecret`,需要加密存储时设为 `true`。
242
248
 
243
- Schema field metadata is written into the built JSON Schema through Zod `.meta()`. Use `InputSchemaMetaType` for input fields, `OutputSchemaMetaType` for output fields, and `SecretSchemaMetaType` for secret fields. Every `secretSchema` field must include `isSecret`; set it to `true` for values that need encrypted storage.
249
+ Schema field metadata is written into the built JSON Schema through Zod `.meta()`. Use `InputSchemaMetaType` for input fields and optionally set `isToolParams: true` for input parameters recommended to be managed by AI. Use `OutputSchemaMetaType` for output fields and `SecretSchemaMetaType` for secret fields. Every `secretSchema` field must include `isSecret`; set it to `true` for values that need encrypted storage.
244
250
 
245
251
  ```ts
246
252
  const secretSchema = z.object({
@@ -257,7 +263,8 @@ const secretSchema = z.object({
257
263
  const handler = createToolHandler({
258
264
  inputSchema: z.object({
259
265
  prompt: z.string().meta({
260
- title: 'Prompt'
266
+ title: 'Prompt',
267
+ isToolParams: true
261
268
  } satisfies InputSchemaMetaType)
262
269
  }),
263
270
  outputSchema: z.object({
@@ -284,7 +291,8 @@ Use `ctx.invoke` to call FastGPT host capabilities. The SDK currently exposes fi
284
291
  const handler = createToolHandler({
285
292
  inputSchema: z.object({
286
293
  content: z.string().meta({
287
- title: 'Content'
294
+ title: 'Content',
295
+ isToolParams: true
288
296
  } satisfies InputSchemaMetaType)
289
297
  }),
290
298
  outputSchema: z.object({
package/dist/index.d.ts CHANGED
@@ -62,6 +62,9 @@ type I18nStringType = z.infer<typeof I18nStringSchema>;
62
62
  type ResultFailure<E extends Error = Error> = {
63
63
  reason: I18nStringType;
64
64
  error: E;
65
+ code?: string;
66
+ message?: string;
67
+ data?: unknown;
65
68
  };
66
69
  type Result<T = unknown, E extends Error = Error> = [T, null] | [null, ResultFailure<E>];
67
70
  //#endregion
@@ -219,7 +222,10 @@ type ToolHandlerDefinition<TInput extends ToolInputSchema = ToolInputSchema, TOu
219
222
  declare function createToolHandler<TInput extends ToolInputSchema, TOutput extends ToolOutputSchema, TSecret extends ToolSecretSchema = undefined>(def: ToolHandlerDefinition<TInput, TOutput, TSecret>): ToolHandlerDefinition<TInput, TOutput, TSecret>;
220
223
  //#endregion
221
224
  //#region src/index.d.ts
222
- type InputSchemaMetaType = z.GlobalMeta;
225
+ type InputSchemaMetaType = z.GlobalMeta & {
226
+ /** 面向模型的参数说明 */toolDescription?: string; /** 标注该字段是否推荐由 AI 托管补充 */
227
+ isToolParams?: boolean;
228
+ };
223
229
  type OutputSchemaMetaType = z.GlobalMeta;
224
230
  type SecretSchemaMetaType = z.GlobalMeta & {
225
231
  /** 标注该字段是否需要加密存储 */isSecret: boolean;
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import e from"zod";import{Readable as t}from"node:stream";import n,{Readable as r}from"stream";import{randomUUID as i}from"node:crypto";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={ready:`client.ready`,stdio:`client.stdio`,fail:`client.fail`,request:`client.request`},s=e.enum([o.ready,o.stdio,o.fail,o.request]);e.object({pid:e.number().optional(),version:e.string().optional(),runtimeMode:e.string().optional(),capabilities:e.array(e.string()).optional(),startedAt:e.number().optional(),meta:e.unknown().optional()}),e.object({stream:e.enum([`stdout`,`stderr`]),chunk:e.string(),timestamp:e.number().optional()}),e.object({reason:e.enum([`startup`,`runtime`,`crash`,`shutdown`,`unknown`]),error:e.object({code:e.string(),message:e.string(),data:e.unknown().optional()}).optional(),exitCode:e.number().nullable().optional(),signal:e.string().nullable().optional(),timestamp:e.number().optional()});const c={streamFrame:`channel.stream`},l=e.enum([c.streamFrame]),u={request:`host.request`,ping:`host.ping`,shutdown:`host.shutdown`},ee=e.enum([u.request,u.ping,u.shutdown]);e.object({timestamp:e.number()}),e.object({timestamp:e.number(),receivedAt:e.number().optional()}),e.object({reason:e.string().optional(),timeoutMs:e.number().nonnegative().optional()}),e.object({accepted:e.boolean(),message:e.string().optional()}),e.union([s,l,ee]);const d=e.literal(`1.0`),f=e.union([e.string(),e.number()]),p=e.object({code:e.string(),message:e.string(),data:e.unknown().optional(),cause:e.lazy(()=>p).optional()}),m={parseError:`PARSE_ERROR`,invalidMessage:`INVALID_MESSAGE`,methodNotFound:`METHOD_NOT_FOUND`,invalidParams:`INVALID_PARAMS`,internalError:`INTERNAL_ERROR`,requestTimeout:`REQUEST_TIMEOUT`,channelClosed:`CHANNEL_CLOSED`,streamError:`STREAM_ERROR`},h=e.object({protocol:d,id:f,method:e.string(),params:e.unknown().optional(),traceId:e.string().optional(),timestamp:e.number().optional()}),te=h.omit({id:!0}),ne=e.object({protocol:d,id:f,result:e.unknown().optional(),traceId:e.string().optional(),timestamp:e.number().optional()}),re=e.object({protocol:d,id:f,error:p,traceId:e.string().optional(),timestamp:e.number().optional()});e.union([h,te,ne,re]),e.enum([`ipc`,`tcp`,`http`]),e.enum([`host`,`client`]),e.object({streamId:e.string(),streamName:e.string(),meta:e.unknown().optional()}),e.discriminatedUnion(`type`,[e.object({type:e.literal(`start`),streamId:e.string(),streamName:e.string(),meta:e.unknown().optional()}),e.object({type:e.literal(`chunk`),streamId:e.string(),chunk:e.unknown()}),e.object({type:e.literal(`end`),streamId:e.string()}),e.object({type:e.literal(`error`),streamId:e.string(),error:p})]);const ie=e=>(e=e.replace(/(?<=https?:\/\/)[^\s]+/g,`xxx`),e=e.replace(/ns-[\w-]+/g,`xxx`),e),g=(e,t=``)=>ie(_(e)||t),_=(e,t=0)=>t>5?void 0:typeof e==`string`?e:!e||typeof e!=`object`?void 0:v(e,t)||ae(e,t)||oe(y(e?.response?.data,t),y(e?.response?.body,t),y(e?.body,t),x(e?.response?.data?.error?.reason),x(e?.response?.error?.reason),x(e?.error?.reason),x(e?.response?.data?.reason),x(e?.response?.reason),e?.response?.data?.message||e?.response?.data?.error?.message||e?.response?.error?.message||e?.error?.message||e?.response?.message||e?.message||e?.response?.data?.msg||e?.response?.msg||e?.msg,x(e?.response?.data?.error),x(e?.response?.error),x(e?.error),x(e?.reason),_(e?.error,t+1),_(e?.cause,t+1),x(e)),ae=(e,t)=>{if(!(!(`reason`in e)||!(`cause`in e)))return S(x(e.reason),b(e.cause,t+1))},v=(e,t)=>{if(`error`in e)return S(x(e.reason),_(e.error,t+1))},y=(e,t)=>{if(e){if(typeof e!=`string`)return _(e,t+1);try{let n=JSON.parse(e);return _(n?.error,t+1)||_(n,t+1)}catch{return e||void 0}}},b=(e,t)=>!e||typeof e!=`object`?_(e,t):S(x(e.reason),`cause`in e?b(e.cause,t+1):void 0)??_(e,t),x=e=>{if(!e||typeof e!=`object`)return;let t=e,n=t[`zh-CN`]??t.en??t[`zh-Hant`];return typeof n==`string`?n:void 0},oe=(...e)=>e.find(e=>typeof e==`string`&&e.length>0),S=(...e)=>{let t=e.filter((t,n)=>!!t&&e.indexOf(t)===n).filter((e,t,n)=>!n.some((n,r)=>r>t&&n.startsWith(`${e}:`)));return t.length>0?t.join(`: `):void 0},se=`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(`,`),C=e.enum(se).and(e.string().regex(/^[^/]+\/[^/]+$/)),ce=e.object({fileKey:e.string(),fileName:e.string(),contentType:C,size:e.number(),etag:e.string(),createTime:e.date()}),le=e.object({fileKey:e.string().optional(),path:e.string().optional(),fileName:e.string().optional(),contentType:C.optional(),overwrite:e.boolean().optional(),file:e.union([e.instanceof(r,{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({...le.omit({fileKey:!0,overwrite:!0,path:!0}).shape});const ue=e.object({...ce.omit({fileKey:!0}).partial().shape,accessURL:e.string()});e.object({username:e.string(),contact:e.string().nullish(),memberName:e.string().nullish(),orgs:e.array(e.object({pathId:e.string(),name:e.string()})),groups:e.array(e.object({name:e.string()}))});const w=e.enum([`uploadFile`,`userInfo`,`wecomCorpToken`]).enum,T={en:`Internal Server Error`,"zh-CN":`服务器内部错误`},E=new Map;var D=class extends Error{code;reason;httpStatus;telemetryKind;visibility;severity;data;constructor(e,t={}){super(t.message??e.message,{cause:me(t.cause)}),this.name=`RegisteredError`,this.code=e.code,this.reason=t.reason??e.reason,this.httpStatus=e.httpStatus??500,this.telemetryKind=e.telemetryKind,this.visibility=e.visibility??`public`,this.severity=e.severity??`expected`,this.data=t.data}};function de(e){for(let t of e){if(E.get(t.code))throw Error(`Error definition already registered: ${t.code}`);E.set(t.code,t)}}function O(e){return E.get(e)}function k(e,t=`Unknown error`){if(e instanceof Error)return e;if(typeof e==`string`)return Error(e);if(R(e)){let n=z(e.reason),r=k(e.error,n??t);return Error(he(n,r.message)??t,{cause:r})}return Error(N(e,t),{cause:e})}function fe(e,t){if(e instanceof D)return e.visibility===`internal`?T:e.reason;if(I(e))return e;let n=L(e)?e:void 0;return n&&I(n.reason)?n.reason:n&&I(n.error)?n.error:t||(e instanceof Error?{en:e.message,"zh-CN":e.message}:typeof e==`string`?{en:e,"zh-CN":e}:T)}function pe(e,t){return j(e,{includeStack:t?.includeStack??!1,depth:0,seen:new WeakSet})}function A(e){let t=e.cause?A(e.cause):void 0,n=e.code?O(e.code):void 0;if(n){let r=new D(n,{message:e.message,reason:e.reason,cause:t,data:L(e.data)?e.data:void 0});return e.stack&&(r.stack=e.stack),r}let r=Error(e.message,{cause:t});return r.name=e.name||`Error`,e.stack&&(r.stack=e.stack),Object.assign(r,{...e.code===void 0?{}:{code:e.code},...e.data===void 0?{}:{data:e.data}})}function me(e){return e===void 0||e instanceof Error?e:typeof e==`string`?Error(e):e}function j(e,t){return t.depth>5?{name:`Error`,message:`Error cause depth exceeded`}:e instanceof D?{name:e.name,code:e.code,message:e.message,reason:e.reason,httpStatus:e.httpStatus,telemetryKind:e.telemetryKind,visibility:e.visibility,data:e.data,...t.includeStack&&e.stack!==void 0?{stack:e.stack}:{},...e.cause===void 0?{}:{cause:j(e.cause,M(t,e))}}:e instanceof Error?{name:e.name,code:P(e,`code`),message:e.message,data:F(e,`data`),...t.includeStack&&e.stack!==void 0?{stack:e.stack}:{},...e.cause===void 0?{}:{cause:j(e.cause,M(t,e))}}:typeof e==`string`?{name:`Error`,message:e}:{name:`Error`,message:N(e,`Unknown error`)}}function M(e,t){return e.seen.has(t)?{...e,depth:6}:(e.seen.add(t),{...e,depth:e.depth+1})}function N(e,t){if(e==null)return t;if(L(e)){let t=e.message??e.msg;if(typeof t==`string`&&t.length>0)return t}return String(e)}function P(e,t){let n=e[t];return typeof n==`string`?n:void 0}function F(e,t){return e[t]}function I(e){return L(e)&&typeof e.en==`string`&&(e[`zh-CN`]===void 0||typeof e[`zh-CN`]==`string`)&&(e[`zh-Hant`]===void 0||typeof e[`zh-Hant`]==`string`)}function L(e){return!!(e&&typeof e==`object`)}function R(e){return L(e)&&`error`in e&&(`reason`in e||L(e.error))}function z(e){if(I(e))return e[`zh-CN`]??e.en??e[`zh-Hant`]}function he(...e){let t=e.filter((t,n)=>!!t&&e.indexOf(t)===n).filter((e,t,n)=>!n.some((n,r)=>r>t&&n.startsWith(`${e}:`)));return t.length>0?t.join(`: `):void 0}const B=e=>[e,null];function V(e,t){if(ge(e)){let t=k(e.error);return[null,{reason:e.reason,error:t}]}return e instanceof Error?[null,{reason:fe(e),error:e}]:[null,{reason:e,error:t===void 0?Error(e.en,{cause:e}):_e(e,t)}]}function ge(e){return!!(e&&typeof e==`object`&&`reason`in e&&`error`in e)}function _e(e,t){let n=k(t,e.en);return Error(H(ve(e),n.message)??e.en,{cause:n})}function ve(e){return e[`zh-CN`]??e.en??e[`zh-Hant`]}function H(...e){let t=e.filter((t,n)=>!!t&&e.indexOf(t)===n).filter((e,t,n)=>!n.some((n,r)=>r>t&&n.startsWith(`${e}:`)));return t.length>0?t.join(`: `):void 0}var ye=class{constructor(e,t={}){this.channel=e,this.options=t}async getWecomCorpToken(){try{let[e,t]=(await this.channel.request(o.request,{method:w.wecomCorpToken,args:{}},{traceId:this.options.invocationId})).result;return t?V({en:`Failed to get wecom corp token`,"zh-CN":`获取企业微信企业令牌失败`},t):B(e)}catch(e){return V({en:`Failed to get wecom corp token`,"zh-CN":`获取企业微信企业令牌失败`},e)}}async userInfo(){try{let[e,t]=(await this.channel.request(o.request,{method:w.userInfo,args:{}},{traceId:this.options.invocationId})).result;return t?V({en:`Failed to get user info`,"zh-CN":`获取用户信息失败`},t):B(e)}catch(e){return V({en:`Failed to get user info`,"zh-CN":`获取用户信息失败`},e)}}async uploadFile(e){try{let[t,n]=(await this.channel.request(o.request,{method:w.uploadFile,args:{contentType:e.contentType,fileName:e.fileName}},{traceId:this.options.invocationId,input:this.toUploadInputStream(e.file)})).result;return n?V({en:`Failed to upload file`,"zh-CN":`上传文件失败`},n):B(ue.parse(t))}catch(e){return V({en:`Failed to upload file`,"zh-CN":`上传文件失败`},e)}}toUploadInputStream(e){return e instanceof n.Readable?e:n.Readable.from(Buffer.from(e))}};const U=e.enum([`en`,`zh-CN`,`zh-Hant`]).enum;e.object({[U.en]:e.string(),[U[`zh-CN`]]:e.string().optional(),[U[`zh-Hant`]]:e.string().optional()});const be=e.object({[U.en]:e.string(),[U[`zh-CN`]]:e.string(),[U[`zh-Hant`]]:e.string()});e.object({pluginId:e.string(),version:e.string(),etag:e.string()});const xe=e.literal(`system`).or(e.string()),Se=e.object({id:e.string(),name:be});e.array(Se);const W=e.enum([`localPool`,`serverless`]).enum;e.object({pluginId:e.string(),version:e.string().optional(),source:xe.optional()});const G={unknown:`common.unknown`,operationFailed:`common.operation_failed`,badRequest:`http.bad_request`,unauthorized:`http.unauthorized`,notFound:`http.not_found`,validationFailed:`http.validation_failed`,internalServerError:`http.internal_server_error`,pluginRuntimeManagerDestroyed:`plugin.runtime.manager_destroyed`,pluginRuntimeAlreadyRegistered:`plugin.runtime.already_registered`,pluginRuntimeReplacementPluginNotFound:`plugin.runtime.replacement_plugin_not_found`,pluginRuntimeConfigLoadFailed:`plugin.runtime.config_load_failed`,pluginRuntimeConfigUpdateFailed:`plugin.runtime.config_update_failed`,pluginRuntimeConfigResetFailed:`plugin.runtime.config_reset_failed`,pluginRuntimeConfigInvalid:`plugin.runtime.config_invalid`,pluginRuntimePodQuotaExceeded:`plugin.runtime.pod_quota_exceeded`,pluginRuntimePluginInfoLoadFailed:`plugin.runtime.plugin_info_load_failed`,pluginRuntimeInitializeFailed:`plugin.runtime.initialize_failed`,pluginRuntimePluginNotFound:`plugin.runtime.plugin_not_found`,pluginRuntimeEventNotSupported:`plugin.runtime.event_not_supported`,pluginRuntimeShutdownFailed:`plugin.runtime.shutdown_failed`,pluginInvokeFailed:`plugin.invoke.failed`,pluginInvokeTimeout:`plugin.invoke.timeout`,pluginInvokeQueueTimeout:`plugin.invoke.queue_timeout`};de([{code:G.unknown,message:`Unknown error`,reason:{en:`Unknown error`,"zh-CN":`未知错误`},visibility:`internal`,severity:`unexpected`},{code:G.operationFailed,message:`Operation failed`,reason:{en:`Operation failed`,"zh-CN":`操作失败`}},{code:G.badRequest,message:`Bad Request`,reason:{en:`Bad Request`,"zh-CN":`请求参数错误`},httpStatus:400},{code:G.unauthorized,message:`Unauthorized`,reason:{en:`Unauthorized`,"zh-CN":`未授权`},httpStatus:401},{code:G.notFound,message:`Not Found`,reason:{en:`Not Found`,"zh-CN":`资源未找到`},httpStatus:404},{code:G.validationFailed,message:`Validation failed`,reason:{en:`Validation failed`,"zh-CN":`数据校验失败`},httpStatus:400},{code:G.internalServerError,message:`Internal Server Error`,reason:{en:`Internal Server Error`,"zh-CN":`服务器内部错误`},httpStatus:500,visibility:`internal`,severity:`unexpected`},{code:G.pluginRuntimeManagerDestroyed,message:`Plugin manager already destroyed`,reason:{en:`Plugin manager already destroyed`,"zh-CN":`插件管理器已销毁`}},{code:G.pluginRuntimeAlreadyRegistered,message:`Plugin already registered`,reason:{en:`Plugin already registered`,"zh-CN":`插件已注册`},httpStatus:409,telemetryKind:`plugin_already_registered`},{code:G.pluginRuntimeReplacementPluginNotFound,message:`Replacement plugin not found`,reason:{en:`Replacement plugin not found`,"zh-CN":`替换插件未找到`},httpStatus:404},{code:G.pluginRuntimeConfigLoadFailed,message:`Failed to get plugin runtime config`,reason:{en:`Failed to get plugin runtime config`,"zh-CN":`获取插件运行时配置失败`},telemetryKind:`runtime_config_load_failed`},{code:G.pluginRuntimeConfigUpdateFailed,message:`Failed to update plugin runtime config`,reason:{en:`Failed to update plugin runtime config`,"zh-CN":`更新插件运行时配置失败`},telemetryKind:`runtime_config_update_failed`},{code:G.pluginRuntimeConfigResetFailed,message:`Failed to reset plugin runtime config`,reason:{en:`Failed to reset plugin runtime config`,"zh-CN":`重置插件运行时配置失败`},telemetryKind:`runtime_config_reset_failed`},{code:G.pluginRuntimeConfigInvalid,message:`Invalid plugin runtime config`,reason:{en:`Invalid plugin runtime config`,"zh-CN":`插件运行时配置无效`},httpStatus:400,telemetryKind:`runtime_config_invalid`},{code:G.pluginRuntimePodQuotaExceeded,message:`Pod quota exceeded`,reason:{en:`Pod quota exceeded`,"zh-CN":`Pod 配额超出`},httpStatus:429,telemetryKind:`pod_quota_exceeded`},{code:G.pluginRuntimePluginInfoLoadFailed,message:`Register plugin error, can not get plugin info`,reason:{en:`Register plugin error, can not get plugin info`,"zh-CN":`注册插件失败,无法获取插件信息`}},{code:G.pluginRuntimeInitializeFailed,message:`Failed to initialize plugin runtime`,reason:{en:`Failed to initialize plugin runtime`,"zh-CN":`初始化插件运行时失败`},telemetryKind:`plugin_runtime_initialize_failed`},{code:G.pluginRuntimePluginNotFound,message:`Plugin not found`,reason:{en:`Plugin not found`,"zh-CN":`插件未找到`},httpStatus:404},{code:G.pluginRuntimeEventNotSupported,message:`Event not supported`,reason:{en:`Event not supported`,"zh-CN":`不支持的事件`},httpStatus:400},{code:G.pluginRuntimeShutdownFailed,message:`Error during shutdown`,reason:{en:`Error during shutdown`,"zh-CN":`关闭过程中发生错误`},telemetryKind:`runtime_shutdown_failed`},{code:G.pluginInvokeFailed,message:`Invoke failed`,reason:{en:`Invoke failed`,"zh-CN":`调用失败`},telemetryKind:`invoke_failed`},{code:G.pluginInvokeTimeout,message:`Plugin invocation timed out`,reason:{en:`Plugin invocation timed out`,"zh-CN":`插件调用超时`},httpStatus:504,telemetryKind:`invoke_timeout`},{code:G.pluginInvokeQueueTimeout,message:`Plugin invocation waited too long for an available local-pool pod`,reason:{en:`Plugin invocation waited too long for an available local-pool pod`,"zh-CN":`插件调用等待空闲本地运行实例超时`},httpStatus:503,telemetryKind:`queue_timeout`}]);const K=`__fastgptChannelReply__`;var Ce=class{transport=`ipc`;pending=new Map;errorHandlers=new Set;closeHandlers=new Set;incomingStreams=new Map;bufferedIncomingStreams=new Map;pendingStreamWaiters=new Map;requestHandler=null;notificationHandler=null;closed=!1;unsubscribeMessage;constructor(e,t,n={}){this.side=e,this.endpoint=t,this.options=n,this.unsubscribeMessage=Te(t,e=>{this.dispatch(e).catch(e=>{this.emitError(e instanceof Error?e:Error(String(e)))})})}async request(e,t,n){this.ensureOpen();let r=n?.id??i(),a=n?.timeoutMs??this.options.defaultTimeoutMs??3e4,o=n?.input===void 0?void 0:this.pipeStream(Z(r),n.input,{traceId:n.traceId,streamId:n.inputStream?.streamId,streamName:n.inputStream?.streamName,meta:n.inputStream?.meta}),s=Date.now();o&&o.catch(e=>{this.emitError(e instanceof Error?e:Error(String(e)))});let c=await new Promise((i,o)=>{let s=setTimeout(()=>{this.pending.delete(r),o(Object.assign(Error(`Request timeout: ${String(e)}`),{code:m.requestTimeout,requestId:r,method:e}))},a);this.pending.set(r,{method:String(e),resolve:i,reject:o,timer:s}),this.send({protocol:`1.0`,id:r,method:String(e),params:t,...n?.traceId===void 0?{}:{traceId:n.traceId},timestamp:Date.now()}).catch(e=>{clearTimeout(s),this.pending.delete(r),o(e)})}),l=c,u;if(je(c)&&(l=c.result,c.hasOutputStream)){let e=Date.now()-s,t=Math.max(1,a-e);u=await this.waitForStream(Q(r),{timeoutMs:t})}return{requestId:r,result:l,...u===void 0?{}:{output:u},...o===void 0?{}:{inputDone:o}}}async notify(e,t,n){this.ensureOpen(),await this.send({protocol:`1.0`,method:String(e),params:t,...n?.traceId===void 0?{}:{traceId:n.traceId},timestamp:Date.now()})}setRequestHandler(e){this.requestHandler=e}setNotificationHandler(e){this.notificationHandler=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 n=t?.streamId??i(),r=!1,a=t?.streamName??e;return await this.sendStreamFrame({type:`start`,streamId:n,streamName:a,...t?.meta===void 0?{}:{meta:t.meta}},t?.traceId),{streamId:n,streamName:a,...t?.meta===void 0?{}:{meta:t.meta},write:async e=>{if(r)throw Error(`Stream is already closed: ${n}`);await this.sendStreamFrame({type:`chunk`,streamId:n,chunk:e},t?.traceId)},end:async()=>{r||(r=!0,await this.sendStreamFrame({type:`end`,streamId:n},t?.traceId))},fail:async e=>{r||(r=!0,await this.sendStreamFrame({type:`error`,streamId:n,error:q(e)},t?.traceId))}}}async pipeStream(e,t,n){let r=await this.createWritableStream(e,n);try{for await(let e of Me(t))await r.write(e);await r.end()}catch(e){throw await r.fail(e instanceof Error?e:Error(String(e))),e}}onError(e){return this.errorHandlers.add(e),()=>{this.errorHandlers.delete(e)}}onClose(e){return this.closeHandlers.add(e),()=>{this.closeHandlers.delete(e)}}async close(e=Error(`Channel closed`)){this.closed||(this.closed=!0,this.unsubscribeMessage(),this.rejectAllPending(e),this.rejectAllPendingStreamWaiters(e),this.failAllIncomingStreams(e),this.closeHandlers.forEach(t=>t(e)))}createReply(e,t){return{[K]:!0,...e===void 0?{}:{result:e},...t?.output===void 0?{}:{output:t.output},...t?.outputStream===void 0?{}:{outputStream:t.outputStream}}}async dispatch(e){if(!this.closed){if(ke(e)){this.handleResponse(e);return}if(Oe(e)){await this.handleRequest(e);return}await this.handleNotification(e)}}handleResponse(e){let t=this.pending.get(e.id);if(t){if(clearTimeout(t.timer),this.pending.delete(e.id),`error`in e){t.reject(J(e.error));return}t.resolve(e.result)}}async handleRequest(e){if(!this.requestHandler){await this.replyError(e,{code:m.methodNotFound,message:`Method not found: ${e.method}`});return}try{let t=await this.requestHandler({id:e.id,method:e.method,params:e.params,...e.traceId===void 0?{}:{traceId:e.traceId},raw:e,waitForInputStream:t=>this.waitForStream(Z(e.id),t)});if(Ae(t)){await this.handleReplyDescriptor(e,t);return}await this.reply(e,t)}catch(t){await this.replyError(e,t instanceof Error?t:Error(String(t)))}}async handleNotification(e){if(e.method===c.streamFrame){await this.handleStreamFrame(e.params,e.traceId);return}await this.notificationHandler?.({method:e.method,params:e.params,...e.traceId===void 0?{}:{traceId:e.traceId},raw:e})}async handleReplyDescriptor(e,t){t.output!==void 0&&this.pipeStream(Q(e.id),t.output,{traceId:t.outputStream?.traceId??e.traceId,streamId:t.outputStream?.streamId,streamName:t.outputStream?.streamName,meta:t.outputStream?.meta}).catch(e=>{this.emitError(e instanceof Error?e:Error(String(e)))}),await this.reply(e,{[K]:!0,hasOutputStream:t.output!==void 0,...t.result===void 0?{}:{result:t.result}})}async reply(e,t){await this.send({protocol:`1.0`,id:e.id,...t===void 0?{}:{result:t},...e.traceId===void 0?{}:{traceId:e.traceId},timestamp:Date.now()})}async replyError(e,t){await this.send({protocol:`1.0`,id:e.id,error:q(t),...e.traceId===void 0?{}:{traceId:e.traceId},timestamp:Date.now()})}async sendStreamFrame(e,t){await this.notify(c.streamFrame,e,{traceId:t})}async handleStreamFrame(e,t){switch(e.type){case`start`:{let n={streamId:e.streamId,streamName:e.streamName,stream:a.create(),...t===void 0?{}:{traceId:t},...e.meta===void 0?{}:{meta:e.meta}};this.incomingStreams.set(e.streamId,n),this.dispatchIncomingStream(n);return}case`chunk`:{let t=this.incomingStreams.get(e.streamId);if(!t){this.emitError(Error(`Stream not found: ${e.streamId}`));return}t.stream.write(e.chunk);return}case`end`:{let t=this.incomingStreams.get(e.streamId);if(!t)return;t.stream.end(),this.incomingStreams.delete(e.streamId);return}case`error`:{let t=this.incomingStreams.get(e.streamId);if(!t)return;t.stream.fail(J(e.error)),this.incomingStreams.delete(e.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}t||(this.bufferedIncomingStreams.has(e.streamName)||this.bufferedIncomingStreams.set(e.streamName,[]),this.bufferedIncomingStreams.get(e.streamName).push(e))}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()}emitError(e){this.errorHandlers.forEach(t=>t(e))}async send(e){await Ee(this.endpoint,e)}ensureOpen(){if(this.closed)throw Error(`Channel closed`)}};function we(e){return new Ce(`client`,process,e)}function Te(e,t){let n=e,r=e=>{De(e)&&t(e)};return n.on(`message`,r),()=>n.off(`message`,r)}function Ee(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 De(e){return!!(e&&typeof e==`object`&&`protocol`in e)}function Oe(e){return`id`in e&&`method`in e}function ke(e){return`id`in e&&!(`method`in e)&&(`result`in e||`error`in e)}function Ae(e){return!!(e&&typeof e==`object`&&K in e&&e[K]===!0)}function je(e){return!!(e&&typeof e==`object`&&K in e&&`hasOutputStream`in e)}function q(e){return typeof e==`string`?{code:m.internalError,message:e}:e instanceof Error?Y(pe(e)):e}function J(e){return A(X(e))}function Y(e){return{code:e.code??m.internalError,message:e.message,...e.data===void 0?{}:{data:e.data},...e.cause===void 0?{}:{cause:Y(e.cause)}}}function X(e){return{name:`Error`,code:e.code,message:e.message,data:e.data,...e.cause===void 0?{}:{cause:X(e.cause)}}}function Me(e){return e instanceof a?e.values():e}function Z(e){return`request.input:${String(e)}`}function Q(e){return`request.output:${String(e)}`}var Ne=class{channel;getChannel(){if(!this.channel)throw Error(`Channel is not initialized yet.`);return this.channel}mode;checkRuntimeMode(){process.env.RUNTIME_MODE==W.localPool&&(this.mode=W.localPool),process.env.RUNTIME_MODE===`dev`&&(this.mode=`dev`)}async init(){this.checkRuntimeMode(),this.mode===`localPool`&&(this.channel=we(),setImmediate(()=>{this.getChannel().notify(o.ready,{pid:process.pid,runtimeMode:W.localPool,startedAt:Date.now()})})),this.mode===`dev`&&(this.channel=Pe().pluginChannel,setImmediate(()=>{this.getChannel().notify(o.ready,{pid:process.pid,runtimeMode:`dev`,startedAt:Date.now()})}))}constructor(){this.init()}};function Pe(){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 Fe(e){return e}var $=class t extends Ne{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===u.request&&e.params.eventName===`run`)try{let{input:t,systemVar:n,childId:r,secrets:i}=e.params.payload,o=this.toolHandlers.get(r??`tool`);if(!o)throw Error(`No tool registered`);let s=a.create();return(async()=>{try{let r=await o.handler(t,{systemVar:n,secrets:i,invoke:new ye(this.getChannel(),{invocationId:e.traceId}),streamResponse:e=>{s.send({type:`stream`,data:e})}});s.send({type:`response`,data:r})}catch(e){s.send({data:g(e,`Unknown error during tool execution`),type:`error`})}finally{s.end()}})(),this.getChannel().createReply(void 0,{output:s})}catch(e){let t=a.create();return t.write({data:g(e,`Unknown error during tool execution`),type:`error`}),t.end(),this.getChannel().createReply(void 0,{output:t})}})}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 Ie=({manifest:t,handler:n})=>{let r=$.getInstance(t);return r.setSecretSchema(n.secretSchema??e.object()),r.registerTool(n),r},Le=({manifest:t,children:n,secretSchema:r})=>{let i=$.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{Fe as createToolHandler,Ie as defineTool,Le as defineToolSet};
1
+ import e from"zod";import{Readable as t}from"node:stream";import n,{Readable as r}from"stream";import{randomUUID as i}from"node:crypto";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={ready:`client.ready`,stdio:`client.stdio`,fail:`client.fail`,request:`client.request`},s=e.enum([o.ready,o.stdio,o.fail,o.request]);e.object({pid:e.number().optional(),version:e.string().optional(),runtimeMode:e.string().optional(),capabilities:e.array(e.string()).optional(),startedAt:e.number().optional(),meta:e.unknown().optional()}),e.object({stream:e.enum([`stdout`,`stderr`]),chunk:e.string(),timestamp:e.number().optional()}),e.object({reason:e.enum([`startup`,`runtime`,`crash`,`shutdown`,`unknown`]),error:e.object({code:e.string(),message:e.string(),data:e.unknown().optional()}).optional(),exitCode:e.number().nullable().optional(),signal:e.string().nullable().optional(),timestamp:e.number().optional()});const c={streamFrame:`channel.stream`},l=e.enum([c.streamFrame]),u={request:`host.request`,ping:`host.ping`,shutdown:`host.shutdown`},ee=e.enum([u.request,u.ping,u.shutdown]);e.object({timestamp:e.number()}),e.object({timestamp:e.number(),receivedAt:e.number().optional()}),e.object({reason:e.string().optional(),timeoutMs:e.number().nonnegative().optional()}),e.object({accepted:e.boolean(),message:e.string().optional()}),e.union([s,l,ee]);const d=e.enum([`en`,`zh-CN`,`zh-Hant`]).enum,te=e.object({[d.en]:e.string(),[d[`zh-CN`]]:e.string().optional(),[d[`zh-Hant`]]:e.string().optional()}),ne=e.object({[d.en]:e.string(),[d[`zh-CN`]]:e.string(),[d[`zh-Hant`]]:e.string()}),f=e.literal(`1.0`),p=e.union([e.string(),e.number()]),m=e.object({code:e.string(),message:e.string(),reason:te.optional(),data:e.unknown().optional(),cause:e.lazy(()=>m).optional()}),h={parseError:`PARSE_ERROR`,invalidMessage:`INVALID_MESSAGE`,methodNotFound:`METHOD_NOT_FOUND`,invalidParams:`INVALID_PARAMS`,internalError:`INTERNAL_ERROR`,requestTimeout:`REQUEST_TIMEOUT`,channelClosed:`CHANNEL_CLOSED`,streamError:`STREAM_ERROR`},g=e.object({protocol:f,id:p,method:e.string(),params:e.unknown().optional(),traceId:e.string().optional(),timestamp:e.number().optional()}),re=g.omit({id:!0}),ie=e.object({protocol:f,id:p,result:e.unknown().optional(),traceId:e.string().optional(),timestamp:e.number().optional()}),ae=e.object({protocol:f,id:p,error:m,traceId:e.string().optional(),timestamp:e.number().optional()});e.union([g,re,ie,ae]),e.enum([`ipc`,`tcp`,`http`]),e.enum([`host`,`client`]),e.object({streamId:e.string(),streamName:e.string(),meta:e.unknown().optional()}),e.discriminatedUnion(`type`,[e.object({type:e.literal(`start`),streamId:e.string(),streamName:e.string(),meta:e.unknown().optional()}),e.object({type:e.literal(`chunk`),streamId:e.string(),chunk:e.unknown()}),e.object({type:e.literal(`end`),streamId:e.string()}),e.object({type:e.literal(`error`),streamId:e.string(),error:m})]);const oe=e=>(e=e.replace(/(?<=https?:\/\/)[^\s]+/g,`xxx`),e=e.replace(/ns-[\w-]+/g,`xxx`),e),_=(e,t=``)=>oe(v(e)||t),v=(e,t=0)=>t>5?void 0:typeof e==`string`?e:!e||typeof e!=`object`?void 0:ce(e,t)||se(e,t)||le(y(e?.response?.data,t),y(e?.response?.body,t),y(e?.body,t),x(e?.response?.data?.error?.reason),x(e?.response?.error?.reason),x(e?.error?.reason),x(e?.response?.data?.reason),x(e?.response?.reason),e?.response?.data?.message||e?.response?.data?.error?.message||e?.response?.error?.message||e?.error?.message||e?.response?.message||e?.message||e?.response?.data?.msg||e?.response?.msg||e?.msg,x(e?.response?.data?.error),x(e?.response?.error),x(e?.error),x(e?.reason),v(e?.error,t+1),v(e?.cause,t+1),x(e)),se=(e,t)=>{if(!(!(`reason`in e)||!(`cause`in e)))return S(x(e.reason),b(e.cause,t+1))},ce=(e,t)=>{if(`error`in e)return S(x(e.reason),v(e.error,t+1))},y=(e,t)=>{if(e){if(typeof e!=`string`)return v(e,t+1);try{let n=JSON.parse(e);return v(n?.error,t+1)||v(n,t+1)}catch{return e||void 0}}},b=(e,t)=>!e||typeof e!=`object`?v(e,t):S(x(e.reason),`cause`in e?b(e.cause,t+1):void 0)??v(e,t),x=e=>{if(!e||typeof e!=`object`)return;let t=e,n=t[`zh-CN`]??t.en??t[`zh-Hant`];return typeof n==`string`?n:void 0},le=(...e)=>e.find(e=>typeof e==`string`&&e.length>0),S=(...e)=>{let t=e.filter((t,n)=>!!t&&e.indexOf(t)===n).filter((e,t,n)=>!n.some((n,r)=>r>t&&n.startsWith(`${e}:`)));return t.length>0?t.join(`: `):void 0},ue=`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(`,`),C=e.enum(ue).and(e.string().regex(/^[^/]+\/[^/]+$/)),de=e.object({fileKey:e.string(),fileName:e.string(),contentType:C,size:e.number(),etag:e.string(),createTime:e.date()}),fe=e.object({fileKey:e.string().optional(),path:e.string().optional(),fileName:e.string().optional(),contentType:C.optional(),overwrite:e.boolean().optional(),file:e.union([e.instanceof(r,{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({...fe.omit({fileKey:!0,overwrite:!0,path:!0}).shape});const pe=e.object({...de.omit({fileKey:!0}).partial().shape,accessURL:e.string()});e.object({username:e.string(),contact:e.string().nullish(),memberName:e.string().nullish(),orgs:e.array(e.object({pathId:e.string(),name:e.string()})),groups:e.array(e.object({name:e.string()}))});const w=e.enum([`uploadFile`,`userInfo`,`wecomCorpToken`]).enum,me={en:`Internal Server Error`,"zh-CN":`服务器内部错误`},T=new Map;var E=class extends Error{code;reason;httpStatus;telemetryKind;visibility;severity;data;constructor(e,t={}){super(t.message??e.message,{cause:M(t.cause)}),this.name=`RegisteredError`,this.code=e.code,this.reason=t.reason??e.reason,this.httpStatus=e.httpStatus??500,this.telemetryKind=e.telemetryKind,this.visibility=e.visibility??`public`,this.severity=e.severity??`expected`,this.data=t.data}};function he(e){for(let t of e){if(T.get(t.code))throw Error(`Error definition already registered: ${t.code}`);T.set(t.code,t)}}function ge(e){return T.get(e)}function D(e,t={}){let n=M(t.cause),r=n===void 0?Error(e.en):Error(e.en,{cause:n});return Object.assign(r,{reason:e})}function O(e,t=`Unknown error`){if(e instanceof Error)return e;if(typeof e==`string`)return Error(e);if(I(e))return D(e);if(R(e)){let n=I(e.reason)?e.reason:void 0,r=O(e.error,n?.en??t);return n&&be(n,k(r))?r:n?D(n,{cause:r}):Error(t,{cause:r})}return Error(F(e,t),{cause:e})}function k(e,t){if(e instanceof E)return e.visibility===`internal`?me:e.reason;if(I(e))return e;let n=L(e)?e:void 0;return n&&I(n.reason)?n.reason:n&&I(n.error)?n.error:t||(e instanceof Error?{en:e.message,"zh-CN":e.message}:typeof e==`string`?{en:e,"zh-CN":e}:me)}function A(e,t){return N(e,{includeStack:t?.includeStack??!1,depth:0,seen:new WeakSet})}function j(e){let t=e.cause?j(e.cause):void 0,n=e.code?ge(e.code):void 0;if(n){let r=new E(n,{message:e.message,reason:e.reason,cause:t,data:L(e.data)?e.data:void 0});return e.stack&&(r.stack=e.stack),r}let r=Error(e.message,{cause:t});return r.name=e.name||`Error`,e.stack&&(r.stack=e.stack),Object.assign(r,{...e.code===void 0?{}:{code:e.code},...e.reason===void 0?{}:{reason:e.reason},...e.data===void 0?{}:{data:e.data}})}function M(e){return e===void 0||e instanceof Error?e:typeof e==`string`?Error(e):I(e)?D(e):R(e)?O(e):e}function N(e,t){if(t.depth>5)return{name:`Error`,message:`Error cause depth exceeded`};if(e instanceof E)return{name:e.name,code:e.code,message:e.message,reason:e.reason,httpStatus:e.httpStatus,telemetryKind:e.telemetryKind,visibility:e.visibility,data:e.data,...t.includeStack&&e.stack!==void 0?{stack:e.stack}:{},...e.cause===void 0?{}:{cause:N(e.cause,P(t,e))}};if(e instanceof Error){let n=ve(e,`reason`);return{name:e.name,code:_e(e,`code`),message:e.message,...n===void 0?{}:{reason:n},data:ye(e,`data`),...t.includeStack&&e.stack!==void 0?{stack:e.stack}:{},...e.cause===void 0?{}:{cause:N(e.cause,P(t,e))}}}return typeof e==`string`?{name:`Error`,message:e}:{name:`Error`,message:F(e,`Unknown error`)}}function P(e,t){return e.seen.has(t)?{...e,depth:6}:(e.seen.add(t),{...e,depth:e.depth+1})}function F(e,t){if(e==null)return t;if(L(e)){let t=e.message??e.msg;if(typeof t==`string`&&t.length>0)return t}return String(e)}function _e(e,t){let n=e[t];return typeof n==`string`?n:void 0}function ve(e,t){let n=e[t];return I(n)?n:void 0}function ye(e,t){return e[t]}function I(e){return L(e)&&typeof e.en==`string`&&(e[`zh-CN`]===void 0||typeof e[`zh-CN`]==`string`)&&(e[`zh-Hant`]===void 0||typeof e[`zh-Hant`]==`string`)}function be(e,t){return e.en===t.en&&e[`zh-CN`]===t[`zh-CN`]&&e[`zh-Hant`]===t[`zh-Hant`]}function L(e){return!!(e&&typeof e==`object`)}function R(e){return L(e)&&`error`in e&&(`reason`in e||L(e.error))}const z=e=>[e,null];function B(e,t){return xe(e)?[null,V(e.reason,e.error,e)]:e instanceof Error?[null,V(k(e),e)]:[null,V(e,t===void 0?D(e):Se(e,t))]}function xe(e){return!!(e&&typeof e==`object`&&`reason`in e&&`error`in e)}function Se(e,t){return D(e,{cause:O(t,e.en)})}function V(e,t,n){let r=A(t),i=n?.code??r.code,a=n?.message??r.message,o=n&&`data`in n?n.data:r.data;return{reason:e,error:t,...i===void 0?{}:{code:i},...a===void 0?{}:{message:a},...o===void 0?{}:{data:o}}}var Ce=class{constructor(e,t={}){this.channel=e,this.options=t}async getWecomCorpToken(){try{let[e,t]=(await this.channel.request(o.request,{method:w.wecomCorpToken,args:{}},{traceId:this.options.invocationId})).result;return t?B({en:`Failed to get wecom corp token`,"zh-CN":`获取企业微信企业令牌失败`},t):z(e)}catch(e){return B({en:`Failed to get wecom corp token`,"zh-CN":`获取企业微信企业令牌失败`},e)}}async userInfo(){try{let[e,t]=(await this.channel.request(o.request,{method:w.userInfo,args:{}},{traceId:this.options.invocationId})).result;return t?B({en:`Failed to get user info`,"zh-CN":`获取用户信息失败`},t):z(e)}catch(e){return B({en:`Failed to get user info`,"zh-CN":`获取用户信息失败`},e)}}async uploadFile(e){try{let[t,n]=(await this.channel.request(o.request,{method:w.uploadFile,args:{contentType:e.contentType,fileName:e.fileName}},{traceId:this.options.invocationId,input:this.toUploadInputStream(e.file)})).result;return n?B({en:`Failed to upload file`,"zh-CN":`上传文件失败`},n):z(pe.parse(t))}catch(e){return B({en:`Failed to upload file`,"zh-CN":`上传文件失败`},e)}}toUploadInputStream(e){return e instanceof n.Readable?e:n.Readable.from(Buffer.from(e))}};e.object({pluginId:e.string(),version:e.string(),etag:e.string()});const we=e.literal(`system`).or(e.string()),Te=e.object({id:e.string(),name:ne});e.array(Te);const H=e.enum([`localPool`,`serverless`]).enum;e.object({pluginId:e.string(),version:e.string().optional(),source:we.optional()});const U={unknown:`common.unknown`,operationFailed:`common.operation_failed`,badRequest:`http.bad_request`,unauthorized:`http.unauthorized`,notFound:`http.not_found`,validationFailed:`http.validation_failed`,internalServerError:`http.internal_server_error`,pluginRuntimeManagerDestroyed:`plugin.runtime.manager_destroyed`,pluginRuntimeAlreadyRegistered:`plugin.runtime.already_registered`,pluginRuntimeReplacementPluginNotFound:`plugin.runtime.replacement_plugin_not_found`,pluginRuntimeConfigLoadFailed:`plugin.runtime.config_load_failed`,pluginRuntimeConfigUpdateFailed:`plugin.runtime.config_update_failed`,pluginRuntimeConfigResetFailed:`plugin.runtime.config_reset_failed`,pluginRuntimeConfigInvalid:`plugin.runtime.config_invalid`,pluginRuntimePodQuotaExceeded:`plugin.runtime.pod_quota_exceeded`,pluginRuntimePluginInfoLoadFailed:`plugin.runtime.plugin_info_load_failed`,pluginRuntimeInitializeFailed:`plugin.runtime.initialize_failed`,pluginRuntimePluginNotFound:`plugin.runtime.plugin_not_found`,pluginRuntimeEventNotSupported:`plugin.runtime.event_not_supported`,pluginRuntimeShutdownFailed:`plugin.runtime.shutdown_failed`,pluginRemoteDebugDisabled:`plugin.remote_debug_disabled`,pluginInvokeFailed:`plugin.invoke.failed`,pluginInvokeTimeout:`plugin.invoke.timeout`,pluginInvokeQueueTimeout:`plugin.invoke.queue_timeout`,connectionGatewayInvalidToken:`connection_gateway.invalid_token`,connectionGatewayTokenExpired:`connection_gateway.token_expired`,connectionGatewayTransportMismatch:`connection_gateway.transport_mismatch`,connectionGatewayCapabilityDenied:`connection_gateway.capability_denied`,connectionGatewayStaleGeneration:`connection_gateway.stale_generation`,connectionGatewaySessionNotFound:`connection_gateway.session_not_found`,connectionGatewaySessionAlreadyBound:`connection_gateway.session_already_bound`,connectionGatewaySessionOwnerExpired:`connection_gateway.session_owner_expired`,connectionGatewayResourceLimitExceeded:`connection_gateway.resource_limit_exceeded`,connectionGatewayEnvelopeTooLarge:`connection_gateway.envelope_too_large`};he([{code:U.unknown,message:`Unknown error`,reason:{en:`Unknown error`,"zh-CN":`未知错误`},visibility:`internal`,severity:`unexpected`},{code:U.operationFailed,message:`Operation failed`,reason:{en:`Operation failed`,"zh-CN":`操作失败`}},{code:U.badRequest,message:`Bad Request`,reason:{en:`Bad Request`,"zh-CN":`请求参数错误`},httpStatus:400},{code:U.unauthorized,message:`Unauthorized`,reason:{en:`Unauthorized`,"zh-CN":`未授权`},httpStatus:401},{code:U.notFound,message:`Not Found`,reason:{en:`Not Found`,"zh-CN":`资源未找到`},httpStatus:404},{code:U.validationFailed,message:`Validation failed`,reason:{en:`Validation failed`,"zh-CN":`数据校验失败`},httpStatus:400},{code:U.internalServerError,message:`Internal Server Error`,reason:{en:`Internal Server Error`,"zh-CN":`服务器内部错误`},httpStatus:500,visibility:`internal`,severity:`unexpected`},{code:U.pluginRuntimeManagerDestroyed,message:`Plugin manager already destroyed`,reason:{en:`Plugin manager already destroyed`,"zh-CN":`插件管理器已销毁`}},{code:U.pluginRuntimeAlreadyRegistered,message:`Plugin already registered`,reason:{en:`Plugin already registered`,"zh-CN":`插件已注册`},httpStatus:409,telemetryKind:`plugin_already_registered`},{code:U.pluginRuntimeReplacementPluginNotFound,message:`Replacement plugin not found`,reason:{en:`Replacement plugin not found`,"zh-CN":`替换插件未找到`},httpStatus:404},{code:U.pluginRuntimeConfigLoadFailed,message:`Failed to get plugin runtime config`,reason:{en:`Failed to get plugin runtime config`,"zh-CN":`获取插件运行时配置失败`},telemetryKind:`runtime_config_load_failed`},{code:U.pluginRuntimeConfigUpdateFailed,message:`Failed to update plugin runtime config`,reason:{en:`Failed to update plugin runtime config`,"zh-CN":`更新插件运行时配置失败`},telemetryKind:`runtime_config_update_failed`},{code:U.pluginRuntimeConfigResetFailed,message:`Failed to reset plugin runtime config`,reason:{en:`Failed to reset plugin runtime config`,"zh-CN":`重置插件运行时配置失败`},telemetryKind:`runtime_config_reset_failed`},{code:U.pluginRuntimeConfigInvalid,message:`Invalid plugin runtime config`,reason:{en:`Invalid plugin runtime config`,"zh-CN":`插件运行时配置无效`},httpStatus:400,telemetryKind:`runtime_config_invalid`},{code:U.pluginRuntimePodQuotaExceeded,message:`Pod quota exceeded`,reason:{en:`Pod quota exceeded`,"zh-CN":`Pod 配额超出`},httpStatus:429,telemetryKind:`pod_quota_exceeded`},{code:U.pluginRuntimePluginInfoLoadFailed,message:`Register plugin error, can not get plugin info`,reason:{en:`Register plugin error, can not get plugin info`,"zh-CN":`注册插件失败,无法获取插件信息`}},{code:U.pluginRuntimeInitializeFailed,message:`Failed to initialize plugin runtime`,reason:{en:`Failed to initialize plugin runtime`,"zh-CN":`初始化插件运行时失败`},telemetryKind:`plugin_runtime_initialize_failed`},{code:U.pluginRuntimePluginNotFound,message:`Plugin not found`,reason:{en:`Plugin not found`,"zh-CN":`插件未找到`},httpStatus:404},{code:U.pluginRuntimeEventNotSupported,message:`Event not supported`,reason:{en:`Event not supported`,"zh-CN":`不支持的事件`},httpStatus:400},{code:U.pluginRuntimeShutdownFailed,message:`Error during shutdown`,reason:{en:`Error during shutdown`,"zh-CN":`关闭过程中发生错误`},telemetryKind:`runtime_shutdown_failed`},{code:U.pluginRemoteDebugDisabled,message:`Remote debug is disabled`,reason:{en:`Remote debug is disabled for this Plugin service`,"zh-CN":`当前 Plugin 服务未启用远程调试功能`},httpStatus:400,telemetryKind:`remote_debug_disabled`},{code:U.pluginInvokeFailed,message:`Invoke failed`,reason:{en:`Invoke failed`,"zh-CN":`调用失败`},telemetryKind:`invoke_failed`},{code:U.pluginInvokeTimeout,message:`Plugin invocation timed out`,reason:{en:`Plugin invocation timed out`,"zh-CN":`插件调用超时`},httpStatus:504,telemetryKind:`invoke_timeout`},{code:U.pluginInvokeQueueTimeout,message:`Plugin invocation waited too long for an available local-pool pod`,reason:{en:`Plugin invocation waited too long for an available local-pool pod`,"zh-CN":`插件调用等待空闲本地运行实例超时`},httpStatus:503,telemetryKind:`queue_timeout`},{code:U.connectionGatewayInvalidToken,message:`Invalid connection token`,reason:{en:`Invalid connection token`,"zh-CN":`连接令牌无效`},httpStatus:401,telemetryKind:`connection_gateway_invalid_token`},{code:U.connectionGatewayTokenExpired,message:`Connection token expired`,reason:{en:`Connection token expired`,"zh-CN":`连接令牌已过期`},httpStatus:401,telemetryKind:`connection_gateway_token_expired`},{code:U.connectionGatewayTransportMismatch,message:`Connection token transport mismatch`,reason:{en:`Connection token transport mismatch`,"zh-CN":`连接令牌传输类型不匹配`},httpStatus:400,telemetryKind:`connection_gateway_transport_mismatch`},{code:U.connectionGatewayCapabilityDenied,message:`Connection token capability denied`,reason:{en:`Connection token capability denied`,"zh-CN":`连接令牌缺少能力授权`},httpStatus:403,telemetryKind:`connection_gateway_capability_denied`},{code:U.connectionGatewayStaleGeneration,message:`Stale gateway generation`,reason:{en:`Stale gateway generation`,"zh-CN":`Gateway generation 已过期`},httpStatus:409,telemetryKind:`connection_gateway_stale_generation`},{code:U.connectionGatewaySessionNotFound,message:`Gateway session not found`,reason:{en:`Gateway session not found`,"zh-CN":`Gateway 会话不存在`},httpStatus:404,telemetryKind:`connection_gateway_session_not_found`},{code:U.connectionGatewaySessionAlreadyBound,message:`Gateway session already bound`,reason:{en:`Gateway session already bound`,"zh-CN":`Gateway 会话已存在在线连接`},httpStatus:409,telemetryKind:`connection_gateway_session_already_bound`},{code:U.connectionGatewaySessionOwnerExpired,message:`Gateway session owner expired`,reason:{en:`Gateway session owner expired`,"zh-CN":`Gateway 会话 owner 已过期`},httpStatus:409,telemetryKind:`connection_gateway_session_owner_expired`},{code:U.connectionGatewayResourceLimitExceeded,message:`Gateway resource limit exceeded`,reason:{en:`Gateway resource limit exceeded`,"zh-CN":`Gateway 资源限制已达到`},httpStatus:429,telemetryKind:`connection_gateway_resource_limit_exceeded`},{code:U.connectionGatewayEnvelopeTooLarge,message:`Gateway envelope too large`,reason:{en:`Gateway envelope too large`,"zh-CN":`Gateway 消息体过大`},httpStatus:413,telemetryKind:`connection_gateway_envelope_too_large`}]);const W=`__fastgptChannelReply__`,G=`__fastgptChannelError__`;var Ee=class{transport=`ipc`;pending=new Map;errorHandlers=new Set;closeHandlers=new Set;incomingStreams=new Map;bufferedIncomingStreams=new Map;pendingStreamWaiters=new Map;requestHandler=null;notificationHandler=null;closed=!1;unsubscribeMessage;constructor(e,t,n={}){this.side=e,this.endpoint=t,this.options=n,this.unsubscribeMessage=Oe(t,e=>{this.dispatch(e).catch(e=>{this.emitError(e instanceof Error?e:Error(String(e)))})})}async request(e,t,n){this.ensureOpen();let r=n?.id??i(),a=n?.timeoutMs??this.options.defaultTimeoutMs??3e4,o=n?.input===void 0?void 0:this.pipeStream(Re(r),n.input,{traceId:n.traceId,streamId:n.inputStream?.streamId,streamName:n.inputStream?.streamName,meta:n.inputStream?.meta}),s=Date.now();o&&o.catch(e=>{this.emitError(e instanceof Error?e:Error(String(e)))});let c=await new Promise((i,o)=>{let s=setTimeout(()=>{this.pending.delete(r),o(Object.assign(Error(`Request timeout: ${String(e)}`),{code:h.requestTimeout,requestId:r,method:e}))},a);this.pending.set(r,{method:String(e),resolve:i,reject:o,timer:s}),this.send({protocol:`1.0`,id:r,method:String(e),params:t,...n?.traceId===void 0?{}:{traceId:n.traceId},timestamp:Date.now()}).catch(e=>{clearTimeout(s),this.pending.delete(r),o(e)})}),l=c,u;if(Pe(c)&&(l=c.result,c.hasOutputStream)){let e=Date.now()-s,t=Math.max(1,a-e);u=await this.waitForStream(Q(r),{timeoutMs:t})}return{requestId:r,result:l,...u===void 0?{}:{output:u},...o===void 0?{}:{inputDone:o}}}async notify(e,t,n){this.ensureOpen(),await this.send({protocol:`1.0`,method:String(e),params:t,...n?.traceId===void 0?{}:{traceId:n.traceId},timestamp:Date.now()})}setRequestHandler(e){this.requestHandler=e}setNotificationHandler(e){this.notificationHandler=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 n=t?.streamId??i(),r=!1,a=t?.streamName??e;return await this.sendStreamFrame({type:`start`,streamId:n,streamName:a,...t?.meta===void 0?{}:{meta:t.meta}},t?.traceId),{streamId:n,streamName:a,...t?.meta===void 0?{}:{meta:t.meta},write:async e=>{if(r)throw Error(`Stream is already closed: ${n}`);await this.sendStreamFrame({type:`chunk`,streamId:n,chunk:e},t?.traceId)},end:async()=>{r||(r=!0,await this.sendStreamFrame({type:`end`,streamId:n},t?.traceId))},fail:async e=>{r||(r=!0,await this.sendStreamFrame({type:`error`,streamId:n,error:K(e)},t?.traceId))}}}async pipeStream(e,t,n){let r=await this.createWritableStream(e,n);try{for await(let e of Le(t))await r.write(e);await r.end()}catch(e){throw await r.fail(e instanceof Error?e:Error(String(e))),e}}onError(e){return this.errorHandlers.add(e),()=>{this.errorHandlers.delete(e)}}onClose(e){return this.closeHandlers.add(e),()=>{this.closeHandlers.delete(e)}}async close(e=Error(`Channel closed`)){this.closed||(this.closed=!0,this.unsubscribeMessage(),this.rejectAllPending(e),this.rejectAllPendingStreamWaiters(e),this.failAllIncomingStreams(e),this.closeHandlers.forEach(t=>t(e)))}createReply(e,t){return{[W]:!0,...e===void 0?{}:{result:e},...t?.output===void 0?{}:{output:t.output},...t?.outputStream===void 0?{}:{outputStream:t.outputStream}}}async dispatch(e){if(!this.closed){if(Me(e)){this.handleResponse(e);return}if(je(e)){await this.handleRequest(e);return}await this.handleNotification(e)}}handleResponse(e){let t=this.pending.get(e.id);if(t){if(clearTimeout(t.timer),this.pending.delete(e.id),`error`in e){t.reject(q(e.error));return}t.resolve(X(e.result))}}async handleRequest(e){if(!this.requestHandler){await this.replyError(e,{code:h.methodNotFound,message:`Method not found: ${e.method}`});return}try{let t=await this.requestHandler({id:e.id,method:e.method,params:e.params,...e.traceId===void 0?{}:{traceId:e.traceId},raw:e,waitForInputStream:t=>this.waitForStream(Re(e.id),t)});if(Ne(t)){await this.handleReplyDescriptor(e,t);return}await this.reply(e,t)}catch(t){await this.replyError(e,t instanceof Error?t:Error(String(t)))}}async handleNotification(e){if(e.method===c.streamFrame){await this.handleStreamFrame(e.params,e.traceId);return}await this.notificationHandler?.({method:e.method,params:e.params,...e.traceId===void 0?{}:{traceId:e.traceId},raw:e})}async handleReplyDescriptor(e,t){t.output!==void 0&&this.pipeStream(Q(e.id),t.output,{traceId:t.outputStream?.traceId??e.traceId,streamId:t.outputStream?.streamId,streamName:t.outputStream?.streamName,meta:t.outputStream?.meta}).catch(e=>{this.emitError(e instanceof Error?e:Error(String(e)))}),await this.reply(e,{[W]:!0,hasOutputStream:t.output!==void 0,...t.result===void 0?{}:{result:t.result}})}async reply(e,t){await this.send({protocol:`1.0`,id:e.id,...t===void 0?{}:{result:Y(t)},...e.traceId===void 0?{}:{traceId:e.traceId},timestamp:Date.now()})}async replyError(e,t){await this.send({protocol:`1.0`,id:e.id,error:K(t),...e.traceId===void 0?{}:{traceId:e.traceId},timestamp:Date.now()})}async sendStreamFrame(e,t){await this.notify(c.streamFrame,e,{traceId:t})}async handleStreamFrame(e,t){switch(e.type){case`start`:{let n={streamId:e.streamId,streamName:e.streamName,stream:a.create(),...t===void 0?{}:{traceId:t},...e.meta===void 0?{}:{meta:e.meta}};this.incomingStreams.set(e.streamId,n),this.dispatchIncomingStream(n);return}case`chunk`:{let t=this.incomingStreams.get(e.streamId);if(!t){this.emitError(Error(`Stream not found: ${e.streamId}`));return}t.stream.write(e.chunk);return}case`end`:{let t=this.incomingStreams.get(e.streamId);if(!t)return;t.stream.end(),this.incomingStreams.delete(e.streamId);return}case`error`:{let t=this.incomingStreams.get(e.streamId);if(!t)return;t.stream.fail(q(e.error)),this.incomingStreams.delete(e.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}t||(this.bufferedIncomingStreams.has(e.streamName)||this.bufferedIncomingStreams.set(e.streamName,[]),this.bufferedIncomingStreams.get(e.streamName).push(e))}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()}emitError(e){this.errorHandlers.forEach(t=>t(e))}async send(e){await ke(this.endpoint,e)}ensureOpen(){if(this.closed)throw Error(`Channel closed`)}};function De(e){return new Ee(`client`,process,e)}function Oe(e,t){let n=e,r=e=>{Ae(e)&&t(e)};return n.on(`message`,r),()=>n.off(`message`,r)}function ke(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 Ae(e){return!!(e&&typeof e==`object`&&`protocol`in e)}function je(e){return`id`in e&&`method`in e}function Me(e){return`id`in e&&!(`method`in e)&&(`result`in e||`error`in e)}function Ne(e){return!!(e&&typeof e==`object`&&W in e&&e[W]===!0)}function Pe(e){return!!(e&&typeof e==`object`&&W in e&&`hasOutputStream`in e)}function K(e){return typeof e==`string`?{code:h.internalError,message:e}:e instanceof Error?J(A(e)):e}function q(e){return j(Fe(e))}function J(e){return{code:e.code??h.internalError,message:e.message,...e.reason===void 0?{}:{reason:e.reason},...e.data===void 0?{}:{data:e.data},...e.cause===void 0?{}:{cause:J(e.cause)}}}function Fe(e){return{name:`Error`,code:e.code,message:e.message,reason:e.reason,data:e.data,...e.cause===void 0?{}:{cause:Fe(e.cause)}}}function Y(e,t=new WeakSet){return e instanceof Error?{[G]:!0,error:A(e)}:!e||typeof e!=`object`||t.has(e)?e:(t.add(e),Array.isArray(e)?e.map(e=>Y(e,t)):Z(e)?Object.fromEntries(Object.entries(e).map(([e,n])=>[e,Y(n,t)])):e)}function X(e,t=new WeakMap){if(Ie(e))return j(e.error);if(!e||typeof e!=`object`)return e;let n=t.get(e);if(n)return n;if(Array.isArray(e)){let n=[];return t.set(e,n),n.push(...e.map(e=>X(e,t))),n}if(!Z(e))return e;let r={};t.set(e,r);for(let[n,i]of Object.entries(e))r[n]=X(i,t);return r}function Ie(e){return Z(e)&&e[G]===!0&&Z(e.error)&&typeof e.error.name==`string`&&typeof e.error.message==`string`}function Z(e){if(!e||typeof e!=`object`)return!1;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null}function Le(e){return e instanceof a?e.values():e}function Re(e){return`request.input:${String(e)}`}function Q(e){return`request.output:${String(e)}`}var ze=class{channel;getChannel(){if(!this.channel)throw Error(`Channel is not initialized yet.`);return this.channel}mode;checkRuntimeMode(){process.env.RUNTIME_MODE==H.localPool&&(this.mode=H.localPool),process.env.RUNTIME_MODE===`dev`&&(this.mode=`dev`)}async init(){this.checkRuntimeMode(),this.mode===`localPool`&&(this.channel=De(),setImmediate(()=>{this.getChannel().notify(o.ready,{pid:process.pid,runtimeMode:H.localPool,startedAt:Date.now()})})),this.mode===`dev`&&(this.channel=Be().pluginChannel,setImmediate(()=>{this.getChannel().notify(o.ready,{pid:process.pid,runtimeMode:`dev`,startedAt:Date.now()})}))}constructor(){this.init()}};function Be(){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 Ve(e){return e}var $=class t extends ze{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===u.request&&e.params.eventName===`run`)try{let{input:t,systemVar:n,childId:r,secrets:i}=e.params.payload,o=this.toolHandlers.get(r??`tool`);if(!o)throw Error(`No tool registered`);let s=a.create();return(async()=>{try{let r=await o.handler(t,{systemVar:n,secrets:i,invoke:new Ce(this.getChannel(),{invocationId:e.traceId}),streamResponse:e=>{s.send({type:`stream`,data:e})}});s.send({type:`response`,data:r})}catch(e){s.send({data:_(e,`Unknown error during tool execution`),type:`error`})}finally{s.end()}})(),this.getChannel().createReply(void 0,{output:s})}catch(e){let t=a.create();return t.write({data:_(e,`Unknown error during tool execution`),type:`error`}),t.end(),this.getChannel().createReply(void 0,{output:t})}})}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 He=({manifest:t,handler:n})=>{let r=$.getInstance(t);return r.setSecretSchema(n.secretSchema??e.object()),r.registerTool(n),r},Ue=({manifest:t,children:n,secretSchema:r})=>{let i=$.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{Ve as createToolHandler,He as defineTool,Ue as defineToolSet};
package/package.json CHANGED
@@ -1,6 +1,14 @@
1
1
  {
2
2
  "name": "@fastgpt-plugin/sdk-factory",
3
- "version": "0.0.1-alpha.9",
3
+ "version": "0.1.0-alpha.1",
4
+ "description": "TypeScript factory SDK for authoring FastGPT Plugin tools and tool suites.",
5
+ "keywords": [
6
+ "fastgpt",
7
+ "fastgpt-plugin",
8
+ "sdk",
9
+ "tool",
10
+ "typescript"
11
+ ],
4
12
  "devDependencies": {
5
13
  "tsdown": "^0.21.10"
6
14
  },
@@ -24,6 +32,7 @@
24
32
  "publishConfig": {
25
33
  "access": "public"
26
34
  },
35
+ "license": "MIT",
27
36
  "scripts": {
28
37
  "build": "tsdown"
29
38
  }
@@ -23,7 +23,8 @@ import z from 'zod';
23
23
  const handler = createToolHandler({
24
24
  inputSchema: z.object({
25
25
  text: z.string().meta({
26
- title: 'Text'
26
+ title: 'Text',
27
+ isToolParams: true
27
28
  } satisfies InputSchemaMetaType)
28
29
  }),
29
30
  outputSchema: z.object({
@@ -64,6 +65,7 @@ export default defineTool({
64
65
  - Use `z.object(...)` for `inputSchema` and `outputSchema` so TypeScript can infer `input` and return types.
65
66
  - Import `InputSchemaMetaType`, `OutputSchemaMetaType`, and `SecretSchemaMetaType` from `@fastgpt-plugin/sdk-factory` when schema metadata is used.
66
67
  - Add `.meta({ ... } satisfies InputSchemaMetaType)` to input fields and `.meta({ ... } satisfies OutputSchemaMetaType)` to output fields that need UI/manifest metadata.
68
+ - Set `isToolParams: true` in an input field's `.meta()` when that field is recommended to be managed by AI; use `toolDescription` for the model-facing parameter description.
67
69
  - Add `.meta({ isSecret: true | false, ... } satisfies SecretSchemaMetaType)` to every `secretSchema` field; set `isSecret: true` for values that must be encrypted at rest.
68
70
  - Return an object that matches `outputSchema`; throw errors for failed operations.
69
71
  - Add `secretSchema` when plugin configuration needs secrets such as API keys.
@@ -82,7 +84,9 @@ const secretSchema = z.object({
82
84
  const handler = createToolHandler({
83
85
  inputSchema: z.object({
84
86
  query: z.string().meta({
85
- title: 'Query'
87
+ title: 'Query',
88
+ toolDescription: 'Search query',
89
+ isToolParams: true
86
90
  } satisfies InputSchemaMetaType)
87
91
  }),
88
92
  outputSchema: z.object({
@@ -124,6 +128,16 @@ Common optional fields:
124
128
  - `icon`
125
129
  - `toolDescription`
126
130
 
131
+ Use `permission` to declare host capabilities required by the plugin. It is an array of permission strings. Declare only the minimum permissions that the plugin actually uses. When using `ctx.invoke` to call host capabilities, declare the matching permission.
132
+
133
+ Supported permissions:
134
+
135
+ - `userInfo:read`: read user information, for example through `ctx.invoke.userInfo()`.
136
+ - `teamInfo:read`: read team information.
137
+ - `model:read`: read model information.
138
+ - `dataset:read`: read dataset information.
139
+ - `file-upload:allow`: upload files, for example through `ctx.invoke.uploadFile()`.
140
+
127
141
  Keep `pluginId`, child tool `id`, input field names, and output field names stable for compatibility.
128
142
 
129
143
  ## Tool Sets
@@ -150,7 +164,8 @@ const secretSchema = z.object({
150
164
  const searchHandler = createToolHandler({
151
165
  inputSchema: z.object({
152
166
  query: z.string().meta({
153
- title: 'Query'
167
+ title: 'Query',
168
+ isToolParams: true
154
169
  } satisfies InputSchemaMetaType)
155
170
  }),
156
171
  outputSchema: z.object({
@@ -196,13 +211,14 @@ export default defineToolSet({
196
211
 
197
212
  ## Host Invocation
198
213
 
199
- Use `ctx.invoke` for host capabilities. The SDK exposes `userInfo()` and `uploadFile()`. These methods return a `Result` tuple shaped as `[result, err]`; check `err` before using `result`. When `err` is present, throw or return that original `err` so host-side error details are preserved.
214
+ Use `ctx.invoke` for host capabilities. The SDK exposes `userInfo()` and `uploadFile()`. Declare the corresponding `manifest.permission` item before using a host capability; for example, `ctx.invoke.uploadFile()` requires `file-upload:allow`. These methods return a `Result` tuple shaped as `[result, err]`; check `err` before using `result`. When `err` is present, throw or return that original `err` so host-side error details are preserved.
200
215
 
201
216
  ```ts
202
217
  const uploadHandler = createToolHandler({
203
218
  inputSchema: z.object({
204
219
  content: z.string().meta({
205
- title: 'Content'
220
+ title: 'Content',
221
+ isToolParams: true
206
222
  } satisfies InputSchemaMetaType)
207
223
  }),
208
224
  outputSchema: z.object({
@@ -62,6 +62,7 @@ npx @fastgpt-plugin/cli pack --entry <plugin-directory> --dist <plugin-directory
62
62
  - 每个 handler 用 `createToolHandler()` 定义。
63
63
  - `inputSchema` 和 `outputSchema` 使用 `z.object(...)`。
64
64
  - `inputSchema` 字段使用 `.meta({ ... } satisfies InputSchemaMetaType)`。
65
+ - `inputSchema` 字段推荐由 AI 托管补充时,在 `.meta()` 中设置 `isToolParams: true`;`toolDescription` 可用于补充面向模型的参数说明。
65
66
  - `outputSchema` 字段使用 `.meta({ ... } satisfies OutputSchemaMetaType)`。
66
67
  - `secretSchema` 字段使用 `.meta({ isSecret: true | false, ... } satisfies SecretSchemaMetaType)`;需要加密存储的字段标记为 `isSecret: true`。
67
68
  - handler 返回值必须匹配 `outputSchema`。
@@ -84,13 +85,23 @@ manifest 至少包含:
84
85
  - `icon`
85
86
  - `toolDescription`
86
87
 
88
+ `permission` 用于声明插件运行时需要的宿主能力,类型为权限字符串数组。只声明当前插件实际使用的最小权限;没有使用对应能力时不要添加。使用 `ctx.invoke` 反向调用宿主能力时,必须声明对应权限。
89
+
90
+ 当前可用权限:
91
+
92
+ - `userInfo:read`: 读取用户信息,例如调用 `ctx.invoke.userInfo()`。
93
+ - `teamInfo:read`: 读取团队信息。
94
+ - `model:read`: 读取模型信息。
95
+ - `dataset:read`: 读取知识库信息。
96
+ - `file-upload:allow`: 上传文件,例如调用 `ctx.invoke.uploadFile()`。
97
+
87
98
  兼容性要求:
88
99
 
89
100
  - `pluginId`、工具 `id`、输入字段名、输出字段名对外使用后保持稳定。
90
101
  - 用户配置的密钥、API Key、Base URL 等使用 `secretSchema` 描述,通过 `ctx.secrets` 读取。
91
102
  - 错误信息应能帮助定位问题,同时避免暴露密钥、令牌和完整上游敏感响应。
92
103
  - 需要向用户展示执行进度时使用 `ctx.streamResponse()`。
93
- - 需要生成文件时使用 `ctx.invoke.uploadFile()`;收到 `[result, err]` 的 `err` 时保留原始 `err`,避免覆盖反向调用的宿主错误信息。
104
+ - 需要生成文件时使用 `ctx.invoke.uploadFile()`,并在 `manifest.permission` 中声明 `file-upload:allow`;收到 `[result, err]` 的 `err` 时保留原始 `err`,避免覆盖反向调用的宿主错误信息。
94
105
 
95
106
  ## 头像规范
96
107
 
@@ -125,7 +136,9 @@ import z from 'zod';
125
136
  const handler = createToolHandler({
126
137
  inputSchema: z.object({
127
138
  text: z.string().min(1).meta({
128
- title: 'Text'
139
+ title: 'Text',
140
+ toolDescription: 'Text to normalize',
141
+ isToolParams: true
129
142
  } satisfies InputSchemaMetaType)
130
143
  }),
131
144
  outputSchema: z.object({
@@ -155,7 +168,8 @@ export default defineTool({
155
168
  versionDescription: {
156
169
  en: 'Initial version',
157
170
  'zh-CN': '初始版本'
158
- }
171
+ },
172
+ permission: []
159
173
  },
160
174
  handler
161
175
  });
@@ -183,7 +197,9 @@ const secretSchema = z.object({
183
197
  const searchHandler = createToolHandler({
184
198
  inputSchema: z.object({
185
199
  query: z.string().min(1).meta({
186
- title: 'Query'
200
+ title: 'Query',
201
+ toolDescription: 'Search query',
202
+ isToolParams: true
187
203
  } satisfies InputSchemaMetaType)
188
204
  }),
189
205
  outputSchema: z.object({
@@ -216,7 +232,8 @@ export default defineToolSet({
216
232
  description: {
217
233
  en: 'Search public example data',
218
234
  'zh-CN': '搜索公开示例数据'
219
- }
235
+ },
236
+ permission: []
220
237
  },
221
238
  children: [
222
239
  {
@@ -240,7 +257,9 @@ export default defineToolSet({
240
257
 
241
258
  - `index.ts` 默认导出正确。
242
259
  - `manifest` 字段完整,国际化字段包含 `en` 和 `zh-CN`。
243
- - Zod schema 覆盖全部用户可见输入和输出。
260
+ - `permission` 只包含插件实际使用的权限,且权限值来自当前支持的枚举。
261
+ - 使用 `ctx.invoke` 时已在 `manifest.permission` 声明对应权限,例如上传文件声明 `file-upload:allow`。
262
+ - Zod schema 覆盖全部用户可见输入和输出;推荐由 AI 托管补充的输入字段设置 `isToolParams: true`。
244
263
  - handler 成功路径返回值与 `outputSchema` 一致。
245
264
  - 外部调用失败、空响应、超时、鉴权失败都有明确错误。
246
265
  - 密钥配置只通过 `secretSchema` 和 `ctx.secrets` 处理。