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.
@@ -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>', 'JS 后端 zip 包路径')
195
- .option('--backend-entry <entry>', 'JS 后端入口文件(默认 index.js)')
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>', 'JS 后端 zip 包路径')
236
- .option('--backend-entry <entry>', 'JS 后端入口文件(默认 index.js)')
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.16";
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.16";
39
- readonly sdkVersion = "1.5.16";
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.16";
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.16";
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
- has_server: boolean;
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
- has_server: boolean;
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
- "version": "1.5.16",
3
+ "version": "1.5.18",
4
4
  "description": "TypeScript SDK for AI World Platform - Chat Models, Image Generation, and Video Generation",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -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
- - 需要初始化 ai-world-sdk 配置
17
- - 使用 `createProvider` 调用 Vercel AI SDK
18
- - 使用 Doubao 豆包图像/视频生成
19
- - 使用 MinIO 对象存储(文件上传/下载/管理)
20
- - 使用资源管理系统(权限控制: private/specific_users/public
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 开发模式(推荐:Vite Plugin 自动登录)
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
- 首次 `npm run dev` 时自动打开浏览器完成飞书登录,后续启动 token 有效则直接跳过。无需在代码中手动调用 `sdkConfig.setToken()` 或 `sdkConfig.setPluginId()`,一切自动完成。
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
- import { createProvider } from 'ai-world-sdk';
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
- 详细用法见 → `docs/vscode-login.md`
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
- 也可通过环境变量自动初始化(SDK 自动读取 `.env.local` > `.env` > `AI_WORLD_ENV_FILE` 指定文件):
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
- **重要**:插件后端 **必须** 监听 `PORT` 环境变量指定的端口,否则平台无法正确代理请求。
66
+ ### 插件后端环境
143
67
 
144
- ```javascript
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
- WebSocket 同理:`ws(s)://{host}/api/{plugin-id}/server/ws`
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 对照表:`api2img`(推荐) / `gemini` / `aihubmix` / `shubiaobiao` / `aiping` / `openrouter` / `kunpo`
195
- - **OpenRouter**:使用专用代理端点 `/api/llm/openrouter`,模型 ID 格式为 `provider/model`(如 `openai/gpt-4o-mini`、`anthropic/claude-sonnet-4-20250514`、`google/gemini-2.5-flash-preview`)
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, streamText } from 'ai';
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('google/gemini-3.1-pro-preview'),
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`(豆包视频理解,基于 Chat Completions API)
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()` — 获取当前用户信息(id / email / full_name / is_superuser 等)
307
- - `DownloadClient` — 经后端代理下载任意 URL 文件,支持流式下载和浏览器下载
308
- - HTTP 通用代理 `/api/http-proxy/{path}`(`X-Target-URL` 头指定目标)
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)` 一步初始化(登录 + sdkConfig + 状态栏 + 命令)
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` — 配置驱动的 AI 供应商与模型列表(登录即可):`getProviders()`、`getProviderModels(providerId)`
328
- - `PluginManagementClient` — 插件 CRUD(`create` / `update` 分离)、启用/禁用/重载/重启、模板管理
329
- - `create(data)` — 创建新插件(POST `/api/plugins/create`),插件不存在时使用
330
- - `update(data)` — 更新已有插件(POST `/api/plugins/update`),插件必须已存在,未传入的字段保留原值
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 管理(创建/列表/更新/删除/重新生成/启停),需 `can_manage_plugin_tokens` 权限 → `docs/admin-api.md`
337
-
338
- ### CLI 命令行工具 → `docs/cli-usage.md`
149
+ - `PluginTokenClient` — 插件 Token 管理
339
150
 
340
- - `ai-world` 命令行工具,所有 SDK 功能的命令行模式
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
- ai-world login
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
- # 配置默认值(可选,默认 kunpo/openai/deepseek-v4-pro)
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 version
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, // 可选,JS 后端 zip
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