@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.
- package/README.md +523 -0
- package/apps/desktop/README.md +210 -0
- package/apps/desktop/renderer/dist/assets/index-CYkSfhcp.css +10 -0
- package/apps/desktop/renderer/dist/assets/index-FI6O25Ms.js +89 -0
- package/apps/desktop/renderer/dist/index.html +22 -0
- package/dist/cli/cli.d.ts +2 -0
- package/dist/cli/cli.js +198 -0
- package/dist/cli/service.d.ts +13 -0
- package/dist/cli/service.js +243 -0
- package/dist/cli.d.ts +5 -0
- package/dist/cli.js +5 -0
- package/dist/core/agent/agent-dir.d.ts +14 -0
- package/dist/core/agent/agent-dir.js +75 -0
- package/dist/core/agent/agent-manager.d.ts +64 -0
- package/dist/core/agent/agent-manager.js +278 -0
- package/dist/core/agent/config-manager.d.ts +25 -0
- package/dist/core/agent/config-manager.js +84 -0
- package/dist/core/agent/run.d.ts +26 -0
- package/dist/core/agent/run.js +65 -0
- package/dist/core/agent/skills.d.ts +20 -0
- package/dist/core/agent/skills.js +86 -0
- package/dist/core/config/desktop-config.d.ts +90 -0
- package/dist/core/config/desktop-config.js +521 -0
- package/dist/core/config/provider-support-default.d.ts +21 -0
- package/dist/core/config/provider-support-default.js +57 -0
- package/dist/core/installer/index.d.ts +1 -0
- package/dist/core/installer/index.js +1 -0
- package/dist/core/installer/skill-installer.d.ts +39 -0
- package/dist/core/installer/skill-installer.js +215 -0
- package/dist/core/mcp/adapter.d.ts +17 -0
- package/dist/core/mcp/adapter.js +49 -0
- package/dist/core/mcp/client.d.ts +24 -0
- package/dist/core/mcp/client.js +70 -0
- package/dist/core/mcp/config.d.ts +22 -0
- package/dist/core/mcp/config.js +69 -0
- package/dist/core/mcp/index.d.ts +18 -0
- package/dist/core/mcp/index.js +20 -0
- package/dist/core/mcp/operator.d.ts +15 -0
- package/dist/core/mcp/operator.js +72 -0
- package/dist/core/mcp/transport/index.d.ts +11 -0
- package/dist/core/mcp/transport/index.js +16 -0
- package/dist/core/mcp/transport/sse.d.ts +20 -0
- package/dist/core/mcp/transport/sse.js +82 -0
- package/dist/core/mcp/transport/stdio.d.ts +32 -0
- package/dist/core/mcp/transport/stdio.js +132 -0
- package/dist/core/mcp/types.d.ts +72 -0
- package/dist/core/mcp/types.js +5 -0
- package/dist/core/memory/build-summary.d.ts +6 -0
- package/dist/core/memory/build-summary.js +27 -0
- package/dist/core/memory/compaction-extension.d.ts +6 -0
- package/dist/core/memory/compaction-extension.js +23 -0
- package/dist/core/memory/embedding.d.ts +4 -0
- package/dist/core/memory/embedding.js +15 -0
- package/dist/core/memory/index.d.ts +29 -0
- package/dist/core/memory/index.js +70 -0
- package/dist/core/memory/remote-embedding.d.ts +10 -0
- package/dist/core/memory/remote-embedding.js +36 -0
- package/dist/core/memory/types.d.ts +16 -0
- package/dist/core/memory/types.js +1 -0
- package/dist/core/memory/vector-store.d.ts +15 -0
- package/dist/core/memory/vector-store.js +65 -0
- package/dist/core/tools/bookmark-tool.d.ts +9 -0
- package/dist/core/tools/bookmark-tool.js +118 -0
- package/dist/core/tools/browser-tool.d.ts +10 -0
- package/dist/core/tools/browser-tool.js +362 -0
- package/dist/core/tools/index.d.ts +4 -0
- package/dist/core/tools/index.js +4 -0
- package/dist/core/tools/install-skill-tool.d.ts +6 -0
- package/dist/core/tools/install-skill-tool.js +53 -0
- package/dist/core/tools/save-experience-tool.d.ts +5 -0
- package/dist/core/tools/save-experience-tool.js +54 -0
- package/dist/gateway/auth-hooks.d.ts +17 -0
- package/dist/gateway/auth-hooks.js +19 -0
- package/dist/gateway/backend-url.d.ts +2 -0
- package/dist/gateway/backend-url.js +11 -0
- package/dist/gateway/channel-handler.d.ts +6 -0
- package/dist/gateway/channel-handler.js +3 -0
- package/dist/gateway/clients.d.ts +5 -0
- package/dist/gateway/clients.js +4 -0
- package/dist/gateway/connection-handler.d.ts +6 -0
- package/dist/gateway/connection-handler.js +48 -0
- package/dist/gateway/index.d.ts +3 -0
- package/dist/gateway/index.js +2 -0
- package/dist/gateway/message-handler.d.ts +5 -0
- package/dist/gateway/message-handler.js +65 -0
- package/dist/gateway/methods/agent-cancel.d.ts +10 -0
- package/dist/gateway/methods/agent-cancel.js +17 -0
- package/dist/gateway/methods/agent-chat.d.ts +8 -0
- package/dist/gateway/methods/agent-chat.js +148 -0
- package/dist/gateway/methods/connect.d.ts +9 -0
- package/dist/gateway/methods/connect.js +18 -0
- package/dist/gateway/methods/install-skill-from-path.d.ts +13 -0
- package/dist/gateway/methods/install-skill-from-path.js +15 -0
- package/dist/gateway/methods/install-skill-from-upload.d.ts +14 -0
- package/dist/gateway/methods/install-skill-from-upload.js +13 -0
- package/dist/gateway/methods/run-scheduled-task.d.ts +15 -0
- package/dist/gateway/methods/run-scheduled-task.js +127 -0
- package/dist/gateway/paths.d.ts +20 -0
- package/dist/gateway/paths.js +19 -0
- package/dist/gateway/server.d.ts +8 -0
- package/dist/gateway/server.js +190 -0
- package/dist/gateway/sse-handler.d.ts +6 -0
- package/dist/gateway/sse-handler.js +3 -0
- package/dist/gateway/types.d.ts +90 -0
- package/dist/gateway/types.js +1 -0
- package/dist/gateway/utils.d.ts +22 -0
- package/dist/gateway/utils.js +67 -0
- package/dist/gateway/voice-handler.d.ts +12 -0
- package/dist/gateway/voice-handler.js +18 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/server/agent-config/agent-config.controller.d.ts +30 -0
- package/dist/server/agent-config/agent-config.controller.js +83 -0
- package/dist/server/agent-config/agent-config.module.d.ts +2 -0
- package/dist/server/agent-config/agent-config.module.js +19 -0
- package/dist/server/agent-config/agent-config.service.d.ts +53 -0
- package/dist/server/agent-config/agent-config.service.js +213 -0
- package/dist/server/agents/agents.controller.d.ts +41 -0
- package/dist/server/agents/agents.controller.js +118 -0
- package/dist/server/agents/agents.gateway.d.ts +21 -0
- package/dist/server/agents/agents.gateway.js +103 -0
- package/dist/server/agents/agents.module.d.ts +2 -0
- package/dist/server/agents/agents.module.js +20 -0
- package/dist/server/agents/agents.service.d.ts +63 -0
- package/dist/server/agents/agents.service.js +169 -0
- package/dist/server/app.module.d.ts +2 -0
- package/dist/server/app.module.js +38 -0
- package/dist/server/auth/auth.controller.d.ts +20 -0
- package/dist/server/auth/auth.controller.js +64 -0
- package/dist/server/auth/auth.module.d.ts +2 -0
- package/dist/server/auth/auth.module.js +19 -0
- package/dist/server/bootstrap.d.ts +15 -0
- package/dist/server/bootstrap.js +38 -0
- package/dist/server/config/config.controller.d.ts +73 -0
- package/dist/server/config/config.controller.js +95 -0
- package/dist/server/config/config.module.d.ts +2 -0
- package/dist/server/config/config.module.js +21 -0
- package/dist/server/config/config.service.d.ts +82 -0
- package/dist/server/config/config.service.js +123 -0
- package/dist/server/database/database.module.d.ts +2 -0
- package/dist/server/database/database.module.js +18 -0
- package/dist/server/database/database.service.d.ts +26 -0
- package/dist/server/database/database.service.js +253 -0
- package/dist/server/main.d.ts +1 -0
- package/dist/server/main.js +9 -0
- package/dist/server/saved-items/saved-items.controller.d.ts +57 -0
- package/dist/server/saved-items/saved-items.controller.js +229 -0
- package/dist/server/saved-items/saved-items.module.d.ts +2 -0
- package/dist/server/saved-items/saved-items.module.js +25 -0
- package/dist/server/saved-items/saved-items.service.d.ts +31 -0
- package/dist/server/saved-items/saved-items.service.js +105 -0
- package/dist/server/saved-items/tags.controller.d.ts +30 -0
- package/dist/server/saved-items/tags.controller.js +85 -0
- package/dist/server/saved-items/tags.service.d.ts +24 -0
- package/dist/server/saved-items/tags.service.js +84 -0
- package/dist/server/skills/skills.controller.d.ts +63 -0
- package/dist/server/skills/skills.controller.js +194 -0
- package/dist/server/skills/skills.module.d.ts +2 -0
- package/dist/server/skills/skills.module.js +22 -0
- package/dist/server/skills/skills.service.d.ts +65 -0
- package/dist/server/skills/skills.service.js +388 -0
- package/dist/server/tasks/tasks.controller.d.ts +52 -0
- package/dist/server/tasks/tasks.controller.js +163 -0
- package/dist/server/tasks/tasks.module.d.ts +2 -0
- package/dist/server/tasks/tasks.module.js +23 -0
- package/dist/server/tasks/tasks.service.d.ts +86 -0
- package/dist/server/tasks/tasks.service.js +327 -0
- package/dist/server/usage/usage.controller.d.ts +12 -0
- package/dist/server/usage/usage.controller.js +46 -0
- package/dist/server/usage/usage.module.d.ts +2 -0
- package/dist/server/usage/usage.module.js +19 -0
- package/dist/server/usage/usage.service.d.ts +21 -0
- package/dist/server/usage/usage.service.js +55 -0
- package/dist/server/users/users.controller.d.ts +35 -0
- package/dist/server/users/users.controller.js +69 -0
- package/dist/server/users/users.module.d.ts +2 -0
- package/dist/server/users/users.module.js +19 -0
- package/dist/server/users/users.service.d.ts +39 -0
- package/dist/server/users/users.service.js +140 -0
- package/dist/server/workspace/workspace.controller.d.ts +24 -0
- package/dist/server/workspace/workspace.controller.js +132 -0
- package/dist/server/workspace/workspace.module.d.ts +2 -0
- package/dist/server/workspace/workspace.module.js +21 -0
- package/dist/server/workspace/workspace.service.d.ts +36 -0
- package/dist/server/workspace/workspace.service.js +142 -0
- package/package.json +90 -0
- package/skills/agent-browser/SKILL.md +207 -0
- package/skills/agent-browser/references/authentication.md +202 -0
- package/skills/agent-browser/references/commands.md +259 -0
- package/skills/agent-browser/references/proxy-support.md +188 -0
- package/skills/agent-browser/references/session-management.md +193 -0
- package/skills/agent-browser/references/snapshot-refs.md +194 -0
- package/skills/agent-browser/references/video-recording.md +173 -0
- package/skills/agent-browser/templates/authenticated-session.sh +97 -0
- package/skills/agent-browser/templates/capture-workflow.sh +69 -0
- package/skills/agent-browser/templates/form-automation.sh +62 -0
- package/skills/find-skills/SKILL.md +140 -0
- package/skills/url-bookmark/SKILL.md +36 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP SSE/HTTP 传输:通过 HTTP POST 向远程 URL 发送 JSON-RPC 请求,响应在 response body。
|
|
3
|
+
*/
|
|
4
|
+
const MCP_PROTOCOL_VERSION = "2024-11-05";
|
|
5
|
+
export class SseTransport {
|
|
6
|
+
config;
|
|
7
|
+
initTimeoutMs;
|
|
8
|
+
requestTimeoutMs;
|
|
9
|
+
_connected = false;
|
|
10
|
+
constructor(config, options = {}) {
|
|
11
|
+
this.config = config;
|
|
12
|
+
this.initTimeoutMs = options.initTimeoutMs ?? 15_000;
|
|
13
|
+
this.requestTimeoutMs = options.requestTimeoutMs ?? 30_000;
|
|
14
|
+
}
|
|
15
|
+
async start() {
|
|
16
|
+
if (this._connected)
|
|
17
|
+
return;
|
|
18
|
+
const url = (this.config.url ?? "").trim();
|
|
19
|
+
if (!url)
|
|
20
|
+
throw new Error("MCP SSE url is required");
|
|
21
|
+
const initResult = await this.request({
|
|
22
|
+
jsonrpc: "2.0",
|
|
23
|
+
id: 1,
|
|
24
|
+
method: "initialize",
|
|
25
|
+
params: {
|
|
26
|
+
protocolVersion: MCP_PROTOCOL_VERSION,
|
|
27
|
+
capabilities: {},
|
|
28
|
+
clientInfo: { name: "openbot", version: "0.1.0" },
|
|
29
|
+
},
|
|
30
|
+
}, this.initTimeoutMs);
|
|
31
|
+
if (initResult.error) {
|
|
32
|
+
throw new Error(`MCP initialize failed: ${initResult.error.message}`);
|
|
33
|
+
}
|
|
34
|
+
await this.request({ jsonrpc: "2.0", id: 2, method: "notifications/initialized" }, this.requestTimeoutMs).catch(() => ({}));
|
|
35
|
+
this._connected = true;
|
|
36
|
+
}
|
|
37
|
+
request(req, timeoutMs) {
|
|
38
|
+
const url = (this.config.url ?? "").trim();
|
|
39
|
+
if (!url) {
|
|
40
|
+
return Promise.reject(new Error("MCP SSE url is required"));
|
|
41
|
+
}
|
|
42
|
+
const t = timeoutMs ?? this.requestTimeoutMs;
|
|
43
|
+
const controller = new AbortController();
|
|
44
|
+
const timeoutId = setTimeout(() => controller.abort(), t);
|
|
45
|
+
const headers = {
|
|
46
|
+
"Content-Type": "application/json",
|
|
47
|
+
Accept: "application/json",
|
|
48
|
+
...this.config.headers,
|
|
49
|
+
};
|
|
50
|
+
return fetch(url, {
|
|
51
|
+
method: "POST",
|
|
52
|
+
headers,
|
|
53
|
+
body: JSON.stringify(req),
|
|
54
|
+
signal: controller.signal,
|
|
55
|
+
})
|
|
56
|
+
.then(async (res) => {
|
|
57
|
+
clearTimeout(timeoutId);
|
|
58
|
+
if (!res.ok) {
|
|
59
|
+
const text = await res.text();
|
|
60
|
+
throw new Error(`MCP HTTP ${res.status}: ${text.slice(0, 200)}`);
|
|
61
|
+
}
|
|
62
|
+
const json = (await res.json());
|
|
63
|
+
if (!json || json.jsonrpc !== "2.0") {
|
|
64
|
+
throw new Error("Invalid MCP response");
|
|
65
|
+
}
|
|
66
|
+
return json;
|
|
67
|
+
})
|
|
68
|
+
.catch((err) => {
|
|
69
|
+
clearTimeout(timeoutId);
|
|
70
|
+
if (err.name === "AbortError") {
|
|
71
|
+
throw new Error(`MCP request timeout (${t}ms)`);
|
|
72
|
+
}
|
|
73
|
+
throw err;
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
async close() {
|
|
77
|
+
this._connected = false;
|
|
78
|
+
}
|
|
79
|
+
get isConnected() {
|
|
80
|
+
return this._connected;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP stdio 传输:子进程 + 标准输入/输出上的 Newline-delimited JSON-RPC。
|
|
3
|
+
*/
|
|
4
|
+
import type { McpServerConfigStdio } from "../types.js";
|
|
5
|
+
import type { JsonRpcRequest, JsonRpcResponse } from "../types.js";
|
|
6
|
+
export interface StdioTransportOptions {
|
|
7
|
+
/** 初始化超时(毫秒) */
|
|
8
|
+
initTimeoutMs?: number;
|
|
9
|
+
/** 单次请求超时(毫秒) */
|
|
10
|
+
requestTimeoutMs?: number;
|
|
11
|
+
}
|
|
12
|
+
export declare class StdioTransport {
|
|
13
|
+
private process;
|
|
14
|
+
private config;
|
|
15
|
+
private initTimeoutMs;
|
|
16
|
+
private requestTimeoutMs;
|
|
17
|
+
private nextId;
|
|
18
|
+
private pending;
|
|
19
|
+
private buffer;
|
|
20
|
+
constructor(config: McpServerConfigStdio, options?: StdioTransportOptions);
|
|
21
|
+
/** 启动子进程并完成 MCP initialize 握手 */
|
|
22
|
+
start(): Promise<void>;
|
|
23
|
+
private flushLines;
|
|
24
|
+
private rejectAll;
|
|
25
|
+
private initialize;
|
|
26
|
+
private sendNotification;
|
|
27
|
+
/** 发送 JSON-RPC 请求并等待响应 */
|
|
28
|
+
request(req: JsonRpcRequest, timeoutMs?: number): Promise<JsonRpcResponse>;
|
|
29
|
+
/** 关闭子进程 */
|
|
30
|
+
close(): Promise<void>;
|
|
31
|
+
get isConnected(): boolean;
|
|
32
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP stdio 传输:子进程 + 标准输入/输出上的 Newline-delimited JSON-RPC。
|
|
3
|
+
*/
|
|
4
|
+
import { spawn } from "node:child_process";
|
|
5
|
+
const MCP_PROTOCOL_VERSION = "2024-11-05";
|
|
6
|
+
export class StdioTransport {
|
|
7
|
+
process = null;
|
|
8
|
+
config;
|
|
9
|
+
initTimeoutMs;
|
|
10
|
+
requestTimeoutMs;
|
|
11
|
+
nextId = 1;
|
|
12
|
+
pending = new Map();
|
|
13
|
+
buffer = "";
|
|
14
|
+
constructor(config, options = {}) {
|
|
15
|
+
this.config = config;
|
|
16
|
+
this.initTimeoutMs = options.initTimeoutMs ?? 10_000;
|
|
17
|
+
this.requestTimeoutMs = options.requestTimeoutMs ?? 30_000;
|
|
18
|
+
}
|
|
19
|
+
/** 启动子进程并完成 MCP initialize 握手 */
|
|
20
|
+
async start() {
|
|
21
|
+
if (this.process) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const env = { ...process.env, ...this.config.env };
|
|
25
|
+
this.process = spawn(this.config.command, this.config.args ?? [], {
|
|
26
|
+
env,
|
|
27
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
28
|
+
});
|
|
29
|
+
const child = this.process;
|
|
30
|
+
child.stdout?.on("data", (chunk) => {
|
|
31
|
+
this.buffer += chunk.toString("utf-8");
|
|
32
|
+
this.flushLines();
|
|
33
|
+
});
|
|
34
|
+
child.stderr?.on("data", (data) => {
|
|
35
|
+
const msg = data.toString("utf-8").trim();
|
|
36
|
+
if (msg)
|
|
37
|
+
console.warn("[mcp stdio stderr]", msg);
|
|
38
|
+
});
|
|
39
|
+
child.on("error", (err) => {
|
|
40
|
+
this.rejectAll(new Error(`MCP process error: ${err.message}`));
|
|
41
|
+
});
|
|
42
|
+
child.on("exit", (code, signal) => {
|
|
43
|
+
this.rejectAll(new Error(`MCP process exited: code=${code} signal=${signal}`));
|
|
44
|
+
this.process = null;
|
|
45
|
+
});
|
|
46
|
+
await this.initialize();
|
|
47
|
+
}
|
|
48
|
+
flushLines() {
|
|
49
|
+
const lines = this.buffer.split("\n");
|
|
50
|
+
this.buffer = lines.pop() ?? "";
|
|
51
|
+
for (const line of lines) {
|
|
52
|
+
const trimmed = line.trim();
|
|
53
|
+
if (!trimmed)
|
|
54
|
+
continue;
|
|
55
|
+
try {
|
|
56
|
+
const msg = JSON.parse(trimmed);
|
|
57
|
+
if ("id" in msg && msg.id !== undefined) {
|
|
58
|
+
const pending = this.pending.get(msg.id);
|
|
59
|
+
if (pending) {
|
|
60
|
+
clearTimeout(pending.timer);
|
|
61
|
+
this.pending.delete(msg.id);
|
|
62
|
+
if (msg.error) {
|
|
63
|
+
pending.reject(new Error(msg.error.message));
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
pending.resolve(msg);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// 忽略非 JSON 行
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
rejectAll(err) {
|
|
77
|
+
for (const [, { reject, timer }] of this.pending) {
|
|
78
|
+
clearTimeout(timer);
|
|
79
|
+
reject(err);
|
|
80
|
+
}
|
|
81
|
+
this.pending.clear();
|
|
82
|
+
}
|
|
83
|
+
async initialize() {
|
|
84
|
+
const initResult = await this.request({
|
|
85
|
+
jsonrpc: "2.0",
|
|
86
|
+
id: this.nextId++,
|
|
87
|
+
method: "initialize",
|
|
88
|
+
params: {
|
|
89
|
+
protocolVersion: MCP_PROTOCOL_VERSION,
|
|
90
|
+
capabilities: {},
|
|
91
|
+
clientInfo: { name: "openbot", version: "0.1.0" },
|
|
92
|
+
},
|
|
93
|
+
}, this.initTimeoutMs);
|
|
94
|
+
if (initResult.error) {
|
|
95
|
+
throw new Error(`MCP initialize failed: ${initResult.error.message}`);
|
|
96
|
+
}
|
|
97
|
+
this.sendNotification({ jsonrpc: "2.0", method: "notifications/initialized" });
|
|
98
|
+
}
|
|
99
|
+
sendNotification(msg) {
|
|
100
|
+
if (!this.process?.stdin?.writable)
|
|
101
|
+
return;
|
|
102
|
+
this.process.stdin.write(JSON.stringify(msg) + "\n", "utf-8");
|
|
103
|
+
}
|
|
104
|
+
/** 发送 JSON-RPC 请求并等待响应 */
|
|
105
|
+
request(req, timeoutMs) {
|
|
106
|
+
return new Promise((resolve, reject) => {
|
|
107
|
+
if (!this.process?.stdin?.writable) {
|
|
108
|
+
reject(new Error("MCP transport not connected"));
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const t = timeoutMs ?? this.requestTimeoutMs;
|
|
112
|
+
const timer = setTimeout(() => {
|
|
113
|
+
if (this.pending.delete(req.id)) {
|
|
114
|
+
reject(new Error(`MCP request timeout (${t}ms)`));
|
|
115
|
+
}
|
|
116
|
+
}, t);
|
|
117
|
+
this.pending.set(req.id, { resolve, reject, timer });
|
|
118
|
+
this.process.stdin.write(JSON.stringify(req) + "\n", "utf-8");
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
/** 关闭子进程 */
|
|
122
|
+
async close() {
|
|
123
|
+
if (this.process) {
|
|
124
|
+
this.rejectAll(new Error("MCP transport closed"));
|
|
125
|
+
this.process.kill("SIGTERM");
|
|
126
|
+
this.process = null;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
get isConnected() {
|
|
130
|
+
return this.process != null && this.process.stdin?.writable === true;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP (Model Context Protocol) 相关类型定义。
|
|
3
|
+
* 仅实现 Client 端 Tools 能力;Resources/Prompts 类型预留扩展。
|
|
4
|
+
*/
|
|
5
|
+
/** stdio 传输:通过子进程 stdin/stdout 进行 JSON-RPC 通信 */
|
|
6
|
+
export interface McpServerConfigStdio {
|
|
7
|
+
transport: "stdio";
|
|
8
|
+
/** 可执行路径(禁止从用户输入拼接,需白名单或配置) */
|
|
9
|
+
command: string;
|
|
10
|
+
/** 命令行参数 */
|
|
11
|
+
args?: string[];
|
|
12
|
+
/** 环境变量(可选,合并到 process.env) */
|
|
13
|
+
env?: Record<string, string>;
|
|
14
|
+
}
|
|
15
|
+
/** SSE/HTTP 远程传输:通过 URL POST JSON-RPC 请求 */
|
|
16
|
+
export interface McpServerConfigSse {
|
|
17
|
+
transport: "sse";
|
|
18
|
+
/** 远程 MCP 服务地址(如 https://example.com/mcp) */
|
|
19
|
+
url: string;
|
|
20
|
+
/** 可选请求头(如 Authorization) */
|
|
21
|
+
headers?: Record<string, string>;
|
|
22
|
+
}
|
|
23
|
+
/** 单条 MCP 服务器配置(由调用方在创建 Session 时传入,与 Skill 类似) */
|
|
24
|
+
export type McpServerConfig = McpServerConfigStdio | McpServerConfigSse;
|
|
25
|
+
/** MCP 协议中 Tool 的 inputSchema 为 JSON Schema 对象 */
|
|
26
|
+
export interface McpToolInputSchema {
|
|
27
|
+
type?: string;
|
|
28
|
+
properties?: Record<string, unknown>;
|
|
29
|
+
required?: string[];
|
|
30
|
+
[key: string]: unknown;
|
|
31
|
+
}
|
|
32
|
+
/** MCP tools/list 返回的单个工具描述 */
|
|
33
|
+
export interface McpTool {
|
|
34
|
+
name: string;
|
|
35
|
+
description?: string;
|
|
36
|
+
inputSchema?: McpToolInputSchema;
|
|
37
|
+
}
|
|
38
|
+
/** MCP tools/call 返回的 content 项 */
|
|
39
|
+
export interface McpToolCallContentItem {
|
|
40
|
+
type: "text";
|
|
41
|
+
text: string;
|
|
42
|
+
}
|
|
43
|
+
/** MCP tools/call 的 result */
|
|
44
|
+
export interface McpToolCallResult {
|
|
45
|
+
content: McpToolCallContentItem[];
|
|
46
|
+
isError?: boolean;
|
|
47
|
+
}
|
|
48
|
+
/** JSON-RPC 2.0 请求 */
|
|
49
|
+
export interface JsonRpcRequest {
|
|
50
|
+
jsonrpc: "2.0";
|
|
51
|
+
id: number | string;
|
|
52
|
+
method: string;
|
|
53
|
+
params?: unknown;
|
|
54
|
+
}
|
|
55
|
+
/** JSON-RPC 2.0 成功响应 */
|
|
56
|
+
export interface JsonRpcResponse<T = unknown> {
|
|
57
|
+
jsonrpc: "2.0";
|
|
58
|
+
id: number | string;
|
|
59
|
+
result?: T;
|
|
60
|
+
error?: {
|
|
61
|
+
code: number;
|
|
62
|
+
message: string;
|
|
63
|
+
data?: unknown;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/** 传输层抽象:stdio / sse 等实现此接口供 McpClient 使用 */
|
|
67
|
+
export interface IMcpTransport {
|
|
68
|
+
start(): Promise<void>;
|
|
69
|
+
request(req: JsonRpcRequest, timeoutMs?: number): Promise<JsonRpcResponse>;
|
|
70
|
+
close(): Promise<void>;
|
|
71
|
+
readonly isConnected: boolean;
|
|
72
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { getLatestCompactionEntry, } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
import { convertToLlm } from "@mariozechner/pi-coding-agent";
|
|
3
|
+
/**
|
|
4
|
+
* 从 session entries 中提取可用于存入向量库的摘要文本:
|
|
5
|
+
* 优先使用最新 compaction 的 summary,否则用最近若干条消息拼成短文。
|
|
6
|
+
*/
|
|
7
|
+
export function buildSessionSummaryFromEntries(entries) {
|
|
8
|
+
const compaction = getLatestCompactionEntry(entries);
|
|
9
|
+
if (compaction?.summary) {
|
|
10
|
+
return compaction.summary;
|
|
11
|
+
}
|
|
12
|
+
const messageEntries = entries.filter((e) => e.type === "message");
|
|
13
|
+
if (messageEntries.length === 0)
|
|
14
|
+
return null;
|
|
15
|
+
const recent = messageEntries.slice(-20);
|
|
16
|
+
const messages = recent.map((e) => e.message);
|
|
17
|
+
const parts = [];
|
|
18
|
+
for (const msg of convertToLlm(messages)) {
|
|
19
|
+
const role = msg.role === "user" ? "User" : "Assistant";
|
|
20
|
+
const content = typeof msg.content === "string"
|
|
21
|
+
? msg.content
|
|
22
|
+
: msg.content?.map((c) => c.text ?? "").join(" ") ?? "";
|
|
23
|
+
if (content.trim())
|
|
24
|
+
parts.push(`${role}: ${content.trim()}`);
|
|
25
|
+
}
|
|
26
|
+
return parts.length > 0 ? parts.join("\n") : null;
|
|
27
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ExtensionFactory } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
/**
|
|
3
|
+
* 创建用于在 session_compact 事件时把 compaction summary 写入向量库的 extension factory。
|
|
4
|
+
* 在 AgentSession 发生 compaction(自动或手动)后触发,无需等到关闭会话。
|
|
5
|
+
*/
|
|
6
|
+
export declare function createCompactionMemoryExtensionFactory(sessionId: string): ExtensionFactory;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { addMemory } from "./index.js";
|
|
2
|
+
/**
|
|
3
|
+
* 创建用于在 session_compact 事件时把 compaction summary 写入向量库的 extension factory。
|
|
4
|
+
* 在 AgentSession 发生 compaction(自动或手动)后触发,无需等到关闭会话。
|
|
5
|
+
*/
|
|
6
|
+
export function createCompactionMemoryExtensionFactory(sessionId) {
|
|
7
|
+
return (pi) => {
|
|
8
|
+
pi.on("session_compact", async (event) => {
|
|
9
|
+
const summary = event.compactionEntry?.summary?.trim();
|
|
10
|
+
if (!summary)
|
|
11
|
+
return;
|
|
12
|
+
try {
|
|
13
|
+
await addMemory(summary, {
|
|
14
|
+
infotype: "compaction",
|
|
15
|
+
sessionId,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
catch (_) {
|
|
19
|
+
// 写入失败不打断会话
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 文本 embedding:使用 config.json 中配置的远端 RAG embedding 模型。
|
|
3
|
+
* 未配置时返回 null,调用方(长记忆)空转。
|
|
4
|
+
*/
|
|
5
|
+
import { getRagEmbeddingConfigSync } from "../config/desktop-config.js";
|
|
6
|
+
import { embedRemote } from "./remote-embedding.js";
|
|
7
|
+
/**
|
|
8
|
+
* 对单条文本做 embedding。未配置 RAG embedding 时返回 null。
|
|
9
|
+
*/
|
|
10
|
+
export async function embed(text) {
|
|
11
|
+
const config = getRagEmbeddingConfigSync();
|
|
12
|
+
if (!config)
|
|
13
|
+
return null;
|
|
14
|
+
return embedRemote(text, config);
|
|
15
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { InfoType, MemorySearchResult } from "./types.js";
|
|
2
|
+
export { buildSessionSummaryFromEntries } from "./build-summary.js";
|
|
3
|
+
export type { InfoType, MemoryMetadata, MemorySearchResult } from "./types.js";
|
|
4
|
+
export declare function initMemory(): Promise<void>;
|
|
5
|
+
/**
|
|
6
|
+
* 将一条文本写入向量库。未配置 RAG embedding 时跳过写入并返回占位 id(长记忆空转)。
|
|
7
|
+
* @param text 内容
|
|
8
|
+
* @param metadata.infotype 经验总结(experience) 或 compaction 摘要(compaction)
|
|
9
|
+
* @param metadata.sessionId 会话 id
|
|
10
|
+
*/
|
|
11
|
+
export declare function addMemory(text: string, metadata: {
|
|
12
|
+
infotype: InfoType;
|
|
13
|
+
sessionId: string;
|
|
14
|
+
}): Promise<string>;
|
|
15
|
+
export interface SearchMemoryOptions {
|
|
16
|
+
topK?: number;
|
|
17
|
+
infotype?: import("./types.js").InfoType;
|
|
18
|
+
sessionId?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* 按语义搜索记忆,可按 infotype、sessionId 过滤。未配置 RAG embedding 时返回空数组(空转)。
|
|
22
|
+
*/
|
|
23
|
+
export declare function searchMemory(query: string, topK?: number, options?: SearchMemoryOptions): Promise<MemorySearchResult[]>;
|
|
24
|
+
/** 拉取 3 条经验,格式化为「经验内容」文本(用于拼入用户消息) */
|
|
25
|
+
export declare function getExperienceContextForUserMessage(): Promise<string>;
|
|
26
|
+
/**
|
|
27
|
+
* 拉取 1 条 compaction 摘要,用于拼入 system prompt(新建 AgentSession 时调用,可选 sessionId)
|
|
28
|
+
*/
|
|
29
|
+
export declare function getCompactionContextForSystemPrompt(sessionId?: string): Promise<string>;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { embed } from "./embedding.js";
|
|
3
|
+
import { addToStore, queryStore } from "./vector-store.js";
|
|
4
|
+
export { buildSessionSummaryFromEntries } from "./build-summary.js";
|
|
5
|
+
let initialized = false;
|
|
6
|
+
export async function initMemory() {
|
|
7
|
+
if (initialized)
|
|
8
|
+
return;
|
|
9
|
+
const { getChroma } = await import("./vector-store.js");
|
|
10
|
+
await getChroma();
|
|
11
|
+
initialized = true;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* 将一条文本写入向量库。未配置 RAG embedding 时跳过写入并返回占位 id(长记忆空转)。
|
|
15
|
+
* @param text 内容
|
|
16
|
+
* @param metadata.infotype 经验总结(experience) 或 compaction 摘要(compaction)
|
|
17
|
+
* @param metadata.sessionId 会话 id
|
|
18
|
+
*/
|
|
19
|
+
export async function addMemory(text, metadata) {
|
|
20
|
+
const id = randomUUID();
|
|
21
|
+
const vec = await embed(text);
|
|
22
|
+
if (vec === null)
|
|
23
|
+
return id;
|
|
24
|
+
const meta = {
|
|
25
|
+
...metadata,
|
|
26
|
+
createdAt: new Date().toISOString(),
|
|
27
|
+
};
|
|
28
|
+
await addToStore(id, vec, text, meta);
|
|
29
|
+
return id;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* 按语义搜索记忆,可按 infotype、sessionId 过滤。未配置 RAG embedding 时返回空数组(空转)。
|
|
33
|
+
*/
|
|
34
|
+
export async function searchMemory(query, topK = 10, options) {
|
|
35
|
+
const vec = await embed(query);
|
|
36
|
+
if (vec === null)
|
|
37
|
+
return [];
|
|
38
|
+
const opts = typeof options === "object" && options != null ? options : {};
|
|
39
|
+
const k = opts.topK ?? topK;
|
|
40
|
+
const filter = opts.infotype != null || opts.sessionId != null
|
|
41
|
+
? { infotype: opts.infotype, sessionId: opts.sessionId }
|
|
42
|
+
: undefined;
|
|
43
|
+
const rows = await queryStore(vec, k, filter);
|
|
44
|
+
return rows.map((r) => ({
|
|
45
|
+
document: r.document,
|
|
46
|
+
metadata: r.metadata,
|
|
47
|
+
distance: r.distance,
|
|
48
|
+
}));
|
|
49
|
+
}
|
|
50
|
+
/** 拉取 3 条经验,格式化为「经验内容」文本(用于拼入用户消息) */
|
|
51
|
+
export async function getExperienceContextForUserMessage() {
|
|
52
|
+
const results = await searchMemory("经验 总结 技巧 方法", 3, { infotype: "experience" });
|
|
53
|
+
if (results.length === 0)
|
|
54
|
+
return "";
|
|
55
|
+
const lines = results.map((r, i) => `${i + 1}. ${(r.document || "").trim()}`).filter(Boolean);
|
|
56
|
+
if (lines.length === 0)
|
|
57
|
+
return "";
|
|
58
|
+
return "以下为相关经验:\n\n" + lines.join("\n\n");
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* 拉取 1 条 compaction 摘要,用于拼入 system prompt(新建 AgentSession 时调用,可选 sessionId)
|
|
62
|
+
*/
|
|
63
|
+
export async function getCompactionContextForSystemPrompt(sessionId) {
|
|
64
|
+
const filter = sessionId != null ? { infotype: "compaction", sessionId } : { infotype: "compaction" };
|
|
65
|
+
const results = await searchMemory("对话 摘要 历史 上下文", 1, { ...filter, topK: 1 });
|
|
66
|
+
const doc = results[0]?.document?.trim();
|
|
67
|
+
if (!doc)
|
|
68
|
+
return "";
|
|
69
|
+
return "## 历史对话摘要\n\n" + doc;
|
|
70
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 远端 embedding:调用 OpenAI 兼容的 /embeddings 接口。
|
|
3
|
+
* 配置来自 ~/.openbot/desktop/config.json 的 rag.embeddingProvider + rag.embeddingModel;
|
|
4
|
+
* 未配置时由调用方(embedding.ts)返回 null,长记忆空转。
|
|
5
|
+
*/
|
|
6
|
+
import type { RagEmbeddingConfig } from "../config/desktop-config.js";
|
|
7
|
+
/**
|
|
8
|
+
* 单条文本请求远端 embedding,返回归一化向量;失败抛错。
|
|
9
|
+
*/
|
|
10
|
+
export declare function embedRemote(text: string, config: RagEmbeddingConfig): Promise<number[]>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const EMBEDDINGS_PATH = "/embeddings";
|
|
2
|
+
/**
|
|
3
|
+
* 单条文本请求远端 embedding,返回归一化向量;失败抛错。
|
|
4
|
+
*/
|
|
5
|
+
export async function embedRemote(text, config) {
|
|
6
|
+
const base = config.baseUrl.replace(/\/$/, "");
|
|
7
|
+
const url = base + (base.endsWith("/v1") ? EMBEDDINGS_PATH : "/v1" + EMBEDDINGS_PATH);
|
|
8
|
+
const res = await fetch(url, {
|
|
9
|
+
method: "POST",
|
|
10
|
+
headers: {
|
|
11
|
+
"Content-Type": "application/json",
|
|
12
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
13
|
+
},
|
|
14
|
+
body: JSON.stringify({
|
|
15
|
+
model: config.modelId,
|
|
16
|
+
input: text,
|
|
17
|
+
}),
|
|
18
|
+
});
|
|
19
|
+
if (!res.ok) {
|
|
20
|
+
const errText = await res.text();
|
|
21
|
+
throw new Error(`Embedding API error ${res.status}: ${errText}`);
|
|
22
|
+
}
|
|
23
|
+
const data = (await res.json());
|
|
24
|
+
const embedding = data?.data?.[0]?.embedding;
|
|
25
|
+
if (!Array.isArray(embedding) || embedding.length === 0) {
|
|
26
|
+
throw new Error("Embedding API returned empty embedding");
|
|
27
|
+
}
|
|
28
|
+
return L2Normalize(embedding.map(Number));
|
|
29
|
+
}
|
|
30
|
+
function L2Normalize(vec) {
|
|
31
|
+
let sum = 0;
|
|
32
|
+
for (let i = 0; i < vec.length; i++)
|
|
33
|
+
sum += vec[i] * vec[i];
|
|
34
|
+
const norm = Math.sqrt(sum) || 1;
|
|
35
|
+
return vec.map((x) => x / norm);
|
|
36
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 长记忆 metadata 中的信息类型
|
|
3
|
+
* - experience: 对话结束时 Agent 调用 save_experience 工具存入的经验总结
|
|
4
|
+
* - compaction: 关闭 session 时从 compaction 取出的会话摘要
|
|
5
|
+
*/
|
|
6
|
+
export type InfoType = "experience" | "compaction";
|
|
7
|
+
export interface MemoryMetadata {
|
|
8
|
+
infotype: InfoType;
|
|
9
|
+
sessionId: string;
|
|
10
|
+
createdAt: string;
|
|
11
|
+
}
|
|
12
|
+
export interface MemorySearchResult {
|
|
13
|
+
document: string;
|
|
14
|
+
metadata: MemoryMetadata;
|
|
15
|
+
distance?: number;
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { InfoType, MemoryMetadata } from "./types.js";
|
|
2
|
+
export declare function getChroma(): Promise<{
|
|
3
|
+
client: null;
|
|
4
|
+
collection: null;
|
|
5
|
+
}>;
|
|
6
|
+
export declare function addToStore(id: string, embedding: number[], document: string, metadata: MemoryMetadata): Promise<void>;
|
|
7
|
+
export type MemoryFilter = {
|
|
8
|
+
infotype?: InfoType;
|
|
9
|
+
sessionId?: string;
|
|
10
|
+
};
|
|
11
|
+
export declare function queryStore(queryEmbedding: number[], topK?: number, filter?: MemoryFilter): Promise<{
|
|
12
|
+
document: string;
|
|
13
|
+
metadata: MemoryMetadata;
|
|
14
|
+
distance?: number;
|
|
15
|
+
}[]>;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { LocalIndex } from "vectra";
|
|
3
|
+
import { getOpenbotAgentDir } from "../agent/agent-dir.js";
|
|
4
|
+
const INDEX_DIR_NAME = "memory";
|
|
5
|
+
let indexDir = null;
|
|
6
|
+
let index = null;
|
|
7
|
+
function getIndexDir() {
|
|
8
|
+
if (!indexDir) {
|
|
9
|
+
indexDir = join(getOpenbotAgentDir(), INDEX_DIR_NAME);
|
|
10
|
+
}
|
|
11
|
+
return indexDir;
|
|
12
|
+
}
|
|
13
|
+
async function getIndex() {
|
|
14
|
+
if (index)
|
|
15
|
+
return index;
|
|
16
|
+
const path = getIndexDir();
|
|
17
|
+
index = new LocalIndex(path);
|
|
18
|
+
if (!(await index.isIndexCreated())) {
|
|
19
|
+
await index.createIndex();
|
|
20
|
+
}
|
|
21
|
+
return index;
|
|
22
|
+
}
|
|
23
|
+
/** Vectra 返回 score(相似度 0~1,越大越相似);转为 distance = 1 - score,与现有 API 一致 */
|
|
24
|
+
function scoreToDistance(score) {
|
|
25
|
+
return 1 - score;
|
|
26
|
+
}
|
|
27
|
+
export async function getChroma() {
|
|
28
|
+
await getIndex();
|
|
29
|
+
return { client: null, collection: null };
|
|
30
|
+
}
|
|
31
|
+
export async function addToStore(id, embedding, document, metadata) {
|
|
32
|
+
const idx = await getIndex();
|
|
33
|
+
await idx.upsertItem({
|
|
34
|
+
id,
|
|
35
|
+
vector: embedding,
|
|
36
|
+
metadata: {
|
|
37
|
+
document,
|
|
38
|
+
infotype: metadata.infotype,
|
|
39
|
+
sessionId: metadata.sessionId,
|
|
40
|
+
createdAt: metadata.createdAt,
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
export async function queryStore(queryEmbedding, topK = 10, filter) {
|
|
45
|
+
const idx = await getIndex();
|
|
46
|
+
const vectraFilter = filter && (filter.infotype != null || filter.sessionId != null)
|
|
47
|
+
? {
|
|
48
|
+
...(filter.infotype != null && { infotype: { $eq: filter.infotype } }),
|
|
49
|
+
...(filter.sessionId != null && { sessionId: { $eq: filter.sessionId } }),
|
|
50
|
+
}
|
|
51
|
+
: undefined;
|
|
52
|
+
const results = await idx.queryItems(queryEmbedding, "", topK, vectraFilter);
|
|
53
|
+
return results.map((r) => {
|
|
54
|
+
const meta = (r.item.metadata ?? {});
|
|
55
|
+
return {
|
|
56
|
+
document: String(meta.document ?? ""),
|
|
57
|
+
metadata: {
|
|
58
|
+
infotype: meta.infotype,
|
|
59
|
+
sessionId: String(meta.sessionId ?? ""),
|
|
60
|
+
createdAt: String(meta.createdAt ?? ""),
|
|
61
|
+
},
|
|
62
|
+
distance: scoreToDistance(r.score),
|
|
63
|
+
};
|
|
64
|
+
});
|
|
65
|
+
}
|