ai-world-sdk 1.2.7 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/example.test.js +27 -0
- package/dist/__tests__/vscode-login.test.d.ts +1 -0
- package/dist/__tests__/vscode-login.test.js +447 -0
- package/dist/agent-skills.d.ts +96 -0
- package/dist/agent-skills.js +186 -0
- package/dist/config.d.ts +3 -3
- package/dist/config.js +2 -2
- package/dist/database-requests.d.ts +151 -0
- package/dist/database-requests.js +242 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +7 -1
- package/dist/llm.js +5 -0
- package/dist/login.d.ts +6 -0
- package/dist/login.js +33 -1
- package/dist/openai_video_generation.d.ts +5 -4
- package/dist/provider_config.d.ts +1 -1
- package/dist/provider_config.js +2 -0
- package/dist/vscode-login.d.ts +102 -0
- package/dist/vscode-login.js +517 -0
- package/package.json +7 -1
- package/skills/ai-world-sdk/SKILL.md +65 -2
- package/skills/ai-world-sdk/docs/agent-skills.md +78 -0
- package/skills/ai-world-sdk/docs/common-mistakes.md +3 -0
- package/skills/ai-world-sdk/docs/database-requests.md +68 -0
- package/skills/ai-world-sdk/docs/provider-and-models.md +28 -8
- package/skills/ai-world-sdk/docs/vscode-login.md +198 -0
|
@@ -0,0 +1,517 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* AI World SDK - VSCode Extension Login
|
|
4
|
+
* 完整 VSCode 扩展登录集成,一步到位初始化
|
|
5
|
+
*
|
|
6
|
+
* 使用方式(最简):
|
|
7
|
+
* import { initAIWorld } from 'ai-world-sdk/vscode';
|
|
8
|
+
* export async function activate(ctx: vscode.ExtensionContext) {
|
|
9
|
+
* const ai = await initAIWorld(ctx);
|
|
10
|
+
* }
|
|
11
|
+
*
|
|
12
|
+
* 导出路径: ai-world-sdk/vscode
|
|
13
|
+
*
|
|
14
|
+
* 存储:token 等信息通过 vscode.SecretStorage 持久化
|
|
15
|
+
* pluginId:默认取扩展 package.json 的 name 字段
|
|
16
|
+
*/
|
|
17
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
18
|
+
if (k2 === undefined) k2 = k;
|
|
19
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
20
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
21
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
22
|
+
}
|
|
23
|
+
Object.defineProperty(o, k2, desc);
|
|
24
|
+
}) : (function(o, m, k, k2) {
|
|
25
|
+
if (k2 === undefined) k2 = k;
|
|
26
|
+
o[k2] = m[k];
|
|
27
|
+
}));
|
|
28
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
29
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
30
|
+
}) : function(o, v) {
|
|
31
|
+
o["default"] = v;
|
|
32
|
+
});
|
|
33
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
34
|
+
var ownKeys = function(o) {
|
|
35
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
36
|
+
var ar = [];
|
|
37
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
38
|
+
return ar;
|
|
39
|
+
};
|
|
40
|
+
return ownKeys(o);
|
|
41
|
+
};
|
|
42
|
+
return function (mod) {
|
|
43
|
+
if (mod && mod.__esModule) return mod;
|
|
44
|
+
var result = {};
|
|
45
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
46
|
+
__setModuleDefault(result, mod);
|
|
47
|
+
return result;
|
|
48
|
+
};
|
|
49
|
+
})();
|
|
50
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
51
|
+
exports.AIWorldExtension = void 0;
|
|
52
|
+
exports.initAIWorld = initAIWorld;
|
|
53
|
+
exports.getAIWorld = getAIWorld;
|
|
54
|
+
const vscode = __importStar(require("vscode"));
|
|
55
|
+
const http = __importStar(require("http"));
|
|
56
|
+
const config_1 = require("./config");
|
|
57
|
+
const login_1 = require("./login");
|
|
58
|
+
// ── Constants ────────────────────────────────────────────────────────────────
|
|
59
|
+
const CALLBACK_PORT = 18000;
|
|
60
|
+
const DEFAULT_BASE_URL = "https://aiworld.local:8000";
|
|
61
|
+
const LOGIN_TIMEOUT_MS = 120000;
|
|
62
|
+
const SECRET_TOKEN = "ai-world:token";
|
|
63
|
+
const SECRET_USER = "ai-world:user";
|
|
64
|
+
const SECRET_BASE_URL = "ai-world:baseUrl";
|
|
65
|
+
// ── Singleton ────────────────────────────────────────────────────────────────
|
|
66
|
+
let _instance = null;
|
|
67
|
+
// ── Main Class ───────────────────────────────────────────────────────────────
|
|
68
|
+
class AIWorldExtension {
|
|
69
|
+
// ── Construction (use initAIWorld) ─────────────────────────────────────
|
|
70
|
+
/** @internal 使用 initAIWorld() 创建实例 */
|
|
71
|
+
constructor(context, options = {}) {
|
|
72
|
+
this._disposables = [];
|
|
73
|
+
this._onDidChangeAuth = new vscode.EventEmitter();
|
|
74
|
+
/** 认证状态变化事件 */
|
|
75
|
+
this.onDidChangeAuthentication = this._onDidChangeAuth.event;
|
|
76
|
+
this._context = context;
|
|
77
|
+
this._baseUrl = options.baseUrl || DEFAULT_BASE_URL;
|
|
78
|
+
this._timeout = options.timeout || LOGIN_TIMEOUT_MS;
|
|
79
|
+
this._callbackPort = options.callbackPort || CALLBACK_PORT;
|
|
80
|
+
this._debug = options.debug || false;
|
|
81
|
+
this._pluginId = options.pluginId || context.extension?.packageJSON?.name || "unknown";
|
|
82
|
+
this._statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 100);
|
|
83
|
+
this._statusBar.command = "aiWorld.showLoginPanel";
|
|
84
|
+
this._disposables.push(this._statusBar);
|
|
85
|
+
this._registerCommands();
|
|
86
|
+
this._updateStatusBar();
|
|
87
|
+
}
|
|
88
|
+
// ── Public API ─────────────────────────────────────────────────────────
|
|
89
|
+
get token() { return this._token; }
|
|
90
|
+
get user() { return this._user; }
|
|
91
|
+
get pluginId() { return this._pluginId; }
|
|
92
|
+
get baseUrl() { return this._baseUrl; }
|
|
93
|
+
get isAuthenticated() { return !!this._token; }
|
|
94
|
+
/**
|
|
95
|
+
* 执行登录:有缓存 token 则验证复用,否则打开浏览器 OAuth
|
|
96
|
+
* 登录成功后自动配置 sdkConfig 并持久化到 SecretStorage
|
|
97
|
+
*/
|
|
98
|
+
async login() {
|
|
99
|
+
if (this._token) {
|
|
100
|
+
const valid = await (0, login_1.validateToken)(this._baseUrl, this._token);
|
|
101
|
+
if (valid)
|
|
102
|
+
return this._token;
|
|
103
|
+
}
|
|
104
|
+
const { token } = await this._performBrowserLogin();
|
|
105
|
+
await this._onLoginSuccess(token);
|
|
106
|
+
return token;
|
|
107
|
+
}
|
|
108
|
+
/** 登出:清除 token、sdkConfig、SecretStorage */
|
|
109
|
+
async logout() {
|
|
110
|
+
this._token = undefined;
|
|
111
|
+
this._user = undefined;
|
|
112
|
+
config_1.sdkConfig.setToken("");
|
|
113
|
+
await this._clearSecrets();
|
|
114
|
+
this._updateStatusBar();
|
|
115
|
+
this._onDidChangeAuth.fire(false);
|
|
116
|
+
this._refreshPanel();
|
|
117
|
+
vscode.window.showInformationMessage("AI World: 已退出登录");
|
|
118
|
+
}
|
|
119
|
+
/** 显示 / 聚焦登录 Webview 面板 */
|
|
120
|
+
showLoginPanel() {
|
|
121
|
+
if (this._panel) {
|
|
122
|
+
this._panel.reveal(vscode.ViewColumn.One);
|
|
123
|
+
this._refreshPanel();
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
this._panel = vscode.window.createWebviewPanel("aiWorldLogin", "AI World 登录", vscode.ViewColumn.One, { enableScripts: true, retainContextWhenHidden: true });
|
|
127
|
+
this._panel.onDidDispose(() => { this._panel = undefined; }, null, this._disposables);
|
|
128
|
+
this._panel.webview.onDidReceiveMessage(async (msg) => {
|
|
129
|
+
if (msg.command === "login") {
|
|
130
|
+
try {
|
|
131
|
+
await this.login();
|
|
132
|
+
this._refreshPanel();
|
|
133
|
+
this._panel?.webview.postMessage({ command: "loginComplete" });
|
|
134
|
+
}
|
|
135
|
+
catch (err) {
|
|
136
|
+
vscode.window.showErrorMessage(`AI World 登录失败: ${err.message}`);
|
|
137
|
+
this._panel?.webview.postMessage({ command: "loginFailed" });
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
else if (msg.command === "logout") {
|
|
141
|
+
await this.logout();
|
|
142
|
+
}
|
|
143
|
+
else if (msg.command === "refresh") {
|
|
144
|
+
await this._validateAndRefresh();
|
|
145
|
+
}
|
|
146
|
+
}, null, this._disposables);
|
|
147
|
+
this._refreshPanel();
|
|
148
|
+
}
|
|
149
|
+
/** 释放所有资源 */
|
|
150
|
+
dispose() {
|
|
151
|
+
this._onDidChangeAuth.dispose();
|
|
152
|
+
this._disposables.forEach((d) => d.dispose());
|
|
153
|
+
this._panel?.dispose();
|
|
154
|
+
_instance = null;
|
|
155
|
+
}
|
|
156
|
+
// ── SecretStorage ──────────────────────────────────────────────────────
|
|
157
|
+
get _secrets() {
|
|
158
|
+
return this._context.secrets;
|
|
159
|
+
}
|
|
160
|
+
async _loadSecrets() {
|
|
161
|
+
const [token, userJson, baseUrl] = await Promise.all([
|
|
162
|
+
this._secrets.get(SECRET_TOKEN),
|
|
163
|
+
this._secrets.get(SECRET_USER),
|
|
164
|
+
this._secrets.get(SECRET_BASE_URL),
|
|
165
|
+
]);
|
|
166
|
+
if (token)
|
|
167
|
+
this._token = token;
|
|
168
|
+
if (baseUrl)
|
|
169
|
+
this._baseUrl = baseUrl;
|
|
170
|
+
if (userJson) {
|
|
171
|
+
try {
|
|
172
|
+
this._user = JSON.parse(userJson);
|
|
173
|
+
}
|
|
174
|
+
catch { /* ignore */ }
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
async _saveSecrets() {
|
|
178
|
+
const ops = [];
|
|
179
|
+
if (this._token) {
|
|
180
|
+
ops.push(this._secrets.store(SECRET_TOKEN, this._token));
|
|
181
|
+
}
|
|
182
|
+
if (this._user) {
|
|
183
|
+
ops.push(this._secrets.store(SECRET_USER, JSON.stringify(this._user)));
|
|
184
|
+
}
|
|
185
|
+
ops.push(this._secrets.store(SECRET_BASE_URL, this._baseUrl));
|
|
186
|
+
await Promise.all(ops);
|
|
187
|
+
}
|
|
188
|
+
async _clearSecrets() {
|
|
189
|
+
await Promise.all([
|
|
190
|
+
Promise.resolve(this._secrets.delete(SECRET_TOKEN)),
|
|
191
|
+
Promise.resolve(this._secrets.delete(SECRET_USER)),
|
|
192
|
+
Promise.resolve(this._secrets.delete(SECRET_BASE_URL)),
|
|
193
|
+
]);
|
|
194
|
+
}
|
|
195
|
+
// ── SDK Config ─────────────────────────────────────────────────────────
|
|
196
|
+
_applySdkConfig() {
|
|
197
|
+
config_1.sdkConfig.setBaseUrl(this._baseUrl);
|
|
198
|
+
if (this._token)
|
|
199
|
+
config_1.sdkConfig.setToken(this._token);
|
|
200
|
+
config_1.sdkConfig.setPluginId(this._pluginId);
|
|
201
|
+
config_1.sdkConfig.setHeaders({ "X-Plugin-Id": this._pluginId });
|
|
202
|
+
if (this._debug)
|
|
203
|
+
config_1.sdkConfig.setDebug(true);
|
|
204
|
+
}
|
|
205
|
+
// ── Internal ───────────────────────────────────────────────────────────
|
|
206
|
+
/** 初始化流程:读 SecretStorage → 验证 → (可选)弹窗提示登录 → 配置 SDK */
|
|
207
|
+
async _bootstrap(autoLogin) {
|
|
208
|
+
await this._loadSecrets();
|
|
209
|
+
this._applySdkConfig();
|
|
210
|
+
if (this._token) {
|
|
211
|
+
const valid = await (0, login_1.validateToken)(this._baseUrl, this._token);
|
|
212
|
+
if (valid) {
|
|
213
|
+
await this._fetchUserInfo();
|
|
214
|
+
this._applySdkConfig();
|
|
215
|
+
await this._saveSecrets();
|
|
216
|
+
this._updateStatusBar();
|
|
217
|
+
this._onDidChangeAuth.fire(true);
|
|
218
|
+
if (this._debug)
|
|
219
|
+
console.log("[ai-world] Token 有效,已自动登录");
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
this._token = undefined;
|
|
223
|
+
this._user = undefined;
|
|
224
|
+
await this._clearSecrets();
|
|
225
|
+
if (this._debug)
|
|
226
|
+
console.log("[ai-world] 缓存 token 已过期");
|
|
227
|
+
}
|
|
228
|
+
this._updateStatusBar();
|
|
229
|
+
if (autoLogin) {
|
|
230
|
+
this._promptLogin();
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
/** 弹出小通知提示用户登录,用户点击"登录"按钮后才打开浏览器 */
|
|
234
|
+
_promptLogin() {
|
|
235
|
+
vscode.window
|
|
236
|
+
.showWarningMessage("AI World: 未登录,部分功能不可用", "登录", "稍后")
|
|
237
|
+
.then((choice) => {
|
|
238
|
+
if (choice === "登录") {
|
|
239
|
+
this.login().catch((err) => {
|
|
240
|
+
vscode.window.showErrorMessage(`AI World 登录失败: ${err.message}`);
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
async _onLoginSuccess(token) {
|
|
246
|
+
this._token = token;
|
|
247
|
+
await this._fetchUserInfo();
|
|
248
|
+
this._applySdkConfig();
|
|
249
|
+
await this._saveSecrets();
|
|
250
|
+
this._updateStatusBar();
|
|
251
|
+
this._onDidChangeAuth.fire(true);
|
|
252
|
+
vscode.window.showInformationMessage(`AI World: 登录成功${this._user?.full_name ? ` (${this._user.full_name})` : ""}`);
|
|
253
|
+
}
|
|
254
|
+
async _fetchUserInfo() {
|
|
255
|
+
if (!this._token)
|
|
256
|
+
return;
|
|
257
|
+
try {
|
|
258
|
+
const url = new URL(`${this._baseUrl}/api/auth/me`);
|
|
259
|
+
const mod = url.protocol === "https:" ? require("https") : require("http");
|
|
260
|
+
const data = await new Promise((resolve) => {
|
|
261
|
+
const req = mod.get(url.href, {
|
|
262
|
+
headers: { Authorization: `Bearer ${this._token}`, "Content-Type": "application/json" },
|
|
263
|
+
rejectUnauthorized: false,
|
|
264
|
+
}, (res) => {
|
|
265
|
+
let body = "";
|
|
266
|
+
res.on("data", (c) => { body += c; });
|
|
267
|
+
res.on("end", () => {
|
|
268
|
+
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
269
|
+
try {
|
|
270
|
+
resolve(JSON.parse(body));
|
|
271
|
+
}
|
|
272
|
+
catch {
|
|
273
|
+
resolve(null);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
resolve(null);
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
req.on("error", () => resolve(null));
|
|
282
|
+
req.end();
|
|
283
|
+
});
|
|
284
|
+
if (data) {
|
|
285
|
+
this._user = {
|
|
286
|
+
id: data.id,
|
|
287
|
+
full_name: data.full_name,
|
|
288
|
+
email: data.email,
|
|
289
|
+
avatar_url: data.avatar_url,
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
catch {
|
|
294
|
+
// ignore
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
_registerCommands() {
|
|
298
|
+
this._disposables.push(vscode.commands.registerCommand("aiWorld.login", () => this.login().catch((e) => vscode.window.showErrorMessage(`AI World 登录失败: ${e.message}`))), vscode.commands.registerCommand("aiWorld.logout", () => this.logout()), vscode.commands.registerCommand("aiWorld.showLoginPanel", () => this.showLoginPanel()));
|
|
299
|
+
}
|
|
300
|
+
_updateStatusBar() {
|
|
301
|
+
if (this._token && this._user) {
|
|
302
|
+
this._statusBar.text = `$(pass-filled) AI World: ${this._user.full_name || "已登录"}`;
|
|
303
|
+
this._statusBar.tooltip = `已登录 · ${this._user.full_name || ""}\nPlugin: ${this._pluginId}\n点击管理`;
|
|
304
|
+
this._statusBar.backgroundColor = undefined;
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
this._statusBar.text = "$(key) AI World: 未登录";
|
|
308
|
+
this._statusBar.tooltip = "点击登录 AI World";
|
|
309
|
+
this._statusBar.backgroundColor = new vscode.ThemeColor("statusBarItem.warningBackground");
|
|
310
|
+
}
|
|
311
|
+
this._statusBar.show();
|
|
312
|
+
}
|
|
313
|
+
async _validateAndRefresh() {
|
|
314
|
+
if (this._token) {
|
|
315
|
+
const valid = await (0, login_1.validateToken)(this._baseUrl, this._token);
|
|
316
|
+
if (valid) {
|
|
317
|
+
await this._fetchUserInfo();
|
|
318
|
+
await this._saveSecrets();
|
|
319
|
+
this._updateStatusBar();
|
|
320
|
+
this._refreshPanel();
|
|
321
|
+
vscode.window.showInformationMessage("AI World: Token 有效");
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
this._token = undefined;
|
|
325
|
+
this._user = undefined;
|
|
326
|
+
await this._clearSecrets();
|
|
327
|
+
}
|
|
328
|
+
this._updateStatusBar();
|
|
329
|
+
this._refreshPanel();
|
|
330
|
+
vscode.window.showWarningMessage("AI World: Token 无效或已过期");
|
|
331
|
+
}
|
|
332
|
+
_refreshPanel() {
|
|
333
|
+
if (!this._panel)
|
|
334
|
+
return;
|
|
335
|
+
this._panel.webview.html = this._getLoginPanelHtml();
|
|
336
|
+
}
|
|
337
|
+
// ── Browser OAuth Login ────────────────────────────────────────────────
|
|
338
|
+
_performBrowserLogin() {
|
|
339
|
+
const port = this._callbackPort;
|
|
340
|
+
return new Promise((resolve, reject) => {
|
|
341
|
+
(0, login_1.ensurePort)(port);
|
|
342
|
+
let settled = false;
|
|
343
|
+
let timeoutId;
|
|
344
|
+
const server = http.createServer((req, res) => {
|
|
345
|
+
const url = new URL(req.url || "/", `http://localhost:${port}`);
|
|
346
|
+
if (url.pathname === "/callback") {
|
|
347
|
+
const token = url.searchParams.get("token");
|
|
348
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
349
|
+
if (token) {
|
|
350
|
+
res.end(CALLBACK_SUCCESS_HTML);
|
|
351
|
+
if (!settled) {
|
|
352
|
+
settled = true;
|
|
353
|
+
clearTimeout(timeoutId);
|
|
354
|
+
server.close();
|
|
355
|
+
resolve({ token });
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
res.end(CALLBACK_FAILURE_HTML);
|
|
360
|
+
}
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
res.writeHead(404);
|
|
364
|
+
res.end("Not Found");
|
|
365
|
+
});
|
|
366
|
+
server.on("error", (err) => {
|
|
367
|
+
if (!settled) {
|
|
368
|
+
settled = true;
|
|
369
|
+
reject(new Error(`无法启动本地回调服务器: ${err.message}`));
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
server.listen(port, () => {
|
|
373
|
+
const callbackUrl = `http://localhost:${port}/callback`;
|
|
374
|
+
const loginUrl = `${this._baseUrl}/api/auth/login?redirect=${encodeURIComponent(callbackUrl)}`;
|
|
375
|
+
vscode.env.openExternal(vscode.Uri.parse(loginUrl)).then(undefined, () => {
|
|
376
|
+
vscode.window.showWarningMessage(`请手动打开: ${loginUrl}`);
|
|
377
|
+
});
|
|
378
|
+
timeoutId = setTimeout(() => {
|
|
379
|
+
if (!settled) {
|
|
380
|
+
settled = true;
|
|
381
|
+
server.close();
|
|
382
|
+
reject(new Error(`登录超时(${this._timeout / 1000}s),请重试`));
|
|
383
|
+
}
|
|
384
|
+
}, this._timeout);
|
|
385
|
+
});
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
// ── Webview HTML ───────────────────────────────────────────────────────
|
|
389
|
+
_getLoginPanelHtml() {
|
|
390
|
+
const auth = this.isAuthenticated;
|
|
391
|
+
const name = this._user?.full_name || "AI World User";
|
|
392
|
+
const avatar = this._user?.avatar_url;
|
|
393
|
+
const pid = this._pluginId;
|
|
394
|
+
return `<!DOCTYPE html>
|
|
395
|
+
<html lang="zh-CN">
|
|
396
|
+
<head>
|
|
397
|
+
<meta charset="UTF-8">
|
|
398
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
399
|
+
<title>AI World</title>
|
|
400
|
+
<style>
|
|
401
|
+
:root{--bg:#1e1e1e;--card:#252526;--border:#3c3c3c;--txt:#ccc;--muted:#888;--accent:#0078d4;--ok:#4ec9b0;--err:#f14c4c}
|
|
402
|
+
*{margin:0;padding:0;box-sizing:border-box}
|
|
403
|
+
body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;background:var(--bg);color:var(--txt);min-height:100vh;display:flex;justify-content:center;align-items:center;padding:20px}
|
|
404
|
+
.c{background:var(--card);border:1px solid var(--border);border-radius:12px;padding:40px;max-width:420px;width:100%;text-align:center}
|
|
405
|
+
.logo{font-size:40px;margin-bottom:8px}
|
|
406
|
+
h1{font-size:22px;font-weight:600;margin-bottom:4px}
|
|
407
|
+
.sub{color:var(--muted);font-size:13px;margin-bottom:32px}
|
|
408
|
+
.badge{display:inline-flex;align-items:center;gap:6px;padding:6px 14px;border-radius:20px;font-size:13px;font-weight:500;margin-bottom:24px}
|
|
409
|
+
.badge.ok{background:rgba(78,201,176,.12);color:var(--ok);border:1px solid rgba(78,201,176,.25)}
|
|
410
|
+
.badge.no{background:rgba(241,76,76,.12);color:var(--err);border:1px solid rgba(241,76,76,.25)}
|
|
411
|
+
.dot{width:8px;height:8px;border-radius:50%}
|
|
412
|
+
.ok .dot{background:var(--ok)}.no .dot{background:var(--err)}
|
|
413
|
+
.user{margin-bottom:24px}
|
|
414
|
+
.av{width:56px;height:56px;border-radius:50%;margin:0 auto 12px;background:var(--border);display:flex;align-items:center;justify-content:center;font-size:24px;overflow:hidden}
|
|
415
|
+
.av img{width:100%;height:100%;object-fit:cover}
|
|
416
|
+
.nm{font-size:16px;font-weight:600;margin-bottom:4px}
|
|
417
|
+
.pid{color:var(--muted);font-size:12px;font-family:'SF Mono',Monaco,Menlo,monospace;background:rgba(255,255,255,.05);padding:2px 8px;border-radius:4px}
|
|
418
|
+
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;width:100%;padding:12px 24px;border:none;border-radius:8px;font-size:14px;font-weight:500;cursor:pointer;transition:background .15s,opacity .15s}
|
|
419
|
+
.btn:hover{opacity:.9}.btn:active{opacity:.8}
|
|
420
|
+
.btn-p{background:var(--accent);color:#fff}.btn-p:hover{background:#1a8ae8}
|
|
421
|
+
.btn-d{background:transparent;color:var(--err);border:1px solid rgba(241,76,76,.3);margin-top:12px}.btn-d:hover{background:rgba(241,76,76,.1)}
|
|
422
|
+
.btn-s{background:transparent;color:var(--muted);border:1px solid var(--border);margin-top:12px}.btn-s:hover{background:rgba(255,255,255,.05)}
|
|
423
|
+
.sp{display:none;width:16px;height:16px;border:2px solid rgba(255,255,255,.3);border-top-color:#fff;border-radius:50%;animation:spin .6s linear infinite}
|
|
424
|
+
.ld .sp{display:inline-block}.ld .lb{display:none}
|
|
425
|
+
@keyframes spin{to{transform:rotate(360deg)}}
|
|
426
|
+
.ft{margin-top:32px;color:var(--muted);font-size:11px}
|
|
427
|
+
.info-row{display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px solid var(--border);font-size:13px;text-align:left}
|
|
428
|
+
.info-row:last-child{border-bottom:none}
|
|
429
|
+
.info-key{color:var(--muted)}.info-val{color:var(--txt);font-family:'SF Mono',Monaco,Menlo,monospace;font-size:12px}
|
|
430
|
+
</style>
|
|
431
|
+
</head>
|
|
432
|
+
<body>
|
|
433
|
+
<div class="c">
|
|
434
|
+
<div class="logo">🌐</div>
|
|
435
|
+
<h1>AI World</h1>
|
|
436
|
+
<p class="sub">VSCode Extension · ${pid}</p>
|
|
437
|
+
<div class="badge ${auth ? "ok" : "no"}"><span class="dot"></span>${auth ? "已登录" : "未登录"}</div>
|
|
438
|
+
${auth ? `
|
|
439
|
+
<div class="user">
|
|
440
|
+
<div class="av">${avatar ? `<img src="${avatar}" alt="">` : "👤"}</div>
|
|
441
|
+
<div class="nm">${this._escHtml(name)}</div>
|
|
442
|
+
<span class="pid">${this._escHtml(pid)}</span>
|
|
443
|
+
</div>
|
|
444
|
+
<div style="background:rgba(255,255,255,.03);border-radius:8px;padding:12px 16px;margin-bottom:20px">
|
|
445
|
+
<div class="info-row"><span class="info-key">BaseUrl</span><span class="info-val">${this._escHtml(this._baseUrl)}</span></div>
|
|
446
|
+
<div class="info-row"><span class="info-key">PluginId</span><span class="info-val">${this._escHtml(pid)}</span></div>
|
|
447
|
+
<div class="info-row"><span class="info-key">UserId</span><span class="info-val">${this._user?.id ?? "-"}</span></div>
|
|
448
|
+
</div>
|
|
449
|
+
<button class="btn btn-s" onclick="post('refresh')"><span class="lb">🔄 刷新状态</span></button>
|
|
450
|
+
<button class="btn btn-d" onclick="post('logout')"><span class="lb">退出登录</span></button>
|
|
451
|
+
` : `
|
|
452
|
+
<button id="lb" class="btn btn-p" onclick="doLogin()"><span class="sp"></span><span class="lb">🔑 通过飞书登录</span></button>
|
|
453
|
+
<button class="btn btn-s" onclick="post('refresh')"><span class="lb">🔄 刷新状态</span></button>
|
|
454
|
+
`}
|
|
455
|
+
<div class="ft">AI World SDK v${config_1.sdkConfig.sdkVersion}</div>
|
|
456
|
+
</div>
|
|
457
|
+
<script>
|
|
458
|
+
const vsc=acquireVsCodeApi();
|
|
459
|
+
function post(c){vsc.postMessage({command:c})}
|
|
460
|
+
function doLogin(){const b=document.getElementById('lb');if(b)b.classList.add('ld');post('login')}
|
|
461
|
+
window.addEventListener('message',e=>{const m=e.data;if(m.command==='loginComplete'||m.command==='loginFailed'){const b=document.getElementById('lb');if(b)b.classList.remove('ld')}});
|
|
462
|
+
</script>
|
|
463
|
+
</body></html>`;
|
|
464
|
+
}
|
|
465
|
+
_escHtml(s) {
|
|
466
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
exports.AIWorldExtension = AIWorldExtension;
|
|
470
|
+
// ── Public API ───────────────────────────────────────────────────────────────
|
|
471
|
+
/**
|
|
472
|
+
* 一步初始化 AI World VSCode 扩展
|
|
473
|
+
*
|
|
474
|
+
* 自动完成:读 SecretStorage → 验证 token → 配置 sdkConfig → 注册命令 → 显示状态栏
|
|
475
|
+
* pluginId 默认取扩展 package.json 的 name 字段
|
|
476
|
+
*
|
|
477
|
+
* @example
|
|
478
|
+
* import { initAIWorld } from 'ai-world-sdk/vscode';
|
|
479
|
+
* export async function activate(ctx: vscode.ExtensionContext) {
|
|
480
|
+
* const ai = await initAIWorld(ctx);
|
|
481
|
+
* // sdkConfig 已自动配置,可直接使用 createProvider 等 SDK 功能
|
|
482
|
+
* }
|
|
483
|
+
*/
|
|
484
|
+
async function initAIWorld(context, options = {}) {
|
|
485
|
+
if (_instance) {
|
|
486
|
+
_instance.dispose();
|
|
487
|
+
}
|
|
488
|
+
const ext = new AIWorldExtension(context, options);
|
|
489
|
+
_instance = ext;
|
|
490
|
+
context.subscriptions.push({ dispose: () => ext.dispose() });
|
|
491
|
+
await ext._bootstrap(options.autoLogin !== false);
|
|
492
|
+
return ext;
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* 获取已初始化的 AIWorldExtension 实例
|
|
496
|
+
* @throws 如果 initAIWorld 尚未调用
|
|
497
|
+
*/
|
|
498
|
+
function getAIWorld() {
|
|
499
|
+
if (!_instance)
|
|
500
|
+
throw new Error("请先调用 initAIWorld(context) 初始化");
|
|
501
|
+
return _instance;
|
|
502
|
+
}
|
|
503
|
+
// ── Static HTML for Browser Callback ─────────────────────────────────────────
|
|
504
|
+
const CALLBACK_SUCCESS_HTML = `<!DOCTYPE html>
|
|
505
|
+
<html><head><meta charset="utf-8"><title>AI World</title></head>
|
|
506
|
+
<body style="display:flex;justify-content:center;align-items:center;height:100vh;font-family:system-ui;background:#1e1e1e;color:#ccc">
|
|
507
|
+
<div style="text-align:center;padding:40px;background:#252526;border-radius:12px;border:1px solid #3c3c3c">
|
|
508
|
+
<h2 style="color:#4ec9b0">✓ 登录成功</h2>
|
|
509
|
+
<p style="color:#888">Token 已保存,可以关闭此页面回到 VSCode</p>
|
|
510
|
+
</div></body></html>`;
|
|
511
|
+
const CALLBACK_FAILURE_HTML = `<!DOCTYPE html>
|
|
512
|
+
<html><head><meta charset="utf-8"><title>AI World</title></head>
|
|
513
|
+
<body style="display:flex;justify-content:center;align-items:center;height:100vh;font-family:system-ui;background:#1e1e1e;color:#ccc">
|
|
514
|
+
<div style="text-align:center;padding:40px;background:#252526;border-radius:12px;border:1px solid #3c3c3c">
|
|
515
|
+
<h2 style="color:#f14c4c">✗ 登录失败</h2>
|
|
516
|
+
<p style="color:#888">未收到 token,请重试</p>
|
|
517
|
+
</div></body></html>`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-world-sdk",
|
|
3
|
-
"version": "1.2
|
|
3
|
+
"version": "1.3.2",
|
|
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",
|
|
@@ -12,6 +12,10 @@
|
|
|
12
12
|
"./vite": {
|
|
13
13
|
"types": "./dist/vite-plugin.d.ts",
|
|
14
14
|
"default": "./dist/vite-plugin.js"
|
|
15
|
+
},
|
|
16
|
+
"./vscode": {
|
|
17
|
+
"types": "./dist/vscode-login.d.ts",
|
|
18
|
+
"default": "./dist/vscode-login.js"
|
|
15
19
|
}
|
|
16
20
|
},
|
|
17
21
|
"scripts": {
|
|
@@ -59,6 +63,7 @@
|
|
|
59
63
|
"@types/dotenv": "^8.2.0",
|
|
60
64
|
"@types/jest": "^29.5.0",
|
|
61
65
|
"@types/node": "^20.0.0",
|
|
66
|
+
"@types/vscode": "^1.110.0",
|
|
62
67
|
"dotenv": "^16.0.0",
|
|
63
68
|
"jest": "^29.5.0",
|
|
64
69
|
"ts-jest": "^29.1.0",
|
|
@@ -73,6 +78,7 @@
|
|
|
73
78
|
"@ai-sdk/anthropic": "^3.0.43",
|
|
74
79
|
"@ai-sdk/google": "^3.0.29",
|
|
75
80
|
"@ai-sdk/openai": "^3.0.28",
|
|
81
|
+
"@openrouter/ai-sdk-provider": "^2.3.1",
|
|
76
82
|
"ai": "^6.0.85"
|
|
77
83
|
}
|
|
78
84
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ai-world-sdk
|
|
3
|
-
description: Use when initializing ai-world-sdk or calling AI models in ai-world projects. Covers sdkConfig initialization, createProvider with Vercel AI SDK (generateText, streamText, generateImage), Doubao image/video generation, MinIO object storage (MinioStorageClient),
|
|
3
|
+
description: Use when initializing ai-world-sdk or calling AI models in ai-world projects. Covers sdkConfig initialization, createProvider with Vercel AI SDK (generateText, streamText, generateImage), Doubao image/video generation, MinIO object storage (MinioStorageClient), resource management with ACL (ResourceClient), database requests (DatabaseRequestClient), agent skills management (AgentSkillClient), and VSCode extension login (initAIWorld). Triggers on ai-world-sdk, createProvider, sdkConfig, generateText, streamText, generateImage, doubao, MinioStorageClient, minio, ResourceClient, resource management, access control, file sharing, DatabaseRequestClient, database request, table creation, AgentSkillClient, agent skill, vscode extension, initAIWorld, or any AI model integration.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# AI World SDK
|
|
@@ -18,6 +18,9 @@ ai-world-sdk 是 AI World 平台的 TypeScript SDK,通过 `createProvider` 创
|
|
|
18
18
|
- 使用 Doubao 豆包图像/视频生成
|
|
19
19
|
- 使用 MinIO 对象存储(文件上传/下载/管理)
|
|
20
20
|
- 使用资源管理系统(权限控制: private/specific_users/public)
|
|
21
|
+
- 提交数据库建表/迁移请求
|
|
22
|
+
- 管理 Agent Skills(上传/下载/删除/列表)
|
|
23
|
+
- **开发 VSCode 扩展**时集成 AI World 登录和 SDK 功能
|
|
21
24
|
|
|
22
25
|
## 安装
|
|
23
26
|
|
|
@@ -62,6 +65,48 @@ export default defineConfig({
|
|
|
62
65
|
aiWorldPlugin({ baseUrl: 'https://your-server:8000' })
|
|
63
66
|
```
|
|
64
67
|
|
|
68
|
+
### VSCode 扩展(推荐:initAIWorld 一步到位)
|
|
69
|
+
|
|
70
|
+
在 VSCode 扩展中使用 `ai-world-sdk/vscode`,一行代码完成所有配置:
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
import * as vscode from 'vscode';
|
|
74
|
+
import { initAIWorld } from 'ai-world-sdk/vscode';
|
|
75
|
+
import { createProvider } from 'ai-world-sdk';
|
|
76
|
+
import { generateText } from 'ai';
|
|
77
|
+
|
|
78
|
+
export async function activate(context: vscode.ExtensionContext) {
|
|
79
|
+
const ai = await initAIWorld(context, {
|
|
80
|
+
pluginId: 'my-extension', // 可选,默认从 package.json name 推断
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// sdkConfig 已自动配置,直接使用 SDK 功能
|
|
84
|
+
if (ai.isAuthenticated) {
|
|
85
|
+
const provider = createProvider('api2img', 'openai', ai.pluginId);
|
|
86
|
+
const model = provider.languageModel('gpt-4o-mini');
|
|
87
|
+
const result = await generateText({ model, prompt: '你好' });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`initAIWorld` 自动完成:登录认证、sdkConfig 配置、命令注册、状态栏、缓存持久化到 `vscode.SecretStorage`。pluginId 默认取 `package.json` 的 `name`。
|
|
93
|
+
|
|
94
|
+
扩展 `package.json` 需声明 SDK 自动注册的命令:
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"contributes": {
|
|
99
|
+
"commands": [
|
|
100
|
+
{ "command": "aiWorld.login", "title": "AI World: 登录" },
|
|
101
|
+
{ "command": "aiWorld.logout", "title": "AI World: 退出登录" },
|
|
102
|
+
{ "command": "aiWorld.showLoginPanel", "title": "AI World: 登录面板" }
|
|
103
|
+
]
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
详细用法见 → `docs/vscode-login.md`
|
|
109
|
+
|
|
65
110
|
### 非浏览器环境(测试、脚本)
|
|
66
111
|
|
|
67
112
|
```typescript
|
|
@@ -81,7 +126,7 @@ sdkConfig.setPluginId('my-plugin');
|
|
|
81
126
|
### Provider 与模型 → `docs/provider-and-models.md`
|
|
82
127
|
|
|
83
128
|
- `createProvider(provider, endpointType, pluginId)` 创建 Vercel AI SDK Provider
|
|
84
|
-
- Provider 对照表:`api2img`(推荐) / `gemini` / `aihubmix` / `shubiaobiao`
|
|
129
|
+
- Provider 对照表:`api2img`(推荐) / `gemini` / `aihubmix` / `shubiaobiao` / `aiping`
|
|
85
130
|
- 常用模型列表和 AI SDK 函数速查
|
|
86
131
|
|
|
87
132
|
### 文本生成 → `docs/text-generation.md`
|
|
@@ -110,6 +155,24 @@ sdkConfig.setPluginId('my-plugin');
|
|
|
110
155
|
- 三级权限: `private` / `specific_users` / `public`
|
|
111
156
|
- 插件隔离与跨插件访问
|
|
112
157
|
|
|
158
|
+
### 数据库请求 → `docs/database-requests.md`
|
|
159
|
+
|
|
160
|
+
- `DatabaseRequestClient` — 建表/迁移请求提交、列表、审核
|
|
161
|
+
- 表名规则: `{pluginId}_{tableName}`
|
|
162
|
+
|
|
163
|
+
### Agent Skills → `docs/agent-skills.md`
|
|
164
|
+
|
|
165
|
+
- `AgentSkillClient` — Skills 上传/下载/删除/列表
|
|
166
|
+
- Skill 名称全小写,zip 必须包含 SKILL.md
|
|
167
|
+
|
|
168
|
+
### VSCode 扩展登录 → `docs/vscode-login.md`
|
|
169
|
+
|
|
170
|
+
- `initAIWorld(context)` 一步初始化(登录 + sdkConfig + 状态栏 + 命令)
|
|
171
|
+
- `getAIWorld()` 获取实例
|
|
172
|
+
- 认证信息通过 `vscode.SecretStorage` 安全存储(token / user / baseUrl)
|
|
173
|
+
- `pluginId` 默认取扩展 `package.json` 的 `name` 字段
|
|
174
|
+
- 完整示例见 `examples/vscode-test-extension/`
|
|
175
|
+
|
|
113
176
|
### 常见错误 → `docs/common-mistakes.md`
|
|
114
177
|
|
|
115
178
|
- endpointType 匹配规则、AI SDK v6 用法变更、MinIO 路径规范等
|