@next-open-ai/openclawx 0.8.40 → 0.8.58

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 (48) hide show
  1. package/README.md +10 -0
  2. package/apps/desktop/renderer/dist/assets/index-M5VGUUpo.js +93 -0
  3. package/apps/desktop/renderer/dist/assets/index-y8oE2q_u.css +10 -0
  4. package/apps/desktop/renderer/dist/index.html +2 -2
  5. package/dist/cli/cli.js +107 -0
  6. package/dist/core/agent/agent-manager.js +13 -2
  7. package/dist/core/agent/proxy/adapters/local-adapter.js +1 -1
  8. package/dist/core/config/desktop-config.d.ts +4 -1
  9. package/dist/core/config/desktop-config.js +108 -21
  10. package/dist/core/config/provider-support-default.js +26 -0
  11. package/dist/core/local-llm-server/download-model.d.ts +16 -0
  12. package/dist/core/local-llm-server/download-model.js +37 -0
  13. package/dist/core/local-llm-server/index.d.ts +32 -0
  14. package/dist/core/local-llm-server/index.js +147 -0
  15. package/dist/core/local-llm-server/llm-context.d.ts +65 -0
  16. package/dist/core/local-llm-server/llm-context.js +242 -0
  17. package/dist/core/local-llm-server/model-resolve.d.ts +27 -0
  18. package/dist/core/local-llm-server/model-resolve.js +90 -0
  19. package/dist/core/local-llm-server/server.d.ts +1 -0
  20. package/dist/core/local-llm-server/server.js +234 -0
  21. package/dist/core/local-llm-server/start-from-config.d.ts +5 -0
  22. package/dist/core/local-llm-server/start-from-config.js +50 -0
  23. package/dist/core/mcp/transport/stdio.d.ts +6 -0
  24. package/dist/core/mcp/transport/stdio.js +107 -27
  25. package/dist/core/memory/local-embedding-llama.js +2 -4
  26. package/dist/core/memory/local-embedding.d.ts +4 -3
  27. package/dist/core/memory/local-embedding.js +43 -3
  28. package/dist/gateway/methods/agent-chat.js +80 -41
  29. package/dist/gateway/server.js +10 -0
  30. package/dist/server/agent-config/agent-config.controller.d.ts +1 -1
  31. package/dist/server/agent-config/agent-config.service.d.ts +2 -0
  32. package/dist/server/agent-config/agent-config.service.js +5 -0
  33. package/dist/server/bootstrap.d.ts +1 -0
  34. package/dist/server/bootstrap.js +3 -0
  35. package/dist/server/config/config.controller.d.ts +81 -4
  36. package/dist/server/config/config.controller.js +185 -3
  37. package/dist/server/config/config.module.js +3 -2
  38. package/dist/server/config/config.service.d.ts +4 -1
  39. package/dist/server/config/config.service.js +62 -9
  40. package/dist/server/config/local-models.service.d.ts +67 -0
  41. package/dist/server/config/local-models.service.js +243 -0
  42. package/package.json +1 -1
  43. package/presets/preset-agents.json +6 -2
  44. package/presets/preset-config.json +24 -6
  45. package/presets/recommended-local-models.json +42 -0
  46. package/apps/desktop/renderer/dist/assets/index-BSfTiTKo.css +0 -10
  47. package/apps/desktop/renderer/dist/assets/index-DgLpQsA-.js +0 -89
  48. package/presets/workspaces/finance-expert/skills/akshare-helper/SKILL.md +0 -9
