opc-agent 4.1.3 → 4.1.5
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/COMPETITIVE-GAP.md +92 -0
- package/dist/cli.js +81 -45
- package/dist/cli.js.map +1 -1
- package/dist/core/agent.d.ts +1 -0
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +3 -0
- package/dist/core/agent.js.map +1 -1
- package/dist/core/model-recommender.d.ts +40 -0
- package/dist/core/model-recommender.d.ts.map +1 -0
- package/dist/core/model-recommender.js +186 -0
- package/dist/core/model-recommender.js.map +1 -0
- package/dist/core/runtime.d.ts.map +1 -1
- package/dist/core/runtime.js +33 -0
- package/dist/core/runtime.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -5
- package/dist/index.js.map +1 -1
- package/dist/memory/user-profiler.d.ts +8 -0
- package/dist/memory/user-profiler.d.ts.map +1 -1
- package/dist/memory/user-profiler.js +89 -0
- package/dist/memory/user-profiler.js.map +1 -1
- package/models.json +164 -0
- package/package.json +1 -1
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# OPC vs OpenClaw vs Hermes — 体验差距对照表
|
|
2
|
+
|
|
3
|
+
## 状态说明
|
|
4
|
+
- ✅ OPC 已有且不差于竞品
|
|
5
|
+
- 🟡 OPC 有但不如竞品
|
|
6
|
+
- ❌ OPC 缺失,竞品有
|
|
7
|
+
- 🔵 OPC 独有优势
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 一、安装 & 初始化
|
|
12
|
+
|
|
13
|
+
| # | 体验点 | OpenClaw | Hermes | OPC 现状 | 差距 | 优先级 |
|
|
14
|
+
|---|--------|----------|--------|----------|------|--------|
|
|
15
|
+
| 1 | 一行安装 | `npx openclaw@latest init` | `curl \| sh` one-liner | `npm i -g opc-agent && opc init` | 🟡 两步 vs 一步 | P1 |
|
|
16
|
+
| 2 | 本地模型支持 | ✅ 支持 Ollama | ✅ 支持 | ✅ v4.1.4 Ollama-first | ✅ | - |
|
|
17
|
+
| 3 | 智能模型推荐 | ❌ 手动选 | ❌ 手动选 | ✅ 硬件检测 + 分级推荐 | 🔵 领先 | - |
|
|
18
|
+
| 4 | Setup Wizard | 交互式向导 | `hermes setup` 向导 | 交互式模板+LLM选择 | ✅ | - |
|
|
19
|
+
| 5 | 迁移工具 | - | 从 OpenClaw 迁移 | ❌ 无迁移工具 | ❌ | P3 |
|
|
20
|
+
| 6 | Doctor 诊断 | ❌ 无 | ❌ 无 | ✅ `opc doctor` 11项检查 | 🔵 领先 | - |
|
|
21
|
+
| 7 | `npx opc-agent init` 一行启动 | - | - | ❌ 需要先全局安装 | 🟡 | P1 |
|
|
22
|
+
|
|
23
|
+
## 二、自主学习 & 技能进化
|
|
24
|
+
|
|
25
|
+
| # | 体验点 | OpenClaw | Hermes | OPC 现状 | 差距 | 优先级 |
|
|
26
|
+
|---|--------|----------|--------|----------|------|--------|
|
|
27
|
+
| 8 | Agent 自写 Skill | ✅ AgentSkills | ✅ 自动创建 Skill MD | ❌ 代码未实现 | ❌ 关键差距 | P0 |
|
|
28
|
+
| 9 | 学习触发条件 | 自动 | 5步闭环(execute→evaluate→extract→refine→retrieve) | ❌ | ❌ | P0 |
|
|
29
|
+
| 10 | Skill 格式标准 | AgentSkills | agentskills.io MD | 无标准 | ❌ | P0 |
|
|
30
|
+
| 11 | 预置 Skill 数量 | 100+ ClawHub | 40+ bundled | 12 模板 | 🟡 | P2 |
|
|
31
|
+
| 12 | Skill 市场 | ClawHub | 社区共享 | ❌ 无 | ❌ | P3 |
|
|
32
|
+
| 13 | 知识进化(evolve) | ❌ | ❌ | ✅ DeepBrain evolve 架构 | 🔵 领先 | - |
|
|
33
|
+
| 14 | 多层知识体系 | ❌ | ❌ | ✅ 行业→岗位→工位→Agent | 🔵 领先 | - |
|
|
34
|
+
|
|
35
|
+
## 三、用户建模 & 记忆
|
|
36
|
+
|
|
37
|
+
| # | 体验点 | OpenClaw | Hermes | OPC 现状 | 差距 | 优先级 |
|
|
38
|
+
|---|--------|----------|--------|----------|------|--------|
|
|
39
|
+
| 15 | USER.md 用户画像 | Workspace MD | ✅ USER.md + Honcho | ❌ 无 | ❌ | P1 |
|
|
40
|
+
| 16 | SOUL.md 人格 | ✅ | ❌ | ✅ | ✅ | - |
|
|
41
|
+
| 17 | 持久记忆 | SQLite + Memory | SQLite + FTS5 | FileBackedStore JSON | 🟡 | P2 |
|
|
42
|
+
| 18 | 对话历史搜索 | ✅ | FTS5 全文搜索 | ❌ | ❌ | P2 |
|
|
43
|
+
|
|
44
|
+
## 四、渠道 & 交互
|
|
45
|
+
|
|
46
|
+
| # | 体验点 | OpenClaw | Hermes | OPC 现状 | 差距 | 优先级 |
|
|
47
|
+
|---|--------|----------|--------|----------|------|--------|
|
|
48
|
+
| 19 | 多渠道(TG/Discord/Slack) | ✅ 全部 | ✅ TG/Discord/Slack | ✅ 代码有 | ✅ | - |
|
|
49
|
+
| 20 | Web UI 聊天 | ✅ | ✅ | ✅ | ✅ | - |
|
|
50
|
+
| 21 | Studio 管理面板 | 无(CLI only) | 无(CLI only) | ✅ OPC Studio | 🔵 领先 | - |
|
|
51
|
+
| 22 | CLI 对话模式 | ❌ | ✅ `hermes chat` | ❌ | 🟡 | P2 |
|
|
52
|
+
|
|
53
|
+
## 五、开发者体验
|
|
54
|
+
|
|
55
|
+
| # | 体验点 | OpenClaw | Hermes | OPC 现状 | 差距 | 优先级 |
|
|
56
|
+
|---|--------|----------|--------|----------|------|--------|
|
|
57
|
+
| 23 | TypeScript SDK | ❌ | ❌ | ✅ | 🔵 领先 | - |
|
|
58
|
+
| 24 | A2A 协议 | ❌ | ❌ | ✅ | 🔵 领先 | - |
|
|
59
|
+
| 25 | MCP 协议 | ✅ | ✅ | ✅ | ✅ | - |
|
|
60
|
+
| 26 | Workstation 模板 | ❌ | ❌ | ✅ 100+ 角色 | 🔵 领先 | - |
|
|
61
|
+
| 27 | `npx opc-agent` 零安装运行 | ✅ npx openclaw | ❌ | ❌ | 🟡 | P1 |
|
|
62
|
+
|
|
63
|
+
## 六、自动化 & 调度
|
|
64
|
+
|
|
65
|
+
| # | 体验点 | OpenClaw | Hermes | OPC 现状 | 差距 | 优先级 |
|
|
66
|
+
|---|--------|----------|--------|----------|------|--------|
|
|
67
|
+
| 28 | Cron 定时任务 | ✅ Heartbeat | ❌ | ✅ cron-engine | ✅ | - |
|
|
68
|
+
| 29 | 主动触达 | ✅ Proactive | ❌ | 🟡 有 cron 但无主动逻辑 | 🟡 | P2 |
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## 优先级行动计划
|
|
73
|
+
|
|
74
|
+
### P0 — 今天必须启动(自主学习闭环)
|
|
75
|
+
1. **#8 Agent 自写 Skill** — 实现 auto-skill-creation
|
|
76
|
+
2. **#9 学习触发条件** — 实现 5 步闭环
|
|
77
|
+
3. **#10 Skill 格式标准** — 采用 agentskills.io 兼容格式
|
|
78
|
+
|
|
79
|
+
### P1 — 今天尽量完成(安装体验 + 用户建模)
|
|
80
|
+
4. **#1/#7/#27 一行安装** — 支持 `npx opc-agent init`
|
|
81
|
+
5. **#15 USER.md 用户建模** — Agent 自动学习用户偏好
|
|
82
|
+
|
|
83
|
+
### P2 — 本周完成
|
|
84
|
+
6. **#11 更多预置 Skill** — 扩充到 40+
|
|
85
|
+
7. **#17 SQLite 持久记忆** — 替代 JSON
|
|
86
|
+
8. **#18 对话历史搜索** — FTS5
|
|
87
|
+
9. **#22 CLI 对话模式** — `opc chat`
|
|
88
|
+
10. **#29 主动触达** — Agent 主动发消息
|
|
89
|
+
|
|
90
|
+
### P3 — 后续
|
|
91
|
+
11. **#5 迁移工具** — 从 OpenClaw/Hermes 迁移
|
|
92
|
+
12. **#12 Skill 市场** — OPC Hub
|
package/dist/cli.js
CHANGED
|
@@ -37,10 +37,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
37
37
|
const commander_1 = require("commander");
|
|
38
38
|
const fs = __importStar(require("fs"));
|
|
39
39
|
const path = __importStar(require("path"));
|
|
40
|
-
const os = __importStar(require("os"));
|
|
41
40
|
const yaml = __importStar(require("js-yaml"));
|
|
42
41
|
const readline = __importStar(require("readline"));
|
|
43
42
|
const runtime_1 = require("./core/runtime");
|
|
43
|
+
const model_recommender_1 = require("./core/model-recommender");
|
|
44
44
|
const customer_service_1 = require("./templates/customer-service");
|
|
45
45
|
const sales_assistant_1 = require("./templates/sales-assistant");
|
|
46
46
|
const knowledge_base_1 = require("./templates/knowledge-base");
|
|
@@ -366,32 +366,9 @@ export class EchoSkill extends BaseSkill {
|
|
|
366
366
|
template = await select('Select a template:', Object.entries(TEMPLATES).map(([value, { label }]) => ({ value, label })));
|
|
367
367
|
}
|
|
368
368
|
// ── 硬件检测 + 智能模型推荐 ──
|
|
369
|
-
|
|
370
|
-
const
|
|
371
|
-
const
|
|
372
|
-
const MODEL_RECOMMENDATIONS = [
|
|
373
|
-
// Tier 1: 超轻量 (≤4GB RAM)
|
|
374
|
-
{ name: 'qwen2.5:0.5b', size: '0.4GB', minRAM: 2, desc: '超轻量,适合低配机器', priority: 1 },
|
|
375
|
-
{ name: 'qwen2.5:1.5b', size: '1.0GB', minRAM: 4, desc: '轻量但更智能', priority: 2 },
|
|
376
|
-
// Tier 2: 轻量 (4-8GB RAM)
|
|
377
|
-
{ name: 'qwen2.5:3b', size: '2.0GB', minRAM: 6, desc: '性价比最优', priority: 3 },
|
|
378
|
-
{ name: 'llama3.2:3b', size: '2.0GB', minRAM: 6, desc: 'Meta 最新轻量模型', priority: 3 },
|
|
379
|
-
{ name: 'phi3:mini', size: '2.3GB', minRAM: 6, desc: '微软高效小模型', priority: 3 },
|
|
380
|
-
// Tier 3: 标准 (8-16GB RAM)
|
|
381
|
-
{ name: 'qwen2.5:7b', size: '4.7GB', minRAM: 8, desc: '推荐:中文最强 7B', priority: 4 },
|
|
382
|
-
{ name: 'llama3.1:8b', size: '4.7GB', minRAM: 8, desc: 'Meta 通用 8B', priority: 4 },
|
|
383
|
-
{ name: 'mistral:7b', size: '4.1GB', minRAM: 8, desc: 'Mistral 经典 7B', priority: 4 },
|
|
384
|
-
{ name: 'gemma2:9b', size: '5.4GB', minRAM: 10, desc: 'Google 高效 9B', priority: 4 },
|
|
385
|
-
// Tier 4: 高配 (16-32GB RAM)
|
|
386
|
-
{ name: 'qwen2.5:14b', size: '9.0GB', minRAM: 16, desc: '中文强力 14B', priority: 5 },
|
|
387
|
-
{ name: 'deepseek-coder-v2:16b', size: '9.0GB', minRAM: 16, desc: '编程专用', priority: 5 },
|
|
388
|
-
// Tier 5: 旗舰 (32GB+ RAM)
|
|
389
|
-
{ name: 'qwen2.5:32b', size: '20GB', minRAM: 32, desc: '接近 GPT-4 水平', priority: 6 },
|
|
390
|
-
{ name: 'llama3.1:70b', size: '40GB', minRAM: 64, desc: '开源最强', priority: 7 },
|
|
391
|
-
];
|
|
392
|
-
// 根据可用内存筛选合适的模型
|
|
393
|
-
const suitableModels = MODEL_RECOMMENDATIONS.filter(m => m.minRAM <= freeMem + 2); // +2GB 容差
|
|
394
|
-
const bestRec = suitableModels.length > 0 ? suitableModels[suitableModels.length - 1] : MODEL_RECOMMENDATIONS[0];
|
|
369
|
+
// ── 硬件检测 + 远程模型推荐 ──
|
|
370
|
+
const sys = (0, model_recommender_1.detectSystem)();
|
|
371
|
+
const allModels = await (0, model_recommender_1.fetchModelList)();
|
|
395
372
|
// ── LLM Provider 选择(Ollama-first)──
|
|
396
373
|
let llmProvider = 'ollama';
|
|
397
374
|
let llmModel = 'qwen2.5';
|
|
@@ -409,20 +386,23 @@ export class EchoSkill extends BaseSkill {
|
|
|
409
386
|
modelNames = (ollamaData.models || []).map((m) => m.name || m.model);
|
|
410
387
|
ollamaRunning = true;
|
|
411
388
|
if (opts.yes && modelNames.length > 0) {
|
|
412
|
-
|
|
413
|
-
|
|
389
|
+
const rec = (0, model_recommender_1.recommendModels)(allModels, sys, modelNames);
|
|
390
|
+
// --yes: prefer best installed recommended model
|
|
391
|
+
const bestInstalled = rec.installed.length > 0 ? rec.installed[rec.installed.length - 1] : null;
|
|
414
392
|
llmModel = bestInstalled ? bestInstalled.name : modelNames[0];
|
|
415
393
|
}
|
|
416
394
|
}
|
|
417
395
|
catch {
|
|
418
396
|
ollamaRunning = false;
|
|
419
397
|
}
|
|
398
|
+
// Compute recommendation (used by both interactive branches)
|
|
399
|
+
const rec = (0, model_recommender_1.recommendModels)(allModels, sys, modelNames);
|
|
420
400
|
if (!opts.yes) {
|
|
421
401
|
if (ollamaRunning) {
|
|
422
402
|
console.log(`\n ${icon.info} ${color.dim('正在检测 Ollama...')}`);
|
|
423
403
|
console.log(` ${icon.success} Ollama 已运行,发现 ${modelNames.length} 个模型`);
|
|
424
|
-
console.log(` ${icon.info} 系统: ${totalRAM}GB RAM (${
|
|
425
|
-
console.log(` ${icon.info} 推荐模型: ${color.cyan(
|
|
404
|
+
console.log(` ${icon.info} 系统: ${sys.totalRAM}GB RAM (${sys.freeRAM}GB 可用), ${sys.cpuCount} CPU cores`);
|
|
405
|
+
console.log(` ${icon.info} 推荐模型: ${color.cyan(rec.best.name)} (${rec.best.size}) - ${rec.best.desc}`);
|
|
426
406
|
// 选择 provider
|
|
427
407
|
llmProvider = await select('选择 LLM 引擎:', [
|
|
428
408
|
{ value: 'ollama', label: '🟢 Ollama (免费本地,推荐) - 已检测到运行中' },
|
|
@@ -433,16 +413,14 @@ export class EchoSkill extends BaseSkill {
|
|
|
433
413
|
]);
|
|
434
414
|
if (llmProvider === 'ollama') {
|
|
435
415
|
// 已有模型 + 推荐未下载的模型
|
|
436
|
-
const existingSet = new Set(modelNames);
|
|
437
|
-
const recommendedNotInstalled = suitableModels.filter(m => !existingSet.has(m.name)).slice(-3); // 推荐最多3个未下载的
|
|
438
416
|
const modelOptions = [
|
|
439
|
-
...
|
|
440
|
-
const
|
|
441
|
-
|
|
442
|
-
const isBest = m === bestRec.name ? ' ⭐推荐' : '';
|
|
443
|
-
return { value: m, label: `${m}${recLabel}${isBest} [已安装]` };
|
|
417
|
+
...rec.installed.map((m) => {
|
|
418
|
+
const isBest = m.name === rec.best.name ? ' ⭐推荐' : '';
|
|
419
|
+
return { value: m.name, label: `${m.name} (${m.size}, ${m.desc})${isBest} [已安装]` };
|
|
444
420
|
}),
|
|
445
|
-
|
|
421
|
+
// Also show installed models not in recommendation list
|
|
422
|
+
...modelNames.filter(n => !rec.installed.find(m => m.name === n)).map(n => ({ value: n, label: `${n} [已安装]` })),
|
|
423
|
+
...rec.toDownload.map((m) => ({
|
|
446
424
|
value: `pull:${m.name}`,
|
|
447
425
|
label: `${m.name} (${m.size}, ${m.desc}) [需下载]`,
|
|
448
426
|
})),
|
|
@@ -463,11 +441,11 @@ export class EchoSkill extends BaseSkill {
|
|
|
463
441
|
else {
|
|
464
442
|
// 没有本地模型,推荐下载
|
|
465
443
|
console.log(` ${color.yellow('⚠️')} 没有发现已下载的模型`);
|
|
466
|
-
console.log(` ${icon.info} 根据你的硬件 (${
|
|
467
|
-
for (const m of
|
|
444
|
+
console.log(` ${icon.info} 根据你的硬件 (${sys.freeRAM}GB 可用),推荐下载:`);
|
|
445
|
+
for (const m of rec.suitable.slice(-3)) {
|
|
468
446
|
console.log(` ${color.cyan(`ollama pull ${m.name}`)} (${m.size}, ${m.desc})`);
|
|
469
447
|
}
|
|
470
|
-
llmModel =
|
|
448
|
+
llmModel = rec.best.name;
|
|
471
449
|
}
|
|
472
450
|
}
|
|
473
451
|
}
|
|
@@ -485,12 +463,12 @@ export class EchoSkill extends BaseSkill {
|
|
|
485
463
|
if (llmProvider === 'ollama') {
|
|
486
464
|
console.log(`\n ${icon.info} Ollama 安装指南:`);
|
|
487
465
|
console.log(` 1. 访问 ${color.cyan('https://ollama.ai')} 下载并安装`);
|
|
488
|
-
console.log(` ${icon.info} 根据你的硬件 (${totalRAM}GB RAM, ${
|
|
489
|
-
for (const m of
|
|
466
|
+
console.log(` ${icon.info} 根据你的硬件 (${sys.totalRAM}GB RAM, ${sys.freeRAM}GB 可用),推荐:`);
|
|
467
|
+
for (const m of rec.suitable.slice(-3)) {
|
|
490
468
|
console.log(` ${color.cyan(`ollama pull ${m.name}`)} (${m.size}, ${m.desc})`);
|
|
491
469
|
}
|
|
492
470
|
console.log(` 3. 然后 ${color.cyan('opc run')} 即可开始对话\n`);
|
|
493
|
-
llmModel =
|
|
471
|
+
llmModel = rec.best.name;
|
|
494
472
|
}
|
|
495
473
|
}
|
|
496
474
|
// 商业模型需要 API key
|
|
@@ -2436,6 +2414,64 @@ program
|
|
|
2436
2414
|
await voice.start();
|
|
2437
2415
|
`));
|
|
2438
2416
|
});
|
|
2417
|
+
// ── Models command ──────────────────────────────────────────────
|
|
2418
|
+
program
|
|
2419
|
+
.command('models')
|
|
2420
|
+
.description('Show recommended Ollama models for your system')
|
|
2421
|
+
.option('--refresh', 'Force refresh model list from remote')
|
|
2422
|
+
.option('--json', 'Output as JSON')
|
|
2423
|
+
.action(async (opts) => {
|
|
2424
|
+
if (opts.refresh) {
|
|
2425
|
+
(0, model_recommender_1.clearModelCache)();
|
|
2426
|
+
console.log(`${icon.success} 模型推荐缓存已清除`);
|
|
2427
|
+
}
|
|
2428
|
+
const sys = (0, model_recommender_1.detectSystem)();
|
|
2429
|
+
const models = await (0, model_recommender_1.fetchModelList)();
|
|
2430
|
+
const cache = (0, model_recommender_1.cacheInfo)();
|
|
2431
|
+
// Detect Ollama
|
|
2432
|
+
let installedModels = [];
|
|
2433
|
+
try {
|
|
2434
|
+
const ctrl = new AbortController();
|
|
2435
|
+
const t = setTimeout(() => ctrl.abort(), 3000);
|
|
2436
|
+
const res = await fetch('http://localhost:11434/api/tags', { signal: ctrl.signal });
|
|
2437
|
+
clearTimeout(t);
|
|
2438
|
+
const data = await res.json();
|
|
2439
|
+
installedModels = (data.models || []).map((m) => m.name || m.model);
|
|
2440
|
+
}
|
|
2441
|
+
catch { /* Ollama not running */ }
|
|
2442
|
+
const rec = (0, model_recommender_1.recommendModels)(models, sys, installedModels);
|
|
2443
|
+
if (opts.json) {
|
|
2444
|
+
console.log(JSON.stringify({ system: sys, cache, recommendation: rec }, null, 2));
|
|
2445
|
+
return;
|
|
2446
|
+
}
|
|
2447
|
+
console.log(`\n${icon.rocket} ${color.bold('OPC 模型推荐')}\n`);
|
|
2448
|
+
console.log(` 系统: ${sys.totalRAM}GB RAM (${sys.freeRAM}GB 可用), ${sys.cpuCount} cores, ${sys.platform}/${sys.arch}`);
|
|
2449
|
+
if (cache.exists) {
|
|
2450
|
+
console.log(` 推荐列表: v${cache.version} (${cache.age})`);
|
|
2451
|
+
}
|
|
2452
|
+
else {
|
|
2453
|
+
console.log(` 推荐列表: 内置 (运行 ${color.cyan('opc models --refresh')} 获取最新)`);
|
|
2454
|
+
}
|
|
2455
|
+
console.log(` Ollama: ${installedModels.length > 0 ? color.green(`运行中, ${installedModels.length} 个模型`) : color.yellow('未运行')}`);
|
|
2456
|
+
console.log(`\n ${color.bold('⭐ 推荐:')} ${color.cyan(rec.best.name)} (${rec.best.size}) - ${rec.best.desc}\n`);
|
|
2457
|
+
// Table
|
|
2458
|
+
console.log(` ${'模型'.padEnd(28)} ${'大小'.padEnd(8)} ${'最低RAM'.padEnd(8)} ${'状态'.padEnd(10)} 说明`);
|
|
2459
|
+
console.log(` ${'─'.repeat(28)} ${'─'.repeat(8)} ${'─'.repeat(8)} ${'─'.repeat(10)} ${'─'.repeat(20)}`);
|
|
2460
|
+
for (const m of rec.suitable) {
|
|
2461
|
+
const installed = installedModels.includes(m.name);
|
|
2462
|
+
const isBest = m.name === rec.best.name;
|
|
2463
|
+
const status = installed ? color.green('已安装') : color.dim('未安装');
|
|
2464
|
+
const star = isBest ? ' ⭐' : (m.recommended ? ' 💎' : '');
|
|
2465
|
+
console.log(` ${(m.name + star).padEnd(28)} ${m.size.padEnd(8)} ${(m.minRAM + 'GB').padEnd(8)} ${status.padEnd(10)} ${m.desc}`);
|
|
2466
|
+
}
|
|
2467
|
+
if (rec.toDownload.length > 0) {
|
|
2468
|
+
console.log(`\n ${color.bold('推荐下载:')}`);
|
|
2469
|
+
for (const m of rec.toDownload) {
|
|
2470
|
+
console.log(` ${color.cyan(`ollama pull ${m.name}`)} (${m.size}, ${m.desc})`);
|
|
2471
|
+
}
|
|
2472
|
+
}
|
|
2473
|
+
console.log();
|
|
2474
|
+
});
|
|
2439
2475
|
program.parse();
|
|
2440
2476
|
// ── Keys command ──────────────────────────────────────────────
|
|
2441
2477
|
const keys_1 = require("./security/keys");
|