@next-open-ai/openclawx 0.8.36 → 0.8.48
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 +60 -42
- package/apps/desktop/renderer/dist/assets/index-BHY1xIZQ.css +10 -0
- package/apps/desktop/renderer/dist/assets/index-DQxlVuBe.js +93 -0
- package/apps/desktop/renderer/dist/index.html +2 -2
- package/dist/cli/cli.js +29 -0
- package/dist/cli/extension-cmd.d.ts +15 -0
- package/dist/cli/extension-cmd.js +107 -0
- package/dist/core/agent/agent-dir.d.ts +6 -0
- package/dist/core/agent/agent-dir.js +8 -0
- package/dist/core/agent/agent-manager.d.ts +13 -0
- package/dist/core/agent/agent-manager.js +77 -7
- package/dist/core/agent/proxy/adapters/claude-code-adapter.d.ts +2 -0
- package/dist/core/agent/proxy/adapters/claude-code-adapter.js +186 -0
- package/dist/core/agent/proxy/adapters/local-adapter.js +3 -1
- package/dist/core/agent/proxy/adapters/opencode-adapter.js +65 -29
- package/dist/core/agent/proxy/adapters/opencode-local-runner.js +9 -0
- package/dist/core/agent/proxy/index.js +2 -0
- package/dist/core/agent/token-usage-log-extension.d.ts +14 -0
- package/dist/core/agent/token-usage-log-extension.js +61 -0
- package/dist/core/config/desktop-config.d.ts +24 -2
- package/dist/core/config/desktop-config.js +87 -10
- package/dist/core/config/provider-support-default.js +26 -0
- package/dist/core/extensions/index.d.ts +1 -0
- package/dist/core/extensions/index.js +1 -0
- package/dist/core/extensions/load.d.ts +11 -0
- package/dist/core/extensions/load.js +101 -0
- package/dist/core/local-llm-server/index.d.ts +32 -0
- package/dist/core/local-llm-server/index.js +126 -0
- package/dist/core/local-llm-server/llm-context.d.ts +60 -0
- package/dist/core/local-llm-server/llm-context.js +221 -0
- package/dist/core/local-llm-server/model-resolve.d.ts +20 -0
- package/dist/core/local-llm-server/model-resolve.js +58 -0
- package/dist/core/local-llm-server/server.d.ts +1 -0
- package/dist/core/local-llm-server/server.js +235 -0
- package/dist/core/mcp/adapter.d.ts +4 -2
- package/dist/core/mcp/adapter.js +10 -4
- package/dist/core/mcp/index.d.ts +2 -0
- package/dist/core/mcp/index.js +1 -0
- package/dist/core/mcp/operator.d.ts +2 -0
- package/dist/core/mcp/operator.js +1 -1
- package/dist/core/memory/local-embedding.d.ts +4 -3
- package/dist/core/memory/local-embedding.js +43 -3
- package/dist/core/tools/index.d.ts +1 -0
- package/dist/core/tools/index.js +1 -0
- package/dist/core/tools/truncate-result.d.ts +14 -0
- package/dist/core/tools/truncate-result.js +27 -0
- package/dist/core/tools/web-search/create-web-search-tool.d.ts +17 -0
- package/dist/core/tools/web-search/create-web-search-tool.js +87 -0
- package/dist/core/tools/web-search/index.d.ts +4 -0
- package/dist/core/tools/web-search/index.js +2 -0
- package/dist/core/tools/web-search/providers/brave.d.ts +2 -0
- package/dist/core/tools/web-search/providers/brave.js +87 -0
- package/dist/core/tools/web-search/providers/duck-duck-scrape.d.ts +2 -0
- package/dist/core/tools/web-search/providers/duck-duck-scrape.js +47 -0
- package/dist/core/tools/web-search/providers/index.d.ts +5 -0
- package/dist/core/tools/web-search/providers/index.js +13 -0
- package/dist/core/tools/web-search/types.d.ts +35 -0
- package/dist/core/tools/web-search/types.js +4 -0
- package/dist/gateway/methods/agent-chat.js +74 -42
- package/dist/gateway/methods/run-scheduled-task.js +2 -0
- package/dist/gateway/server.js +54 -1
- package/dist/server/agent-config/agent-config.controller.d.ts +1 -1
- package/dist/server/agent-config/agent-config.service.d.ts +17 -3
- package/dist/server/agent-config/agent-config.service.js +23 -0
- package/dist/server/config/config.controller.d.ts +84 -4
- package/dist/server/config/config.controller.js +135 -3
- package/dist/server/config/config.module.js +3 -2
- package/dist/server/config/config.service.d.ts +14 -0
- package/dist/server/config/local-models.service.d.ts +52 -0
- package/dist/server/config/local-models.service.js +211 -0
- package/package.json +3 -1
- package/presets/preset-agents.json +121 -91
- package/presets/recommended-local-models.json +42 -0
- package/presets/workspaces/finance-expert/skills/akshare-helper/SKILL.md +9 -0
- package/presets/workspaces/office-automation/skills/rpa-helper/SKILL.md +9 -0
- package/presets/workspaces/self-media-bot/skills/self-media-tools/SKILL.md +9 -0
- package/apps/desktop/renderer/dist/assets/index-BGHtXhm3.js +0 -89
- package/apps/desktop/renderer/dist/assets/index-CB2-m4ae.css +0 -10
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
<link
|
|
12
12
|
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Roboto+Mono:wght@400;500&display=swap"
|
|
13
13
|
rel="stylesheet">
|
|
14
|
-
<script type="module" crossorigin src="/assets/index-
|
|
15
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
14
|
+
<script type="module" crossorigin src="/assets/index-DQxlVuBe.js"></script>
|
|
15
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BHY1xIZQ.css">
|
|
16
16
|
</head>
|
|
17
17
|
|
|
18
18
|
<body>
|
package/dist/cli/cli.js
CHANGED
|
@@ -7,6 +7,7 @@ import { getOpenbotAgentDir } from "../core/agent/agent-dir.js";
|
|
|
7
7
|
import { run } from "../core/agent/run.js";
|
|
8
8
|
import { loadDesktopAgentConfig, getBoundAgentIdForCli, setProviderApiKey, setDefaultModel, getDesktopConfigList, syncDesktopConfigToModelsJson, ensureDesktopConfigInitialized, } from "../core/config/desktop-config.js";
|
|
9
9
|
import { writeGatewayPid, removeGatewayPidFile, serviceInstall, serviceUninstall, serviceStop, } from "./service.js";
|
|
10
|
+
import { installExtension, listExtensions, uninstallExtension } from "./extension-cmd.js";
|
|
10
11
|
const require = createRequire(import.meta.url);
|
|
11
12
|
const PKG = require("../../package.json");
|
|
12
13
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -189,6 +190,34 @@ configCmd
|
|
|
189
190
|
await syncDesktopConfigToModelsJson();
|
|
190
191
|
console.log("[openbot] Synced desktop providers to agent models.json");
|
|
191
192
|
});
|
|
193
|
+
// Extension: 在 ~/.openbot/plugins 下通过 npm 包安装/列出/卸载扩展,Server 运行时从该目录加载
|
|
194
|
+
const extensionCmd = program
|
|
195
|
+
.command("extension")
|
|
196
|
+
.description("Install, list, or uninstall extensions (npm packages in ~/.openbot/plugins)");
|
|
197
|
+
extensionCmd
|
|
198
|
+
.command("install <pkg>")
|
|
199
|
+
.description("Install an extension package (e.g. openbot extension install my-extension)")
|
|
200
|
+
.action((pkg) => {
|
|
201
|
+
installExtension(pkg);
|
|
202
|
+
});
|
|
203
|
+
extensionCmd
|
|
204
|
+
.command("list")
|
|
205
|
+
.description("List installed extensions")
|
|
206
|
+
.action(() => {
|
|
207
|
+
const list = listExtensions();
|
|
208
|
+
if (list.length === 0) {
|
|
209
|
+
console.log("No extensions installed. Run: openbot extension install <package>");
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
console.log("Installed extensions:\n");
|
|
213
|
+
console.table(list.map((r) => ({ Package: r.name, Spec: r.spec })));
|
|
214
|
+
});
|
|
215
|
+
extensionCmd
|
|
216
|
+
.command("uninstall <pkg>")
|
|
217
|
+
.description("Uninstall an extension package")
|
|
218
|
+
.action((pkg) => {
|
|
219
|
+
uninstallExtension(pkg);
|
|
220
|
+
});
|
|
192
221
|
(async () => {
|
|
193
222
|
await ensureDesktopConfigInitialized();
|
|
194
223
|
await program.parseAsync(process.argv);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 安装扩展:将包加入 dependencies 并在 plugins 目录下执行 npm install
|
|
3
|
+
*/
|
|
4
|
+
export declare function installExtension(pkgSpec: string): void;
|
|
5
|
+
/**
|
|
6
|
+
* 列出已安装的扩展(package.json 的 dependencies)
|
|
7
|
+
*/
|
|
8
|
+
export declare function listExtensions(): {
|
|
9
|
+
name: string;
|
|
10
|
+
spec: string;
|
|
11
|
+
}[];
|
|
12
|
+
/**
|
|
13
|
+
* 卸载扩展:从 dependencies 移除并在 plugins 目录下执行 npm install
|
|
14
|
+
*/
|
|
15
|
+
export declare function uninstallExtension(pkgName: string): void;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* openbot extension install / list / uninstall
|
|
3
|
+
* 在 ~/.openbot/plugins 下维护 package.json 与 node_modules,Server 运行时从该目录加载扩展。
|
|
4
|
+
*/
|
|
5
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
6
|
+
import { execSync } from "node:child_process";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import { getOpenbotPluginsDir } from "../core/agent/agent-dir.js";
|
|
9
|
+
const PLUGINS_PACKAGE_NAME = "openbot-plugins-root";
|
|
10
|
+
function ensurePluginsDir() {
|
|
11
|
+
const dir = getOpenbotPluginsDir();
|
|
12
|
+
if (!existsSync(dir)) {
|
|
13
|
+
mkdirSync(dir, { recursive: true });
|
|
14
|
+
}
|
|
15
|
+
const pkgPath = join(dir, "package.json");
|
|
16
|
+
if (!existsSync(pkgPath)) {
|
|
17
|
+
writeFileSync(pkgPath, JSON.stringify({
|
|
18
|
+
name: PLUGINS_PACKAGE_NAME,
|
|
19
|
+
version: "1.0.0",
|
|
20
|
+
private: true,
|
|
21
|
+
description: "OpenClawX extension packages (managed by openbot extension install)",
|
|
22
|
+
dependencies: {},
|
|
23
|
+
}, null, 2), "utf-8");
|
|
24
|
+
}
|
|
25
|
+
return dir;
|
|
26
|
+
}
|
|
27
|
+
function readPluginsPackageJson(pluginsDir) {
|
|
28
|
+
const pkgPath = join(pluginsDir, "package.json");
|
|
29
|
+
if (!existsSync(pkgPath))
|
|
30
|
+
return {};
|
|
31
|
+
try {
|
|
32
|
+
return JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function writePluginsPackageJson(pluginsDir, pkg) {
|
|
39
|
+
const pkgPath = join(pluginsDir, "package.json");
|
|
40
|
+
const full = {
|
|
41
|
+
name: PLUGINS_PACKAGE_NAME,
|
|
42
|
+
version: "1.0.0",
|
|
43
|
+
private: true,
|
|
44
|
+
description: "OpenClawX extension packages (managed by openbot extension install)",
|
|
45
|
+
...pkg,
|
|
46
|
+
};
|
|
47
|
+
writeFileSync(pkgPath, JSON.stringify(full, null, 2), "utf-8");
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* 解析用户输入的 pkgSpec(如 "foo" 或 "foo@1.0.0")为 { name, spec }。
|
|
51
|
+
*/
|
|
52
|
+
function parsePkgSpec(pkgSpec) {
|
|
53
|
+
const at = pkgSpec.indexOf("@");
|
|
54
|
+
if (at === -1)
|
|
55
|
+
return { name: pkgSpec.trim(), spec: "*" };
|
|
56
|
+
return { name: pkgSpec.slice(0, at).trim(), spec: pkgSpec.slice(at + 1).trim() || "*" };
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* 安装扩展:将包加入 dependencies 并在 plugins 目录下执行 npm install
|
|
60
|
+
*/
|
|
61
|
+
export function installExtension(pkgSpec) {
|
|
62
|
+
const pluginsDir = ensurePluginsDir();
|
|
63
|
+
const { name, spec } = parsePkgSpec(pkgSpec);
|
|
64
|
+
const pkg = readPluginsPackageJson(pluginsDir);
|
|
65
|
+
const deps = { ...pkg.dependencies };
|
|
66
|
+
deps[name] = spec; // 覆盖为本次指定的版本
|
|
67
|
+
writePluginsPackageJson(pluginsDir, { ...pkg, dependencies: deps });
|
|
68
|
+
execSync("npm install", {
|
|
69
|
+
cwd: pluginsDir,
|
|
70
|
+
stdio: "inherit",
|
|
71
|
+
});
|
|
72
|
+
console.log(`[openbot] Installed extension: ${name} (in ${pluginsDir})`);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* 列出已安装的扩展(package.json 的 dependencies)
|
|
76
|
+
*/
|
|
77
|
+
export function listExtensions() {
|
|
78
|
+
const pluginsDir = getOpenbotPluginsDir();
|
|
79
|
+
if (!existsSync(pluginsDir))
|
|
80
|
+
return [];
|
|
81
|
+
const pkg = readPluginsPackageJson(pluginsDir);
|
|
82
|
+
const deps = pkg.dependencies ?? {};
|
|
83
|
+
return Object.entries(deps).map(([name, spec]) => ({ name, spec }));
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* 卸载扩展:从 dependencies 移除并在 plugins 目录下执行 npm install
|
|
87
|
+
*/
|
|
88
|
+
export function uninstallExtension(pkgName) {
|
|
89
|
+
const pluginsDir = getOpenbotPluginsDir();
|
|
90
|
+
if (!existsSync(pluginsDir)) {
|
|
91
|
+
console.warn("[openbot] No plugins directory found.");
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const pkg = readPluginsPackageJson(pluginsDir);
|
|
95
|
+
const deps = { ...(pkg.dependencies ?? {}) };
|
|
96
|
+
if (!(pkgName in deps)) {
|
|
97
|
+
console.warn(`[openbot] Extension "${pkgName}" is not installed.`);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
delete deps[pkgName];
|
|
101
|
+
writePluginsPackageJson(pluginsDir, { ...pkg, dependencies: deps });
|
|
102
|
+
execSync("npm install", {
|
|
103
|
+
cwd: pluginsDir,
|
|
104
|
+
stdio: "inherit",
|
|
105
|
+
});
|
|
106
|
+
console.log(`[openbot] Uninstalled extension: ${pkgName}`);
|
|
107
|
+
}
|
|
@@ -8,6 +8,12 @@ export declare function getOpenbotAgentDir(): string;
|
|
|
8
8
|
* 可通过环境变量 OPENBOT_WORKSPACE_DIR 覆盖
|
|
9
9
|
*/
|
|
10
10
|
export declare function getOpenbotWorkspaceDir(): string;
|
|
11
|
+
/**
|
|
12
|
+
* 获取 openbot 扩展(插件)目录(默认 ~/.openbot/plugins)
|
|
13
|
+
* 可通过环境变量 OPENBOT_PLUGINS_DIR 覆盖
|
|
14
|
+
* openbot extension install 会在此目录下维护 package.json 与 node_modules
|
|
15
|
+
*/
|
|
16
|
+
export declare function getOpenbotPluginsDir(): string;
|
|
11
17
|
/**
|
|
12
18
|
* 确保 agent 目录存在,并创建默认配置文件
|
|
13
19
|
*/
|
|
@@ -15,6 +15,14 @@ export function getOpenbotAgentDir() {
|
|
|
15
15
|
export function getOpenbotWorkspaceDir() {
|
|
16
16
|
return process.env.OPENBOT_WORKSPACE_DIR ?? join(homedir(), ".openbot", "workspace");
|
|
17
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* 获取 openbot 扩展(插件)目录(默认 ~/.openbot/plugins)
|
|
20
|
+
* 可通过环境变量 OPENBOT_PLUGINS_DIR 覆盖
|
|
21
|
+
* openbot extension install 会在此目录下维护 package.json 与 node_modules
|
|
22
|
+
*/
|
|
23
|
+
export function getOpenbotPluginsDir() {
|
|
24
|
+
return process.env.OPENBOT_PLUGINS_DIR ?? join(homedir(), ".openbot", "plugins");
|
|
25
|
+
}
|
|
18
26
|
/**
|
|
19
27
|
* 确保 agent 目录存在,并创建默认配置文件
|
|
20
28
|
*/
|
|
@@ -60,10 +60,23 @@ export declare class AgentManager {
|
|
|
60
60
|
maxSessions?: number;
|
|
61
61
|
targetAgentId?: string;
|
|
62
62
|
mcpServers?: McpServerConfig[] | McpServersStandardFormat;
|
|
63
|
+
/** MCP 单次返回最大 token;不配置则不限制 */
|
|
64
|
+
mcpMaxResultTokens?: number;
|
|
63
65
|
/** 自定义系统提示词(来自 agent 配置),会与技能等一起组成最终 systemPrompt */
|
|
64
66
|
systemPrompt?: string;
|
|
65
67
|
/** 是否使用长记忆(memory_recall/save_experience);默认 true */
|
|
66
68
|
useLongMemory?: boolean;
|
|
69
|
+
/** 在线搜索:启用时注册 web_search 工具 */
|
|
70
|
+
webSearch?: {
|
|
71
|
+
enabled: boolean;
|
|
72
|
+
provider: "brave" | "duck-duck-scrape";
|
|
73
|
+
apiKey?: string;
|
|
74
|
+
timeoutSeconds?: number;
|
|
75
|
+
cacheTtlMinutes?: number;
|
|
76
|
+
maxResults?: number;
|
|
77
|
+
/** 单次搜索返回最大 token;不配置则不限制;前端默认 64K */
|
|
78
|
+
maxResultTokens?: number;
|
|
79
|
+
};
|
|
67
80
|
}): Promise<AgentSession>;
|
|
68
81
|
/** 按复合 key 获取(key = sessionId + "::" + agentId) */
|
|
69
82
|
getSession(compositeKey: string): AgentSession | undefined;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { createAgentSession, AuthStorage, DefaultResourceLoader, ModelRegistry, SessionManager as CoreSessionManager, createReadTool, createWriteTool, createEditTool, createBashTool, createFindTool, createGrepTool, createLsTool } from "@mariozechner/pi-coding-agent";
|
|
1
|
+
import { createAgentSession, AuthStorage, DefaultResourceLoader, ModelRegistry, SessionManager as CoreSessionManager, SettingsManager, createReadTool, createWriteTool, createEditTool, createBashTool, createFindTool, createGrepTool, createLsTool } from "@mariozechner/pi-coding-agent";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { existsSync, mkdirSync } from "node:fs";
|
|
4
4
|
import { createCompactionMemoryExtensionFactory } from "../memory/compaction-extension.js";
|
|
5
|
+
import { loadExtensionFactories } from "../extensions/index.js";
|
|
5
6
|
import { addMemory } from "../memory/index.js";
|
|
6
7
|
import { persistStoredCompactionForSession, persistStoredCompactionForBusinessSession, } from "../memory/persist-compaction-on-close.js";
|
|
7
|
-
import { createBrowserTool, createSaveExperienceTool, createMemoryRecallTool, createInstallSkillTool, createSwitchAgentTool, createListAgentsTool, createCreateAgentTool, createGetBookmarkTagsTool, createSaveBookmarkTool, createAddBookmarkTagTool } from "../tools/index.js";
|
|
8
|
+
import { createBrowserTool, createSaveExperienceTool, createMemoryRecallTool, createInstallSkillTool, createSwitchAgentTool, createListAgentsTool, createCreateAgentTool, createGetBookmarkTagsTool, createSaveBookmarkTool, createAddBookmarkTagTool, createWebSearchTool } from "../tools/index.js";
|
|
8
9
|
/** Agent Session 缓存 key:sessionId + "::" + agentId,同一业务 session 下不同 agent 各自一个 Core Session */
|
|
9
10
|
const COMPOSITE_KEY_SEP = "::";
|
|
10
11
|
function toCompositeKey(sessionId, agentId) {
|
|
@@ -12,10 +13,16 @@ function toCompositeKey(sessionId, agentId) {
|
|
|
12
13
|
}
|
|
13
14
|
import { createMcpToolsForSession } from "../mcp/index.js";
|
|
14
15
|
import { registerBuiltInApiProviders } from "@mariozechner/pi-ai/dist/providers/register-builtins.js";
|
|
15
|
-
import { getOpenbotAgentDir, getOpenbotWorkspaceDir
|
|
16
|
+
import { getOpenbotAgentDir, getOpenbotWorkspaceDir } from "./agent-dir.js";
|
|
16
17
|
import { formatSkillsForPrompt } from "./skills.js";
|
|
18
|
+
import { createTokenUsageLogExtensionFactory, setTokenUsageInitialStats, } from "./token-usage-log-extension.js";
|
|
17
19
|
// Ensure all built-in providers are registered
|
|
18
20
|
registerBuiltInApiProviders();
|
|
21
|
+
/** 粗略按字符估算 token(中英混合约 1/3,纯英文约 1/4) */
|
|
22
|
+
function estTokensFromChars(chars) {
|
|
23
|
+
return Math.ceil(chars / 3);
|
|
24
|
+
}
|
|
25
|
+
const TOKEN_USAGE_LOG_PREFIX = "[token-usage]";
|
|
19
26
|
/** system prompt 中每个技能描述最大字符数,超出截断以省 token */
|
|
20
27
|
const MAX_SKILL_DESC_IN_PROMPT = 250;
|
|
21
28
|
/**
|
|
@@ -123,9 +130,17 @@ For downloads, provide either a direct URL or a selector to click.`;
|
|
|
123
130
|
agentDir: this.agentDir,
|
|
124
131
|
noSkills: true, // Disable SDK's built-in skills logic to take full control
|
|
125
132
|
additionalSkillPaths: this.resolveSkillPaths(workspaceDir),
|
|
126
|
-
extensionFactories:
|
|
127
|
-
|
|
128
|
-
|
|
133
|
+
extensionFactories: (() => {
|
|
134
|
+
const compositeKeyForLoader = sessionId && identity?.agentId
|
|
135
|
+
? sessionId + COMPOSITE_KEY_SEP + identity.agentId
|
|
136
|
+
: "";
|
|
137
|
+
const tokenLog = createTokenUsageLogExtensionFactory(compositeKeyForLoader);
|
|
138
|
+
const base = sessionId && onUpdateLatestCompaction
|
|
139
|
+
? [createCompactionMemoryExtensionFactory(sessionId, onUpdateLatestCompaction)]
|
|
140
|
+
: [];
|
|
141
|
+
const pluginFactories = loadExtensionFactories();
|
|
142
|
+
return [tokenLog, ...base, ...pluginFactories];
|
|
143
|
+
})(),
|
|
129
144
|
systemPromptOverride: (base) => {
|
|
130
145
|
const loadedSkills = loader.getSkills().skills;
|
|
131
146
|
const basePrompt = this.buildSystemPrompt(loadedSkills);
|
|
@@ -203,7 +218,12 @@ For downloads, provide either a direct URL or a selector to click.`;
|
|
|
203
218
|
const provider = options.provider ?? process.env.OPENBOT_PROVIDER ?? "deepseek";
|
|
204
219
|
const modelId = options.modelId ?? process.env.OPENBOT_MODEL ?? "deepseek-chat";
|
|
205
220
|
const apiKey = options.apiKey;
|
|
206
|
-
|
|
221
|
+
// local provider:指向本地 node-llama-cpp 子进程服务
|
|
222
|
+
if (provider === "local") {
|
|
223
|
+
const localBaseUrl = process.env.LOCAL_LLM_BASE_URL ?? "http://127.0.0.1:11435/v1";
|
|
224
|
+
process.env.OPENAI_API_KEY = process.env.OPENAI_API_KEY || "local";
|
|
225
|
+
process.env.OPENAI_BASE_URL = localBaseUrl;
|
|
226
|
+
}
|
|
207
227
|
const authPath = join(this.agentDir, "auth.json");
|
|
208
228
|
const modelsPath = join(this.agentDir, "models.json");
|
|
209
229
|
const authStorage = new AuthStorage(authPath);
|
|
@@ -245,6 +265,8 @@ For downloads, provide either a direct URL or a selector to click.`;
|
|
|
245
265
|
return process.env.MOONSHOT_API_KEY || process.env.KIMI_API_KEY || process.env.OPENAI_API_KEY;
|
|
246
266
|
if (p === "openai" || p === "openai-custom")
|
|
247
267
|
return process.env.OPENAI_API_KEY;
|
|
268
|
+
if (p === "local")
|
|
269
|
+
return process.env.OPENAI_API_KEY || "local";
|
|
248
270
|
return process.env.OPENAI_API_KEY;
|
|
249
271
|
});
|
|
250
272
|
const loader = this.createResourceLoader(sessionWorkspaceDir, sessionId, options.systemPrompt, { agentId, workspace: workspaceName }, (summary) => this.sessionLatestCompactionSummary.set(compositeKey, summary));
|
|
@@ -262,7 +284,9 @@ For downloads, provide either a direct URL or a selector to click.`;
|
|
|
262
284
|
const mcpTools = await createMcpToolsForSession({
|
|
263
285
|
mcpServers: options.mcpServers,
|
|
264
286
|
sessionId,
|
|
287
|
+
mcpMaxResultTokens: options.mcpMaxResultTokens,
|
|
265
288
|
});
|
|
289
|
+
const webSearchTool = options.webSearch?.enabled === true ? createWebSearchTool(options.webSearch) : null;
|
|
266
290
|
const customTools = [
|
|
267
291
|
createBrowserTool(sessionWorkspaceDir),
|
|
268
292
|
createSaveExperienceTool(sessionId),
|
|
@@ -274,11 +298,57 @@ For downloads, provide either a direct URL or a selector to click.`;
|
|
|
274
298
|
createGetBookmarkTagsTool(),
|
|
275
299
|
createSaveBookmarkTool(),
|
|
276
300
|
createAddBookmarkTagTool(),
|
|
301
|
+
...(webSearchTool ? [webSearchTool] : []),
|
|
277
302
|
...mcpTools,
|
|
278
303
|
];
|
|
304
|
+
// 分类打印 token 占用估算(字符数 + 估算 token),便于分析超长请求来源
|
|
305
|
+
try {
|
|
306
|
+
const loadedSkills = loader.getSkills().skills;
|
|
307
|
+
const shortSkills = loadedSkills.map((s) => ({
|
|
308
|
+
...s,
|
|
309
|
+
description: s.description.length <= MAX_SKILL_DESC_IN_PROMPT
|
|
310
|
+
? s.description
|
|
311
|
+
: s.description.slice(0, MAX_SKILL_DESC_IN_PROMPT) + "…",
|
|
312
|
+
}));
|
|
313
|
+
const skillsBlock = formatSkillsForPrompt(shortSkills);
|
|
314
|
+
const basePrompt = this.buildSystemPrompt(loadedSkills);
|
|
315
|
+
const withCustom = options.systemPrompt?.trim()
|
|
316
|
+
? options.systemPrompt.trim() + "\n\n" + basePrompt
|
|
317
|
+
: basePrompt;
|
|
318
|
+
const sessionIdentity = { agentId, workspace: workspaceName };
|
|
319
|
+
const withIdentity = sessionIdentity?.agentId
|
|
320
|
+
? `[Session identity] You are the agent with ID: ${sessionIdentity.agentId}, workspace: ${sessionIdentity.workspace || sessionIdentity.agentId}. When asked which agent you are, answer according to this identity.\n\n` + withCustom
|
|
321
|
+
: withCustom;
|
|
322
|
+
const systemPromptChars = withIdentity.length;
|
|
323
|
+
const toolsDefs = [
|
|
324
|
+
...Object.values(coreTools),
|
|
325
|
+
...customTools,
|
|
326
|
+
].map((t) => ({
|
|
327
|
+
name: t?.name ?? "",
|
|
328
|
+
description: typeof t?.description === "string" ? t.description : "",
|
|
329
|
+
parameters: t?.parameters ?? {},
|
|
330
|
+
}));
|
|
331
|
+
const toolsJsonChars = JSON.stringify(toolsDefs).length;
|
|
332
|
+
const systemPromptEstTokens = estTokensFromChars(systemPromptChars);
|
|
333
|
+
const skillsBlockEstTokens = estTokensFromChars(skillsBlock.length);
|
|
334
|
+
const toolsDefsEstTokens = estTokensFromChars(toolsJsonChars);
|
|
335
|
+
console.log(`${TOKEN_USAGE_LOG_PREFIX} session create (${compositeKey}) | systemPrompt chars=${systemPromptChars} estTokens=${systemPromptEstTokens} | skillsBlock chars=${skillsBlock.length} estTokens=${skillsBlockEstTokens} | toolsDefs chars=${toolsJsonChars} estTokens=${toolsDefsEstTokens}`);
|
|
336
|
+
setTokenUsageInitialStats(compositeKey, {
|
|
337
|
+
systemPromptEstTokens,
|
|
338
|
+
skillsBlockEstTokens,
|
|
339
|
+
toolsDefsEstTokens,
|
|
340
|
+
});
|
|
341
|
+
console.log(`${TOKEN_USAGE_LOG_PREFIX} compaction (SDK): 触发条件 contextTokens > contextWindow - reserveTokens (默认 16384);保留最近 keepRecentTokens (默认 20000)。配置见 pi 文档 settings.json 或传入 settingsManager。`);
|
|
342
|
+
}
|
|
343
|
+
catch (e) {
|
|
344
|
+
console.warn(`${TOKEN_USAGE_LOG_PREFIX} session create log failed:`, e);
|
|
345
|
+
}
|
|
279
346
|
const { session } = await createAgentSession({
|
|
280
347
|
agentDir: this.agentDir,
|
|
281
348
|
sessionManager: CoreSessionManager.inMemory(),
|
|
349
|
+
settingsManager: SettingsManager.inMemory({
|
|
350
|
+
compaction: { enabled: true, reserveTokens: 16384, keepRecentTokens: 20000 },
|
|
351
|
+
}),
|
|
282
352
|
authStorage,
|
|
283
353
|
modelRegistry,
|
|
284
354
|
cwd: sessionWorkspaceDir,
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code CLI AgentProxy 适配器:通过 @instantlyeasy/claude-code-sdk-ts 调用本机 Claude Code CLI,
|
|
3
|
+
* 将用户消息作为 prompt 提交,流式/一次性返回 CLI 输出(类似 Codea 的代理方式)。
|
|
4
|
+
* 底层 CLI 按完整 message 返回,无 token 级流式;此处对收到的整段正文做分片推送,使前端能逐片渲染,避免长时间空白后一次性刷屏。
|
|
5
|
+
* 需本机已安装并可用 `claude` 命令。
|
|
6
|
+
*/
|
|
7
|
+
import { join, resolve } from "path";
|
|
8
|
+
import { query } from "@instantlyeasy/claude-code-sdk-ts";
|
|
9
|
+
import { getOpenbotWorkspaceDir } from "../../agent-dir.js";
|
|
10
|
+
const DEFAULT_TIMEOUT_MS = 300_000;
|
|
11
|
+
function getCwd(config) {
|
|
12
|
+
const custom = config.claudeCode?.workingDirectory;
|
|
13
|
+
if (typeof custom === "string" && custom.trim()) {
|
|
14
|
+
return resolve(custom.trim());
|
|
15
|
+
}
|
|
16
|
+
const w = config.workspace;
|
|
17
|
+
if (typeof w !== "string" || !w.trim())
|
|
18
|
+
return undefined;
|
|
19
|
+
const name = w.trim();
|
|
20
|
+
return join(getOpenbotWorkspaceDir(), name);
|
|
21
|
+
}
|
|
22
|
+
/** 工具名到简短中文描述的映射,用于过程提示 */
|
|
23
|
+
const TOOL_LABELS = {
|
|
24
|
+
Read: "读取文件",
|
|
25
|
+
Write: "写入文件",
|
|
26
|
+
Edit: "编辑",
|
|
27
|
+
Bash: "执行命令",
|
|
28
|
+
Grep: "搜索",
|
|
29
|
+
Glob: "文件匹配",
|
|
30
|
+
LS: "列出目录",
|
|
31
|
+
MultiEdit: "多文件编辑",
|
|
32
|
+
NotebookRead: "读取 Notebook",
|
|
33
|
+
NotebookEdit: "编辑 Notebook",
|
|
34
|
+
WebFetch: "网页抓取",
|
|
35
|
+
TodoRead: "读取待办",
|
|
36
|
+
TodoWrite: "写入待办",
|
|
37
|
+
WebSearch: "网页搜索",
|
|
38
|
+
Task: "任务",
|
|
39
|
+
MCPTool: "MCP 工具",
|
|
40
|
+
};
|
|
41
|
+
/** 从 tool_use input 中取一行简短摘要(用于前端过程展示) */
|
|
42
|
+
function toolUseSummary(name, input) {
|
|
43
|
+
if (name === "Read" && typeof input.path === "string")
|
|
44
|
+
return input.path;
|
|
45
|
+
if (name === "Write" && typeof input.path === "string")
|
|
46
|
+
return input.path;
|
|
47
|
+
if (name === "Bash" && typeof input.command === "string") {
|
|
48
|
+
const cmd = input.command.trim().split(/\n/)[0] ?? "";
|
|
49
|
+
return cmd.length > 60 ? cmd.slice(0, 57) + "…" : cmd;
|
|
50
|
+
}
|
|
51
|
+
if (name === "Grep" && typeof input.pattern === "string")
|
|
52
|
+
return `"${input.pattern.slice(0, 40)}"`;
|
|
53
|
+
if (name === "Edit" && typeof input.path === "string")
|
|
54
|
+
return input.path;
|
|
55
|
+
return "";
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* 处理单条 Message:提取可展示的正文,并通过 onChunk 推送过程信息(工具调用、系统消息等)。
|
|
59
|
+
*/
|
|
60
|
+
function processMessage(msg, callbacks) {
|
|
61
|
+
if (msg.type === "result" && typeof msg.content === "string") {
|
|
62
|
+
return msg.content;
|
|
63
|
+
}
|
|
64
|
+
if (msg.type === "system") {
|
|
65
|
+
const subtype = msg.subtype;
|
|
66
|
+
const data = msg.data;
|
|
67
|
+
const label = subtype === "init" ? "初始化" : subtype ? String(subtype) : "系统";
|
|
68
|
+
const extra = data != null && typeof data === "object" && !Array.isArray(data)
|
|
69
|
+
? ""
|
|
70
|
+
: typeof data === "string" ? ` ${data.slice(0, 80)}` : "";
|
|
71
|
+
callbacks.onChunk(`\n[Claude Code] ${label}${extra}\n`);
|
|
72
|
+
return "";
|
|
73
|
+
}
|
|
74
|
+
if (msg.type === "assistant" && Array.isArray(msg.content)) {
|
|
75
|
+
const blocks = msg.content;
|
|
76
|
+
let text = "";
|
|
77
|
+
for (const b of blocks) {
|
|
78
|
+
if (b.type === "text" && typeof b.text === "string") {
|
|
79
|
+
text += b.text;
|
|
80
|
+
}
|
|
81
|
+
else if (b.type === "tool_use" && b.name) {
|
|
82
|
+
const label = TOOL_LABELS[b.name] ?? b.name;
|
|
83
|
+
const summary = b.input && typeof b.input === "object" ? toolUseSummary(b.name, b.input) : "";
|
|
84
|
+
const detail = summary ? `: ${summary}` : "";
|
|
85
|
+
callbacks.onChunk(`\n\n---\n🔧 **使用工具**: ${label}${detail}\n---\n\n`);
|
|
86
|
+
}
|
|
87
|
+
else if (b.type === "tool_result") {
|
|
88
|
+
callbacks.onChunk("\n✓ 工具返回\n");
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return text;
|
|
92
|
+
}
|
|
93
|
+
return "";
|
|
94
|
+
}
|
|
95
|
+
export const claudeCodeAdapter = {
|
|
96
|
+
type: "claude_code",
|
|
97
|
+
async runStream(options, config, callbacks) {
|
|
98
|
+
const cwd = getCwd(config);
|
|
99
|
+
const controller = new AbortController();
|
|
100
|
+
const timeoutId = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT_MS);
|
|
101
|
+
const userSignal = options.signal;
|
|
102
|
+
if (userSignal) {
|
|
103
|
+
if (userSignal.aborted)
|
|
104
|
+
controller.abort();
|
|
105
|
+
else
|
|
106
|
+
userSignal.addEventListener("abort", () => controller.abort(), { once: true });
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
callbacks.onChunk("⏳ Claude Code CLI 正在处理…\n\n");
|
|
110
|
+
const generator = query(options.message, {
|
|
111
|
+
cwd: cwd ?? process.cwd(),
|
|
112
|
+
signal: controller.signal,
|
|
113
|
+
timeout: Math.floor(DEFAULT_TIMEOUT_MS / 1000),
|
|
114
|
+
});
|
|
115
|
+
for await (const message of generator) {
|
|
116
|
+
if (controller.signal.aborted)
|
|
117
|
+
break;
|
|
118
|
+
const text = processMessage(message, callbacks);
|
|
119
|
+
if (text)
|
|
120
|
+
callbacks.onChunk(text);
|
|
121
|
+
}
|
|
122
|
+
callbacks.onDone();
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
126
|
+
const name = err instanceof Error ? err.name : "";
|
|
127
|
+
const isUserOrTimeoutAbort = controller.signal.aborted ||
|
|
128
|
+
name === "AbortError" ||
|
|
129
|
+
(typeof msg === "string" && (msg === "Aborted" || msg === "The operation was aborted"));
|
|
130
|
+
if (isUserOrTimeoutAbort) {
|
|
131
|
+
callbacks.onChunk("\n\n[已中止]");
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
console.error("[Claude Code adapter] runStream error:", err);
|
|
135
|
+
callbacks.onChunk(`\n\n[错误] ${msg}`);
|
|
136
|
+
}
|
|
137
|
+
callbacks.onDone();
|
|
138
|
+
}
|
|
139
|
+
finally {
|
|
140
|
+
clearTimeout(timeoutId);
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
async runCollect(options, config) {
|
|
144
|
+
const cwd = getCwd(config);
|
|
145
|
+
const controller = new AbortController();
|
|
146
|
+
const timeoutId = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT_MS);
|
|
147
|
+
const userSignal = options.signal;
|
|
148
|
+
if (userSignal) {
|
|
149
|
+
if (userSignal.aborted)
|
|
150
|
+
controller.abort();
|
|
151
|
+
else
|
|
152
|
+
userSignal.addEventListener("abort", () => controller.abort(), { once: true });
|
|
153
|
+
}
|
|
154
|
+
const parts = [];
|
|
155
|
+
const collectChunk = (delta) => parts.push(delta);
|
|
156
|
+
try {
|
|
157
|
+
const generator = query(options.message, {
|
|
158
|
+
cwd: cwd ?? process.cwd(),
|
|
159
|
+
signal: controller.signal,
|
|
160
|
+
timeout: Math.floor(DEFAULT_TIMEOUT_MS / 1000),
|
|
161
|
+
});
|
|
162
|
+
for await (const message of generator) {
|
|
163
|
+
if (controller.signal.aborted)
|
|
164
|
+
break;
|
|
165
|
+
const text = processMessage(message, { onChunk: collectChunk });
|
|
166
|
+
if (text)
|
|
167
|
+
parts.push(text);
|
|
168
|
+
}
|
|
169
|
+
return parts.join("").trim() || "(无文本回复)";
|
|
170
|
+
}
|
|
171
|
+
catch (err) {
|
|
172
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
173
|
+
const name = err instanceof Error ? err.name : "";
|
|
174
|
+
const isAbort = controller.signal.aborted ||
|
|
175
|
+
name === "AbortError" ||
|
|
176
|
+
(typeof msg === "string" && (msg === "Aborted" || msg === "The operation was aborted"));
|
|
177
|
+
if (isAbort)
|
|
178
|
+
return parts.join("").trim() + "\n\n[已中止]";
|
|
179
|
+
console.error("[Claude Code adapter] runCollect error:", err);
|
|
180
|
+
throw err;
|
|
181
|
+
}
|
|
182
|
+
finally {
|
|
183
|
+
clearTimeout(timeoutId);
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
};
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { agentManager } from "../../agent-manager.js";
|
|
5
5
|
import { getDesktopConfig } from "../../../config/desktop-config.js";
|
|
6
|
-
const CHANNEL_AGENT_TIMEOUT_MS =
|
|
6
|
+
const CHANNEL_AGENT_TIMEOUT_MS = 300_000; // 5 分钟,本地/大模型回复可能较慢
|
|
7
7
|
export const localAdapter = {
|
|
8
8
|
type: "local",
|
|
9
9
|
async runStream(options, config, callbacks) {
|
|
@@ -21,6 +21,7 @@ export const localAdapter = {
|
|
|
21
21
|
mcpServers: config.mcpServers,
|
|
22
22
|
systemPrompt: config.systemPrompt,
|
|
23
23
|
useLongMemory: config.useLongMemory,
|
|
24
|
+
webSearch: config.webSearch,
|
|
24
25
|
});
|
|
25
26
|
let resolveDone;
|
|
26
27
|
const donePromise = new Promise((r) => {
|
|
@@ -64,6 +65,7 @@ export const localAdapter = {
|
|
|
64
65
|
mcpServers: config.mcpServers,
|
|
65
66
|
systemPrompt: config.systemPrompt,
|
|
66
67
|
useLongMemory: config.useLongMemory,
|
|
68
|
+
webSearch: config.webSearch,
|
|
67
69
|
});
|
|
68
70
|
const chunks = [];
|
|
69
71
|
let resolveDone;
|