@next-open-ai/openbot 0.1.8 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/{desktop → apps/desktop}/README.md +3 -3
- package/{desktop/renderer/dist/assets/index-DL_hPION.js → apps/desktop/renderer/dist/assets/index-BOS-F8a4.js} +37 -37
- package/{desktop/renderer/dist/assets/index-BBoPEPR6.css → apps/desktop/renderer/dist/assets/index-DxqxayUL.css} +2 -2
- package/{desktop → apps/desktop}/renderer/dist/index.html +2 -2
- package/dist/cli/cli.d.ts +2 -0
- package/dist/cli/cli.js +172 -0
- package/dist/cli.d.ts +4 -1
- package/dist/cli.js +4 -172
- package/dist/gateway/auth-hooks.d.ts +17 -0
- package/dist/gateway/auth-hooks.js +19 -0
- package/dist/gateway/channel-handler.d.ts +6 -0
- package/dist/gateway/channel-handler.js +3 -0
- package/dist/gateway/index.d.ts +1 -1
- package/dist/gateway/index.js +1 -1
- package/dist/gateway/methods/agent-cancel.js +1 -1
- package/dist/gateway/methods/agent-chat.js +3 -3
- package/dist/gateway/methods/install-skill-from-path.js +1 -1
- package/dist/gateway/methods/run-scheduled-task.js +3 -3
- package/dist/gateway/paths.d.ts +20 -0
- package/dist/gateway/paths.js +19 -0
- package/dist/gateway/server.d.ts +2 -4
- package/dist/gateway/server.js +98 -208
- package/dist/gateway/sse-handler.d.ts +6 -0
- package/dist/gateway/sse-handler.js +3 -0
- package/dist/gateway/voice-handler.d.ts +12 -0
- package/dist/gateway/voice-handler.js +18 -0
- package/dist/index.d.ts +5 -5
- package/dist/index.js +5 -5
- package/dist/server/agents/agents.service.js +1 -1
- package/dist/server/bootstrap.d.ts +15 -0
- package/dist/server/bootstrap.js +38 -0
- package/dist/server/config/config.service.d.ts +1 -1
- package/dist/server/config/config.service.js +1 -1
- package/dist/server/database/database.service.d.ts +14 -6
- package/dist/server/database/database.service.js +99 -32
- package/dist/server/main.js +6 -19
- package/dist/server/skills/skills.service.js +1 -1
- package/dist/server/workspace/workspace.service.js +1 -1
- package/package.json +23 -20
- package/desktop/renderer/dist/assets/logo-RfPiqt5-.png +0 -0
- /package/dist/{agent → core/agent}/agent-dir.d.ts +0 -0
- /package/dist/{agent → core/agent}/agent-dir.js +0 -0
- /package/dist/{agent → core/agent}/agent-manager.d.ts +0 -0
- /package/dist/{agent → core/agent}/agent-manager.js +0 -0
- /package/dist/{agent → core/agent}/config-manager.d.ts +0 -0
- /package/dist/{agent → core/agent}/config-manager.js +0 -0
- /package/dist/{agent → core/agent}/run.d.ts +0 -0
- /package/dist/{agent → core/agent}/run.js +0 -0
- /package/dist/{agent → core/agent}/skills.d.ts +0 -0
- /package/dist/{agent → core/agent}/skills.js +0 -0
- /package/dist/{config → core/config}/desktop-config.d.ts +0 -0
- /package/dist/{config → core/config}/desktop-config.js +0 -0
- /package/dist/{config → core/config}/provider-support-default.d.ts +0 -0
- /package/dist/{config → core/config}/provider-support-default.js +0 -0
- /package/dist/{installer → core/installer}/index.d.ts +0 -0
- /package/dist/{installer → core/installer}/index.js +0 -0
- /package/dist/{installer → core/installer}/skill-installer.d.ts +0 -0
- /package/dist/{installer → core/installer}/skill-installer.js +0 -0
- /package/dist/{memory → core/memory}/build-summary.d.ts +0 -0
- /package/dist/{memory → core/memory}/build-summary.js +0 -0
- /package/dist/{memory → core/memory}/compaction-extension.d.ts +0 -0
- /package/dist/{memory → core/memory}/compaction-extension.js +0 -0
- /package/dist/{memory → core/memory}/embedding.d.ts +0 -0
- /package/dist/{memory → core/memory}/embedding.js +0 -0
- /package/dist/{memory → core/memory}/index.d.ts +0 -0
- /package/dist/{memory → core/memory}/index.js +0 -0
- /package/dist/{memory → core/memory}/remote-embedding.d.ts +0 -0
- /package/dist/{memory → core/memory}/remote-embedding.js +0 -0
- /package/dist/{memory → core/memory}/types.d.ts +0 -0
- /package/dist/{memory → core/memory}/types.js +0 -0
- /package/dist/{memory → core/memory}/vector-store.d.ts +0 -0
- /package/dist/{memory → core/memory}/vector-store.js +0 -0
- /package/dist/{tools → core/tools}/browser-tool.d.ts +0 -0
- /package/dist/{tools → core/tools}/browser-tool.js +0 -0
- /package/dist/{tools → core/tools}/index.d.ts +0 -0
- /package/dist/{tools → core/tools}/index.js +0 -0
- /package/dist/{tools → core/tools}/install-skill-tool.d.ts +0 -0
- /package/dist/{tools → core/tools}/install-skill-tool.js +0 -0
- /package/dist/{tools → core/tools}/save-experience-tool.d.ts +0 -0
- /package/dist/{tools → core/tools}/save-experience-tool.js +0 -0
package/dist/gateway/server.js
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Gateway 单进程入口:内嵌 Nest,按 path 分流。
|
|
3
|
+
* - /server-api → Nest(业务 REST)
|
|
4
|
+
* - /ws → Agent 对话 WebSocket
|
|
5
|
+
* - /ws/voice → 语音通道(占位)
|
|
6
|
+
* - /sse → SSE(占位)
|
|
7
|
+
* - /channel → 通道模块(占位)
|
|
8
|
+
* - 其余 → 静态资源
|
|
4
9
|
*/
|
|
5
|
-
/* Avoid MaxListenersExceededWarning
|
|
10
|
+
/* Avoid MaxListenersExceededWarning */
|
|
6
11
|
const Et = globalThis.EventTarget;
|
|
7
12
|
if (Et?.prototype?.addEventListener && Et.prototype.setMaxListeners) {
|
|
8
13
|
const add = Et.prototype.addEventListener;
|
|
@@ -13,70 +18,26 @@ if (Et?.prototype?.addEventListener && Et.prototype.setMaxListeners) {
|
|
|
13
18
|
return add.call(this, type, listener, options);
|
|
14
19
|
};
|
|
15
20
|
}
|
|
21
|
+
import express from "express";
|
|
22
|
+
import { createServer } from "http";
|
|
16
23
|
import { WebSocketServer } from "ws";
|
|
17
|
-
import { createServer, request as httpRequest } from "http";
|
|
18
|
-
import { handleConnection } from "./connection-handler.js";
|
|
19
24
|
import { readFile, stat } from "fs/promises";
|
|
20
25
|
import { join, extname, dirname } from "path";
|
|
21
26
|
import { fileURLToPath } from "node:url";
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
27
|
+
import { PATHS } from "./paths.js";
|
|
28
|
+
import { authHookServerApi, authHookChannel, authHookSse, authHookWs } from "./auth-hooks.js";
|
|
29
|
+
import { handleChannel } from "./channel-handler.js";
|
|
30
|
+
import { handleSse } from "./sse-handler.js";
|
|
31
|
+
import { handleVoiceUpgrade } from "./voice-handler.js";
|
|
32
|
+
import { handleConnection } from "./connection-handler.js";
|
|
25
33
|
import { handleRunScheduledTask } from "./methods/run-scheduled-task.js";
|
|
26
34
|
import { handleInstallSkillFromPath } from "./methods/install-skill-from-path.js";
|
|
27
35
|
import { setBackendBaseUrl } from "./backend-url.js";
|
|
28
|
-
import { ensureDesktopConfigInitialized } from "../config/desktop-config.js";
|
|
36
|
+
import { ensureDesktopConfigInitialized } from "../core/config/desktop-config.js";
|
|
37
|
+
import { createNestAppEmbedded } from "../server/bootstrap.js";
|
|
29
38
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
30
|
-
/** 包根目录(dist/gateway 的上级的上级),npm 安装后静态资源在 desktop/renderer/dist */
|
|
31
39
|
const PACKAGE_ROOT = join(__dirname, "..", "..");
|
|
32
|
-
const STATIC_DIR = join(PACKAGE_ROOT, "desktop", "renderer", "dist");
|
|
33
|
-
/**
|
|
34
|
-
* Find an available port starting from startPort
|
|
35
|
-
*/
|
|
36
|
-
async function findAvailablePort(startPort) {
|
|
37
|
-
let port = startPort;
|
|
38
|
-
while (true) {
|
|
39
|
-
try {
|
|
40
|
-
await new Promise((resolve, reject) => {
|
|
41
|
-
const server = createNetServer();
|
|
42
|
-
server.once("error", (err) => {
|
|
43
|
-
if (err.code === "EADDRINUSE") {
|
|
44
|
-
resolve(); // Port taken, try next
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
reject(err);
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
server.once("listening", () => {
|
|
51
|
-
server.close(() => resolve()); // Port available
|
|
52
|
-
});
|
|
53
|
-
server.listen(port);
|
|
54
|
-
});
|
|
55
|
-
// If we get here and the server listened successfully (then closed), checking if it was actually available logic needs care.
|
|
56
|
-
// Actually the above logic is slightly flawed for "resolve on error".
|
|
57
|
-
// Let's refine:
|
|
58
|
-
// If listen succeeds -> port is free. return it.
|
|
59
|
-
// If EADDRINUSE -> port busy. loop continue.
|
|
60
|
-
const isAvailable = await new Promise((resolve) => {
|
|
61
|
-
const server = createNetServer();
|
|
62
|
-
server.once("error", () => resolve(false));
|
|
63
|
-
server.once("listening", () => {
|
|
64
|
-
server.close(() => resolve(true));
|
|
65
|
-
});
|
|
66
|
-
server.listen(port);
|
|
67
|
-
});
|
|
68
|
-
if (isAvailable)
|
|
69
|
-
return port;
|
|
70
|
-
port++;
|
|
71
|
-
}
|
|
72
|
-
catch (e) {
|
|
73
|
-
port++;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* MIME types for static files
|
|
79
|
-
*/
|
|
40
|
+
const STATIC_DIR = join(PACKAGE_ROOT, "apps", "desktop", "renderer", "dist");
|
|
80
41
|
const MIME_TYPES = {
|
|
81
42
|
".html": "text/html",
|
|
82
43
|
".js": "text/javascript",
|
|
@@ -92,137 +53,65 @@ const MIME_TYPES = {
|
|
|
92
53
|
".ttf": "font/ttf",
|
|
93
54
|
".eot": "application/vnd.ms-fontobject",
|
|
94
55
|
};
|
|
95
|
-
/**
|
|
96
|
-
* Start WebSocket gateway server
|
|
97
|
-
*/
|
|
98
56
|
export async function startGatewayServer(port = 38080) {
|
|
99
57
|
await ensureDesktopConfigInitialized();
|
|
100
58
|
console.log(`Starting gateway server on port ${port}...`);
|
|
101
|
-
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
const str = data.toString().trim();
|
|
117
|
-
if (str)
|
|
118
|
-
console.log(`[Desktop Server] ${str}`);
|
|
119
|
-
});
|
|
120
|
-
backendProcess.stderr?.on("data", (data) => {
|
|
121
|
-
const str = data.toString().trim();
|
|
122
|
-
if (str)
|
|
123
|
-
console.error(`[Desktop Server Error] ${str}`);
|
|
124
|
-
});
|
|
125
|
-
backendProcess.on("exit", (code) => {
|
|
126
|
-
if (code !== 0 && code !== null) {
|
|
127
|
-
console.error(`Desktop Server exited with code ${code}`);
|
|
128
|
-
}
|
|
59
|
+
setBackendBaseUrl(`http://localhost:${port}`);
|
|
60
|
+
const { app: nestApp, express: nestExpress } = await createNestAppEmbedded();
|
|
61
|
+
const gatewayExpress = express();
|
|
62
|
+
gatewayExpress.get(PATHS.HEALTH, (_req, res) => {
|
|
63
|
+
res.status(200).json({ status: "ok", timestamp: Date.now() });
|
|
64
|
+
});
|
|
65
|
+
gatewayExpress.post(PATHS.RUN_SCHEDULED_TASK, async (req, res) => {
|
|
66
|
+
await handleRunScheduledTask(req, res);
|
|
67
|
+
});
|
|
68
|
+
gatewayExpress.post(`${PATHS.SERVER_API}/skills/install-from-path`, async (req, res) => {
|
|
69
|
+
const body = await new Promise((resolve, reject) => {
|
|
70
|
+
const chunks = [];
|
|
71
|
+
req.on("data", (chunk) => chunks.push(chunk));
|
|
72
|
+
req.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
|
|
73
|
+
req.on("error", reject);
|
|
129
74
|
});
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
const httpServer = createServer(async (req, res) => {
|
|
137
|
-
// Simple health check endpoint
|
|
138
|
-
if (req.url === "/health") {
|
|
139
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
140
|
-
res.end(JSON.stringify({ status: "ok", timestamp: Date.now() }));
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
// Scheduled task: run agent and POST assistant message back to Nest
|
|
144
|
-
const pathname = req.url?.split("?")[0] || "";
|
|
145
|
-
if (req.method === "POST" && pathname === "/run-scheduled-task") {
|
|
146
|
-
await handleRunScheduledTask(req, res);
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
// 本地技能目录安装:在 Gateway 层直接处理,不依赖 Nest,保证桌面端稳定可用
|
|
150
|
-
if (req.method === "POST" && pathname === "/server-api/skills/install-from-path") {
|
|
151
|
-
const body = await new Promise((resolve, reject) => {
|
|
152
|
-
const chunks = [];
|
|
153
|
-
req.on("data", (chunk) => chunks.push(chunk));
|
|
154
|
-
req.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
|
|
155
|
-
req.on("error", reject);
|
|
75
|
+
try {
|
|
76
|
+
const parsed = JSON.parse(body || "{}");
|
|
77
|
+
const result = await handleInstallSkillFromPath({
|
|
78
|
+
path: parsed.path ?? "",
|
|
79
|
+
scope: parsed.scope === "workspace" ? "workspace" : "global",
|
|
80
|
+
workspace: parsed.workspace,
|
|
156
81
|
});
|
|
157
|
-
|
|
158
|
-
const parsed = JSON.parse(body || "{}");
|
|
159
|
-
const result = await handleInstallSkillFromPath({
|
|
160
|
-
path: parsed.path ?? "",
|
|
161
|
-
scope: parsed.scope === "workspace" ? "workspace" : "global",
|
|
162
|
-
workspace: parsed.workspace,
|
|
163
|
-
});
|
|
164
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
165
|
-
res.end(JSON.stringify(result));
|
|
166
|
-
}
|
|
167
|
-
catch (err) {
|
|
168
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
169
|
-
const code = message.includes("required") || message.includes("不存在") || message.includes("SKILL.md") || message.includes("目录名") ? 400 : 500;
|
|
170
|
-
res.writeHead(code, { "Content-Type": "application/json" });
|
|
171
|
-
res.end(JSON.stringify({ success: false, message }));
|
|
172
|
-
}
|
|
173
|
-
return;
|
|
82
|
+
res.status(200).json(result);
|
|
174
83
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
port: backendPort, // Use discovered port
|
|
180
|
-
path: req.url,
|
|
181
|
-
method: req.method,
|
|
182
|
-
headers: req.headers,
|
|
183
|
-
};
|
|
184
|
-
const proxyReq = httpRequest(options, (proxyRes) => {
|
|
185
|
-
res.writeHead(proxyRes.statusCode || 500, proxyRes.headers);
|
|
186
|
-
proxyRes.pipe(res, { end: true });
|
|
187
|
-
});
|
|
188
|
-
proxyReq.on("error", (err) => {
|
|
189
|
-
console.error(`Proxy error to :${backendPort}`, err);
|
|
190
|
-
if (!res.headersSent) {
|
|
191
|
-
res.writeHead(502, { "Content-Type": "application/json" });
|
|
192
|
-
res.end(JSON.stringify({ error: "Bad Gateway", message: "Failed to connect to backend server" }));
|
|
193
|
-
}
|
|
194
|
-
});
|
|
195
|
-
req.pipe(proxyReq, { end: true });
|
|
196
|
-
return;
|
|
84
|
+
catch (err) {
|
|
85
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
86
|
+
const code = message.includes("required") || message.includes("不存在") || message.includes("SKILL.md") || message.includes("目录名") ? 400 : 500;
|
|
87
|
+
res.status(code).json({ success: false, message });
|
|
197
88
|
}
|
|
198
|
-
|
|
89
|
+
});
|
|
90
|
+
gatewayExpress.use(PATHS.SERVER_API, authHookServerApi, nestExpress);
|
|
91
|
+
gatewayExpress.use(PATHS.CHANNEL, authHookChannel, (req, res) => handleChannel(req, res));
|
|
92
|
+
gatewayExpress.use(PATHS.SSE, authHookSse, (req, res) => handleSse(req, res));
|
|
93
|
+
gatewayExpress.use(async (req, res) => {
|
|
94
|
+
const staticDir = STATIC_DIR;
|
|
95
|
+
const urlPath = req.url?.split("?")[0] || "/";
|
|
96
|
+
let filePath = join(staticDir, urlPath === "/" ? "index.html" : urlPath);
|
|
199
97
|
try {
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
filePath = join(filePath, "index.html");
|
|
210
|
-
await stat(filePath); // Check if index.html exists
|
|
211
|
-
}
|
|
98
|
+
const stats = await stat(filePath);
|
|
99
|
+
if (stats.isDirectory()) {
|
|
100
|
+
filePath = join(filePath, "index.html");
|
|
101
|
+
await stat(filePath);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
if (req.headers.accept?.includes("text/html") && req.method === "GET") {
|
|
106
|
+
filePath = join(staticDir, "index.html");
|
|
212
107
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
filePath = join(staticDir, "index.html");
|
|
218
|
-
}
|
|
219
|
-
else {
|
|
220
|
-
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
221
|
-
res.end("Not Found");
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
108
|
+
else {
|
|
109
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
110
|
+
res.end("Not Found");
|
|
111
|
+
return;
|
|
224
112
|
}
|
|
225
|
-
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
226
115
|
const content = await readFile(filePath);
|
|
227
116
|
const ext = extname(filePath).toLowerCase();
|
|
228
117
|
const contentType = MIME_TYPES[ext] || "application/octet-stream";
|
|
@@ -235,41 +124,42 @@ export async function startGatewayServer(port = 38080) {
|
|
|
235
124
|
res.end("Internal Server Error");
|
|
236
125
|
}
|
|
237
126
|
});
|
|
238
|
-
|
|
239
|
-
const wss = new WebSocketServer({
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
127
|
+
const httpServer = createServer(gatewayExpress);
|
|
128
|
+
const wss = new WebSocketServer({ noServer: true });
|
|
129
|
+
wss.on("connection", (ws, req) => handleConnection(ws, req));
|
|
130
|
+
httpServer.on("upgrade", (req, socket, head) => {
|
|
131
|
+
const path = req.url?.split("?")[0] || "";
|
|
132
|
+
if (handleVoiceUpgrade(req, socket, head)) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const isAgentWs = path === PATHS.WS || path === "/";
|
|
136
|
+
if (isAgentWs && authHookWs(req, path)) {
|
|
137
|
+
wss.handleUpgrade(req, socket, head, (ws) => {
|
|
138
|
+
wss.emit("connection", ws, req);
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
socket.destroy();
|
|
143
|
+
}
|
|
243
144
|
});
|
|
244
|
-
|
|
245
|
-
await new Promise((resolve) => {
|
|
145
|
+
const actualPort = await new Promise((resolve) => {
|
|
246
146
|
httpServer.listen(port, () => {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
147
|
+
const addr = httpServer.address();
|
|
148
|
+
const p = typeof addr === "object" && addr && "port" in addr ? addr.port : port;
|
|
149
|
+
console.log(`✅ Gateway server listening on ws://localhost:${p}`);
|
|
150
|
+
console.log(` Health: http://localhost:${p}${PATHS.HEALTH}`);
|
|
151
|
+
console.log(` API: http://localhost:${p}${PATHS.SERVER_API}`);
|
|
152
|
+
console.log(` WS: ws://localhost:${p}${PATHS.WS}`);
|
|
153
|
+
resolve(p);
|
|
250
154
|
});
|
|
251
155
|
});
|
|
252
|
-
// Cleanup function
|
|
253
156
|
const close = async () => {
|
|
254
157
|
console.log("Closing gateway server...");
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
}
|
|
260
|
-
// Close all WebSocket connections
|
|
261
|
-
wss.clients.forEach((client) => {
|
|
262
|
-
client.close();
|
|
263
|
-
});
|
|
264
|
-
// Close WebSocket server
|
|
265
|
-
await new Promise((resolve) => {
|
|
266
|
-
wss.close(() => resolve());
|
|
267
|
-
});
|
|
268
|
-
// Close HTTP server
|
|
269
|
-
await new Promise((resolve) => {
|
|
270
|
-
httpServer.close(() => resolve());
|
|
271
|
-
});
|
|
158
|
+
await nestApp.close();
|
|
159
|
+
wss.clients.forEach((c) => c.close());
|
|
160
|
+
await new Promise((r) => wss.close(() => r()));
|
|
161
|
+
await new Promise((r) => httpServer.close(() => r()));
|
|
272
162
|
console.log("Gateway server closed");
|
|
273
163
|
};
|
|
274
|
-
return { httpServer, wss, close };
|
|
164
|
+
return { httpServer, wss, port: actualPort, close };
|
|
275
165
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 语音通道 WebSocket(占位)。
|
|
3
|
+
* 后续:/ws/voice 建连、二进制/信令、与语音服务对接;当前仅关闭连接并返回 400 或不做 upgrade。
|
|
4
|
+
*/
|
|
5
|
+
import type { IncomingMessage } from 'http';
|
|
6
|
+
import type { Duplex } from 'stream';
|
|
7
|
+
/**
|
|
8
|
+
* 处理 /ws/voice 的 upgrade 请求(占位)。
|
|
9
|
+
* 返回 true 表示已处理(例如拒绝并关闭),调用方不必再交给 agent /ws;
|
|
10
|
+
* 返回 false 表示未处理。
|
|
11
|
+
*/
|
|
12
|
+
export declare function handleVoiceUpgrade(req: IncomingMessage, socket: Duplex, _head: Buffer): boolean;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { PATHS } from './paths.js';
|
|
2
|
+
/**
|
|
3
|
+
* 处理 /ws/voice 的 upgrade 请求(占位)。
|
|
4
|
+
* 返回 true 表示已处理(例如拒绝并关闭),调用方不必再交给 agent /ws;
|
|
5
|
+
* 返回 false 表示未处理。
|
|
6
|
+
*/
|
|
7
|
+
export function handleVoiceUpgrade(req, socket, _head) {
|
|
8
|
+
const path = req.url?.split('?')[0] || '';
|
|
9
|
+
if (path !== PATHS.WS_VOICE) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
socket.write('HTTP/1.1 501 Not Implemented\r\n' +
|
|
13
|
+
'Content-Type: application/json\r\n' +
|
|
14
|
+
'Connection: close\r\n\r\n' +
|
|
15
|
+
JSON.stringify({ ok: false, message: 'Voice WebSocket not implemented yet' }));
|
|
16
|
+
socket.destroy();
|
|
17
|
+
return true;
|
|
18
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { loadSkillsFromDir, loadSkillsFromPaths, formatSkillsForPrompt, type Skill, type LoadSkillsFromDirOptions, } from "./agent/skills.js";
|
|
2
|
-
export { run, type RunOptions, type RunResult } from "./agent/run.js";
|
|
3
|
-
export { getOpenbotAgentDir, ensureDefaultAgentDir } from "./agent/agent-dir.js";
|
|
4
|
-
export { resolveInstallTarget, installSkillByUrl, installSkillFromPath, type InstallByUrlOptions, type InstallByUrlResult, type InstallFromPathOptions, type InstallFromPathResult, } from "./installer/index.js";
|
|
5
|
-
export { getProviderSupport, ensureProviderSupportFile, syncDesktopConfigToModelsJson, type ProviderSupport, type ProviderSupportEntry, type ProviderSupportModel, type ModelSupportType, } from "./config/desktop-config.js";
|
|
1
|
+
export { loadSkillsFromDir, loadSkillsFromPaths, formatSkillsForPrompt, type Skill, type LoadSkillsFromDirOptions, } from "./core/agent/skills.js";
|
|
2
|
+
export { run, type RunOptions, type RunResult } from "./core/agent/run.js";
|
|
3
|
+
export { getOpenbotAgentDir, ensureDefaultAgentDir } from "./core/agent/agent-dir.js";
|
|
4
|
+
export { resolveInstallTarget, installSkillByUrl, installSkillFromPath, type InstallByUrlOptions, type InstallByUrlResult, type InstallFromPathOptions, type InstallFromPathResult, } from "./core/installer/index.js";
|
|
5
|
+
export { getProviderSupport, ensureProviderSupportFile, syncDesktopConfigToModelsJson, type ProviderSupport, type ProviderSupportEntry, type ProviderSupportModel, type ModelSupportType, } from "./core/config/desktop-config.js";
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { loadSkillsFromDir, loadSkillsFromPaths, formatSkillsForPrompt, } from "./agent/skills.js";
|
|
2
|
-
export { run } from "./agent/run.js";
|
|
3
|
-
export { getOpenbotAgentDir, ensureDefaultAgentDir } from "./agent/agent-dir.js";
|
|
4
|
-
export { resolveInstallTarget, installSkillByUrl, installSkillFromPath, } from "./installer/index.js";
|
|
5
|
-
export { getProviderSupport, ensureProviderSupportFile, syncDesktopConfigToModelsJson, } from "./config/desktop-config.js";
|
|
1
|
+
export { loadSkillsFromDir, loadSkillsFromPaths, formatSkillsForPrompt, } from "./core/agent/skills.js";
|
|
2
|
+
export { run } from "./core/agent/run.js";
|
|
3
|
+
export { getOpenbotAgentDir, ensureDefaultAgentDir } from "./core/agent/agent-dir.js";
|
|
4
|
+
export { resolveInstallTarget, installSkillByUrl, installSkillFromPath, } from "./core/installer/index.js";
|
|
5
|
+
export { getProviderSupport, ensureProviderSupportFile, syncDesktopConfigToModelsJson, } from "./core/config/desktop-config.js";
|
|
@@ -9,7 +9,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
9
9
|
};
|
|
10
10
|
import { Injectable } from '@nestjs/common';
|
|
11
11
|
import { randomUUID } from 'crypto';
|
|
12
|
-
import { agentManager } from '../../agent/agent-manager.js';
|
|
12
|
+
import { agentManager } from '../../core/agent/agent-manager.js';
|
|
13
13
|
import { DatabaseService } from '../database/database.service.js';
|
|
14
14
|
/**
|
|
15
15
|
* Agents service: session + history storage in SQLite.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { INestApplication } from '@nestjs/common';
|
|
2
|
+
import type { Express } from 'express';
|
|
3
|
+
export interface NestAppResult {
|
|
4
|
+
app: INestApplication;
|
|
5
|
+
express: Express;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* 创建 Nest 应用(内嵌模式):不 listen,不设置 globalPrefix。
|
|
9
|
+
* Gateway 将返回的 express 挂到 /server-api,请求路径会剥掉前缀后进入 Nest。
|
|
10
|
+
*/
|
|
11
|
+
export declare function createNestAppEmbedded(): Promise<NestAppResult>;
|
|
12
|
+
/**
|
|
13
|
+
* 独立启动时使用:设置 globalPrefix 并监听端口。
|
|
14
|
+
*/
|
|
15
|
+
export declare function createNestAppStandalone(port?: number): Promise<INestApplication>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nest 应用创建:支持「内嵌到 Gateway」与「独立监听」两种模式。
|
|
3
|
+
* - 内嵌:不设置 globalPrefix、不 listen,返回 { app, express } 供 Gateway 挂载到 /server-api。
|
|
4
|
+
* - 独立:设置 globalPrefix('server-api') 并 listen(port),用于单独启动 Desktop Server。
|
|
5
|
+
*/
|
|
6
|
+
import { NestFactory } from '@nestjs/core';
|
|
7
|
+
import { AppModule } from './app.module.js';
|
|
8
|
+
/**
|
|
9
|
+
* 创建 Nest 应用(内嵌模式):不 listen,不设置 globalPrefix。
|
|
10
|
+
* Gateway 将返回的 express 挂到 /server-api,请求路径会剥掉前缀后进入 Nest。
|
|
11
|
+
*/
|
|
12
|
+
export async function createNestAppEmbedded() {
|
|
13
|
+
const app = await NestFactory.create(AppModule, {
|
|
14
|
+
cors: true,
|
|
15
|
+
});
|
|
16
|
+
app.enableCors({
|
|
17
|
+
origin: ['http://localhost:5173', 'http://localhost:38080', 'http://localhost:38081'],
|
|
18
|
+
credentials: true,
|
|
19
|
+
});
|
|
20
|
+
await app.init();
|
|
21
|
+
const express = app.getHttpAdapter().getInstance();
|
|
22
|
+
return { app, express };
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* 独立启动时使用:设置 globalPrefix 并监听端口。
|
|
26
|
+
*/
|
|
27
|
+
export async function createNestAppStandalone(port = 38081) {
|
|
28
|
+
const app = await NestFactory.create(AppModule, {
|
|
29
|
+
cors: true,
|
|
30
|
+
});
|
|
31
|
+
app.setGlobalPrefix('server-api');
|
|
32
|
+
app.enableCors({
|
|
33
|
+
origin: ['http://localhost:5173', 'http://localhost:38080', 'http://localhost:38081'],
|
|
34
|
+
credentials: true,
|
|
35
|
+
});
|
|
36
|
+
await app.listen(port);
|
|
37
|
+
return app;
|
|
38
|
+
}
|
|
@@ -78,5 +78,5 @@ export declare class ConfigService {
|
|
|
78
78
|
types?: string[];
|
|
79
79
|
}[]>;
|
|
80
80
|
/** 完整 provider 目录(支持列表 + 各 provider 的模型),供前端一次拉取 */
|
|
81
|
-
getProviderSupport(): Promise<import("../../config/provider-support-default.js").ProviderSupport>;
|
|
81
|
+
getProviderSupport(): Promise<import("../../core/config/provider-support-default.js").ProviderSupport>;
|
|
82
82
|
}
|
|
@@ -11,7 +11,7 @@ import { Injectable } from '@nestjs/common';
|
|
|
11
11
|
import { readFile, writeFile, mkdir } from 'fs/promises';
|
|
12
12
|
import { join } from 'path';
|
|
13
13
|
import { existsSync } from 'fs';
|
|
14
|
-
import { getProviderSupport, syncDesktopConfigToModelsJson } from '../../config/desktop-config.js';
|
|
14
|
+
import { getProviderSupport, syncDesktopConfigToModelsJson } from '../../core/config/desktop-config.js';
|
|
15
15
|
import { AgentConfigService } from '../agent-config/agent-config.service.js';
|
|
16
16
|
let ConfigService = class ConfigService {
|
|
17
17
|
agentConfigService;
|
|
@@ -1,10 +1,18 @@
|
|
|
1
|
-
import { OnModuleDestroy } from '@nestjs/common';
|
|
2
|
-
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { OnModuleDestroy, OnModuleInit } from '@nestjs/common';
|
|
2
|
+
/** RunResult compatible with better-sqlite3 for callers that use .changes / .lastInsertRowid */
|
|
3
|
+
export interface RunResult {
|
|
4
|
+
changes: number;
|
|
5
|
+
lastInsertRowid: number;
|
|
6
|
+
}
|
|
7
|
+
export declare class DatabaseService implements OnModuleInit, OnModuleDestroy {
|
|
8
|
+
private sqlDb;
|
|
9
|
+
private dbPath;
|
|
10
|
+
private initPromise;
|
|
11
|
+
onModuleInit(): Promise<void>;
|
|
12
|
+
private doInit;
|
|
13
|
+
private getDb;
|
|
6
14
|
private runMigrations;
|
|
7
|
-
run(sql: string, params?: unknown[]):
|
|
15
|
+
run(sql: string, params?: unknown[]): RunResult;
|
|
8
16
|
get<T>(sql: string, params?: unknown[]): T | undefined;
|
|
9
17
|
all<T>(sql: string, params?: unknown[]): T[];
|
|
10
18
|
onModuleDestroy(): void;
|