@fastgpt-plugin/sdk-factory 0.0.1-alpha.9 → 0.0.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 +174 -0
- package/README.md +2 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/skills/fastgpt-sdk-factory/SKILL.md +14 -2
- package/skills/fastgpt-system-tool-development/SKILL.md +23 -6
package/README.en.md
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
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
|
+
} satisfies InputSchemaMetaType)
|
|
33
|
+
}),
|
|
34
|
+
outputSchema: z.object({
|
|
35
|
+
result: z.string().meta({
|
|
36
|
+
title: 'Result'
|
|
37
|
+
} satisfies OutputSchemaMetaType)
|
|
38
|
+
}),
|
|
39
|
+
handler: async (input) => {
|
|
40
|
+
return {
|
|
41
|
+
result: input.text.toUpperCase()
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
export default defineTool({
|
|
47
|
+
manifest: {
|
|
48
|
+
pluginId: 'uppercase',
|
|
49
|
+
version: '1.0.0',
|
|
50
|
+
name: {
|
|
51
|
+
en: 'Uppercase',
|
|
52
|
+
'zh-CN': '转大写'
|
|
53
|
+
},
|
|
54
|
+
description: {
|
|
55
|
+
en: 'Convert text to uppercase',
|
|
56
|
+
'zh-CN': '将文本转换为大写'
|
|
57
|
+
},
|
|
58
|
+
versionDescription: {
|
|
59
|
+
en: 'Initial version',
|
|
60
|
+
'zh-CN': '初始版本'
|
|
61
|
+
},
|
|
62
|
+
tags: ['tools']
|
|
63
|
+
},
|
|
64
|
+
handler
|
|
65
|
+
});
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## API
|
|
69
|
+
|
|
70
|
+
### `createToolHandler(definition)`
|
|
71
|
+
|
|
72
|
+
Defines a tool handler and infers `input`, `output`, and `secrets` types from Zod schemas.
|
|
73
|
+
|
|
74
|
+
The handler context includes:
|
|
75
|
+
|
|
76
|
+
| Field | Description |
|
|
77
|
+
| --- | --- |
|
|
78
|
+
| `systemVar` | System variables injected by FastGPT. |
|
|
79
|
+
| `secrets` | Secret values validated by `secretSchema`. |
|
|
80
|
+
| `invoke` | Client for invoking host capabilities, such as file upload. |
|
|
81
|
+
| `streamResponse` | Sends streaming tool answers. |
|
|
82
|
+
|
|
83
|
+
### `defineTool(options)`
|
|
84
|
+
|
|
85
|
+
Defines a single-tool plugin. `manifest` describes the plugin metadata, and `handler` contains the execution logic.
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
export default defineTool({
|
|
89
|
+
manifest,
|
|
90
|
+
handler
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### `defineToolSet(options)`
|
|
95
|
+
|
|
96
|
+
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.
|
|
97
|
+
|
|
98
|
+
## Manifest
|
|
99
|
+
|
|
100
|
+
`manifest` uses bilingual i18n fields. Common fields:
|
|
101
|
+
|
|
102
|
+
| Field | Required | Description |
|
|
103
|
+
| --- | --- | --- |
|
|
104
|
+
| `pluginId` | Yes | Unique plugin ID. |
|
|
105
|
+
| `version` | Yes | Plugin version. |
|
|
106
|
+
| `name` | Yes | Plugin name in `{ en, 'zh-CN' }` format. |
|
|
107
|
+
| `description` | Yes | Plugin description in `{ en, 'zh-CN' }` format. |
|
|
108
|
+
| `versionDescription` | No | Version description. |
|
|
109
|
+
| `author` | No | Author. |
|
|
110
|
+
| `repoUrl` | No | Repository URL. |
|
|
111
|
+
| `tutorialUrl` | No | Tutorial URL. |
|
|
112
|
+
| `tags` | No | Plugin tags. |
|
|
113
|
+
| `permission` | No | Plugin permission declarations. |
|
|
114
|
+
| `icon` | No | Plugin icon; the build pipeline can fill it automatically. |
|
|
115
|
+
| `toolDescription` | No | Tool description for the model; the build pipeline can fill it automatically. |
|
|
116
|
+
|
|
117
|
+
## Secret Configuration
|
|
118
|
+
|
|
119
|
+
A single tool can declare `secretSchema` in its handler. A tool set can declare a shared `secretSchema` at the top level of `defineToolSet()`.
|
|
120
|
+
|
|
121
|
+
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.
|
|
122
|
+
|
|
123
|
+
## Host Invocation
|
|
124
|
+
|
|
125
|
+
Use `ctx.invoke` to call FastGPT host capabilities. The SDK currently exposes file upload.
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
const handler = createToolHandler({
|
|
129
|
+
inputSchema: z.object({
|
|
130
|
+
content: z.string().meta({
|
|
131
|
+
title: 'Content'
|
|
132
|
+
} satisfies InputSchemaMetaType)
|
|
133
|
+
}),
|
|
134
|
+
outputSchema: z.object({
|
|
135
|
+
accessURL: z.string().meta({
|
|
136
|
+
title: 'Access URL'
|
|
137
|
+
} satisfies OutputSchemaMetaType),
|
|
138
|
+
fileName: z.string().meta({
|
|
139
|
+
title: 'File Name'
|
|
140
|
+
} satisfies OutputSchemaMetaType),
|
|
141
|
+
size: z.number().meta({
|
|
142
|
+
title: 'Size'
|
|
143
|
+
} satisfies OutputSchemaMetaType)
|
|
144
|
+
}),
|
|
145
|
+
handler: async (input, { invoke }) => {
|
|
146
|
+
const [result, err] = await invoke.uploadFile({
|
|
147
|
+
fileName: 'result.txt',
|
|
148
|
+
contentType: 'text/plain',
|
|
149
|
+
file: Buffer.from(input.content, 'utf-8')
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
if (err) {
|
|
153
|
+
throw err;
|
|
154
|
+
}
|
|
155
|
+
if (!result) {
|
|
156
|
+
throw new Error('Failed to upload file');
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
accessURL: result.accessURL,
|
|
161
|
+
fileName: result.fileName,
|
|
162
|
+
size: result.size
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Build
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
pnpm --filter @fastgpt-plugin/sdk-factory build
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
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.
|
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
|
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
|
@@ -64,6 +64,7 @@ export default defineTool({
|
|
|
64
64
|
- Use `z.object(...)` for `inputSchema` and `outputSchema` so TypeScript can infer `input` and return types.
|
|
65
65
|
- Import `InputSchemaMetaType`, `OutputSchemaMetaType`, and `SecretSchemaMetaType` from `@fastgpt-plugin/sdk-factory` when schema metadata is used.
|
|
66
66
|
- Add `.meta({ ... } satisfies InputSchemaMetaType)` to input fields and `.meta({ ... } satisfies OutputSchemaMetaType)` to output fields that need UI/manifest metadata.
|
|
67
|
+
- Set `toolDescription` in an input field's `.meta()` when that field should be available as an automatically filled tool-call parameter; without `toolDescription`, users must specify the field manually.
|
|
67
68
|
- Add `.meta({ isSecret: true | false, ... } satisfies SecretSchemaMetaType)` to every `secretSchema` field; set `isSecret: true` for values that must be encrypted at rest.
|
|
68
69
|
- Return an object that matches `outputSchema`; throw errors for failed operations.
|
|
69
70
|
- Add `secretSchema` when plugin configuration needs secrets such as API keys.
|
|
@@ -82,7 +83,8 @@ const secretSchema = z.object({
|
|
|
82
83
|
const handler = createToolHandler({
|
|
83
84
|
inputSchema: z.object({
|
|
84
85
|
query: z.string().meta({
|
|
85
|
-
title: 'Query'
|
|
86
|
+
title: 'Query',
|
|
87
|
+
toolDescription: 'Search query'
|
|
86
88
|
} satisfies InputSchemaMetaType)
|
|
87
89
|
}),
|
|
88
90
|
outputSchema: z.object({
|
|
@@ -124,6 +126,16 @@ Common optional fields:
|
|
|
124
126
|
- `icon`
|
|
125
127
|
- `toolDescription`
|
|
126
128
|
|
|
129
|
+
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.
|
|
130
|
+
|
|
131
|
+
Supported permissions:
|
|
132
|
+
|
|
133
|
+
- `userInfo:read`: read user information, for example through `ctx.invoke.userInfo()`.
|
|
134
|
+
- `teamInfo:read`: read team information.
|
|
135
|
+
- `model:read`: read model information.
|
|
136
|
+
- `dataset:read`: read dataset information.
|
|
137
|
+
- `file-upload:allow`: upload files, for example through `ctx.invoke.uploadFile()`.
|
|
138
|
+
|
|
127
139
|
Keep `pluginId`, child tool `id`, input field names, and output field names stable for compatibility.
|
|
128
140
|
|
|
129
141
|
## Tool Sets
|
|
@@ -196,7 +208,7 @@ export default defineToolSet({
|
|
|
196
208
|
|
|
197
209
|
## Host Invocation
|
|
198
210
|
|
|
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.
|
|
211
|
+
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
212
|
|
|
201
213
|
```ts
|
|
202
214
|
const uploadHandler = createToolHandler({
|
|
@@ -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` 字段需要被工具调用自动填参时,必须在 `.meta()` 中设置 `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,8 @@ 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'
|
|
129
141
|
} satisfies InputSchemaMetaType)
|
|
130
142
|
}),
|
|
131
143
|
outputSchema: z.object({
|
|
@@ -155,7 +167,8 @@ export default defineTool({
|
|
|
155
167
|
versionDescription: {
|
|
156
168
|
en: 'Initial version',
|
|
157
169
|
'zh-CN': '初始版本'
|
|
158
|
-
}
|
|
170
|
+
},
|
|
171
|
+
permission: []
|
|
159
172
|
},
|
|
160
173
|
handler
|
|
161
174
|
});
|
|
@@ -183,7 +196,8 @@ const secretSchema = z.object({
|
|
|
183
196
|
const searchHandler = createToolHandler({
|
|
184
197
|
inputSchema: z.object({
|
|
185
198
|
query: z.string().min(1).meta({
|
|
186
|
-
title: 'Query'
|
|
199
|
+
title: 'Query',
|
|
200
|
+
toolDescription: 'Search query'
|
|
187
201
|
} satisfies InputSchemaMetaType)
|
|
188
202
|
}),
|
|
189
203
|
outputSchema: z.object({
|
|
@@ -216,7 +230,8 @@ export default defineToolSet({
|
|
|
216
230
|
description: {
|
|
217
231
|
en: 'Search public example data',
|
|
218
232
|
'zh-CN': '搜索公开示例数据'
|
|
219
|
-
}
|
|
233
|
+
},
|
|
234
|
+
permission: []
|
|
220
235
|
},
|
|
221
236
|
children: [
|
|
222
237
|
{
|
|
@@ -240,7 +255,9 @@ export default defineToolSet({
|
|
|
240
255
|
|
|
241
256
|
- `index.ts` 默认导出正确。
|
|
242
257
|
- `manifest` 字段完整,国际化字段包含 `en` 和 `zh-CN`。
|
|
243
|
-
-
|
|
258
|
+
- `permission` 只包含插件实际使用的权限,且权限值来自当前支持的枚举。
|
|
259
|
+
- 使用 `ctx.invoke` 时已在 `manifest.permission` 声明对应权限,例如上传文件声明 `file-upload:allow`。
|
|
260
|
+
- Zod schema 覆盖全部用户可见输入和输出;需要工具调用自动填参的输入字段包含 `toolDescription`。
|
|
244
261
|
- handler 成功路径返回值与 `outputSchema` 一致。
|
|
245
262
|
- 外部调用失败、空响应、超时、鉴权失败都有明确错误。
|
|
246
263
|
- 密钥配置只通过 `secretSchema` 和 `ctx.secrets` 处理。
|