@next-open-ai/openclawx 0.6.6

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.
Files changed (198) hide show
  1. package/README.md +523 -0
  2. package/apps/desktop/README.md +210 -0
  3. package/apps/desktop/renderer/dist/assets/index-CYkSfhcp.css +10 -0
  4. package/apps/desktop/renderer/dist/assets/index-FI6O25Ms.js +89 -0
  5. package/apps/desktop/renderer/dist/index.html +22 -0
  6. package/dist/cli/cli.d.ts +2 -0
  7. package/dist/cli/cli.js +198 -0
  8. package/dist/cli/service.d.ts +13 -0
  9. package/dist/cli/service.js +243 -0
  10. package/dist/cli.d.ts +5 -0
  11. package/dist/cli.js +5 -0
  12. package/dist/core/agent/agent-dir.d.ts +14 -0
  13. package/dist/core/agent/agent-dir.js +75 -0
  14. package/dist/core/agent/agent-manager.d.ts +64 -0
  15. package/dist/core/agent/agent-manager.js +278 -0
  16. package/dist/core/agent/config-manager.d.ts +25 -0
  17. package/dist/core/agent/config-manager.js +84 -0
  18. package/dist/core/agent/run.d.ts +26 -0
  19. package/dist/core/agent/run.js +65 -0
  20. package/dist/core/agent/skills.d.ts +20 -0
  21. package/dist/core/agent/skills.js +86 -0
  22. package/dist/core/config/desktop-config.d.ts +90 -0
  23. package/dist/core/config/desktop-config.js +521 -0
  24. package/dist/core/config/provider-support-default.d.ts +21 -0
  25. package/dist/core/config/provider-support-default.js +57 -0
  26. package/dist/core/installer/index.d.ts +1 -0
  27. package/dist/core/installer/index.js +1 -0
  28. package/dist/core/installer/skill-installer.d.ts +39 -0
  29. package/dist/core/installer/skill-installer.js +215 -0
  30. package/dist/core/mcp/adapter.d.ts +17 -0
  31. package/dist/core/mcp/adapter.js +49 -0
  32. package/dist/core/mcp/client.d.ts +24 -0
  33. package/dist/core/mcp/client.js +70 -0
  34. package/dist/core/mcp/config.d.ts +22 -0
  35. package/dist/core/mcp/config.js +69 -0
  36. package/dist/core/mcp/index.d.ts +18 -0
  37. package/dist/core/mcp/index.js +20 -0
  38. package/dist/core/mcp/operator.d.ts +15 -0
  39. package/dist/core/mcp/operator.js +72 -0
  40. package/dist/core/mcp/transport/index.d.ts +11 -0
  41. package/dist/core/mcp/transport/index.js +16 -0
  42. package/dist/core/mcp/transport/sse.d.ts +20 -0
  43. package/dist/core/mcp/transport/sse.js +82 -0
  44. package/dist/core/mcp/transport/stdio.d.ts +32 -0
  45. package/dist/core/mcp/transport/stdio.js +132 -0
  46. package/dist/core/mcp/types.d.ts +72 -0
  47. package/dist/core/mcp/types.js +5 -0
  48. package/dist/core/memory/build-summary.d.ts +6 -0
  49. package/dist/core/memory/build-summary.js +27 -0
  50. package/dist/core/memory/compaction-extension.d.ts +6 -0
  51. package/dist/core/memory/compaction-extension.js +23 -0
  52. package/dist/core/memory/embedding.d.ts +4 -0
  53. package/dist/core/memory/embedding.js +15 -0
  54. package/dist/core/memory/index.d.ts +29 -0
  55. package/dist/core/memory/index.js +70 -0
  56. package/dist/core/memory/remote-embedding.d.ts +10 -0
  57. package/dist/core/memory/remote-embedding.js +36 -0
  58. package/dist/core/memory/types.d.ts +16 -0
  59. package/dist/core/memory/types.js +1 -0
  60. package/dist/core/memory/vector-store.d.ts +15 -0
  61. package/dist/core/memory/vector-store.js +65 -0
  62. package/dist/core/tools/bookmark-tool.d.ts +9 -0
  63. package/dist/core/tools/bookmark-tool.js +118 -0
  64. package/dist/core/tools/browser-tool.d.ts +10 -0
  65. package/dist/core/tools/browser-tool.js +362 -0
  66. package/dist/core/tools/index.d.ts +4 -0
  67. package/dist/core/tools/index.js +4 -0
  68. package/dist/core/tools/install-skill-tool.d.ts +6 -0
  69. package/dist/core/tools/install-skill-tool.js +53 -0
  70. package/dist/core/tools/save-experience-tool.d.ts +5 -0
  71. package/dist/core/tools/save-experience-tool.js +54 -0
  72. package/dist/gateway/auth-hooks.d.ts +17 -0
  73. package/dist/gateway/auth-hooks.js +19 -0
  74. package/dist/gateway/backend-url.d.ts +2 -0
  75. package/dist/gateway/backend-url.js +11 -0
  76. package/dist/gateway/channel-handler.d.ts +6 -0
  77. package/dist/gateway/channel-handler.js +3 -0
  78. package/dist/gateway/clients.d.ts +5 -0
  79. package/dist/gateway/clients.js +4 -0
  80. package/dist/gateway/connection-handler.d.ts +6 -0
  81. package/dist/gateway/connection-handler.js +48 -0
  82. package/dist/gateway/index.d.ts +3 -0
  83. package/dist/gateway/index.js +2 -0
  84. package/dist/gateway/message-handler.d.ts +5 -0
  85. package/dist/gateway/message-handler.js +65 -0
  86. package/dist/gateway/methods/agent-cancel.d.ts +10 -0
  87. package/dist/gateway/methods/agent-cancel.js +17 -0
  88. package/dist/gateway/methods/agent-chat.d.ts +8 -0
  89. package/dist/gateway/methods/agent-chat.js +148 -0
  90. package/dist/gateway/methods/connect.d.ts +9 -0
  91. package/dist/gateway/methods/connect.js +18 -0
  92. package/dist/gateway/methods/install-skill-from-path.d.ts +13 -0
  93. package/dist/gateway/methods/install-skill-from-path.js +15 -0
  94. package/dist/gateway/methods/install-skill-from-upload.d.ts +14 -0
  95. package/dist/gateway/methods/install-skill-from-upload.js +13 -0
  96. package/dist/gateway/methods/run-scheduled-task.d.ts +15 -0
  97. package/dist/gateway/methods/run-scheduled-task.js +127 -0
  98. package/dist/gateway/paths.d.ts +20 -0
  99. package/dist/gateway/paths.js +19 -0
  100. package/dist/gateway/server.d.ts +8 -0
  101. package/dist/gateway/server.js +190 -0
  102. package/dist/gateway/sse-handler.d.ts +6 -0
  103. package/dist/gateway/sse-handler.js +3 -0
  104. package/dist/gateway/types.d.ts +90 -0
  105. package/dist/gateway/types.js +1 -0
  106. package/dist/gateway/utils.d.ts +22 -0
  107. package/dist/gateway/utils.js +67 -0
  108. package/dist/gateway/voice-handler.d.ts +12 -0
  109. package/dist/gateway/voice-handler.js +18 -0
  110. package/dist/index.d.ts +5 -0
  111. package/dist/index.js +5 -0
  112. package/dist/server/agent-config/agent-config.controller.d.ts +30 -0
  113. package/dist/server/agent-config/agent-config.controller.js +83 -0
  114. package/dist/server/agent-config/agent-config.module.d.ts +2 -0
  115. package/dist/server/agent-config/agent-config.module.js +19 -0
  116. package/dist/server/agent-config/agent-config.service.d.ts +53 -0
  117. package/dist/server/agent-config/agent-config.service.js +213 -0
  118. package/dist/server/agents/agents.controller.d.ts +41 -0
  119. package/dist/server/agents/agents.controller.js +118 -0
  120. package/dist/server/agents/agents.gateway.d.ts +21 -0
  121. package/dist/server/agents/agents.gateway.js +103 -0
  122. package/dist/server/agents/agents.module.d.ts +2 -0
  123. package/dist/server/agents/agents.module.js +20 -0
  124. package/dist/server/agents/agents.service.d.ts +63 -0
  125. package/dist/server/agents/agents.service.js +169 -0
  126. package/dist/server/app.module.d.ts +2 -0
  127. package/dist/server/app.module.js +38 -0
  128. package/dist/server/auth/auth.controller.d.ts +20 -0
  129. package/dist/server/auth/auth.controller.js +64 -0
  130. package/dist/server/auth/auth.module.d.ts +2 -0
  131. package/dist/server/auth/auth.module.js +19 -0
  132. package/dist/server/bootstrap.d.ts +15 -0
  133. package/dist/server/bootstrap.js +38 -0
  134. package/dist/server/config/config.controller.d.ts +73 -0
  135. package/dist/server/config/config.controller.js +95 -0
  136. package/dist/server/config/config.module.d.ts +2 -0
  137. package/dist/server/config/config.module.js +21 -0
  138. package/dist/server/config/config.service.d.ts +82 -0
  139. package/dist/server/config/config.service.js +123 -0
  140. package/dist/server/database/database.module.d.ts +2 -0
  141. package/dist/server/database/database.module.js +18 -0
  142. package/dist/server/database/database.service.d.ts +26 -0
  143. package/dist/server/database/database.service.js +253 -0
  144. package/dist/server/main.d.ts +1 -0
  145. package/dist/server/main.js +9 -0
  146. package/dist/server/saved-items/saved-items.controller.d.ts +57 -0
  147. package/dist/server/saved-items/saved-items.controller.js +229 -0
  148. package/dist/server/saved-items/saved-items.module.d.ts +2 -0
  149. package/dist/server/saved-items/saved-items.module.js +25 -0
  150. package/dist/server/saved-items/saved-items.service.d.ts +31 -0
  151. package/dist/server/saved-items/saved-items.service.js +105 -0
  152. package/dist/server/saved-items/tags.controller.d.ts +30 -0
  153. package/dist/server/saved-items/tags.controller.js +85 -0
  154. package/dist/server/saved-items/tags.service.d.ts +24 -0
  155. package/dist/server/saved-items/tags.service.js +84 -0
  156. package/dist/server/skills/skills.controller.d.ts +63 -0
  157. package/dist/server/skills/skills.controller.js +194 -0
  158. package/dist/server/skills/skills.module.d.ts +2 -0
  159. package/dist/server/skills/skills.module.js +22 -0
  160. package/dist/server/skills/skills.service.d.ts +65 -0
  161. package/dist/server/skills/skills.service.js +388 -0
  162. package/dist/server/tasks/tasks.controller.d.ts +52 -0
  163. package/dist/server/tasks/tasks.controller.js +163 -0
  164. package/dist/server/tasks/tasks.module.d.ts +2 -0
  165. package/dist/server/tasks/tasks.module.js +23 -0
  166. package/dist/server/tasks/tasks.service.d.ts +86 -0
  167. package/dist/server/tasks/tasks.service.js +327 -0
  168. package/dist/server/usage/usage.controller.d.ts +12 -0
  169. package/dist/server/usage/usage.controller.js +46 -0
  170. package/dist/server/usage/usage.module.d.ts +2 -0
  171. package/dist/server/usage/usage.module.js +19 -0
  172. package/dist/server/usage/usage.service.d.ts +21 -0
  173. package/dist/server/usage/usage.service.js +55 -0
  174. package/dist/server/users/users.controller.d.ts +35 -0
  175. package/dist/server/users/users.controller.js +69 -0
  176. package/dist/server/users/users.module.d.ts +2 -0
  177. package/dist/server/users/users.module.js +19 -0
  178. package/dist/server/users/users.service.d.ts +39 -0
  179. package/dist/server/users/users.service.js +140 -0
  180. package/dist/server/workspace/workspace.controller.d.ts +24 -0
  181. package/dist/server/workspace/workspace.controller.js +132 -0
  182. package/dist/server/workspace/workspace.module.d.ts +2 -0
  183. package/dist/server/workspace/workspace.module.js +21 -0
  184. package/dist/server/workspace/workspace.service.d.ts +36 -0
  185. package/dist/server/workspace/workspace.service.js +142 -0
  186. package/package.json +90 -0
  187. package/skills/agent-browser/SKILL.md +207 -0
  188. package/skills/agent-browser/references/authentication.md +202 -0
  189. package/skills/agent-browser/references/commands.md +259 -0
  190. package/skills/agent-browser/references/proxy-support.md +188 -0
  191. package/skills/agent-browser/references/session-management.md +193 -0
  192. package/skills/agent-browser/references/snapshot-refs.md +194 -0
  193. package/skills/agent-browser/references/video-recording.md +173 -0
  194. package/skills/agent-browser/templates/authenticated-session.sh +97 -0
  195. package/skills/agent-browser/templates/capture-workflow.sh +69 -0
  196. package/skills/agent-browser/templates/form-automation.sh +62 -0
  197. package/skills/find-skills/SKILL.md +140 -0
  198. package/skills/url-bookmark/SKILL.md +36 -0