@@ -0,0 +1,243 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ /**
11
+ * 本地 GGUF 模型管理服务。
12
+ * 负责列出、下载(通过 node-llama-cpp resolveModelFile)、删除本地缓存的 GGUF 模型文件。
13
+ * 推荐列表从 presets/recommended-local-models.json 加载,已安装的与推荐使用同一套展示名称且已安装的不再出现在「备下载」列表。
14
+ * 模型缓存目录:~/.openbot/.cached_models/
15
+ */
16
+ import { Injectable } from '@nestjs/common';
17
+ import { readdir, stat, unlink, readFile } from 'node:fs/promises';
18
+ import { join, basename } from 'node:path';
19
+ import { existsSync, mkdirSync } from 'node:fs';
20
+ import { modelUriToFilename, modelUriBasename, LOCAL_LLM_CACHE_DIR } from '../../core/local-llm-server/model-resolve.js';
21
+ /** 根据文件名推断模型类型 */
22
+ function inferModelType(filename) {
23
+ const lower = filename.toLowerCase();
24
+ if (lower.includes('embed') || lower.includes('bge') || lower.includes('e5-'))
25
+ return 'embedding';
26
+ return 'llm';
27
+ }
28
+ const PRESET_FILENAME = 'recommended-local-models.json';
29
+ const DEFAULT_RECOMMENDED = [
30
+ { id: 'hf:Qwen/Qwen3-4B-GGUF/Qwen3-4B-Q4_K_M.gguf', name: 'Qwen3 4B Q4_K_M', type: 'llm', sizeHint: '~2.5GB' },
31
+ { id: 'hf:Qwen/Qwen3-7B-GGUF/Qwen3-7B-Q4_K_M.gguf', name: 'Qwen3 7B Q4_K_M', type: 'llm', sizeHint: '~4.5GB' },
32
+ { id: 'hf:Qwen/Qwen3-14B-GGUF/Qwen3-14B-Q4_K_M.gguf', name: 'Qwen3 14B Q4_K_M', type: 'llm', sizeHint: '~8.5GB' },
33
+ { id: 'hf:ggml-org/embeddinggemma-300M-GGUF/embeddinggemma-300M-Q8_0.gguf', name: 'EmbeddingGemma 300M Q8 (768维)', type: 'embedding', sizeHint: '~300MB' },
34
+ { id: 'hf:gpustack/bge-m3-GGUF/bge-m3-Q8_0.gguf', name: 'BGE-M3 Q8 多语言 (1024维)', type: 'embedding', sizeHint: '~1.2GB' },
35
+ { id: 'hf:mixedbread-ai/mxbai-embed-large-v1-GGUF/mxbai-embed-large-v1-f16.gguf', name: 'MxBai Embed Large v1 (1024维)', type: 'embedding', sizeHint: '~670MB' },
36
+ ];
37
+ function getPresetsDir() {
38
+ return process.env.OPENBOT_PRESETS_DIR || join(process.cwd(), 'presets');
39
+ }
40
+ /** 推荐项是否对应已安装文件(精确预测名 或 安装文件名以 uri 末尾文件名结尾,兼容不同 node-llama-cpp 命名) */
41
+ function recommendedMatchesInstalled(rec, installedFilenames) {
42
+ const predicted = modelUriToFilename(rec.id);
43
+ if (installedFilenames.has(predicted))
44
+ return true;
45
+ const suffix = modelUriBasename(rec.id);
46
+ return Array.from(installedFilenames).some((f) => f.endsWith(suffix) || f === suffix);
47
+ }
48
+ /** 已安装文件名在预设中对应的展示名,若无匹配则返回 filename */
49
+ function displayNameForFilename(filename, recommended) {
50
+ for (const rec of recommended) {
51
+ const predicted = modelUriToFilename(rec.id);
52
+ if (filename === predicted)
53
+ return rec.name;
54
+ const uriBase = modelUriBasename(rec.id);
55
+ if (uriBase && filename.endsWith(uriBase))
56
+ return rec.name;
57
+ }
58
+ return filename;
59
+ }
60
+ let LocalModelsService = class LocalModelsService {
61
+ cacheDir;
62
+ recommendedCache = null;
63
+ /** 正在下载的任务:modelUri → 进度 */
64
+ downloadingMap = new Map();
65
+ constructor() {
66
+ this.cacheDir = LOCAL_LLM_CACHE_DIR;
67
+ if (!existsSync(this.cacheDir)) {
68
+ mkdirSync(this.cacheDir, { recursive: true });
69
+ }
70
+ }
71
+ /** 从 presets/recommended-local-models.json 加载推荐列表,失败则用内置默认 */
72
+ async getRecommendedModelsFromPreset() {
73
+ if (this.recommendedCache)
74
+ return this.recommendedCache;
75
+ const presetPath = join(getPresetsDir(), PRESET_FILENAME);
76
+ try {
77
+ if (existsSync(presetPath)) {
78
+ const raw = await readFile(presetPath, 'utf-8');
79
+ const data = JSON.parse(raw);
80
+ if (Array.isArray(data.models) && data.models.length > 0) {
81
+ this.recommendedCache = data.models;
82
+ return this.recommendedCache;
83
+ }
84
+ }
85
+ }
86
+ catch {
87
+ // ignore, fallback to default
88
+ }
89
+ this.recommendedCache = DEFAULT_RECOMMENDED;
90
+ return this.recommendedCache;
91
+ }
92
+ /** 列出本地已缓存的 GGUF 模型文件,displayName 与推荐列表一致(来自 preset 匹配) */
93
+ async listModels() {
94
+ try {
95
+ const recommended = await this.getRecommendedModelsFromPreset();
96
+ const files = await readdir(this.cacheDir);
97
+ const ggufFiles = files.filter((f) => f.endsWith('.gguf'));
98
+ const infos = await Promise.all(ggufFiles.map(async (f) => {
99
+ const filePath = join(this.cacheDir, f);
100
+ const s = await stat(filePath);
101
+ return {
102
+ filename: f,
103
+ size: s.size,
104
+ updatedAt: s.mtime.toISOString(),
105
+ inferredType: inferModelType(f),
106
+ displayName: displayNameForFilename(f, recommended),
107
+ };
108
+ }));
109
+ return infos.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
110
+ }
111
+ catch {
112
+ return [];
113
+ }
114
+ }
115
+ /** 删除本地缓存的 GGUF 模型文件 */
116
+ async deleteModel(filename) {
117
+ // 安全检查:只允许删除 .gguf 文件,且不含路径分隔符
118
+ if (!filename.endsWith('.gguf') || filename.includes('/') || filename.includes('\\')) {
119
+ throw new Error('非法文件名');
120
+ }
121
+ const filePath = join(this.cacheDir, filename);
122
+ if (!existsSync(filePath))
123
+ throw new Error(`文件不存在: ${filename}`);
124
+ await unlink(filePath);
125
+ }
126
+ /** 获取推荐模型列表(来自 preset,与已安装展示名称一致) */
127
+ async getRecommendedModels() {
128
+ return this.getRecommendedModelsFromPreset();
129
+ }
130
+ /** 仅返回尚未安装的推荐模型;已安装的视为「推荐列表中的一员」不再出现在备下载区 */
131
+ async getRecommendedToDownload() {
132
+ const [recommended, installed] = await Promise.all([
133
+ this.getRecommendedModelsFromPreset(),
134
+ this.listModels(),
135
+ ]);
136
+ const installedFilenames = new Set(installed.map((m) => m.filename));
137
+ return recommended.filter((rec) => !recommendedMatchesInstalled(rec, installedFilenames));
138
+ }
139
+ /** 检查指定模型(uri 或文件名)是否已在缓存目录存在 */
140
+ isModelFilePresent(modelIdOrUri) {
141
+ const filename = modelUriToFilename(modelIdOrUri);
142
+ if (!filename || !filename.endsWith('.gguf'))
143
+ return false;
144
+ return existsSync(join(this.cacheDir, filename));
145
+ }
146
+ /** 正在下载任务的 AbortController,用于取消 */
147
+ abortControllerMap = new Map();
148
+ /** 获取某个 modelUri 的下载进度(不存在则表示未在下载) */
149
+ getDownloadProgress(modelUri) {
150
+ return this.downloadingMap.get(modelUri) ?? null;
151
+ }
152
+ /**
153
+ * 取消指定 modelUri 的下载(若正在下载)。
154
+ */
155
+ cancelDownload(modelUri) {
156
+ const controller = this.abortControllerMap.get(modelUri);
157
+ if (controller) {
158
+ controller.abort();
159
+ this.downloadingMap.set(modelUri, { status: '已取消' });
160
+ this.abortControllerMap.delete(modelUri);
161
+ setTimeout(() => this.downloadingMap.delete(modelUri), 3000);
162
+ }
163
+ }
164
+ /**
165
+ * 返回所有推荐模型及是否已安装,用于前端展示「已下载」或下载按钮。
166
+ */
167
+ async getRecommendedWithStatus() {
168
+ const [recommended, installed] = await Promise.all([
169
+ this.getRecommendedModelsFromPreset(),
170
+ this.listModels(),
171
+ ]);
172
+ const installedFilenames = new Set(installed.map((m) => m.filename));
173
+ return recommended.map((rec) => ({
174
+ ...rec,
175
+ isInstalled: recommendedMatchesInstalled(rec, installedFilenames),
176
+ }));
177
+ }
178
+ /**
179
+ * 后台下载模型(通过 node-llama-cpp resolveModelFile)。
180
+ * useMirror=true 使用国内镜像 hf-mirror.com,false 使用官方 huggingface.co。
181
+ * 返回后立即响应,进度通过 getDownloadProgress 轮询获取。
182
+ */
183
+ async startDownload(modelUri, options) {
184
+ if (this.downloadingMap.has(modelUri)) {
185
+ return { filename: modelUriToFilename(modelUri) };
186
+ }
187
+ const controller = new AbortController();
188
+ this.abortControllerMap.set(modelUri, controller);
189
+ this.downloadingMap.set(modelUri, { status: '准备下载...' });
190
+ this.runDownload(modelUri, controller.signal, options?.useMirror === true).catch((e) => {
191
+ if (e?.name === 'AbortError' || (typeof e?.message === 'string' && e.message.includes('abort'))) {
192
+ this.downloadingMap.set(modelUri, { status: '已取消' });
193
+ }
194
+ else {
195
+ const msg = e instanceof Error ? e.message : String(e);
196
+ let errorMsg = `失败: ${msg}`;
197
+ if (msg.includes('401') || msg.includes('Unauthorized')) {
198
+ errorMsg = '下载失败: 需要 Hugging Face Token。请设置环境变量 HF_TOKEN 或 HUGGING_FACE_TOKEN';
199
+ }
200
+ this.downloadingMap.set(modelUri, { status: errorMsg });
201
+ }
202
+ this.abortControllerMap.delete(modelUri);
203
+ setTimeout(() => this.downloadingMap.delete(modelUri), 5000);
204
+ });
205
+ return { filename: modelUriToFilename(modelUri) };
206
+ }
207
+ async runDownload(modelUri, signal, useMirror) {
208
+ const { resolveModelFile } = await import('node-llama-cpp');
209
+ this.downloadingMap.set(modelUri, { status: '解析模型地址...' });
210
+ const hfToken = process.env.HF_TOKEN || process.env.HUGGING_FACE_TOKEN;
211
+ const options = {
212
+ directory: this.cacheDir,
213
+ onProgress: ({ downloadedSize, totalSize }) => {
214
+ if (signal?.aborted)
215
+ return;
216
+ const percent = totalSize ? Math.round((downloadedSize / totalSize) * 100) : 0;
217
+ this.downloadingMap.set(modelUri, {
218
+ status: '下载中',
219
+ completed: downloadedSize,
220
+ total: totalSize,
221
+ percent,
222
+ });
223
+ },
224
+ signal,
225
+ endpoints: {
226
+ huggingFace: useMirror ? 'https://hf-mirror.com/' : 'https://huggingface.co/',
227
+ },
228
+ };
229
+ if (hfToken) {
230
+ options.headers = { Authorization: `Bearer ${hfToken}` };
231
+ }
232
+ const resolved = await resolveModelFile(modelUri, options);
233
+ const filename = basename(resolved);
234
+ this.downloadingMap.set(modelUri, { status: `完成: ${filename}`, percent: 100 });
235
+ this.abortControllerMap.delete(modelUri);
236
+ setTimeout(() => this.downloadingMap.delete(modelUri), 5000);
237
+ }
238
+ };
239
+ LocalModelsService = __decorate([
240
+ Injectable(),
241
+ __metadata("design:paramtypes", [])
242
+ ], LocalModelsService);
243
+ export { LocalModelsService };
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.8.40",
6
+ "version": "0.8.58",
7
7
  "description": "OpenClawX - A professional desktop application for managing and executing AI agents with real-time chat, session management, and skills browsing.",
