ai-world-sdk 1.5.16 → 1.5.18
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/dist/cli/commands/help.js +3 -1
- package/dist/cli/commands/plugin.js +10 -4
- package/dist/config.d.ts +3 -3
- package/dist/config.js +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/plugin-management.d.ts +19 -6
- package/dist/plugin-management.js +4 -0
- package/package.json +1 -1
- package/skills/ai-world-sdk/SKILL.md +45 -260
- package/skills/ai-world-sdk/docs/cli-commands.md +2 -1
- package/skills/ai-world-sdk/docs/platform-api.md +12 -2
- package/skills/ai-world-sdk/docs/plugin-backend.md +250 -0
- package/skills/ai-world-sdk/docs/provider-and-models.md +26 -0
|
@@ -109,7 +109,7 @@ const COMMAND_DETAILS = {
|
|
|
109
109
|
},
|
|
110
110
|
plugin: {
|
|
111
111
|
desc: '插件管理 —— 部署、启用/禁用、重启、查看日志',
|
|
112
|
-
usage: 'ai-world plugin <list|get|ids|create|enable|disable|reload|restart|logs|status|delete>',
|
|
112
|
+
usage: 'ai-world plugin <list|get|ids|create|update|enable|disable|reload|restart|logs|status|delete>',
|
|
113
113
|
examples: [
|
|
114
114
|
'ai-world plugin list',
|
|
115
115
|
'ai-world plugin ids --enabled-only',
|
|
@@ -117,6 +117,8 @@ const COMMAND_DETAILS = {
|
|
|
117
117
|
'ai-world plugin enable my-plugin',
|
|
118
118
|
'ai-world plugin logs my-plugin --lines 50',
|
|
119
119
|
'ai-world plugin create ./dist.zip --plugin-id my-plugin --name "我的插件"',
|
|
120
|
+
'ai-world plugin create --plugin-id py-plugin --server-zip server.zip --backend-type python',
|
|
121
|
+
'ai-world plugin update --plugin-id my-plugin --server-zip server.zip --backend-type node',
|
|
120
122
|
],
|
|
121
123
|
},
|
|
122
124
|
admin: {
|
|
@@ -191,8 +191,9 @@ function registerPluginCommands(program) {
|
|
|
191
191
|
.option('--description <text>', '插件描述')
|
|
192
192
|
.option('--author <author>', '插件作者')
|
|
193
193
|
.option('--version <ver>', '插件版本')
|
|
194
|
-
.option('--server-zip <path>', '
|
|
195
|
-
.option('--backend-
|
|
194
|
+
.option('--server-zip <path>', '后端代码 zip 包路径')
|
|
195
|
+
.option('--backend-type <type>', '后端类型: node 或 python(默认 node)')
|
|
196
|
+
.option('--backend-entry <entry>', '后端入口文件(Node.js 默认 index.js,Python 默认 start.py)')
|
|
196
197
|
.option('--backend-websocket', '启用 WebSocket 代理')
|
|
197
198
|
.option('--disable-sdk-update-notice', '禁用 SDK 更新提示')
|
|
198
199
|
.action(async (zipFile, options, cmd) => {
|
|
@@ -206,6 +207,7 @@ function registerPluginCommands(program) {
|
|
|
206
207
|
});
|
|
207
208
|
(0, utils_1.initSDK)(auth);
|
|
208
209
|
const client = new plugin_management_1.PluginManagementClient();
|
|
210
|
+
const bt = options.backendType;
|
|
209
211
|
const res = await client.create({
|
|
210
212
|
pluginId: options.pluginId,
|
|
211
213
|
name: options.name,
|
|
@@ -214,6 +216,7 @@ function registerPluginCommands(program) {
|
|
|
214
216
|
version: options.version,
|
|
215
217
|
staticZip: zipFile ? (0, utils_1.readFileAsFile)(zipFile) : undefined,
|
|
216
218
|
serverZip: options.serverZip ? (0, utils_1.readFileAsFile)(options.serverZip) : undefined,
|
|
219
|
+
backendType: bt === 'node' || bt === 'python' ? bt : undefined,
|
|
217
220
|
backendEntry: options.backendEntry,
|
|
218
221
|
backendWebsocket: options.backendWebsocket === true ? true : undefined,
|
|
219
222
|
disableSdkUpdateNotice: options.disableSdkUpdateNotice === true ? true : undefined,
|
|
@@ -232,8 +235,9 @@ function registerPluginCommands(program) {
|
|
|
232
235
|
.option('--description <text>', '插件描述')
|
|
233
236
|
.option('--author <author>', '插件作者')
|
|
234
237
|
.option('--version <ver>', '插件版本')
|
|
235
|
-
.option('--server-zip <path>', '
|
|
236
|
-
.option('--backend-
|
|
238
|
+
.option('--server-zip <path>', '后端代码 zip 包路径')
|
|
239
|
+
.option('--backend-type <type>', '后端类型: node 或 python(默认 node)')
|
|
240
|
+
.option('--backend-entry <entry>', '后端入口文件(Node.js 默认 index.js,Python 默认 start.py)')
|
|
237
241
|
.option('--backend-websocket', '启用 WebSocket 代理')
|
|
238
242
|
.option('--disable-sdk-update-notice', '禁用 SDK 更新提示')
|
|
239
243
|
.action(async (zipFile, options, cmd) => {
|
|
@@ -247,6 +251,7 @@ function registerPluginCommands(program) {
|
|
|
247
251
|
});
|
|
248
252
|
(0, utils_1.initSDK)(auth);
|
|
249
253
|
const client = new plugin_management_1.PluginManagementClient();
|
|
254
|
+
const bt = options.backendType;
|
|
250
255
|
const res = await client.update({
|
|
251
256
|
pluginId: options.pluginId,
|
|
252
257
|
name: options.name,
|
|
@@ -255,6 +260,7 @@ function registerPluginCommands(program) {
|
|
|
255
260
|
version: options.version,
|
|
256
261
|
staticZip: zipFile ? (0, utils_1.readFileAsFile)(zipFile) : undefined,
|
|
257
262
|
serverZip: options.serverZip ? (0, utils_1.readFileAsFile)(options.serverZip) : undefined,
|
|
263
|
+
backendType: bt === 'node' || bt === 'python' ? bt : undefined,
|
|
258
264
|
backendEntry: options.backendEntry,
|
|
259
265
|
backendWebsocket: options.backendWebsocket === true ? true : undefined,
|
|
260
266
|
disableSdkUpdateNotice: options.disableSdkUpdateNotice === true ? true : undefined,
|
package/dist/config.d.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*
|
|
10
10
|
* 注意: {VERSION} 占位符会在构建时被替换为实际版本号
|
|
11
11
|
*/
|
|
12
|
-
export declare const SDK_SIGNATURE = "AI_WORLD_SDK_V:1.5.
|
|
12
|
+
export declare const SDK_SIGNATURE = "AI_WORLD_SDK_V:1.5.18";
|
|
13
13
|
/**
|
|
14
14
|
* 版本兼容性错误
|
|
15
15
|
*/
|
|
@@ -35,8 +35,8 @@ declare class SDKConfig {
|
|
|
35
35
|
private _authCheckPromise;
|
|
36
36
|
private _currentUser;
|
|
37
37
|
private _cliMode;
|
|
38
|
-
readonly sdkSignature = "AI_WORLD_SDK_V:1.5.
|
|
39
|
-
readonly sdkVersion = "1.5.
|
|
38
|
+
readonly sdkSignature = "AI_WORLD_SDK_V:1.5.18";
|
|
39
|
+
readonly sdkVersion = "1.5.18";
|
|
40
40
|
constructor();
|
|
41
41
|
/**
|
|
42
42
|
* Set global base URL
|
package/dist/config.js
CHANGED
|
@@ -7,7 +7,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
7
7
|
exports.sdkConfig = exports.VersionCompatibilityError = exports.SDK_SIGNATURE = void 0;
|
|
8
8
|
// SDK 版本号(构建时自动从 package.json 更新)
|
|
9
9
|
// 此版本号会在运行 npm run build 时自动从 package.json 读取并更新
|
|
10
|
-
const SDK_VERSION = "1.5.
|
|
10
|
+
const SDK_VERSION = "1.5.18";
|
|
11
11
|
/**
|
|
12
12
|
* SDK 特征码 - 用于在构建后的 JS 文件中识别 SDK 版本
|
|
13
13
|
* 格式: AI_WORLD_SDK_V:版本号
|
|
@@ -15,7 +15,7 @@ const SDK_VERSION = "1.5.16";
|
|
|
15
15
|
*
|
|
16
16
|
* 注意: {VERSION} 占位符会在构建时被替换为实际版本号
|
|
17
17
|
*/
|
|
18
|
-
exports.SDK_SIGNATURE = "AI_WORLD_SDK_V:1.5.
|
|
18
|
+
exports.SDK_SIGNATURE = "AI_WORLD_SDK_V:1.5.18";
|
|
19
19
|
/**
|
|
20
20
|
* 版本兼容性错误
|
|
21
21
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -34,7 +34,7 @@ export { VersionedResourceClient, type VersionedResourceClientConfig, type Versi
|
|
|
34
34
|
export { AgentSkillClient, type AgentSkillClientConfig, type AgentSkillInfo, type AgentSkillListResponse, type ListSkillsOptions, } from "./agent-skills";
|
|
35
35
|
export { AuthClient, getCurrentUserInfo, type AuthConfig, type UserInfo, };
|
|
36
36
|
export { AdminClient, type AdminClientConfig, type AdminUserInfo, type AdminUserListResponse, type ListUsersOptions, type RoleInfo, type CreateRoleData, type UpdateRoleData, type RoleUserCount, type TableColumn, type TableDataResponse, type ListTableDataOptions, } from "./admin";
|
|
37
|
-
export { PluginManagementClient, type PluginManagementClientConfig, type PluginInfo, type PluginListResponse, type ListPluginsOptions, type CreatePluginData, type PluginStatus, type PluginTemplateInfo, type PluginTemplateListResponse, type ListPluginTemplatesOptions, type CreatePluginTemplateData, type UpdatePluginTemplateData, } from "./plugin-management";
|
|
37
|
+
export { PluginManagementClient, type PluginManagementClientConfig, type PluginInfo, type PluginListResponse, type ListPluginsOptions, type CreatePluginData, type UpdatePluginData, type PluginStatus, type RestartServerResult, type PluginTemplateInfo, type PluginTemplateListResponse, type ListPluginTemplatesOptions, type CreatePluginTemplateData, type UpdatePluginTemplateData, } from "./plugin-management";
|
|
38
38
|
export { StatsClient, DashboardClient, type StatsClientConfig, type AiApiCallsOptions, type AiApiCallsTrendOptions, type AiApiCallsTrendByUserOptions, type PluginAccessOptions, } from "./stats";
|
|
39
39
|
export { AIConfigClient, type AIConfigClientConfig, } from "./ai-config";
|
|
40
40
|
export { SharedResourceClient, type SharedResourceClientConfig, type SharedResourceInfo, type SharedResourceListResponse, type ListSharedResourceOptions, type UpdateSharedResourceData, } from "./shared-resource";
|
|
@@ -14,7 +14,13 @@ export interface PluginInfo {
|
|
|
14
14
|
author?: string;
|
|
15
15
|
version?: string;
|
|
16
16
|
enabled: boolean;
|
|
17
|
-
|
|
17
|
+
has_backend?: boolean;
|
|
18
|
+
backend_type?: "node" | "python";
|
|
19
|
+
backend_port?: number;
|
|
20
|
+
backend_running?: boolean;
|
|
21
|
+
backend_entry?: string;
|
|
22
|
+
backend_websocket?: boolean;
|
|
23
|
+
has_static_file?: boolean;
|
|
18
24
|
created_at?: string;
|
|
19
25
|
updated_at?: string;
|
|
20
26
|
[key: string]: any;
|
|
@@ -45,6 +51,7 @@ export interface CreatePluginData {
|
|
|
45
51
|
templateId?: string;
|
|
46
52
|
staticZip?: File | Blob;
|
|
47
53
|
serverZip?: File | Blob;
|
|
54
|
+
backendType?: "node" | "python";
|
|
48
55
|
backendEntry?: string;
|
|
49
56
|
backendWebsocket?: boolean;
|
|
50
57
|
disableSdkUpdateNotice?: boolean;
|
|
@@ -57,6 +64,7 @@ export interface UpdatePluginData {
|
|
|
57
64
|
version?: string;
|
|
58
65
|
staticZip?: File | Blob;
|
|
59
66
|
serverZip?: File | Blob;
|
|
67
|
+
backendType?: "node" | "python";
|
|
60
68
|
backendEntry?: string;
|
|
61
69
|
backendWebsocket?: boolean;
|
|
62
70
|
disableSdkUpdateNotice?: boolean;
|
|
@@ -64,8 +72,7 @@ export interface UpdatePluginData {
|
|
|
64
72
|
export interface PluginStatus {
|
|
65
73
|
plugin_id: string;
|
|
66
74
|
enabled: boolean;
|
|
67
|
-
|
|
68
|
-
server_running?: boolean;
|
|
75
|
+
exists: boolean;
|
|
69
76
|
[key: string]: any;
|
|
70
77
|
}
|
|
71
78
|
export interface PluginTemplateInfo {
|
|
@@ -92,6 +99,14 @@ export interface ListPluginTemplatesOptions {
|
|
|
92
99
|
name?: string;
|
|
93
100
|
category?: string;
|
|
94
101
|
}
|
|
102
|
+
export interface RestartServerResult {
|
|
103
|
+
message: string;
|
|
104
|
+
plugin_id: string;
|
|
105
|
+
port: number;
|
|
106
|
+
backend_type: string;
|
|
107
|
+
install_logs: string;
|
|
108
|
+
startup_logs: string;
|
|
109
|
+
}
|
|
95
110
|
export interface CreatePluginTemplateData {
|
|
96
111
|
pluginId: string;
|
|
97
112
|
name: string;
|
|
@@ -135,9 +150,7 @@ export declare class PluginManagementClient {
|
|
|
135
150
|
reload(pluginId: string): Promise<{
|
|
136
151
|
message: string;
|
|
137
152
|
}>;
|
|
138
|
-
restartServer(pluginId: string): Promise<
|
|
139
|
-
message: string;
|
|
140
|
-
}>;
|
|
153
|
+
restartServer(pluginId: string): Promise<RestartServerResult>;
|
|
141
154
|
getServerLogs(pluginId: string, lines?: number): Promise<string>;
|
|
142
155
|
listTemplates(): Promise<any[]>;
|
|
143
156
|
getTemplate(templateId: string): Promise<any>;
|
|
@@ -118,6 +118,8 @@ class PluginManagementClient {
|
|
|
118
118
|
formData.append("static_zip", data.staticZip);
|
|
119
119
|
if (data.serverZip)
|
|
120
120
|
formData.append("server_zip", data.serverZip);
|
|
121
|
+
if (data.backendType)
|
|
122
|
+
formData.append("backend_type", data.backendType);
|
|
121
123
|
if (data.backendEntry)
|
|
122
124
|
formData.append("backend_entry", data.backendEntry);
|
|
123
125
|
if (data.backendWebsocket !== undefined)
|
|
@@ -153,6 +155,8 @@ class PluginManagementClient {
|
|
|
153
155
|
formData.append("static_zip", data.staticZip);
|
|
154
156
|
if (data.serverZip)
|
|
155
157
|
formData.append("server_zip", data.serverZip);
|
|
158
|
+
if (data.backendType)
|
|
159
|
+
formData.append("backend_type", data.backendType);
|
|
156
160
|
if (data.backendEntry)
|
|
157
161
|
formData.append("backend_entry", data.backendEntry);
|
|
158
162
|
if (data.backendWebsocket !== undefined)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ai-world-sdk
|
|
3
|
-
description: Use when initializing ai-world-sdk, calling AI models in ai-world projects, or using the ai-world CLI. Covers sdkConfig initialization, createProvider with Vercel AI SDK (generateText, streamText, generateImage), Doubao image/video generation/understanding, MinIO object storage (MinioStorageClient), resource management with ACL (ResourceClient), versioned resource management (VersionedResourceClient), agent skills management (AgentSkillClient), VSCode extension login (initAIWorld), user auth (AuthClient, getCurrentUserInfo), download proxy (DownloadClient), admin management (AdminClient for user/role/database), plugin management (PluginManagementClient), AI config listing (AIConfigClient, getProviders, getProviderModels), statistics (StatsClient, DashboardClient), shared resources (SharedResourceClient), chrome extensions (ChromeExtensionClient), and CLI command-line tool (ai-world). Triggers on ai-world-sdk, ai-world, cli, 命令行, npx ai-world, command line, createProvider, sdkConfig, generateText, streamText, generateImage, doubao, video understanding, VideoUnderstandingClient, MinioStorageClient, minio, ResourceClient, resource management, access control, file sharing, VersionedResourceClient, versioned resource, version management, AgentSkillClient, agent skill, vscode extension, initAIWorld, AuthClient, getCurrentUserInfo, DownloadClient, AdminClient, admin API, user management, role management, database management, PluginManagementClient, plugin management, StatsClient, DashboardClient, statistics, SharedResourceClient, shared resources, ChromeExtensionClient, chrome extension, or any AI model integration.
|
|
3
|
+
description: Use when initializing ai-world-sdk, calling AI models in ai-world projects, or using the ai-world CLI. Covers sdkConfig initialization, createProvider with Vercel AI SDK (generateText, streamText, generateImage), Doubao image/video generation/understanding, MinIO object storage (MinioStorageClient), resource management with ACL (ResourceClient), versioned resource management (VersionedResourceClient), agent skills management (AgentSkillClient), VSCode extension login (initAIWorld), user auth (AuthClient, getCurrentUserInfo), download proxy (DownloadClient), admin management (AdminClient for user/role/database), plugin management (PluginManagementClient), plugin backend configuration (Python venv, Node.js, backend lifecycle), AI config listing (AIConfigClient, getProviders, getProviderModels), statistics (StatsClient, DashboardClient), shared resources (SharedResourceClient), chrome extensions (ChromeExtensionClient), and CLI command-line tool (ai-world). Triggers on ai-world-sdk, ai-world, cli, 命令行, npx ai-world, command line, createProvider, sdkConfig, generateText, streamText, generateImage, doubao, video understanding, VideoUnderstandingClient, MinioStorageClient, minio, ResourceClient, resource management, access control, file sharing, VersionedResourceClient, versioned resource, version management, AgentSkillClient, agent skill, vscode extension, initAIWorld, AuthClient, getCurrentUserInfo, DownloadClient, AdminClient, admin API, user management, role management, database management, PluginManagementClient, plugin management, plugin backend, backend configuration, venv, python backend, node backend, StatsClient, DashboardClient, statistics, SharedResourceClient, shared resources, ChromeExtensionClient, chrome extension, or any AI model integration.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# AI World SDK
|
|
@@ -13,16 +13,11 @@ ai-world-sdk 是 AI World 平台的 TypeScript SDK,通过 `createProvider` 创
|
|
|
13
13
|
## When to Use
|
|
14
14
|
|
|
15
15
|
- 在 ai-world 插件中调用 AI 模型(文本、图像、视频生成/理解)
|
|
16
|
-
-
|
|
17
|
-
- 使用
|
|
18
|
-
-
|
|
19
|
-
- 使用
|
|
20
|
-
-
|
|
21
|
-
- 使用版本化资源管理(资源名称+版本号管理)
|
|
22
|
-
- 管理 Agent Skills(上传/下载/删除/列表)
|
|
23
|
-
- **开发 VSCode 扩展**时集成 AI World 登录和 SDK 功能
|
|
24
|
-
- **使用命令行(CLI)调用 AI World API**
|
|
25
|
-
- **AI Agent 场景**下需要 CLI 工具调用平台功能
|
|
16
|
+
- 初始化 ai-world-sdk 配置(浏览器/Vite/VSCode/脚本/插件后端)
|
|
17
|
+
- 使用 MinIO 存储、资源管理、版本化资源、Agent Skills
|
|
18
|
+
- 开发 VSCode 扩展集成 AI World
|
|
19
|
+
- 使用 CLI 命令行调用平台功能
|
|
20
|
+
- 配置插件后端(Node.js / Python)
|
|
26
21
|
|
|
27
22
|
## 安装
|
|
28
23
|
|
|
@@ -34,233 +29,65 @@ npm install ai-world-sdk ai
|
|
|
34
29
|
|
|
35
30
|
### 浏览器环境(ai-world 插件内)
|
|
36
31
|
|
|
37
|
-
|
|
38
|
-
- `baseUrl` — 自动从 `window.location.origin`
|
|
39
|
-
- `token` — 自动从 cookie `auth_token`
|
|
40
|
-
- `pluginId` — 自动从 `window.__INITIAL_DATA__.plugin_id`
|
|
32
|
+
**自动初始化**,无需手动配置(baseUrl / token / pluginId 均自动获取)。
|
|
41
33
|
|
|
42
|
-
### Vite
|
|
43
|
-
|
|
44
|
-
在 `vite.config.ts` 中添加 `aiWorldPlugin()`,dev server 启动时自动完成以下所有配置:
|
|
45
|
-
|
|
46
|
-
1. 检测 `.env.local` 中的 `DEBUG_TOKEN`,缺失或过期时通过后端 `/api/auth/login` 自动打开浏览器完成飞书登录
|
|
47
|
-
2. 登录后将 `DEBUG=true`、`DEBUG_TOKEN`、`PLUGIN_ID` 写入 `.env.local`
|
|
48
|
-
3. `PLUGIN_ID` 按优先级:`plugin.json` 的 `plugin_id` → `package.json` 的 `name` → 项目目录名
|
|
49
|
-
4. 通过 `window.__AI_WORLD_CONFIG__` 将配置注入到浏览器页面,SDK 自动读取
|
|
34
|
+
### Vite 开发模式
|
|
50
35
|
|
|
51
36
|
```typescript
|
|
52
37
|
import { aiWorldPlugin } from 'ai-world-sdk/vite'
|
|
53
38
|
|
|
54
39
|
export default defineConfig({
|
|
55
|
-
plugins: [
|
|
56
|
-
vue(), // 或 react() 等
|
|
57
|
-
aiWorldPlugin() // baseUrl 默认 https://aiworld.local:8000
|
|
58
|
-
]
|
|
40
|
+
plugins: [vue(), aiWorldPlugin()] // baseUrl 默认 https://aiworld.local:8000
|
|
59
41
|
})
|
|
60
42
|
```
|
|
61
43
|
|
|
62
|
-
|
|
44
|
+
自动完成:token 获取/刷新、PLUGIN_ID 推断、浏览器注入。详见 → `docs/vscode-login.md`
|
|
63
45
|
|
|
64
|
-
|
|
46
|
+
### VSCode 扩展
|
|
65
47
|
|
|
66
48
|
```typescript
|
|
67
|
-
aiWorldPlugin({ baseUrl: 'https://your-server:8000' })
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
### VSCode 扩展(推荐:initAIWorld 一步到位)
|
|
71
|
-
|
|
72
|
-
在 VSCode 扩展中使用 `ai-world-sdk/vscode`,一行代码完成所有配置:
|
|
73
|
-
|
|
74
|
-
```typescript
|
|
75
|
-
import * as vscode from 'vscode';
|
|
76
49
|
import { initAIWorld } from 'ai-world-sdk/vscode';
|
|
77
|
-
|
|
78
|
-
import { generateText } from 'ai';
|
|
79
|
-
|
|
80
|
-
export async function activate(context: vscode.ExtensionContext) {
|
|
81
|
-
const ai = await initAIWorld(context, {
|
|
82
|
-
pluginId: 'my-extension', // 可选,默认从 package.json name 推断
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
// sdkConfig 已自动配置,直接使用 SDK 功能
|
|
86
|
-
if (ai.isAuthenticated) {
|
|
87
|
-
const provider = createProvider('api2img', 'openai', ai.pluginId);
|
|
88
|
-
const model = provider.languageModel('gpt-4o-mini');
|
|
89
|
-
const result = await generateText({ model, prompt: '你好' });
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
`initAIWorld` 自动完成:登录认证、sdkConfig 配置、命令注册、状态栏、缓存持久化到 `vscode.SecretStorage`。pluginId 默认取 `package.json` 的 `name`。
|
|
95
|
-
|
|
96
|
-
扩展 `package.json` 需声明 SDK 自动注册的命令:
|
|
97
|
-
|
|
98
|
-
```json
|
|
99
|
-
{
|
|
100
|
-
"contributes": {
|
|
101
|
-
"commands": [
|
|
102
|
-
{ "command": "aiWorld.login", "title": "AI World: 登录" },
|
|
103
|
-
{ "command": "aiWorld.logout", "title": "AI World: 退出登录" },
|
|
104
|
-
{ "command": "aiWorld.showLoginPanel", "title": "AI World: 登录面板" }
|
|
105
|
-
]
|
|
106
|
-
}
|
|
107
|
-
}
|
|
50
|
+
const ai = await initAIWorld(context, { pluginId: 'my-extension' });
|
|
108
51
|
```
|
|
109
52
|
|
|
110
|
-
|
|
53
|
+
自动完成:登录认证、sdkConfig、命令注册、状态栏、SecretStorage 缓存。详见 → `docs/vscode-login.md`
|
|
111
54
|
|
|
112
55
|
### 非浏览器环境(测试、脚本)
|
|
113
56
|
|
|
114
57
|
```typescript
|
|
115
58
|
import { sdkConfig } from 'ai-world-sdk';
|
|
116
|
-
|
|
117
59
|
sdkConfig.setBaseUrl('https://aiworld.local:8000');
|
|
118
60
|
sdkConfig.setToken('your-jwt-token');
|
|
119
61
|
sdkConfig.setPluginId('my-plugin');
|
|
120
62
|
```
|
|
121
63
|
|
|
122
|
-
|
|
123
|
-
- `AI_WORLD_BASE_URL` — 服务端地址,插件 JS 后端启动时由平台自动注入
|
|
124
|
-
- `PLUGIN_ID` — 插件 ID
|
|
125
|
-
- `DEBUG_TOKEN` — 调试用 JWT Token
|
|
126
|
-
- `AI_WORLD_TOKEN` — 用户登录后获取的 JWT Token
|
|
127
|
-
- `AI_WORLD_ENV_FILE` — 指定自定义 `.env` 文件路径(绝对或相对路径均可)
|
|
128
|
-
|
|
129
|
-
### 插件 JS 后端环境(平台自动注入)
|
|
130
|
-
|
|
131
|
-
当插件配置了 Node.js 后端(`plugin.json` 的 `backend` 字段),平台启动子进程时会自动注入以下环境变量:
|
|
132
|
-
|
|
133
|
-
| 环境变量 | 说明 |
|
|
134
|
-
|----------|------|
|
|
135
|
-
| `PORT` | **插件后端必须监听的端口**(平台自动分配,范围 19000~19999) |
|
|
136
|
-
| `SERVER_PORT` | 同 `PORT`,兼容别名 |
|
|
137
|
-
| `PLUGIN_ID` | 当前插件 ID |
|
|
138
|
-
| `AI_WORLD_BASE_URL` | 平台后端地址(默认 `http://127.0.0.1:8000`) |
|
|
139
|
-
| `AI_WORLD_ENV_FILE` | 平台写入的 `.env` 文件路径(`server/.env`),SDK 自动加载 |
|
|
140
|
-
| `NODE_ENV` | 固定为 `production` |
|
|
64
|
+
环境变量自动初始化:`AI_WORLD_BASE_URL` / `PLUGIN_ID` / `DEBUG_TOKEN` / `AI_WORLD_TOKEN` / `AI_WORLD_ENV_FILE`
|
|
141
65
|
|
|
142
|
-
|
|
66
|
+
### 插件后端环境
|
|
143
67
|
|
|
144
|
-
|
|
145
|
-
// server/index.js 示例
|
|
146
|
-
const express = require('express');
|
|
147
|
-
const app = express();
|
|
148
|
-
const port = process.env.PORT || 3000;
|
|
149
|
-
|
|
150
|
-
app.get('/health', (req, res) => res.json({ status: 'ok' }));
|
|
151
|
-
app.get('/api/hello', (req, res) => res.json({ message: 'Hello from plugin backend!' }));
|
|
152
|
-
|
|
153
|
-
app.listen(port, () => {
|
|
154
|
-
console.log(`Plugin backend listening on port ${port}`);
|
|
155
|
-
});
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
### 插件前端请求插件后端(HTTP 代理)
|
|
159
|
-
|
|
160
|
-
平台为每个 JS 后端插件注册了 HTTP 反向代理路由,**插件前端不需要知道后端实际端口**,统一通过以下地址访问:
|
|
161
|
-
|
|
162
|
-
```
|
|
163
|
-
{AI_WORLD_BASE_URL}/api/{plugin-id}/server/{path}
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
例如插件 ID 为 `my-plugin`,后端有 `/api/hello` 接口,前端请求地址为:
|
|
167
|
-
|
|
168
|
-
```
|
|
169
|
-
https://aiworld.local:8000/api/my-plugin/server/api/hello
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
在插件前端代码中使用 `sdkConfig` 获取 baseUrl:
|
|
173
|
-
|
|
174
|
-
```typescript
|
|
175
|
-
import { sdkConfig } from 'ai-world-sdk';
|
|
68
|
+
平台启动后端子进程时自动注入 `PORT`、`PLUGIN_ID`、`AI_WORLD_BASE_URL` 等环境变量,SDK 自动读取,无需手动配置。
|
|
176
69
|
|
|
177
|
-
|
|
178
|
-
const baseUrl = sdkConfig.getServerUrl();
|
|
179
|
-
const pluginId = sdkConfig.getPluginId();
|
|
180
|
-
const response = await fetch(`${baseUrl}/api/${pluginId}/server/api/hello`, {
|
|
181
|
-
headers: { 'Authorization': `Bearer ${sdkConfig.getToken()}` }
|
|
182
|
-
});
|
|
183
|
-
```
|
|
70
|
+
**插件后端必须监听 `PORT` 环境变量指定的端口。**
|
|
184
71
|
|
|
185
|
-
|
|
72
|
+
详见 → `docs/plugin-backend.md`
|
|
186
73
|
|
|
187
74
|
## 功能模块
|
|
188
75
|
|
|
189
|
-
以下功能的详细用法和代码示例请查阅对应的子文档。
|
|
190
|
-
|
|
191
76
|
### Provider 与模型 → `docs/provider-and-models.md`
|
|
192
77
|
|
|
193
78
|
- `createProvider(provider, endpointType, pluginId)` 创建 Vercel AI SDK Provider
|
|
194
|
-
- Provider
|
|
195
|
-
-
|
|
196
|
-
- **OpenRouter Reasoning**:通过 `providerOptions.openrouter.reasoning` 启用模型思考,支持 `effort`(`xhigh`/`high`/`medium`/`low`/`minimal`)和 `max_tokens` 参数,适用于 Claude / DeepSeek-R1 / o1 / o3 / Gemini 等支持思考的模型
|
|
197
|
-
- **KUNPO API**:统一 LLM 网关(https://llm.ziy.cc),兼容 OpenAI 接口,使用专用代理端点 `/api/llm/kunpo`,一个 Key 访问 150+ 模型
|
|
198
|
-
- 常用模型列表和 AI SDK 函数速查
|
|
199
|
-
|
|
200
|
-
#### OpenRouter 快速示例
|
|
201
|
-
|
|
202
|
-
```typescript
|
|
203
|
-
import { createProvider } from 'ai-world-sdk';
|
|
204
|
-
import { generateText, streamText } from 'ai';
|
|
205
|
-
|
|
206
|
-
// OpenRouter(endpointType 传 'openai',SDK 内部自动使用 createOpenRouter)
|
|
207
|
-
const provider = createProvider('openrouter', 'openai', 'my-plugin');
|
|
208
|
-
|
|
209
|
-
// 非流式
|
|
210
|
-
const result = await generateText({
|
|
211
|
-
model: provider.languageModel('openai/gpt-4o-mini'),
|
|
212
|
-
prompt: '你好',
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
// 流式
|
|
216
|
-
const stream = streamText({
|
|
217
|
-
model: provider.languageModel('anthropic/claude-sonnet-4-20250514'),
|
|
218
|
-
prompt: '请介绍人工智能',
|
|
219
|
-
});
|
|
220
|
-
for await (const chunk of stream.textStream) {
|
|
221
|
-
process.stdout.write(chunk);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// 启用思考/推理(OpenRouter / KUNPO 均支持)
|
|
225
|
-
const thinkResult = await generateText({
|
|
226
|
-
model: provider.languageModel('anthropic/claude-sonnet-4-20250514'),
|
|
227
|
-
prompt: '证明根号2是无理数',
|
|
228
|
-
providerOptions: {
|
|
229
|
-
openrouter: {
|
|
230
|
-
reasoning: {
|
|
231
|
-
effort: 'high', // 思考深度: xhigh / high / medium / low / minimal
|
|
232
|
-
// max_tokens: 10000, // 可选:限制思考 token 数量
|
|
233
|
-
},
|
|
234
|
-
},
|
|
235
|
-
},
|
|
236
|
-
});
|
|
237
|
-
console.log('思考过程:', thinkResult.reasoning);
|
|
238
|
-
console.log('最终回答:', thinkResult.text);
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
#### KUNPO API 快速示例
|
|
79
|
+
- Provider:`api2img`(推荐) / `gemini` / `shubiaobiao` / `aiping` / `openrouter` / `kunpo`
|
|
80
|
+
- OpenRouter / KUNPO:模型 ID 格式 `provider/model`,支持 reasoning
|
|
242
81
|
|
|
243
82
|
```typescript
|
|
244
83
|
import { createProvider } from 'ai-world-sdk';
|
|
245
|
-
import { generateText
|
|
84
|
+
import { generateText } from 'ai';
|
|
246
85
|
|
|
247
|
-
// KUNPO API(endpointType 传 'openai',兼容 OpenAI 接口)
|
|
248
86
|
const provider = createProvider('kunpo', 'openai', 'my-plugin');
|
|
249
|
-
|
|
250
|
-
// 非流式
|
|
251
87
|
const result = await generateText({
|
|
252
|
-
model: provider.languageModel('
|
|
88
|
+
model: provider.languageModel('deepseek/deepseek-v4-pro'),
|
|
253
89
|
prompt: '你好',
|
|
254
90
|
});
|
|
255
|
-
|
|
256
|
-
// 流式
|
|
257
|
-
const stream = streamText({
|
|
258
|
-
model: provider.languageModel('anthropic/claude-haiku-4.5'),
|
|
259
|
-
prompt: '请介绍人工智能',
|
|
260
|
-
});
|
|
261
|
-
for await (const chunk of stream.textStream) {
|
|
262
|
-
process.stdout.write(chunk);
|
|
263
|
-
}
|
|
264
91
|
```
|
|
265
92
|
|
|
266
93
|
### 文本生成 → `docs/text-generation.md`
|
|
@@ -276,7 +103,7 @@ for await (const chunk of stream.textStream) {
|
|
|
276
103
|
### 视频生成 & 视频理解 → `docs/video-generation.md`
|
|
277
104
|
|
|
278
105
|
- `VideoGenerationClient`(豆包 Seedance)
|
|
279
|
-
- `VideoUnderstandingClient
|
|
106
|
+
- `VideoUnderstandingClient`(豆包视频理解)
|
|
280
107
|
- `OpenAIVideoGenerationClient`(Sora)
|
|
281
108
|
|
|
282
109
|
### MinIO 对象存储 → `docs/minio-storage.md`
|
|
@@ -286,96 +113,54 @@ for await (const chunk of stream.textStream) {
|
|
|
286
113
|
|
|
287
114
|
### 资源管理 → `docs/resource-management.md`
|
|
288
115
|
|
|
289
|
-
- `ResourceClient` — 带 ACL
|
|
290
|
-
- 三级权限: `private` / `specific_users` / `public`
|
|
116
|
+
- `ResourceClient` — 带 ACL 的资源管理(private / specific_users / public)
|
|
291
117
|
- 插件隔离与跨插件访问
|
|
292
118
|
|
|
293
119
|
### 版本化资源 → `docs/versioned-resource.md`
|
|
294
120
|
|
|
295
|
-
- `VersionedResourceClient` —
|
|
296
|
-
- 资源名称为唯一 ID,支持语义化版本号
|
|
297
|
-
- 支持 `latest` 别名获取/下载最新版本
|
|
121
|
+
- `VersionedResourceClient` — 全局版本化资源(名称+语义化版本号)
|
|
298
122
|
|
|
299
123
|
### Agent Skills → `docs/agent-skills.md`
|
|
300
124
|
|
|
301
125
|
- `AgentSkillClient` — Skills 上传/下载/删除/列表
|
|
302
|
-
- Skill 名称全小写,zip 必须包含 SKILL.md
|
|
303
126
|
|
|
304
127
|
### 认证与下载 → `docs/auth-and-download.md`
|
|
305
128
|
|
|
306
|
-
- `AuthClient` / `getCurrentUserInfo()` —
|
|
307
|
-
- `DownloadClient` —
|
|
308
|
-
- HTTP 通用代理 `/api/http-proxy/{path}
|
|
129
|
+
- `AuthClient` / `getCurrentUserInfo()` — 用户信息
|
|
130
|
+
- `DownloadClient` — 代理下载
|
|
131
|
+
- HTTP 通用代理 `/api/http-proxy/{path}`
|
|
309
132
|
|
|
310
133
|
### VSCode 扩展登录 → `docs/vscode-login.md`
|
|
311
134
|
|
|
312
|
-
- `initAIWorld(context)`
|
|
135
|
+
- `initAIWorld(context)` 一步初始化
|
|
313
136
|
- `getAIWorld()` 获取实例
|
|
314
|
-
- 认证信息通过 `vscode.SecretStorage` 安全存储(token / user / baseUrl)
|
|
315
|
-
- `pluginId` 默认取扩展 `package.json` 的 `name` 字段
|
|
316
|
-
- 完整示例见 `examples/vscode-test-extension/`
|
|
317
137
|
|
|
318
138
|
### 管理员 API → `docs/admin-api.md`
|
|
319
139
|
|
|
320
|
-
- `AdminClient` —
|
|
321
|
-
- 用户: `listUsers` / `getUser` / `activateUser` / `deactivateUser` / `makeAdmin` / `removeAdmin` / `setUserRole` / `batchSetRole`
|
|
322
|
-
- 角色: `listRoles` / `getRole` / `createRole` / `updateRole` / `deleteRole` / `getRoleUserCounts`
|
|
323
|
-
- 数据库表: `listTables` / `getTableSchema` / `getTableData` / `insertRow` / `updateRow` / `deleteRow`
|
|
140
|
+
- `AdminClient` — 用户/角色/数据库表管理(需管理员权限)
|
|
324
141
|
|
|
325
142
|
### 平台通用 API → `docs/platform-api.md`
|
|
326
143
|
|
|
327
|
-
- `AIConfigClient` —
|
|
328
|
-
- `PluginManagementClient` — 插件 CRUD(`create` / `update`
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
- 两者均支持: pluginId / name / description / author / version / staticZip / serverZip / backendEntry / backendWebsocket / disableSdkUpdateNotice
|
|
332
|
-
- `StatsClient` — AI 接口调用统计/趋势、插件访问统计(管理员)
|
|
333
|
-
- `DashboardClient` — 当前用户统计(无需管理员权限)
|
|
334
|
-
- `SharedResourceClient` — 平台级文件共享(与 `ResourceClient` 的插件级 ACL 不同)
|
|
144
|
+
- `AIConfigClient` — AI 供应商与模型列表
|
|
145
|
+
- `PluginManagementClient` — 插件 CRUD(`create` / `update` 分离)、启用/禁用/重载/重启
|
|
146
|
+
- `StatsClient` / `DashboardClient` — 统计
|
|
147
|
+
- `SharedResourceClient` — 平台级文件共享
|
|
335
148
|
- `ChromeExtensionClient` — Chrome 插件管理
|
|
336
|
-
- `PluginTokenClient` — 插件 Token
|
|
337
|
-
|
|
338
|
-
### CLI 命令行工具 → `docs/cli-usage.md`
|
|
149
|
+
- `PluginTokenClient` — 插件 Token 管理
|
|
339
150
|
|
|
340
|
-
|
|
341
|
-
- 安装: `npm install -g ai-world-sdk` 或 `npx ai-world-sdk`
|
|
342
|
-
- 认证: `ai-world login` 或环境变量 `DEBUG_TOKEN`
|
|
343
|
-
- 输出: 默认 JSON(AI Agent 友好),支持 `--format table`
|
|
344
|
-
- 默认值: plugin-id=`cli`,provider=`kunpo`,endpoint=`openai`,model=`deepseek/deepseek-v4-pro`
|
|
345
|
-
- 版本检查: `ai-world version`(从后端读取最新版本)
|
|
346
|
-
- 帮助: `ai-world help` / `ai-world help <command>`(中文详细说明)
|
|
347
|
-
- 完整命令参考 → `docs/cli-commands.md`
|
|
348
|
-
|
|
349
|
-
#### 快速开始
|
|
350
|
-
|
|
351
|
-
```bash
|
|
352
|
-
# 安装
|
|
353
|
-
npm install -g ai-world-sdk
|
|
151
|
+
### 插件后端配置 → `docs/plugin-backend.md`
|
|
354
152
|
|
|
355
|
-
|
|
356
|
-
|
|
153
|
+
- Python 后端:自动创建 `.venv` 虚拟环境,隔离依赖
|
|
154
|
+
- Node.js 后端:使用服务器当前 Node.js 版本
|
|
155
|
+
- 更新时 `server/` 原有文件保留,相同路径覆盖替换
|
|
156
|
+
- 代理路由:`/api/{pluginId}/server/{path}` → `http://127.0.0.1:{port}/{path}`
|
|
357
157
|
|
|
358
|
-
|
|
359
|
-
ai-world config set provider=kunpo endpoint=openai model=deepseek/deepseek-v4-pro
|
|
360
|
-
|
|
361
|
-
# 文本生成
|
|
362
|
-
ai-world text generate --prompt "你好"
|
|
363
|
-
|
|
364
|
-
# 流式文本生成
|
|
365
|
-
ai-world text stream --prompt "请介绍人工智能"
|
|
366
|
-
|
|
367
|
-
# 查看插件列表
|
|
368
|
-
ai-world plugin list --format table
|
|
369
|
-
|
|
370
|
-
# 创建插件
|
|
371
|
-
ai-world plugin create ./dist.zip --plugin-id my-plugin --name "My Plugin" --version 1.0.0
|
|
372
|
-
|
|
373
|
-
# 更新插件(插件必须已存在,未传入的字段保留原值)
|
|
374
|
-
ai-world plugin update ./dist.zip --plugin-id my-plugin --version 1.1.0
|
|
158
|
+
### CLI 命令行工具 → `docs/cli-usage.md`
|
|
375
159
|
|
|
376
|
-
|
|
377
|
-
ai-world
|
|
378
|
-
|
|
160
|
+
- 安装: `npm install -g ai-world-sdk`
|
|
161
|
+
- 认证: `ai-world login`
|
|
162
|
+
- 默认值: provider=`kunpo`,model=`deepseek/deepseek-v4-pro`
|
|
163
|
+
- 完整命令参考 → `docs/cli-commands.md`
|
|
379
164
|
|
|
380
165
|
### 常见错误 → `docs/common-mistakes.md`
|
|
381
166
|
|
|
@@ -139,7 +139,8 @@ ai-world plugin disable <plugin-id>
|
|
|
139
139
|
ai-world plugin reload <plugin-id>
|
|
140
140
|
ai-world plugin restart <plugin-id>
|
|
141
141
|
ai-world plugin logs <plugin-id> [--lines 100]
|
|
142
|
-
ai-world plugin create ./plugin.zip --plugin-id my-plugin [--name "My Plugin"] [--description "说明"]
|
|
142
|
+
ai-world plugin create ./plugin.zip --plugin-id my-plugin [--name "My Plugin"] [--description "说明"] [--backend-type node|python] [--server-zip server.zip] [--backend-entry index.js]
|
|
143
|
+
ai-world plugin update [zip-file] --plugin-id <id> [--name "Name"] [--backend-type node|python] [--server-zip server.zip] [--backend-entry entry.js]
|
|
143
144
|
ai-world plugin delete <plugin-id>
|
|
144
145
|
ai-world plugin status <plugin-id>
|
|
145
146
|
```
|
|
@@ -19,13 +19,23 @@ const ids = await plugins.listIds(true); // string[]
|
|
|
19
19
|
const info = await plugins.get('my-plugin');
|
|
20
20
|
const status = await plugins.getStatus('my-plugin');
|
|
21
21
|
|
|
22
|
-
//
|
|
22
|
+
// 创建插件(Node.js 后端)
|
|
23
23
|
await plugins.create({
|
|
24
24
|
pluginId: 'new-plugin',
|
|
25
25
|
name: '新插件',
|
|
26
26
|
description: '描述',
|
|
27
27
|
staticZip: someZipFile, // 可选,含 index.html 的 zip
|
|
28
|
-
serverZip: serverZipFile, //
|
|
28
|
+
serverZip: serverZipFile, // 可选,后端代码 zip
|
|
29
|
+
backendType: 'node', // 可选,'node' | 'python',默认 'node'
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// 创建 Python 后端插件
|
|
33
|
+
await plugins.create({
|
|
34
|
+
pluginId: 'py-plugin',
|
|
35
|
+
name: 'Python 插件',
|
|
36
|
+
serverZip: pyZipFile,
|
|
37
|
+
backendType: 'python',
|
|
38
|
+
backendEntry: 'start.py',
|
|
29
39
|
});
|
|
30
40
|
|
|
31
41
|
// 启用 / 禁用 / 重载 / 重启后端
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
# 插件后端配置与使用
|
|
2
|
+
|
|
3
|
+
平台支持为插件配置独立的后端服务(Node.js 或 Python),由 `PluginManager` 自动管理子进程生命周期并注册反向代理。
|
|
4
|
+
|
|
5
|
+
## plugin.json backend 配置
|
|
6
|
+
|
|
7
|
+
```json
|
|
8
|
+
{
|
|
9
|
+
"plugin_id": "my-plugin",
|
|
10
|
+
"backend": {
|
|
11
|
+
"type": "node",
|
|
12
|
+
"entry": "index.js",
|
|
13
|
+
"install_cmd": "npm install",
|
|
14
|
+
"start_cmd": "node index.js",
|
|
15
|
+
"health_check": "/health",
|
|
16
|
+
"enable_websocket": false,
|
|
17
|
+
"env": {}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
| 字段 | 类型 | 默认值 | 说明 |
|
|
23
|
+
|------|------|--------|------|
|
|
24
|
+
| `type` | string | `"node"` | 后端类型:`"node"` 或 `"python"` |
|
|
25
|
+
| `entry` | string | Node: `"index.js"`, Python: `"start.py"` | 入口文件(相对于 `server/` 目录) |
|
|
26
|
+
| `install_cmd` | string | Node: `"npm install"`, Python: `"pip install -r requirements.txt"` | 依赖安装命令 |
|
|
27
|
+
| `start_cmd` | string | Node: `"node {entry}"`, Python: `".venv/bin/python {entry}"` | 启动命令(可选,覆盖默认) |
|
|
28
|
+
| `health_check` | string | `"/health"` | 健康检查路径 |
|
|
29
|
+
| `enable_websocket` | bool | `false` | 是否启用 WebSocket 代理 |
|
|
30
|
+
| `env` | object | `{}` | 额外环境变量(合并到默认环境变量中) |
|
|
31
|
+
|
|
32
|
+
## 运行环境
|
|
33
|
+
|
|
34
|
+
### Node.js 后端
|
|
35
|
+
|
|
36
|
+
- **运行时**:使用服务器当前安装的 Node.js 版本(即 `PATH` 中的 `node` 命令)
|
|
37
|
+
- **依赖安装**:当 `server/package.json` 存在且 `node_modules/` 不存在时,自动执行 `npm install`(或 `install_cmd` 指定的命令)
|
|
38
|
+
- **启动命令**:默认 `node {entry}`,可通过 `start_cmd` 覆盖
|
|
39
|
+
- **进程检测**:监控 `node`, `npm`, `npx`, `yarn`, `pnpm`, `bun` 进程
|
|
40
|
+
|
|
41
|
+
### Python 后端
|
|
42
|
+
|
|
43
|
+
- **运行时**:自动创建隔离的虚拟环境 `.venv`(通过 `python -m venv .venv`)
|
|
44
|
+
- **虚拟环境**:
|
|
45
|
+
- 首次启动时自动在 `server/` 目录下创建 `.venv`
|
|
46
|
+
- 如果 `.venv` 存在但不完整(缺少 `bin/python`),自动重建
|
|
47
|
+
- `.venv` 在插件更新时不会被删除,持久保留
|
|
48
|
+
- **依赖安装**:当 `server/requirements.txt` 存在时,使用 `.venv/bin/pip install -r requirements.txt` 安装依赖
|
|
49
|
+
- **启动命令**:默认 `.venv/bin/python {entry}`,可通过 `start_cmd` 覆盖
|
|
50
|
+
- **环境变量**:自动设置 `VIRTUAL_ENV=.venv`,并将 `.venv/bin` 加入 `PATH` 头部
|
|
51
|
+
- **进程检测**:监控 `python`, `python3`, `uvicorn`, `gunicorn`, `flask` 进程
|
|
52
|
+
|
|
53
|
+
## 平台自动注入的环境变量
|
|
54
|
+
|
|
55
|
+
| 环境变量 | 说明 | 适用 |
|
|
56
|
+
|----------|------|------|
|
|
57
|
+
| `PORT` | 插件后端必须监听的端口(平台自动分配,范围 19000~19999) | 全部 |
|
|
58
|
+
| `SERVER_PORT` | 同 `PORT`,兼容别名 | Node.js |
|
|
59
|
+
| `PLUGIN_ID` | 当前插件 ID | 全部 |
|
|
60
|
+
| `AI_WORLD_BASE_URL` | 平台后端地址(默认 `http://127.0.0.1:8000`) | 全部 |
|
|
61
|
+
| `AI_WORLD_ENV_FILE` | 空字符串(Node.js 后端不自动加载 .env) | Node.js |
|
|
62
|
+
| `NODE_ENV` | 固定为 `production` | Node.js |
|
|
63
|
+
| `PYTHONUNBUFFERED` | 固定为 `1`,确保日志实时输出 | Python |
|
|
64
|
+
| `VIRTUAL_ENV` | 固定为 `.venv` | Python |
|
|
65
|
+
| `PATH` | Python: `.venv/bin:$PATH`; Node.js: 系统 `$PATH` | 全部 |
|
|
66
|
+
| `HOME` | 系统用户目录 | 全部 |
|
|
67
|
+
| `LANG` | 系统语言设置 | 全部 |
|
|
68
|
+
|
|
69
|
+
可通过 `backend.env` 字段注入自定义环境变量,会合并到上述默认变量之上。
|
|
70
|
+
|
|
71
|
+
## 插件更新时的文件处理
|
|
72
|
+
|
|
73
|
+
### 通过 `POST /api/plugins/update` 更新
|
|
74
|
+
|
|
75
|
+
| 目录 | 行为 |
|
|
76
|
+
|------|------|
|
|
77
|
+
| `server/` | **原有文件保留**,zip 中相同路径的文件会覆盖替换,新文件会添加。仅删除 `node_modules/`(确保重新安装依赖) |
|
|
78
|
+
| `static/` | 如果上传了 `static_zip`,则**清空旧目录**后解压新文件;未上传则保留原有文件 |
|
|
79
|
+
| `.venv/` | **始终保留**,不会被删除(zip 中即使包含也会被忽略,不会解压覆盖) |
|
|
80
|
+
| `plugin.json` | 仅更新传入的字段,未传入的字段保留原值 |
|
|
81
|
+
|
|
82
|
+
### zip 解压时自动忽略的目录/文件
|
|
83
|
+
|
|
84
|
+
解压 `server_zip` 时,以下目录和文件会被自动过滤,不会被写入:
|
|
85
|
+
|
|
86
|
+
- **目录**:`node_modules/`, `__pycache__/`, `.git/`, `.venv/`, `venv/`, `.idea/`, `.vscode/`, `.tox/`, `.mypy_cache/`, `.pytest_cache/`, `.ruff_cache/`
|
|
87
|
+
- **文件**:`.DS_Store`, `Thumbs.db`, `desktop.ini`
|
|
88
|
+
|
|
89
|
+
### 更新后的自动行为
|
|
90
|
+
|
|
91
|
+
1. **卸载旧插件**:停止后端进程,释放端口
|
|
92
|
+
2. **文件操作**:按上述规则处理 `server/` 和 `static/` 目录
|
|
93
|
+
3. **重新加载**:读取 `plugin.json`,根据 `backend.type` 选择对应管理器
|
|
94
|
+
4. **安装依赖**:
|
|
95
|
+
- Node.js:由于 `node_modules/` 被删除,会重新执行 `npm install`
|
|
96
|
+
- Python:`.venv` 已保留,检查 `requirements.txt` 后执行 `pip install`(增量安装)
|
|
97
|
+
5. **启动进程**:执行启动命令,等待健康检查通过
|
|
98
|
+
6. **注册代理**:为新端口注册 HTTP/WebSocket 反向代理路由
|
|
99
|
+
|
|
100
|
+
## 目录结构
|
|
101
|
+
|
|
102
|
+
### Node.js 后端
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
plugins/{plugin_id}/
|
|
106
|
+
├── __init__.py # Python BasePlugin 壳子
|
|
107
|
+
├── plugin.json # 含 backend 配置 (type: "node")
|
|
108
|
+
├── plugin_state.json
|
|
109
|
+
├── static/ # 前端静态文件
|
|
110
|
+
│ └── index.html
|
|
111
|
+
└── server/ # Node.js 后端代码
|
|
112
|
+
├── package.json
|
|
113
|
+
├── node_modules/ # npm install 自动生成(更新时会被清除后重装)
|
|
114
|
+
└── index.js
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Python 后端
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
plugins/{plugin_id}/
|
|
121
|
+
├── __init__.py # Python BasePlugin 壳子
|
|
122
|
+
├── plugin.json # 含 backend 配置 (type: "python")
|
|
123
|
+
├── plugin_state.json
|
|
124
|
+
├── static/ # 前端静态文件
|
|
125
|
+
│ └── index.html
|
|
126
|
+
└── server/ # Python 后端代码
|
|
127
|
+
├── .venv/ # 自动创建的虚拟环境(更新时保留)
|
|
128
|
+
├── requirements.txt
|
|
129
|
+
└── start.py
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## 代理路由
|
|
133
|
+
|
|
134
|
+
平台为每个后端插件注册反向代理,插件前端无需知道实际端口:
|
|
135
|
+
|
|
136
|
+
| 类型 | 请求路径 | 代理目标 |
|
|
137
|
+
|------|----------|----------|
|
|
138
|
+
| HTTP | `/api/{pluginId}/server/{path}` | `http://127.0.0.1:{port}/{path}` |
|
|
139
|
+
| WebSocket | `/api/{pluginId}/server/ws` | `ws://127.0.0.1:{port}/ws` |
|
|
140
|
+
| WebSocket | `/api/{pluginId}/server/ws/{path}` | `ws://127.0.0.1:{port}/ws/{path}` |
|
|
141
|
+
|
|
142
|
+
## 端口规则
|
|
143
|
+
|
|
144
|
+
- 允许范围: 19000 ~ 19999
|
|
145
|
+
- 黑名单端口: 22, 80, 443, 3000, 3306, 5432, 6379, 8000, 8080, 9100, 27017
|
|
146
|
+
- 不指定端口时自动分配(排除运行中插件端口、已配置端口、系统占用端口)
|
|
147
|
+
- Node.js 和 Python 后端共享同一端口池
|
|
148
|
+
|
|
149
|
+
## 完整示例
|
|
150
|
+
|
|
151
|
+
### Node.js 后端
|
|
152
|
+
|
|
153
|
+
**plugin.json:**
|
|
154
|
+
```json
|
|
155
|
+
{
|
|
156
|
+
"plugin_id": "my-node-plugin",
|
|
157
|
+
"name": "My Node Plugin",
|
|
158
|
+
"backend": {
|
|
159
|
+
"type": "node",
|
|
160
|
+
"entry": "index.js",
|
|
161
|
+
"install_cmd": "npm install",
|
|
162
|
+
"health_check": "/health",
|
|
163
|
+
"enable_websocket": true
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
**server/index.js:**
|
|
169
|
+
```javascript
|
|
170
|
+
const express = require('express');
|
|
171
|
+
const app = express();
|
|
172
|
+
const port = process.env.PORT || 3000;
|
|
173
|
+
|
|
174
|
+
app.get('/health', (req, res) => res.json({ status: 'ok' }));
|
|
175
|
+
app.get('/api/hello', (req, res) => res.json({ message: 'Hello!' }));
|
|
176
|
+
|
|
177
|
+
app.listen(port, () => {
|
|
178
|
+
console.log(`Backend listening on port ${port}`);
|
|
179
|
+
});
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
**server/package.json:**
|
|
183
|
+
```json
|
|
184
|
+
{
|
|
185
|
+
"name": "my-node-plugin-server",
|
|
186
|
+
"dependencies": {
|
|
187
|
+
"express": "^4.18.0"
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Python 后端
|
|
193
|
+
|
|
194
|
+
**plugin.json:**
|
|
195
|
+
```json
|
|
196
|
+
{
|
|
197
|
+
"plugin_id": "my-python-plugin",
|
|
198
|
+
"name": "My Python Plugin",
|
|
199
|
+
"backend": {
|
|
200
|
+
"type": "python",
|
|
201
|
+
"entry": "start.py",
|
|
202
|
+
"install_cmd": "pip install -r requirements.txt",
|
|
203
|
+
"health_check": "/health"
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
**server/start.py:**
|
|
209
|
+
```python
|
|
210
|
+
import os
|
|
211
|
+
from fastapi import FastAPI
|
|
212
|
+
import uvicorn
|
|
213
|
+
|
|
214
|
+
app = FastAPI()
|
|
215
|
+
port = int(os.environ.get("PORT", "3000"))
|
|
216
|
+
|
|
217
|
+
@app.get("/health")
|
|
218
|
+
def health():
|
|
219
|
+
return {"status": "ok"}
|
|
220
|
+
|
|
221
|
+
@app.get("/api/hello")
|
|
222
|
+
def hello():
|
|
223
|
+
return {"message": "Hello from Python!"}
|
|
224
|
+
|
|
225
|
+
if __name__ == "__main__":
|
|
226
|
+
uvicorn.run(app, host="0.0.0.0", port=port)
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
**server/requirements.txt:**
|
|
230
|
+
```
|
|
231
|
+
fastapi>=0.100.0
|
|
232
|
+
uvicorn>=0.23.0
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## 生命周期管理 API
|
|
236
|
+
|
|
237
|
+
| 方法 | 路径 | 说明 |
|
|
238
|
+
|------|------|------|
|
|
239
|
+
| POST | `/api/plugins/{plugin_id}/restart-server` | 重启后端(停止 → 重装依赖 → 启动) |
|
|
240
|
+
| GET | `/api/plugins/{plugin_id}/server-logs?lines=200` | 获取后端日志 |
|
|
241
|
+
| DELETE | `/api/plugins/{plugin_id}/server-logs` | 清空后端日志 |
|
|
242
|
+
|
|
243
|
+
## 注意事项
|
|
244
|
+
|
|
245
|
+
1. **必须监听 PORT**:插件后端必须使用 `PORT` 环境变量指定的端口,否则平台代理无法转发请求
|
|
246
|
+
2. **健康检查**:平台启动后端后会请求 `health_check` 路径,确保返回 2xx 状态码
|
|
247
|
+
3. **Python 虚拟环境**:平台为每个 Python 插件自动管理独立 `.venv`,避免依赖冲突。更新插件时 `.venv` 被保留,pip 会增量安装新增依赖
|
|
248
|
+
4. **Node.js 依赖**:每次更新后 `node_modules` 会被清除并重新安装,确保依赖与新代码匹配
|
|
249
|
+
5. **日志输出**:子进程 stdout/stderr 写入 `backend/logs/{plugin_id}/server.log`(5MB 自动轮转,保留 3 份)
|
|
250
|
+
6. **进程清理**:平台启动前会检测并清理端口/目录下的残留进程,确保干净启动
|
|
@@ -70,6 +70,32 @@ kp.languageModel('anthropic/claude-haiku-4.5');
|
|
|
70
70
|
kp.languageModel('GLM-5.1');
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
+
### OpenRouter / KUNPO Reasoning(启用思考)
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
import { createProvider } from 'ai-world-sdk';
|
|
77
|
+
import { generateText } from 'ai';
|
|
78
|
+
|
|
79
|
+
const provider = createProvider('openrouter', 'openai', 'my-plugin');
|
|
80
|
+
|
|
81
|
+
const result = await generateText({
|
|
82
|
+
model: provider.languageModel('anthropic/claude-sonnet-4-20250514'),
|
|
83
|
+
prompt: '证明根号2是无理数',
|
|
84
|
+
providerOptions: {
|
|
85
|
+
openrouter: {
|
|
86
|
+
reasoning: {
|
|
87
|
+
effort: 'high', // xhigh / high / medium / low / minimal
|
|
88
|
+
// max_tokens: 10000, // 可选:限制思考 token 数量
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
console.log('思考过程:', result.reasoning);
|
|
94
|
+
console.log('最终回答:', result.text);
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
适用于 Claude / DeepSeek-R1 / o1 / o3 / Gemini 等支持思考的模型。KUNPO 同样支持 reasoning 参数。
|
|
98
|
+
|
|
73
99
|
### 检测模型可用性
|
|
74
100
|
|
|
75
101
|
```typescript
|