@@ -0,0 +1,19 @@
1
+ /** HTTP:/server-api 进入 Nest 前的鉴权钩子(占位,直接 next) */
2
+ export function authHookServerApi(req, res, next) {
3
+ next();
4
+ }
5
+ /** HTTP:/channel 进入通道模块前的鉴权钩子(占位,直接 next) */
6
+ export function authHookChannel(req, res, next) {
7
+ next();
8
+ }
9
+ /** HTTP:/sse 进入 SSE 前的鉴权钩子(占位,直接 next) */
10
+ export function authHookSse(req, res, next) {
11
+ next();
12
+ }
13
+ /**
14
+ * WebSocket upgrade:/ws 或 /ws/voice 连接前的鉴权钩子(占位)。
15
+ * 返回 true 表示放行,false 表示拒绝(调用方应关闭 socket)。
16
+ */
17
+ export function authHookWs(_req, _path) {
18
+ return true;
19
+ }
@@ -0,0 +1,2 @@
1
+ export declare function setBackendBaseUrl(url: string): void;
2
+ export declare function getBackendBaseUrl(): string | null;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Backend base URL for the Nest Desktop Server (e.g. http://localhost:38081).
3
+ * Set by startGatewayServer so agent-chat / usage reporting can POST to server-api.
4
+ */
5
+ let backendBaseUrl = null;
6
+ export function setBackendBaseUrl(url) {
7
+ backendBaseUrl = url;
8
+ }
9
+ export function getBackendBaseUrl() {
10
+ return backendBaseUrl;
11
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * 通道模块 HTTP 处理(占位)。
3
+ * 后续:接收外部 webhook、回调,校验后转内部事件;当前返回 501。
4
+ */
5
+ import type { Request, Response } from 'express';
6
+ export declare function handleChannel(_req: Request, res: Response): void;
@@ -0,0 +1,3 @@
1
+ export function handleChannel(_req, res) {
2
+ res.status(501).setHeader('Content-Type', 'application/json').end(JSON.stringify({ ok: false, message: 'Channel module not implemented yet' }));
3
+ }
@@ -0,0 +1,5 @@
1
+ import type { GatewayClient } from "./types.js";
2
+ /**
3
+ * Connected clients
4
+ */
5
+ export declare const connectedClients: Set<GatewayClient>;
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Connected clients
3
+ */
4
+ export const connectedClients = new Set();
@@ -0,0 +1,6 @@
1
+ import type { WebSocket } from "ws";
2
+ import type { IncomingMessage } from "http";
3
+ /**
4
+ * Handle new WebSocket connection
5
+ */
6
+ export declare function handleConnection(ws: WebSocket, req: IncomingMessage): void;
@@ -0,0 +1,48 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { send, createEvent } from "./utils.js";
3
+ import { handleMessage } from "./message-handler.js";
4
+ import { connectedClients } from "./clients.js";
5
+ /**
6
+ * Handle new WebSocket connection
7
+ */
8
+ export function handleConnection(ws, req) {
9
+ const connId = randomUUID();
10
+ const remoteAddr = req.socket.remoteAddress;
11
+ console.log(`New connection: ${connId} from ${remoteAddr}`);
12
+ // Create client object
13
+ const client = {
14
+ id: connId,
15
+ ws,
16
+ authenticated: false,
17
+ connectedAt: Date.now(),
18
+ };
19
+ connectedClients.add(client);
20
+ // Send connection challenge
21
+ send(ws, createEvent("connect.challenge", {
22
+ nonce: randomUUID(),
23
+ ts: Date.now(),
24
+ }));
25
+ // Handle incoming messages
26
+ ws.on("message", async (data) => {
27
+ console.log(`Raw message received from ${connId}, type: ${typeof data}`);
28
+ try {
29
+ // Convert RawData to Buffer
30
+ const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data.toString());
31
+ console.log(`Message buffer length: ${buffer.length}`);
32
+ await handleMessage(client, buffer);
33
+ }
34
+ catch (error) {
35
+ console.error(`Error handling message from ${connId}:`, error);
36
+ }
37
+ });
38
+ // Handle connection close
39
+ ws.on("close", (code, reason) => {
40
+ connectedClients.delete(client);
41
+ const duration = Date.now() - client.connectedAt;
42
+ console.log(`Connection closed: ${connId} (duration: ${duration}ms, code: ${code}, reason: ${reason.toString()})`);
43
+ });
44
+ // Handle errors
45
+ ws.on("error", (error) => {
46
+ console.error(`WebSocket error for ${connId}:`, error);
47
+ });
48
+ }
@@ -0,0 +1,3 @@
1
+ export { startGatewayServer } from "./server.js";
2
+ export { agentManager } from "../core/agent/agent-manager.js";
3
+ export type { GatewayClient, GatewayMessage, GatewayRequest, GatewayResponse, GatewayEvent, ConnectParams, AgentChatParams, } from "./types.js";
@@ -0,0 +1,2 @@
1
+ export { startGatewayServer } from "./server.js";
2
+ export { agentManager } from "../core/agent/agent-manager.js";
@@ -0,0 +1,5 @@
1
+ import type { GatewayClient } from "./types.js";
2
+ /**
3
+ * Handle incoming WebSocket message
4
+ */
5
+ export declare function handleMessage(client: GatewayClient, data: Buffer | string): Promise<void>;
@@ -0,0 +1,65 @@
1
+ import { parseMessage, send, createErrorResponse, createSuccessResponse } from "./utils.js";
2
+ import { handleConnect } from "./methods/connect.js";
3
+ import { handleAgentChat } from "./methods/agent-chat.js";
4
+ import { handleAgentCancel } from "./methods/agent-cancel.js";
5
+ /**
6
+ * Handle incoming WebSocket message
7
+ */
8
+ export async function handleMessage(client, data) {
9
+ const message = parseMessage(data);
10
+ if (!message) {
11
+ send(client.ws, createErrorResponse("unknown", "Invalid message format"));
12
+ return;
13
+ }
14
+ // Only handle request messages
15
+ if (message.type !== "request") {
16
+ return;
17
+ }
18
+ const request = message;
19
+ const { id, method, params } = request;
20
+ console.log(`Received request: ${method} (id: ${id})`);
21
+ try {
22
+ let result;
23
+ switch (method) {
24
+ case "connect":
25
+ result = await handleConnect(client, params || {});
26
+ break;
27
+ case "agent.chat":
28
+ // Check if client is authenticated
29
+ if (!client.authenticated) {
30
+ throw new Error("Not authenticated. Call 'connect' first.");
31
+ }
32
+ result = await handleAgentChat(client, params || {});
33
+ break;
34
+ case "agent.cancel":
35
+ if (!client.authenticated) {
36
+ throw new Error("Not authenticated. Call 'connect' first.");
37
+ }
38
+ result = await handleAgentCancel(client, params || {});
39
+ break;
40
+ case "subscribe_session":
41
+ // Handle session subscription
42
+ // Since handleAgentChat manages its own subscription per request,
43
+ // this might be for listening to async updates.
44
+ // For now, we just acknowledge it to prevent client errors.
45
+ // A more robust implementation would hook into agentManager.
46
+ console.log(`Client ${client.id} subscribed to session ${params.sessionId}`);
47
+ client.sessionId = params.sessionId; // Store session ID on client
48
+ result = { status: "subscribed", sessionId: params.sessionId };
49
+ break;
50
+ case "unsubscribe_session":
51
+ console.log(`Client ${client.id} unsubscribed from session`);
52
+ delete client.sessionId;
53
+ result = { status: "unsubscribed" };
54
+ break;
55
+ default:
56
+ throw new Error(`Unknown method: ${method}`);
57
+ }
58
+ // Send success response
59
+ send(client.ws, createSuccessResponse(id, result));
60
+ }
61
+ catch (error) {
62
+ console.error(`Error handling ${method}:`, error);
63
+ send(client.ws, createErrorResponse(id, error.message || "Internal error"));
64
+ }
65
+ }
@@ -0,0 +1,10 @@
1
+ import type { GatewayClient } from "../types.js";
2
+ /**
3
+ * Handle agent.cancel: abort the current turn for the given session.
4
+ * Uses pi-coding-agent's session.abort() to stop the running agent and wait until idle.
5
+ */
6
+ export declare function handleAgentCancel(client: GatewayClient, params: {
7
+ sessionId?: string;
8
+ }): Promise<{
9
+ status: string;
10
+ }>;
@@ -0,0 +1,17 @@
1
+ import { agentManager } from "../../core/agent/agent-manager.js";
2
+ /**
3
+ * Handle agent.cancel: abort the current turn for the given session.
4
+ * Uses pi-coding-agent's session.abort() to stop the running agent and wait until idle.
5
+ */
6
+ export async function handleAgentCancel(client, params) {
7
+ const sessionId = params?.sessionId ?? client.sessionId;
8
+ if (!sessionId) {
9
+ throw new Error("No session ID available");
10
+ }
11
+ const session = agentManager.getSession(sessionId);
12
+ if (!session) {
13
+ return { status: "no_session" };
14
+ }
15
+ await session.abort();
16
+ return { status: "aborted" };
17
+ }
@@ -0,0 +1,8 @@
1
+ import type { GatewayClient, AgentChatParams } from "../types.js";
2
+ /**
3
+ * Handle agent chat request with streaming support
4
+ */
5
+ export declare function handleAgentChat(client: GatewayClient, params: AgentChatParams): Promise<{
6
+ status: string;
7
+ sessionId: string;
8
+ }>;
@@ -0,0 +1,148 @@
1
+ import { agentManager } from "../../core/agent/agent-manager.js";
2
+ import { getExperienceContextForUserMessage } from "../../core/memory/index.js";
3
+ import { send, createEvent } from "../utils.js";
4
+ import { connectedClients } from "../clients.js";
5
+ import { getDesktopConfig, loadDesktopAgentConfig } from "../../core/config/desktop-config.js";
6
+ /**
7
+ * Broadcast message to all clients subscribed to a session
8
+ */
9
+ function broadcastToSession(sessionId, message) {
10
+ for (const client of connectedClients) {
11
+ if (client.sessionId === sessionId) {
12
+ send(client.ws, message);
13
+ }
14
+ }
15
+ }
16
+ /**
17
+ * Handle agent chat request with streaming support
18
+ */
19
+ export async function handleAgentChat(client, params) {
20
+ const { message, sessionId, targetAgentId } = params;
21
+ if (!message || !message.trim()) {
22
+ throw new Error("Message is required");
23
+ }
24
+ // Use client's session ID if not provided
25
+ const targetSessionId = sessionId || client.sessionId;
26
+ if (!targetSessionId) {
27
+ throw new Error("No session ID available");
28
+ }
29
+ console.log(`Agent chat request for session ${targetSessionId}: ${message.substring(0, 50)}...`);
30
+ return handleAgentChatInner(client, targetSessionId, message, params);
31
+ }
32
+ async function handleAgentChatInner(client, targetSessionId, message, params) {
33
+ const { targetAgentId } = params;
34
+ // 客户端在 connect 或 agent.chat 传入 agentId/sessionType,Gateway 不再请求 Nest
35
+ const sessionAgentId = params.agentId ?? client.agentId ?? "default";
36
+ const sessionType = params.sessionType ?? client.sessionType ?? "chat";
37
+ let workspace = "default";
38
+ let provider;
39
+ let modelId;
40
+ let apiKey;
41
+ const agentConfig = await loadDesktopAgentConfig(sessionAgentId);
42
+ if (agentConfig) {
43
+ if (agentConfig.workspace)
44
+ workspace = agentConfig.workspace;
45
+ provider = agentConfig.provider;
46
+ modelId = agentConfig.model;
47
+ if (agentConfig.apiKey)
48
+ apiKey = agentConfig.apiKey;
49
+ }
50
+ // system / scheduled:每次对话前先删再建,对话结束马上关闭,节省资源
51
+ const isEphemeralSession = sessionType === "system" || sessionType === "scheduled";
52
+ if (isEphemeralSession) {
53
+ agentManager.deleteSession(targetSessionId);
54
+ }
55
+ // system 会话用请求里的 targetAgentId;chat/scheduled 用 session 对应的 agentId 传给 install_skill
56
+ const effectiveTargetAgentId = sessionType === "system" ? targetAgentId : sessionAgentId;
57
+ const { maxAgentSessions } = getDesktopConfig();
58
+ let session;
59
+ try {
60
+ session = await agentManager.getOrCreateSession(targetSessionId, {
61
+ workspace,
62
+ provider,
63
+ modelId,
64
+ apiKey,
65
+ maxSessions: maxAgentSessions,
66
+ targetAgentId: effectiveTargetAgentId,
67
+ mcpServers: agentConfig?.mcpServers,
68
+ });
69
+ }
70
+ catch (err) {
71
+ const msg = err?.message ?? String(err);
72
+ if (msg.includes("No API key") || msg.includes("API key")) {
73
+ const prov = provider ?? "deepseek";
74
+ throw new Error(`未配置 ${prov} 的 API Key。请在桌面端「设置」-「模型/API」中配置,或运行:openbot login ${prov} <你的API Key>`);
75
+ }
76
+ throw err;
77
+ }
78
+ // Set up event listener for streaming
79
+ const unsubscribe = session.subscribe((event) => {
80
+ // console.log(`Agent event received: ${event.type}`); // Reduce noise
81
+ let wsMessage = null;
82
+ if (event.type === "message_update") {
83
+ const update = event;
84
+ if (update.assistantMessageEvent && update.assistantMessageEvent.type === "text_delta") {
85
+ wsMessage = createEvent("agent.chunk", { text: update.assistantMessageEvent.delta });
86
+ }
87
+ else if (update.assistantMessageEvent && update.assistantMessageEvent.type === "thinking_delta") {
88
+ wsMessage = createEvent("agent.chunk", { text: update.assistantMessageEvent.delta, isThinking: true });
89
+ }
90
+ }
91
+ else if (event.type === "tool_execution_start") {
92
+ wsMessage = createEvent("agent.tool", {
93
+ type: "start",
94
+ toolCallId: event.toolCallId,
95
+ toolName: event.toolName,
96
+ args: event.args
97
+ });
98
+ }
99
+ else if (event.type === "tool_execution_end") {
100
+ wsMessage = createEvent("agent.tool", {
101
+ type: "end",
102
+ toolCallId: event.toolCallId,
103
+ toolName: event.toolName,
104
+ result: event.result,
105
+ isError: event.isError
106
+ });
107
+ }
108
+ else if (event.type === "turn_end") {
109
+ const usage = event.message?.usage;
110
+ const promptTokens = Number(usage?.input ?? usage?.input_tokens ?? 0) || 0;
111
+ const completionTokens = Number(usage?.output ?? usage?.output_tokens ?? 0) || 0;
112
+ const usagePayload = promptTokens > 0 || completionTokens > 0
113
+ ? { promptTokens, completionTokens }
114
+ : undefined;
115
+ wsMessage = createEvent("message_complete", {
116
+ sessionId: targetSessionId,
117
+ content: "",
118
+ ...(usagePayload && { usage: usagePayload }),
119
+ });
120
+ }
121
+ if (wsMessage) {
122
+ broadcastToSession(targetSessionId, wsMessage);
123
+ }
124
+ if (event.type === "turn_end" && isEphemeralSession) {
125
+ agentManager.deleteSession(targetSessionId);
126
+ }
127
+ });
128
+ try {
129
+ const experienceBlock = await getExperienceContextForUserMessage();
130
+ const userMessageToSend = experienceBlock.trim().length > 0
131
+ ? `${experienceBlock}\n\n用户问题:\n${message}`
132
+ : message;
133
+ // 若 agent 正在流式输出,deliverAs: 'followUp' 将本条消息排队,避免抛出 "Agent is already processing"
134
+ await session.sendUserMessage(userMessageToSend, { deliverAs: "followUp" });
135
+ console.log(`Agent chat completed for session ${targetSessionId}`);
136
+ return {
137
+ status: "completed",
138
+ sessionId: targetSessionId,
139
+ };
140
+ }
141
+ catch (error) {
142
+ console.error(`Error in agent chat:`, error);
143
+ throw error;
144
+ }
145
+ finally {
146
+ unsubscribe();
147
+ }
148
+ }
@@ -0,0 +1,9 @@
1
+ import type { GatewayClient, ConnectParams } from "../types.js";
2
+ /**
3
+ * Handle client connection request.
4
+ * 客户端传入 sessionId、agentId、sessionType,Gateway 不再为配置/类型请求 Nest。
5
+ */
6
+ export declare function handleConnect(client: GatewayClient, params: ConnectParams): Promise<{
7
+ sessionId: string;
8
+ status: string;
9
+ }>;
@@ -0,0 +1,18 @@
1
+ import { randomUUID } from "crypto";
2
+ /**
3
+ * Handle client connection request.
4
+ * 客户端传入 sessionId、agentId、sessionType,Gateway 不再为配置/类型请求 Nest。
5
+ */
6
+ export async function handleConnect(client, params) {
7
+ client.authenticated = true;
8
+ client.sessionId = params.sessionId || randomUUID();
9
+ if (params.agentId !== undefined)
10
+ client.agentId = params.agentId;
11
+ if (params.sessionType !== undefined)
12
+ client.sessionType = params.sessionType;
13
+ console.log(`Client ${client.id} connected with session ${client.sessionId}, agentId=${client.agentId ?? "default"}, type=${client.sessionType ?? "chat"}`);
14
+ return {
15
+ sessionId: client.sessionId || "",
16
+ status: "connected",
17
+ };
18
+ }
@@ -0,0 +1,13 @@
1
+ export interface InstallFromPathBody {
2
+ path: string;
3
+ scope?: "global" | "workspace";
4
+ workspace?: string;
5
+ }
6
+ export interface InstallFromPathResult {
7
+ success: true;
8
+ data: {
9
+ installDir: string;
10
+ name: string;
11
+ };
12
+ }
13
+ export declare function handleInstallSkillFromPath(body: InstallFromPathBody): Promise<InstallFromPathResult>;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * 在 Gateway 层处理 POST /server-api/skills/install-from-path,
3
+ * 委托核心 installer 将本地技能目录复制到全局或工作区,不依赖 Nest。
4
+ */
5
+ import { installSkillFromPath } from "../../core/installer/index.js";
6
+ export async function handleInstallSkillFromPath(body) {
7
+ const localPath = (body?.path ?? "").trim();
8
+ if (!localPath) {
9
+ throw new Error("path is required");
10
+ }
11
+ const scope = body?.scope ?? "global";
12
+ const workspaceName = body?.workspace ?? "default";
13
+ const result = await installSkillFromPath(localPath, { scope, workspace: workspaceName });
14
+ return { success: true, data: { installDir: result.installDir, name: result.name } };
15
+ }
@@ -0,0 +1,14 @@
1
+ export interface InstallFromUploadBody {
2
+ /** zip 文件 buffer */
3
+ buffer: Buffer;
4
+ scope?: "global" | "workspace";
5
+ workspace?: string;
6
+ }
7
+ export interface InstallFromUploadResult {
8
+ success: true;
9
+ data: {
10
+ installDir: string;
11
+ name: string;
12
+ };
13
+ }
14
+ export declare function handleInstallSkillFromUpload(body: InstallFromUploadBody): Promise<InstallFromUploadResult>;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * 在 Gateway 层处理 POST /server-api/skills/install-from-upload,
3
+ * 接收 multipart zip 文件,委托核心 installer 解压并安装到全局或工作区。
4
+ */
5
+ import { installSkillFromUpload } from "../../core/installer/index.js";
6
+ export async function handleInstallSkillFromUpload(body) {
7
+ const { buffer, scope = "global", workspace = "default" } = body;
8
+ if (!buffer || !Buffer.isBuffer(buffer) || buffer.length === 0) {
9
+ throw new Error("请上传 zip 文件");
10
+ }
11
+ const result = await installSkillFromUpload(buffer, { scope, workspace });
12
+ return { success: true, data: { installDir: result.installDir, name: result.name } };
13
+ }
@@ -0,0 +1,15 @@
1
+ import type { IncomingMessage, ServerResponse } from "http";
2
+ export interface RunScheduledTaskBody {
3
+ sessionId: string;
4
+ message: string;
5
+ /** 该 session 绑定的 agentId,Gateway 用其本地取配置,不请求 Nest;不传则 default */
6
+ agentId?: string;
7
+ workspace?: string;
8
+ taskId?: string;
9
+ backendBaseUrl?: string;
10
+ }
11
+ /**
12
+ * Run a scheduled task: configure workspace, send message to agent, collect response, POST back to Nest.
13
+ * 执行完成后关闭并移除 AgentSession,避免空悬占用资源。
14
+ */
15
+ export declare function handleRunScheduledTask(req: IncomingMessage, res: ServerResponse): Promise<void>;
@@ -0,0 +1,127 @@
1
+ import { agentManager } from "../../core/agent/agent-manager.js";
2
+ import { getExperienceContextForUserMessage } from "../../core/memory/index.js";
3
+ import { loadDesktopAgentConfig } from "../../core/config/desktop-config.js";
4
+ async function readBody(req) {
5
+ return new Promise((resolve, reject) => {
6
+ const chunks = [];
7
+ req.on("data", (chunk) => chunks.push(chunk));
8
+ req.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
9
+ req.on("error", reject);
10
+ });
11
+ }
12
+ /**
13
+ * Run a scheduled task: configure workspace, send message to agent, collect response, POST back to Nest.
14
+ * 执行完成后关闭并移除 AgentSession,避免空悬占用资源。
15
+ */
16
+ export async function handleRunScheduledTask(req, res) {
17
+ if (req.method !== "POST") {
18
+ res.writeHead(405, { "Content-Type": "application/json" });
19
+ res.end(JSON.stringify({ error: "Method not allowed" }));
20
+ return;
21
+ }
22
+ let body;
23
+ try {
24
+ const raw = await readBody(req);
25
+ body = JSON.parse(raw);
26
+ }
27
+ catch {
28
+ res.writeHead(400, { "Content-Type": "application/json" });
29
+ res.end(JSON.stringify({ error: "Invalid JSON body" }));
30
+ return;
31
+ }
32
+ const { sessionId, message, agentId: bodyAgentId, backendBaseUrl, taskId } = body;
33
+ if (!sessionId || !message) {
34
+ res.writeHead(400, { "Content-Type": "application/json" });
35
+ res.end(JSON.stringify({ error: "sessionId, message required" }));
36
+ return;
37
+ }
38
+ const sessionAgentId = bodyAgentId ?? "default";
39
+ let resolvedWorkspace = "default";
40
+ let provider;
41
+ let modelId;
42
+ let apiKey;
43
+ const agentConfig = await loadDesktopAgentConfig(sessionAgentId);
44
+ if (agentConfig) {
45
+ if (agentConfig.workspace)
46
+ resolvedWorkspace = agentConfig.workspace;
47
+ provider = agentConfig.provider;
48
+ modelId = agentConfig.model;
49
+ if (agentConfig.apiKey)
50
+ apiKey = agentConfig.apiKey;
51
+ }
52
+ try {
53
+ const session = await agentManager.getOrCreateSession(sessionId, {
54
+ workspace: resolvedWorkspace,
55
+ provider,
56
+ modelId,
57
+ apiKey,
58
+ mcpServers: agentConfig?.mcpServers,
59
+ });
60
+ let assistantContent = "";
61
+ let turnPromptTokens = 0;
62
+ let turnCompletionTokens = 0;
63
+ const unsubscribe = session.subscribe((event) => {
64
+ if (event.type === "message_update" && event.assistantMessageEvent) {
65
+ const ev = event.assistantMessageEvent;
66
+ if (ev.type === "text_delta" && ev.delta) {
67
+ assistantContent += ev.delta;
68
+ }
69
+ }
70
+ else if (event.type === "turn_end") {
71
+ const usage = event.message?.usage;
72
+ if (usage) {
73
+ turnPromptTokens += Number(usage.input ?? usage.input_tokens ?? 0) || 0;
74
+ turnCompletionTokens += Number(usage.output ?? usage.output_tokens ?? 0) || 0;
75
+ }
76
+ }
77
+ });
78
+ const experienceBlock = await getExperienceContextForUserMessage();
79
+ const userMessageToSend = experienceBlock.trim().length > 0
80
+ ? `${experienceBlock}\n\n用户问题:\n${message}`
81
+ : message;
82
+ // 定时任务复用同一 session:若上次执行未结束会报 "Agent is already processing"。先等待空闲再发,避免并发。
83
+ const idleTimeoutMs = 10 * 60 * 1000;
84
+ const pollMs = 2000;
85
+ let waited = 0;
86
+ while (session.isStreaming && waited < idleTimeoutMs) {
87
+ await new Promise((r) => setTimeout(r, pollMs));
88
+ waited += pollMs;
89
+ }
90
+ if (session.isStreaming) {
91
+ throw new Error("Session still busy after waiting; try again later.");
92
+ }
93
+ await session.sendUserMessage(userMessageToSend);
94
+ unsubscribe();
95
+ if (backendBaseUrl && assistantContent !== undefined) {
96
+ const url = `${backendBaseUrl.replace(/\/$/, "")}/server-api/agents/sessions/${encodeURIComponent(sessionId)}/messages`;
97
+ await fetch(url, {
98
+ method: "POST",
99
+ headers: { "Content-Type": "application/json" },
100
+ body: JSON.stringify({ role: "assistant", content: assistantContent }),
101
+ }).catch((err) => console.error("[run-scheduled-task] POST assistant message failed:", err));
102
+ }
103
+ const usage = turnPromptTokens > 0 || turnCompletionTokens > 0
104
+ ? { promptTokens: turnPromptTokens, completionTokens: turnCompletionTokens }
105
+ : undefined;
106
+ res.writeHead(200, { "Content-Type": "application/json" });
107
+ res.end(JSON.stringify({
108
+ success: true,
109
+ sessionId,
110
+ assistantContent: assistantContent ?? "",
111
+ ...(usage && { usage }),
112
+ }));
113
+ }
114
+ catch (error) {
115
+ console.error("[run-scheduled-task] error:", error);
116
+ const msg = error?.message ?? String(error);
117
+ const friendlyError = msg.includes("No API key") || msg.includes("API key")
118
+ ? "未配置大模型 API Key。请在桌面端「设置」-「模型配置」中为当前智能体选择 Provider 并保存,并确保在「Provider 配置」中已填写对应 API Key。"
119
+ : msg || "Internal server error";
120
+ res.writeHead(500, { "Content-Type": "application/json" });
121
+ res.end(JSON.stringify({ success: false, error: friendlyError }));
122
+ }
123
+ finally {
124
+ // 执行完成(成功或失败)后立即关闭并移除该 AgentSession,避免空悬占用内存/连接等资源
125
+ agentManager.deleteSession(sessionId);
126
+ }
127
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Gateway 路径常量:各模块使用独立 path 端点,便于路由与扩展。
3
+ */
4
+ export declare const PATHS: {
5
+ /** 业务 REST API(Nest) */
6
+ readonly SERVER_API: "/server-api";
7
+ /** Agent 对话 WebSocket */
8
+ readonly WS: "/ws";
9
+ /** 语音通道 WebSocket(扩展占位) */
10
+ readonly WS_VOICE: "/ws/voice";
11
+ /** Agent 流式 SSE(占位) */
12
+ readonly SSE: "/sse";
13
+ /** 通道模块(webhook / 回调) */
14
+ readonly CHANNEL: "/channel";
15
+ /** 健康检查 */
16
+ readonly HEALTH: "/health";
17
+ /** 定时任务执行(Nest 回调 Gateway) */
18
+ readonly RUN_SCHEDULED_TASK: "/run-scheduled-task";
19
+ };
20
+ export type PathKey = keyof typeof PATHS;