8
8
  "type": "module",
9
9
  "main": "dist/index.js",
@@ -68,7 +68,7 @@
68
68
  "useLongMemory": true,
69
69
  "mcpServers": {
70
70
  "akshare-tools": {
71
- "command": "npx",
71
+ "command": "uvx",
72
72
  "args": ["-y", "akshare-tools"],
73
73
  "env": {}
74
74
  }
@@ -107,7 +107,11 @@
107
107
  "影刀 RPA MCP Server": {
108
108
  "command": "npx",
109
109
  "args": ["-y", "yingdao-mcp-server"],
110
- "env": {}
110
+ "env": {
111
+ "RPA_MODEL": "qwen3-4b-q4_k_m",
112
+ "SHADOWBOT_PATH": "/Applications/影刀.app",
113
+ "USER_FOLDER": "/Users/ctrip/Library/Application Support/Shadowbot/users/718771925823332354"
114
+ }
111
115
  }
112
116
  }
113
117
  },
@@ -1,11 +1,29 @@
1
1
  {
2
2
  "presetVersion": "1.0",
3
3
  "config": {
4
- "defaultProvider": "deepseek",
5
- "defaultModel": "deepseek-chat",
4
+ "defaultProvider": "local",
5
+ "defaultModel": "hf_Qwen_Qwen3-4B-GGUF_Qwen3-4B-Q4_K_M.gguf",
6
+ "defaultModelItemCode": "local-qwen3-4b",
6
7
  "defaultAgentId": "default",
7
- "maxAgentSessions": 50,
8
- "providers": {},
9
- "configuredModels": []
8
+ "maxAgentSessions": 5,
9
+ "providers": {
10
+ "local": { "baseUrl": "http://127.0.0.1:11435/v1" }
11
+ },
12
+ "configuredModels": [
13
+ {
14
+ "provider": "local",
15
+ "modelId": "hf_Qwen_Qwen3-4B-GGUF_Qwen3-4B-Q4_K_M.gguf",
16
+ "type": "llm",
17
+ "alias": "Qwen3 4B Q4_K_M",
18
+ "modelItemCode": "local-qwen3-4b"
19
+ },
20
+ {
21
+ "provider": "local",
22
+ "modelId": "hf_ggml-org_embeddinggemma-300M-GGUF_embeddinggemma-300M-Q8_0.gguf",
23
+ "type": "embedding",
24
+ "alias": "EmbeddingGemma 300M Q8 (768维)",
25
+ "modelItemCode": "local-embeddinggemma-300m"
26
+ }
27
+ ]
10
28
  }
11
- }
29
+ }
@@ -0,0 +1,42 @@
1
+ {
2
+ "presetVersion": "1.0",
3
+ "description": "推荐本地 GGUF 模型列表,用于「模型管理」备下载与已安装展示。版本升级时可在此增删改,以支持更合适的模型。",
4
+ "models": [
5
+ {
6
+ "id": "hf:Qwen/Qwen3-4B-GGUF/Qwen3-4B-Q4_K_M.gguf",
7
+ "name": "Qwen3 4B Q4_K_M",
8
+ "type": "llm",
9
+ "sizeHint": "~2.5GB"
10
+ },
11
+ {
12
+ "id": "hf:Qwen/Qwen3-7B-GGUF/Qwen3-7B-Q4_K_M.gguf",
13
+ "name": "Qwen3 7B Q4_K_M",
14
+ "type": "llm",
15
+ "sizeHint": "~4.5GB"
16
+ },
17
+ {
18
+ "id": "hf:Qwen/Qwen3-14B-GGUF/Qwen3-14B-Q4_K_M.gguf",
19
+ "name": "Qwen3 14B Q4_K_M",
20
+ "type": "llm",
21
+ "sizeHint": "~8.5GB"
22
+ },
23
+ {
24
+ "id": "hf:ggml-org/embeddinggemma-300M-GGUF/embeddinggemma-300M-Q8_0.gguf",
25
+ "name": "EmbeddingGemma 300M Q8 (768维)",
26
+ "type": "embedding",
27
+ "sizeHint": "~300MB"
28
+ },
29
+ {
30
+ "id": "hf:gpustack/bge-m3-GGUF/bge-m3-Q8_0.gguf",
31
+ "name": "BGE-M3 Q8 多语言 (1024维)",
32
+ "type": "embedding",
33
+ "sizeHint": "~1.2GB"
34
+ },
35
+ {
36
+ "id": "hf:mixedbread-ai/mxbai-embed-large-v1-GGUF/mxbai-embed-large-v1-f16.gguf",
37
+ "name": "MxBai Embed Large v1 (1024维)",
38
+ "type": "embedding",
39
+ "sizeHint": "~670MB"
40
+ }
41
+ ]
42
+ }