opc-agent 4.1.0 → 4.1.2
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/.github/ISSUE_TEMPLATE/bug_report.md +20 -20
- package/.github/ISSUE_TEMPLATE/feature_request.md +14 -14
- package/.github/PULL_REQUEST_TEMPLATE.md +13 -13
- package/CHANGELOG.md +48 -48
- package/CONTRIBUTING.md +36 -36
- package/README.zh-CN.md +497 -497
- package/USABILITY-ISSUES.md +73 -0
- package/dist/channels/web.js +8 -2
- package/dist/channels/wechat.js +6 -6
- package/dist/cli.js +200 -85
- package/dist/core/runtime.js +37 -15
- package/dist/deploy/index.js +56 -56
- package/dist/doctor.d.ts +1 -0
- package/dist/doctor.js +105 -10
- package/dist/memory/deepbrain.d.ts +1 -1
- package/dist/memory/deepbrain.js +95 -4
- package/dist/scheduler/cron-engine.js +3 -36
- package/dist/studio/server.js +30 -1
- package/dist/studio-ui/index.html +230 -10
- package/dist/ui/components.js +105 -105
- package/examples/README.md +22 -22
- package/examples/basic-agent.ts +90 -90
- package/examples/brain-integration.ts +71 -71
- package/examples/multi-channel.ts +74 -74
- package/fix-sidebar.mjs +188 -188
- package/install.ps1 +154 -154
- package/install.sh +164 -164
- package/package.json +1 -1
- package/scripts/install.ps1 +31 -31
- package/scripts/install.sh +40 -40
- package/serve-studio.js +13 -13
- package/serve-test.js +25 -25
- package/src/channels/dingtalk.ts +46 -46
- package/src/channels/email.ts +351 -351
- package/src/channels/feishu.ts +349 -349
- package/src/channels/googlechat.ts +42 -42
- package/src/channels/imessage.ts +31 -31
- package/src/channels/irc.ts +82 -82
- package/src/channels/line.ts +32 -32
- package/src/channels/matrix.ts +33 -33
- package/src/channels/mattermost.ts +57 -57
- package/src/channels/msteams.ts +32 -32
- package/src/channels/nostr.ts +32 -32
- package/src/channels/qq.ts +33 -33
- package/src/channels/signal.ts +32 -32
- package/src/channels/sms.ts +33 -33
- package/src/channels/telegram.ts +616 -616
- package/src/channels/twitch.ts +65 -65
- package/src/channels/voice-call.ts +100 -100
- package/src/channels/web.ts +8 -2
- package/src/channels/websocket.ts +399 -399
- package/src/channels/wechat.ts +329 -329
- package/src/channels/whatsapp.ts +32 -32
- package/src/cli/chat.ts +99 -99
- package/src/cli/setup.ts +314 -314
- package/src/cli.ts +195 -92
- package/src/core/agent.ts +476 -476
- package/src/core/api-server.ts +277 -277
- package/src/core/audio.ts +98 -98
- package/src/core/collaboration.ts +275 -275
- package/src/core/context-discovery.ts +85 -85
- package/src/core/context-refs.ts +140 -140
- package/src/core/gateway.ts +106 -106
- package/src/core/heartbeat.ts +51 -51
- package/src/core/hooks.ts +105 -105
- package/src/core/ide-bridge.ts +133 -133
- package/src/core/node-network.ts +86 -86
- package/src/core/profiles.ts +122 -122
- package/src/core/runtime.ts +25 -0
- package/src/core/scheduler.ts +187 -187
- package/src/core/session-manager.ts +137 -137
- package/src/core/subagent.ts +98 -98
- package/src/core/vision.ts +180 -180
- package/src/core/workflow-graph.ts +365 -365
- package/src/daemon.ts +96 -96
- package/src/deploy/index.ts +255 -255
- package/src/doctor.ts +98 -11
- package/src/eval/index.ts +211 -211
- package/src/eval/suites/basic.json +16 -16
- package/src/eval/suites/memory.json +12 -12
- package/src/eval/suites/safety.json +14 -14
- package/src/hub/brain-seed.ts +54 -54
- package/src/hub/client.ts +60 -60
- package/src/mcp/servers/calculator-mcp.ts +65 -65
- package/src/mcp/servers/crypto-mcp.ts +73 -73
- package/src/mcp/servers/database-mcp.ts +72 -72
- package/src/mcp/servers/datetime-mcp.ts +69 -69
- package/src/mcp/servers/filesystem.ts +66 -66
- package/src/mcp/servers/github-mcp.ts +58 -58
- package/src/mcp/servers/index.ts +63 -63
- package/src/mcp/servers/json-mcp.ts +102 -102
- package/src/mcp/servers/memory-mcp.ts +56 -56
- package/src/mcp/servers/regex-mcp.ts +53 -53
- package/src/mcp/servers/web-mcp.ts +49 -49
- package/src/memory/context-compressor.ts +189 -189
- package/src/memory/deepbrain.ts +99 -5
- package/src/memory/seed-loader.ts +212 -212
- package/src/memory/user-profiler.ts +215 -215
- package/src/plugins/content-filter.ts +23 -23
- package/src/plugins/logger.ts +18 -18
- package/src/plugins/rate-limiter.ts +38 -38
- package/src/protocols/a2a/client.ts +132 -132
- package/src/protocols/a2a/index.ts +8 -8
- package/src/protocols/a2a/server.ts +333 -333
- package/src/protocols/a2a/types.ts +88 -88
- package/src/protocols/a2a/utils.ts +50 -50
- package/src/protocols/agui/client.ts +83 -83
- package/src/protocols/agui/index.ts +4 -4
- package/src/protocols/agui/server.ts +218 -218
- package/src/protocols/agui/types.ts +153 -153
- package/src/protocols/index.ts +2 -2
- package/src/protocols/mcp/agent-tools.ts +134 -134
- package/src/protocols/mcp/index.ts +8 -8
- package/src/protocols/mcp/server.ts +262 -262
- package/src/protocols/mcp/types.ts +69 -69
- package/src/providers/index.ts +632 -632
- package/src/publish/index.ts +376 -376
- package/src/scheduler/cron-engine.ts +191 -191
- package/src/scheduler/index.ts +2 -2
- package/src/schema/oad.ts +217 -217
- package/src/security/approval.ts +131 -131
- package/src/security/approvals.ts +143 -143
- package/src/security/elevated.ts +105 -105
- package/src/security/guardrails.ts +248 -248
- package/src/security/index.ts +9 -9
- package/src/security/keys.ts +87 -87
- package/src/security/secrets.ts +129 -129
- package/src/skills/builtin/index.ts +408 -408
- package/src/skills/marketplace.ts +113 -113
- package/src/skills/types.ts +42 -42
- package/src/studio/server.ts +31 -1
- package/src/studio/templates-data.ts +178 -178
- package/src/studio-ui/index.html +230 -10
- package/src/telemetry/index.ts +324 -324
- package/src/tools/builtin/browser.ts +299 -299
- package/src/tools/builtin/datetime.ts +41 -41
- package/src/tools/builtin/file.ts +107 -107
- package/src/tools/builtin/home-assistant.ts +116 -116
- package/src/tools/builtin/rl-tools.ts +243 -243
- package/src/tools/builtin/shell.ts +43 -43
- package/src/tools/builtin/vision.ts +64 -64
- package/src/tools/builtin/web-search.ts +126 -126
- package/src/tools/builtin/web.ts +35 -35
- package/src/tools/document-processor.ts +213 -213
- package/src/tools/image-generator.ts +150 -150
- package/src/tools/integrations/calendar.ts +73 -73
- package/src/tools/integrations/code-exec.ts +39 -39
- package/src/tools/integrations/csv-analyzer.ts +92 -92
- package/src/tools/integrations/database.ts +44 -44
- package/src/tools/integrations/email-send.ts +76 -76
- package/src/tools/integrations/git-tool.ts +42 -42
- package/src/tools/integrations/github-tool.ts +76 -76
- package/src/tools/integrations/image-gen.ts +56 -56
- package/src/tools/integrations/index.ts +92 -92
- package/src/tools/integrations/jira.ts +83 -83
- package/src/tools/integrations/notion.ts +71 -71
- package/src/tools/integrations/npm-tool.ts +48 -48
- package/src/tools/integrations/pdf-reader.ts +58 -58
- package/src/tools/integrations/slack.ts +65 -65
- package/src/tools/integrations/summarizer.ts +49 -49
- package/src/tools/integrations/translator.ts +48 -48
- package/src/tools/integrations/trello.ts +60 -60
- package/src/tools/integrations/vector-search.ts +42 -42
- package/src/tools/integrations/web-scraper.ts +47 -47
- package/src/tools/integrations/web-search.ts +58 -58
- package/src/tools/integrations/webhook.ts +38 -38
- package/src/tools/mcp-client.ts +131 -131
- package/src/tools/web-scraper.ts +179 -179
- package/src/tools/web-search.ts +180 -180
- package/src/ui/components.ts +127 -127
- package/srv-out.txt +1 -1
- package/templates/ecommerce-assistant/README.md +45 -45
- package/templates/ecommerce-assistant/oad.yaml +47 -47
- package/templates/tech-support/README.md +43 -43
- package/templates/tech-support/oad.yaml +45 -45
- package/test-agent/Dockerfile +9 -9
- package/test-agent/README.md +50 -50
- package/test-agent/agent.yaml +23 -23
- package/test-agent/docker-compose.yml +11 -11
- package/test-agent/oad.yaml +31 -31
- package/test-agent/package-lock.json +1492 -1492
- package/test-agent/package.json +17 -17
- package/test-agent/src/index.ts +24 -24
- package/test-agent/src/skills/echo.ts +15 -15
- package/test-agent/tsconfig.json +24 -24
- package/test-full.js +43 -43
- package/test-sidebar.js +22 -22
- package/test-studio3.js +75 -75
- package/test-studio4.js +41 -41
- package/tests/a2a-protocol.test.ts +285 -285
- package/tests/agui-protocol.test.ts +246 -246
- package/tests/api-server.test.ts +148 -148
- package/tests/approvals.test.ts +89 -89
- package/tests/audio.test.ts +40 -40
- package/tests/brain-seed-extended.test.ts +490 -490
- package/tests/brain-seed.test.ts +239 -239
- package/tests/browser.test.ts +179 -179
- package/tests/channels/discord.test.ts +79 -79
- package/tests/channels/email.test.ts +148 -148
- package/tests/channels/feishu.test.ts +123 -123
- package/tests/channels/telegram.test.ts +129 -129
- package/tests/channels/websocket.test.ts +53 -53
- package/tests/channels/wechat.test.ts +170 -170
- package/tests/channels-extra.test.ts +45 -45
- package/tests/chat-cli.test.ts +160 -160
- package/tests/cli.test.ts +46 -46
- package/tests/context-compressor.test.ts +172 -172
- package/tests/context-refs.test.ts +121 -121
- package/tests/cron-engine.test.ts +101 -101
- package/tests/daemon.test.ts +135 -135
- package/tests/deepbrain-wire.test.ts +234 -234
- package/tests/deploy-and-dag.test.ts +196 -196
- package/tests/doctor.test.ts +38 -38
- package/tests/document-processor.test.ts +69 -69
- package/tests/e2e-nocode.test.ts +442 -442
- package/tests/elevated.test.ts +69 -69
- package/tests/eval.test.ts +173 -173
- package/tests/gateway.test.ts +63 -63
- package/tests/guardrails.test.ts +177 -177
- package/tests/home-assistant.test.ts +40 -40
- package/tests/hooks.test.ts +79 -79
- package/tests/ide-bridge.test.ts +38 -38
- package/tests/image-generator.test.ts +84 -84
- package/tests/init-role.test.ts +124 -124
- package/tests/integrations.test.ts +249 -249
- package/tests/mcp-client.test.ts +92 -92
- package/tests/mcp-server.test.ts +178 -178
- package/tests/mcp-servers.test.ts +260 -260
- package/tests/node-network.test.ts +74 -74
- package/tests/plugin-a2a-enhanced.test.ts +230 -230
- package/tests/profiles.test.ts +61 -61
- package/tests/publish.test.ts +231 -231
- package/tests/rl-tools.test.ts +93 -93
- package/tests/sandbox-manager.test.ts +46 -46
- package/tests/scheduler.test.ts +200 -200
- package/tests/secrets.test.ts +107 -107
- package/tests/security-enhanced.test.ts +233 -233
- package/tests/settings-api.test.ts +148 -148
- package/tests/setup.test.ts +73 -73
- package/tests/subagent.test.ts +193 -193
- package/tests/telegram-discord.test.ts +60 -60
- package/tests/telemetry.test.ts +186 -186
- package/tests/user-profiler.test.ts +169 -169
- package/tests/v090-features.test.ts +254 -254
- package/tests/vision.test.ts +61 -61
- package/tests/voice-call.test.ts +47 -47
- package/tests/voice-enhanced.test.ts +169 -169
- package/tests/voice-interaction.test.ts +38 -38
- package/tests/web-search.test.ts +155 -155
- package/tests/workflow-graph.test.ts +279 -279
- package/tutorial/customer-service-agent/README.md +612 -612
- package/tutorial/customer-service-agent/SOUL.md +26 -26
- package/tutorial/customer-service-agent/agent.yaml +63 -63
- package/tutorial/customer-service-agent/package.json +19 -19
- package/tutorial/customer-service-agent/src/index.ts +69 -69
- package/tutorial/customer-service-agent/src/skills/faq.ts +27 -27
- package/tutorial/customer-service-agent/src/skills/ticket.ts +22 -22
- package/tutorial/customer-service-agent/tsconfig.json +14 -14
package/dist/cli.js
CHANGED
|
@@ -124,7 +124,7 @@ async function select(question, options) {
|
|
|
124
124
|
program
|
|
125
125
|
.name('opc')
|
|
126
126
|
.description('OPC Agent - Open Agent Framework for business workstations')
|
|
127
|
-
.version('
|
|
127
|
+
.version(require('../package.json').version);
|
|
128
128
|
// ── Init command ─────────────────────────────────────────────
|
|
129
129
|
program
|
|
130
130
|
.command('init')
|
|
@@ -186,7 +186,7 @@ program
|
|
|
186
186
|
}
|
|
187
187
|
const roleDisplayName = roleMeta.name || matched.role;
|
|
188
188
|
const roleDescription = roleMeta.name_zh ? `${roleMeta.name} (${roleMeta.name_zh})` : (roleMeta.name || matched.role);
|
|
189
|
-
console.log(` ${icon.info} Matched role: ${color.cyan(matched.category + '/' + matched.role)}
|
|
189
|
+
console.log(` ${icon.info} Matched role: ${color.cyan(matched.category + '/' + matched.role)} - ${roleDisplayName}`);
|
|
190
190
|
// Create directories
|
|
191
191
|
fs.mkdirSync(dir, { recursive: true });
|
|
192
192
|
fs.mkdirSync(path.join(dir, 'src', 'skills'), { recursive: true });
|
|
@@ -205,9 +205,9 @@ program
|
|
|
205
205
|
// Company-specific knowledge belongs to Desk (closed-source), not here.
|
|
206
206
|
const workstationSeedFromRole = workstationMatch?.[0]?.trim() || '';
|
|
207
207
|
fs.writeFileSync(path.join(dir, 'brain-seeds', 'workstation.md'), workstationSeedFromRole || `# Workstation Knowledge\n\n## Tools & Environment\n\nCommon tools and setup for this workstation role.\n\n## Workflows\n\nStandard operating procedures and workflows.\n\n## Best Practices\n\nIndustry best practices for this role.\n`);
|
|
208
|
-
//
|
|
208
|
+
// oad.yaml with role system prompt and brain seeds(不再生成 agent.yaml)
|
|
209
209
|
const firstLine = systemPromptContent.split('\n').find((l) => l.trim() && !l.startsWith('#'))?.trim() || 'You are a helpful AI assistant.';
|
|
210
|
-
fs.writeFileSync(path.join(dir, '
|
|
210
|
+
fs.writeFileSync(path.join(dir, 'oad.yaml'), `apiVersion: opc/v1
|
|
211
211
|
kind: Agent
|
|
212
212
|
metadata:
|
|
213
213
|
name: ${name}
|
|
@@ -251,14 +251,14 @@ spec:
|
|
|
251
251
|
if (roleData.files['oad.yaml']) {
|
|
252
252
|
fs.writeFileSync(path.join(dir, 'oad.yaml'), roleData.files['oad.yaml']);
|
|
253
253
|
}
|
|
254
|
-
// src/index.ts
|
|
254
|
+
// src/index.ts - entry point (same as generic)
|
|
255
255
|
fs.writeFileSync(path.join(dir, 'src', 'index.ts'), `import { AgentRuntime } from 'opc-agent';
|
|
256
256
|
import { EchoSkill } from './skills/echo';
|
|
257
257
|
import { readFileSync, existsSync } from 'fs';
|
|
258
258
|
|
|
259
259
|
async function main() {
|
|
260
260
|
const runtime = new AgentRuntime();
|
|
261
|
-
const config = await runtime.loadConfig('./
|
|
261
|
+
const config = await runtime.loadConfig('./oad.yaml');
|
|
262
262
|
|
|
263
263
|
const soul = existsSync('./SOUL.md') ? readFileSync('./SOUL.md', 'utf-8') : '';
|
|
264
264
|
const context = existsSync('./CONTEXT.md') ? readFileSync('./CONTEXT.md', 'utf-8') : '';
|
|
@@ -305,15 +305,15 @@ export class EchoSkill extends BaseSkill {
|
|
|
305
305
|
fs.writeFileSync(path.join(dir, 'package.json'), JSON.stringify({ name, version: '1.0.0', private: true, scripts: { start: 'opc run', dev: 'opc dev', chat: 'opc chat', build: 'tsc' }, dependencies: { 'opc-agent': '^1.3.0' }, devDependencies: { typescript: '^5.5.0', tsx: '^4.0.0' } }, null, 2));
|
|
306
306
|
// .gitignore, .env.example, .env
|
|
307
307
|
fs.writeFileSync(path.join(dir, '.gitignore'), 'node_modules\ndist\n.env\n.opc-knowledge.json\ndata/\n');
|
|
308
|
-
fs.writeFileSync(path.join(dir, '.env.example'), `# LLM API Configuration\
|
|
309
|
-
fs.writeFileSync(path.join(dir, '.env'),
|
|
308
|
+
fs.writeFileSync(path.join(dir, '.env.example'), `# LLM API Configuration\n# Ollama (免费本地,默认,无需 API key):\nOPC_LLM_BASE_URL=http://localhost:11434/v1\nOPC_LLM_MODEL=qwen2.5\n\n# 如需使用商业模型,取消以下注释:\n# OPC_LLM_API_KEY=your-api-key-here\n# OPC_LLM_BASE_URL=https://api.deepseek.com/v1\n# OPC_LLM_MODEL=deepseek-chat\n`);
|
|
309
|
+
fs.writeFileSync(path.join(dir, '.env'), `# Ollama (免费本地) - 无需 API key\nOPC_LLM_BASE_URL=http://localhost:11434/v1\nOPC_LLM_MODEL=qwen2.5\n`);
|
|
310
310
|
// README.md
|
|
311
311
|
fs.writeFileSync(path.join(dir, 'README.md'), `# ${name}\n\nCreated with [OPC Agent](https://github.com/Deepleaper/opc-agent) using the \`${matched.category}/${matched.role}\` workstation role.\n\n## Quick Start\n\n\`\`\`bash\nnpm install\nollama pull qwen2.5\nnpx tsx src/index.ts\n\`\`\`\n\nOpen [http://localhost:3000](http://localhost:3000)\n`);
|
|
312
312
|
// Dockerfile + docker-compose
|
|
313
|
-
fs.writeFileSync(path.join(dir, 'Dockerfile'), `FROM node:22-alpine\nWORKDIR /app\nCOPY package.json package-lock.json* ./\nRUN npm ci --production 2>/dev/null || npm install --production\nCOPY oad.yaml
|
|
314
|
-
fs.writeFileSync(path.join(dir, 'docker-compose.yml'), `version: '3.8'\nservices:\n agent:\n build: .\n ports:\n - "3000:3000"\n env_file:\n - .env\n volumes:\n - ./
|
|
313
|
+
fs.writeFileSync(path.join(dir, 'Dockerfile'), `FROM node:22-alpine\nWORKDIR /app\nCOPY package.json package-lock.json* ./\nRUN npm ci --production 2>/dev/null || npm install --production\nCOPY oad.yaml .env* ./\nCOPY src/ ./src/\nCOPY prompts/ ./prompts/ 2>/dev/null || true\nEXPOSE 3000\nCMD ["npx", "opc", "run"]\n`);
|
|
314
|
+
fs.writeFileSync(path.join(dir, 'docker-compose.yml'), `version: '3.8'\nservices:\n agent:\n build: .\n ports:\n - "3000:3000"\n env_file:\n - .env\n volumes:\n - ./oad.yaml:/app/oad.yaml:ro\n restart: unless-stopped\n`);
|
|
315
315
|
console.log(`\n${icon.success} Created agent project: ${color.bold(name + '/')} from role ${color.cyan(matched.category + '/' + matched.role)}`);
|
|
316
|
-
console.log(` ${icon.file}
|
|
316
|
+
console.log(` ${icon.file} oad.yaml - Agent definition with role system prompt`);
|
|
317
317
|
console.log(` ${icon.file} SOUL.md - Role personality (${systemPromptContent.split('\n').length} lines)`);
|
|
318
318
|
console.log(` ${icon.file} CONTEXT.md - Role context & documentation`);
|
|
319
319
|
console.log(` ${icon.file} brain-seeds/ - 3-tier brain seed knowledge`);
|
|
@@ -344,7 +344,7 @@ export class EchoSkill extends BaseSkill {
|
|
|
344
344
|
}
|
|
345
345
|
}
|
|
346
346
|
catch {
|
|
347
|
-
// Hub unreachable
|
|
347
|
+
// Hub unreachable - fall back to bundled templates
|
|
348
348
|
}
|
|
349
349
|
let template;
|
|
350
350
|
let selectedHubTemplate;
|
|
@@ -364,6 +364,116 @@ export class EchoSkill extends BaseSkill {
|
|
|
364
364
|
else {
|
|
365
365
|
template = await select('Select a template:', Object.entries(TEMPLATES).map(([value, { label }]) => ({ value, label })));
|
|
366
366
|
}
|
|
367
|
+
// ── LLM Provider 选择(Ollama-first)──
|
|
368
|
+
let llmProvider = 'ollama';
|
|
369
|
+
let llmModel = 'qwen2.5';
|
|
370
|
+
let llmBaseUrl = 'http://localhost:11434/v1';
|
|
371
|
+
let llmApiKey = '';
|
|
372
|
+
let ollamaRunning = false;
|
|
373
|
+
let modelNames = [];
|
|
374
|
+
// 无论 --yes 还是交互式,都先检测 Ollama
|
|
375
|
+
try {
|
|
376
|
+
const controller = new AbortController();
|
|
377
|
+
const ollamaTimeout = setTimeout(() => controller.abort(), 3000);
|
|
378
|
+
const ollamaRes = await fetch('http://localhost:11434/api/tags', { signal: controller.signal });
|
|
379
|
+
clearTimeout(ollamaTimeout);
|
|
380
|
+
const ollamaData = await ollamaRes.json();
|
|
381
|
+
modelNames = (ollamaData.models || []).map((m) => m.name || m.model);
|
|
382
|
+
ollamaRunning = true;
|
|
383
|
+
if (opts.yes && modelNames.length > 0) {
|
|
384
|
+
// --yes 模式:自动选第一个已有模型
|
|
385
|
+
llmModel = modelNames[0];
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
catch {
|
|
389
|
+
ollamaRunning = false;
|
|
390
|
+
}
|
|
391
|
+
if (!opts.yes) {
|
|
392
|
+
if (ollamaRunning) {
|
|
393
|
+
console.log(`\n ${icon.info} ${color.dim('正在检测 Ollama...')}`);
|
|
394
|
+
console.log(` ${icon.success} Ollama 已运行,发现 ${modelNames.length} 个模型`);
|
|
395
|
+
// 选择 provider
|
|
396
|
+
llmProvider = await select('选择 LLM 引擎:', [
|
|
397
|
+
{ value: 'ollama', label: '🟢 Ollama (免费本地,推荐) - 已检测到运行中' },
|
|
398
|
+
{ value: 'deepseek', label: '🔵 DeepSeek - 高性价比国产模型' },
|
|
399
|
+
{ value: 'openai', label: '⚪ OpenAI (GPT-4o)' },
|
|
400
|
+
{ value: 'anthropic', label: '🟣 Anthropic (Claude)' },
|
|
401
|
+
{ value: 'custom', label: '⚙️ 自定义 (手动输入 Base URL)' },
|
|
402
|
+
]);
|
|
403
|
+
if (llmProvider === 'ollama') {
|
|
404
|
+
// 选择本地模型
|
|
405
|
+
const defaultModel = modelNames.includes('qwen2.5') ? 'qwen2.5' : (modelNames.includes('llama3') ? 'llama3' : (modelNames[0] || 'qwen2.5'));
|
|
406
|
+
if (modelNames.length > 0) {
|
|
407
|
+
llmModel = await select('选择 Ollama 模型:', modelNames.map((m) => ({ value: m, label: m + (m === defaultModel ? ' (推荐)' : '') })));
|
|
408
|
+
}
|
|
409
|
+
else {
|
|
410
|
+
console.log(` ${color.yellow('⚠️')} 没有发现已下载的模型,将使用默认 qwen2.5`);
|
|
411
|
+
console.log(` 运行 ${color.cyan('ollama pull qwen2.5')} 下载模型`);
|
|
412
|
+
llmModel = 'qwen2.5';
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
else {
|
|
417
|
+
// Ollama not running
|
|
418
|
+
console.log(`\n ${icon.info} ${color.dim('正在检测 Ollama...')}`);
|
|
419
|
+
console.log(` ${color.yellow('⚠️')} Ollama 未运行或未安装`);
|
|
420
|
+
llmProvider = await select('选择 LLM 引擎:', [
|
|
421
|
+
{ value: 'ollama', label: '🟢 Ollama (免费本地,推荐) - 需先安装: https://ollama.ai' },
|
|
422
|
+
{ value: 'deepseek', label: '🔵 DeepSeek - 高性价比国产模型' },
|
|
423
|
+
{ value: 'openai', label: '⚪ OpenAI (GPT-4o)' },
|
|
424
|
+
{ value: 'anthropic', label: '🟣 Anthropic (Claude)' },
|
|
425
|
+
{ value: 'custom', label: '⚙️ 自定义 (手动输入 Base URL)' },
|
|
426
|
+
]);
|
|
427
|
+
if (llmProvider === 'ollama') {
|
|
428
|
+
console.log(`\n ${icon.info} Ollama 安装指南:`);
|
|
429
|
+
console.log(` 1. 访问 ${color.cyan('https://ollama.ai')} 下载并安装`);
|
|
430
|
+
console.log(` 2. 运行 ${color.cyan('ollama pull qwen2.5')} 下载推荐模型`);
|
|
431
|
+
console.log(` 3. 然后 ${color.cyan('opc run')} 即可开始对话\n`);
|
|
432
|
+
llmModel = 'qwen2.5';
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
// 商业模型需要 API key
|
|
436
|
+
if (llmProvider === 'deepseek') {
|
|
437
|
+
llmBaseUrl = 'https://api.deepseek.com/v1';
|
|
438
|
+
llmModel = 'deepseek-chat';
|
|
439
|
+
llmApiKey = await promptUser('输入 DeepSeek API Key (可稍后在 .env 中配置,直接回车跳过)');
|
|
440
|
+
if (!llmApiKey) {
|
|
441
|
+
console.log(` ${icon.info} 稍后在 ${color.cyan('.env')} 文件中设置 ${color.bold('OPC_LLM_API_KEY')}`);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
else if (llmProvider === 'openai') {
|
|
445
|
+
llmBaseUrl = 'https://api.openai.com/v1';
|
|
446
|
+
llmModel = 'gpt-4o-mini';
|
|
447
|
+
llmApiKey = await promptUser('输入 OpenAI API Key (可稍后在 .env 中配置,直接回车跳过)');
|
|
448
|
+
if (!llmApiKey) {
|
|
449
|
+
console.log(` ${icon.info} 稍后在 ${color.cyan('.env')} 文件中设置 ${color.bold('OPC_LLM_API_KEY')}`);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
else if (llmProvider === 'anthropic') {
|
|
453
|
+
llmBaseUrl = 'https://api.anthropic.com/v1';
|
|
454
|
+
llmModel = 'claude-sonnet-4-20250514';
|
|
455
|
+
llmApiKey = await promptUser('输入 Anthropic API Key (可稍后在 .env 中配置,直接回车跳过)');
|
|
456
|
+
if (!llmApiKey) {
|
|
457
|
+
console.log(` ${icon.info} 稍后在 ${color.cyan('.env')} 文件中设置 ${color.bold('OPC_LLM_API_KEY')}`);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
else if (llmProvider === 'custom') {
|
|
461
|
+
llmBaseUrl = await promptUser('输入 Base URL', 'http://localhost:11434/v1');
|
|
462
|
+
llmModel = await promptUser('输入模型名称', 'qwen2.5');
|
|
463
|
+
llmApiKey = await promptUser('输入 API Key (可选,直接回车跳过)');
|
|
464
|
+
// 尝试推断 provider
|
|
465
|
+
if (llmBaseUrl.includes('deepseek.com'))
|
|
466
|
+
llmProvider = 'deepseek';
|
|
467
|
+
else if (llmBaseUrl.includes('openai.com'))
|
|
468
|
+
llmProvider = 'openai';
|
|
469
|
+
else if (llmBaseUrl.includes('anthropic.com'))
|
|
470
|
+
llmProvider = 'anthropic';
|
|
471
|
+
else if (llmBaseUrl.includes('localhost:11434'))
|
|
472
|
+
llmProvider = 'ollama';
|
|
473
|
+
else
|
|
474
|
+
llmProvider = 'openai'; // OpenAI-compatible fallback
|
|
475
|
+
}
|
|
476
|
+
}
|
|
367
477
|
const dir = path.resolve(name);
|
|
368
478
|
if (fs.existsSync(dir)) {
|
|
369
479
|
console.error(`\n${icon.error} Directory ${color.bold(name)} already exists.`);
|
|
@@ -374,37 +484,16 @@ export class EchoSkill extends BaseSkill {
|
|
|
374
484
|
const factory = TEMPLATES[template]?.factory ?? customer_service_1.createCustomerServiceConfig;
|
|
375
485
|
const config = factory();
|
|
376
486
|
config.metadata.name = name;
|
|
487
|
+
// 用用户选择的 provider 和 model 覆盖模板默认值
|
|
488
|
+
config.spec.model = llmModel;
|
|
489
|
+
config.spec.provider = { default: llmProvider };
|
|
377
490
|
// Ensure web channel exists
|
|
378
491
|
if (!config.spec.channels.some((c) => c.type === 'web')) {
|
|
379
492
|
config.spec.channels.push({ type: 'web', port: 3000 });
|
|
380
493
|
}
|
|
494
|
+
// 只生成 oad.yaml,不生成 agent.yaml
|
|
381
495
|
fs.writeFileSync(path.join(dir, 'oad.yaml'), yaml.dump(config, { lineWidth: 120 }));
|
|
382
|
-
//
|
|
383
|
-
fs.writeFileSync(path.join(dir, 'agent.yaml'), `apiVersion: opc/v1
|
|
384
|
-
kind: Agent
|
|
385
|
-
metadata:
|
|
386
|
-
name: ${name}
|
|
387
|
-
version: 1.0.0
|
|
388
|
-
description: My AI Agent
|
|
389
|
-
spec:
|
|
390
|
-
model: qwen2.5
|
|
391
|
-
provider:
|
|
392
|
-
default: ollama
|
|
393
|
-
systemPrompt: |
|
|
394
|
-
You are a helpful AI assistant named ${name}.
|
|
395
|
-
Be concise, helpful, and friendly.
|
|
396
|
-
channels:
|
|
397
|
-
- type: web
|
|
398
|
-
port: 3000
|
|
399
|
-
memory:
|
|
400
|
-
shortTerm: true
|
|
401
|
-
longTerm:
|
|
402
|
-
provider: deepbrain
|
|
403
|
-
skills:
|
|
404
|
-
- name: echo
|
|
405
|
-
description: Echo test skill
|
|
406
|
-
`);
|
|
407
|
-
// src/index.ts — entry point
|
|
496
|
+
// src/index.ts - entry point
|
|
408
497
|
fs.writeFileSync(path.join(dir, 'src', 'index.ts'), `import { AgentRuntime } from 'opc-agent';
|
|
409
498
|
import { EchoSkill } from './skills/echo';
|
|
410
499
|
import { readFileSync, existsSync } from 'fs';
|
|
@@ -413,7 +502,7 @@ async function main() {
|
|
|
413
502
|
const runtime = new AgentRuntime();
|
|
414
503
|
|
|
415
504
|
// Load OAD config
|
|
416
|
-
const config = await runtime.loadConfig('./
|
|
505
|
+
const config = await runtime.loadConfig('./oad.yaml');
|
|
417
506
|
|
|
418
507
|
// Load personality and context files
|
|
419
508
|
const soul = existsSync('./SOUL.md') ? readFileSync('./SOUL.md', 'utf-8') : '';
|
|
@@ -439,7 +528,7 @@ async function main() {
|
|
|
439
528
|
|
|
440
529
|
main().catch(console.error);
|
|
441
530
|
`);
|
|
442
|
-
// src/skills/echo.ts
|
|
531
|
+
// src/skills/echo.ts - example skill
|
|
443
532
|
fs.writeFileSync(path.join(dir, 'src', 'skills', 'echo.ts'), `import { BaseSkill } from 'opc-agent';
|
|
444
533
|
import type { AgentContext, Message, SkillResult } from 'opc-agent';
|
|
445
534
|
|
|
@@ -477,23 +566,39 @@ export class EchoSkill extends BaseSkill {
|
|
|
477
566
|
}, null, 2));
|
|
478
567
|
// .env.example
|
|
479
568
|
fs.writeFileSync(path.join(dir, '.env.example'), `# LLM API Configuration
|
|
480
|
-
|
|
481
|
-
OPC_LLM_BASE_URL=
|
|
482
|
-
OPC_LLM_MODEL=
|
|
569
|
+
# Ollama (免费本地,默认):
|
|
570
|
+
# OPC_LLM_BASE_URL=http://localhost:11434/v1
|
|
571
|
+
# OPC_LLM_MODEL=qwen2.5
|
|
572
|
+
# (Ollama 无需 API key)
|
|
483
573
|
|
|
484
|
-
#
|
|
574
|
+
# DeepSeek:
|
|
575
|
+
# OPC_LLM_API_KEY=your-deepseek-key
|
|
485
576
|
# OPC_LLM_BASE_URL=https://api.deepseek.com/v1
|
|
486
577
|
# OPC_LLM_MODEL=deepseek-chat
|
|
487
578
|
|
|
488
|
-
#
|
|
489
|
-
#
|
|
490
|
-
#
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
579
|
+
# OpenAI:
|
|
580
|
+
# OPC_LLM_API_KEY=your-openai-key
|
|
581
|
+
# OPC_LLM_BASE_URL=https://api.openai.com/v1
|
|
582
|
+
# OPC_LLM_MODEL=gpt-4o-mini
|
|
583
|
+
|
|
584
|
+
# Anthropic:
|
|
585
|
+
# OPC_LLM_API_KEY=your-anthropic-key
|
|
586
|
+
# OPC_LLM_BASE_URL=https://api.anthropic.com/v1
|
|
587
|
+
# OPC_LLM_MODEL=claude-sonnet-4-20250514
|
|
496
588
|
`);
|
|
589
|
+
// .env - 根据用户选择生成正确的配置
|
|
590
|
+
const envLines = [];
|
|
591
|
+
if (llmProvider === 'ollama') {
|
|
592
|
+
envLines.push('# Ollama (免费本地) - 无需 API key');
|
|
593
|
+
envLines.push(`OPC_LLM_BASE_URL=${llmBaseUrl}`);
|
|
594
|
+
envLines.push(`OPC_LLM_MODEL=${llmModel}`);
|
|
595
|
+
}
|
|
596
|
+
else {
|
|
597
|
+
envLines.push(`OPC_LLM_API_KEY=${llmApiKey || 'your-api-key-here'}`);
|
|
598
|
+
envLines.push(`OPC_LLM_BASE_URL=${llmBaseUrl}`);
|
|
599
|
+
envLines.push(`OPC_LLM_MODEL=${llmModel}`);
|
|
600
|
+
}
|
|
601
|
+
fs.writeFileSync(path.join(dir, '.env'), envLines.join('\n') + '\n');
|
|
497
602
|
// package.json
|
|
498
603
|
fs.writeFileSync(path.join(dir, 'package.json'), JSON.stringify({
|
|
499
604
|
name,
|
|
@@ -520,7 +625,7 @@ OPC_LLM_MODEL=gpt-4o-mini
|
|
|
520
625
|
WORKDIR /app
|
|
521
626
|
COPY package.json package-lock.json* ./
|
|
522
627
|
RUN npm ci --production 2>/dev/null || npm install --production
|
|
523
|
-
COPY oad.yaml
|
|
628
|
+
COPY oad.yaml .env* ./
|
|
524
629
|
COPY src/ ./src/
|
|
525
630
|
COPY prompts/ ./prompts/ 2>/dev/null || true
|
|
526
631
|
EXPOSE 3000
|
|
@@ -536,7 +641,7 @@ services:
|
|
|
536
641
|
env_file:
|
|
537
642
|
- .env
|
|
538
643
|
volumes:
|
|
539
|
-
- ./
|
|
644
|
+
- ./oad.yaml:/app/oad.yaml:ro
|
|
540
645
|
restart: unless-stopped
|
|
541
646
|
`);
|
|
542
647
|
// README.md
|
|
@@ -577,8 +682,7 @@ npx opc chat # CLI chat
|
|
|
577
682
|
|
|
578
683
|
\`\`\`
|
|
579
684
|
${name}/
|
|
580
|
-
├──
|
|
581
|
-
├── oad.yaml # OAD config (used by opc CLI)
|
|
685
|
+
├── oad.yaml # Agent 配置 (唯一配置文件)
|
|
582
686
|
├── src/
|
|
583
687
|
│ ├── index.ts # Entry point
|
|
584
688
|
│ └── skills/
|
|
@@ -589,9 +693,9 @@ ${name}/
|
|
|
589
693
|
|
|
590
694
|
## Configuration
|
|
591
695
|
|
|
592
|
-
Edit \`
|
|
696
|
+
Edit \`oad.yaml\` to customize your agent's personality, skills, and behavior.
|
|
593
697
|
`);
|
|
594
|
-
// SOUL.md
|
|
698
|
+
// SOUL.md - agent personality
|
|
595
699
|
const createdDate = new Date().toISOString().split('T')[0];
|
|
596
700
|
fs.writeFileSync(path.join(dir, 'SOUL.md'), `# ${name} Personality
|
|
597
701
|
|
|
@@ -607,7 +711,7 @@ Edit \`agent.yaml\` to customize your agent's personality, skills, and behavior.
|
|
|
607
711
|
|
|
608
712
|
## Communication Style
|
|
609
713
|
- Use clear, simple language
|
|
610
|
-
- Be direct
|
|
714
|
+
- Be direct - answer the question first, then explain
|
|
611
715
|
- Use markdown formatting when helpful
|
|
612
716
|
|
|
613
717
|
## Rules
|
|
@@ -615,7 +719,7 @@ Edit \`agent.yaml\` to customize your agent's personality, skills, and behavior.
|
|
|
615
719
|
- Ask for clarification when the request is ambiguous
|
|
616
720
|
- Never make up information
|
|
617
721
|
`);
|
|
618
|
-
// CONTEXT.md
|
|
722
|
+
// CONTEXT.md - project context
|
|
619
723
|
fs.writeFileSync(path.join(dir, 'CONTEXT.md'), `# Project Context
|
|
620
724
|
|
|
621
725
|
## About This Agent
|
|
@@ -631,7 +735,8 @@ on startup to understand the project context.
|
|
|
631
735
|
- Add company policies here
|
|
632
736
|
`);
|
|
633
737
|
console.log(`\n${icon.success} Created agent project: ${color.bold(name + '/')}`);
|
|
634
|
-
console.log(` ${icon.file}
|
|
738
|
+
console.log(` ${icon.file} oad.yaml - Agent 配置 (${llmProvider}/${llmModel})`);
|
|
739
|
+
console.log(` ${icon.file} .env - 环境变量${llmProvider === 'ollama' ? '' : ' (API Key)'}`);
|
|
635
740
|
console.log(` ${icon.file} src/index.ts - Entry point`);
|
|
636
741
|
console.log(` ${icon.file} src/skills/echo.ts - Example skill`);
|
|
637
742
|
console.log(` ${icon.file} SOUL.md - Agent personality`);
|
|
@@ -656,14 +761,24 @@ on startup to understand the project context.
|
|
|
656
761
|
}
|
|
657
762
|
}
|
|
658
763
|
catch {
|
|
659
|
-
// Brain-seed download failed
|
|
764
|
+
// Brain-seed download failed - non-fatal, project still usable
|
|
660
765
|
}
|
|
661
766
|
}
|
|
662
767
|
console.log(`\n${color.bold('Next steps:')}`);
|
|
663
768
|
console.log(` 1. cd ${name}`);
|
|
664
769
|
console.log(` 2. npm install`);
|
|
665
|
-
|
|
666
|
-
|
|
770
|
+
if (llmProvider === 'ollama' && !ollamaRunning) {
|
|
771
|
+
console.log(` 3. ollama pull ${llmModel} ${color.dim('# 下载模型')}`);
|
|
772
|
+
console.log(` 4. npx opc run ${color.dim('# 启动 Agent')}`);
|
|
773
|
+
}
|
|
774
|
+
else if (llmProvider !== 'ollama' && !llmApiKey) {
|
|
775
|
+
console.log(` 3. 编辑 .env 设置 OPC_LLM_API_KEY`);
|
|
776
|
+
console.log(` 4. npx opc run`);
|
|
777
|
+
}
|
|
778
|
+
else {
|
|
779
|
+
console.log(` 3. npx opc run ${color.dim('# 启动 Agent')}`);
|
|
780
|
+
}
|
|
781
|
+
console.log(` Open http://localhost:3000\n`);
|
|
667
782
|
console.log(`${color.dim('💡 Tip: Use --role to start from a workstation template:')}`);
|
|
668
783
|
console.log(`${color.dim(' opc init my-agent --role customer-service')}`);
|
|
669
784
|
console.log(`${color.dim(' opc init --list-roles (see all roles)')}\n`);
|
|
@@ -713,7 +828,7 @@ program
|
|
|
713
828
|
// Print startup banner
|
|
714
829
|
const bannerLines = [
|
|
715
830
|
'╔══════════════════════════════════════╗',
|
|
716
|
-
'║ 🤖 OPC Agent
|
|
831
|
+
'║ 🤖 OPC Agent - Interactive Chat ║',
|
|
717
832
|
`║ Agent: ${(agentName + ' v' + agentVersion).padEnd(27)}║`,
|
|
718
833
|
`║ Model: ${((providerName + '/' + (model ?? 'default')).slice(0, 27)).padEnd(27)}║`,
|
|
719
834
|
`║ Skills: ${(String(skillNames.length) + ' loaded').padEnd(26)}║`,
|
|
@@ -740,12 +855,12 @@ program
|
|
|
740
855
|
}
|
|
741
856
|
if (lower === '/help') {
|
|
742
857
|
console.log(`\n ${color.bold('Available commands:')}`);
|
|
743
|
-
console.log(` ${color.cyan('/help')}
|
|
744
|
-
console.log(` ${color.cyan('/quit')}
|
|
745
|
-
console.log(` ${color.cyan('/clear')}
|
|
746
|
-
console.log(` ${color.cyan('/skills')}
|
|
747
|
-
console.log(` ${color.cyan('/memory')}
|
|
748
|
-
console.log(` ${color.cyan('/info')}
|
|
858
|
+
console.log(` ${color.cyan('/help')} - Show this help`);
|
|
859
|
+
console.log(` ${color.cyan('/quit')} - Exit chat (/exit also works)`);
|
|
860
|
+
console.log(` ${color.cyan('/clear')} - Clear conversation history`);
|
|
861
|
+
console.log(` ${color.cyan('/skills')} - List registered skills`);
|
|
862
|
+
console.log(` ${color.cyan('/memory')} - Show memory stats`);
|
|
863
|
+
console.log(` ${color.cyan('/info')} - Show agent info\n`);
|
|
749
864
|
return true;
|
|
750
865
|
}
|
|
751
866
|
if (lower === '/clear') {
|
|
@@ -1460,7 +1575,7 @@ protocolCmd.command('list')
|
|
|
1460
1575
|
const protocols = config?.spec?.protocols || {};
|
|
1461
1576
|
const items = [
|
|
1462
1577
|
{ name: 'a2a', description: 'Agent-to-Agent protocol', enabled: !!protocols.a2a?.enabled, detail: protocols.a2a?.port ? `port ${protocols.a2a.port}` : '' },
|
|
1463
|
-
{ name: 'agui', description: 'AG-UI
|
|
1578
|
+
{ name: 'agui', description: 'AG-UI - Agent-User Interaction (SSE)', enabled: !!protocols.agui?.enabled, detail: protocols.agui?.path || '/agui' },
|
|
1464
1579
|
];
|
|
1465
1580
|
console.log(`\n${icon.gear} ${color.bold('Protocols')}\n`);
|
|
1466
1581
|
for (const p of items) {
|
|
@@ -1593,7 +1708,7 @@ brainCmd
|
|
|
1593
1708
|
.description('Show brain stats (pages, tiers, last evolve)')
|
|
1594
1709
|
.option('--url <url>', 'DeepBrain server URL', 'http://localhost:3333')
|
|
1595
1710
|
.action(async (opts) => {
|
|
1596
|
-
console.log(`\n${icon.gear} ${color.bold('DeepBrain Status')}
|
|
1711
|
+
console.log(`\n${icon.gear} ${color.bold('DeepBrain Status')} - ${color.dim(opts.url)}\n`);
|
|
1597
1712
|
try {
|
|
1598
1713
|
const res = await fetch(`${opts.url}/api/stats`);
|
|
1599
1714
|
if (!res.ok)
|
|
@@ -1626,7 +1741,7 @@ brainCmd
|
|
|
1626
1741
|
brainCmd
|
|
1627
1742
|
.command('seed')
|
|
1628
1743
|
.description('Import brain seed files into memory')
|
|
1629
|
-
.option('-f, --file <file>', 'OAD file', '
|
|
1744
|
+
.option('-f, --file <file>', 'OAD file', 'oad.yaml')
|
|
1630
1745
|
.option('--status', 'Check if seeds have been imported')
|
|
1631
1746
|
.option('--reset', 'Re-import seeds (clear marker and re-seed)')
|
|
1632
1747
|
.action(async (opts) => {
|
|
@@ -1696,7 +1811,7 @@ brainCmd
|
|
|
1696
1811
|
console.log(` ${color.cyan(c.slug)} → ${c.fromTier} → ${c.toTier} (confidence: ${(c.confidence * 100).toFixed(0)}%)`);
|
|
1697
1812
|
}
|
|
1698
1813
|
if (opts.dryRun) {
|
|
1699
|
-
console.log(`\n ${icon.info} Dry run
|
|
1814
|
+
console.log(`\n ${icon.info} Dry run - no changes made.\n`);
|
|
1700
1815
|
}
|
|
1701
1816
|
else {
|
|
1702
1817
|
console.log(`\n ${icon.success} Promoted ${result.promoted} knowledge entries.\n`);
|
|
@@ -2145,7 +2260,7 @@ program
|
|
|
2145
2260
|
return;
|
|
2146
2261
|
}
|
|
2147
2262
|
// Create a minimal mock agent for eval (real usage would load from OAD)
|
|
2148
|
-
const oadPath = path.resolve('agent.yaml');
|
|
2263
|
+
const oadPath = path.resolve(fs.existsSync('oad.yaml') ? 'oad.yaml' : 'agent.yaml');
|
|
2149
2264
|
let agent;
|
|
2150
2265
|
if (fs.existsSync(oadPath)) {
|
|
2151
2266
|
const runtime = new runtime_1.AgentRuntime();
|
|
@@ -2154,7 +2269,7 @@ program
|
|
|
2154
2269
|
agent = runtime.agent;
|
|
2155
2270
|
}
|
|
2156
2271
|
if (!agent) {
|
|
2157
|
-
console.log(`${icon.warn} No agent.yaml found
|
|
2272
|
+
console.log(`${icon.warn} No oad.yaml or agent.yaml found - running with dry-run mock agent.`);
|
|
2158
2273
|
agent = { chat: async (input) => `[mock response to: ${input}]` };
|
|
2159
2274
|
}
|
|
2160
2275
|
const evaluator = new eval_1.AgentEvaluator(agent);
|
|
@@ -2214,7 +2329,7 @@ guardrailsCmd
|
|
|
2214
2329
|
console.log();
|
|
2215
2330
|
const result = await manager.checkInput(message);
|
|
2216
2331
|
if (result.passed) {
|
|
2217
|
-
console.log(color.green('✓ PASSED
|
|
2332
|
+
console.log(color.green('✓ PASSED - no violations'));
|
|
2218
2333
|
}
|
|
2219
2334
|
else {
|
|
2220
2335
|
if (result.blocked)
|
|
@@ -2243,7 +2358,7 @@ program
|
|
|
2243
2358
|
.action(async (opts) => {
|
|
2244
2359
|
console.log(color.bold('🎤 Voice Conversation Mode'));
|
|
2245
2360
|
console.log(` STT: ${opts.stt} | TTS: ${opts.tts} | Voice: ${opts.voice ?? 'default'} | Language: ${opts.language}`);
|
|
2246
|
-
console.log(color.dim(' (Voice conversation requires audio input integration
|
|
2361
|
+
console.log(color.dim(' (Voice conversation requires audio input integration - use as library)'));
|
|
2247
2362
|
console.log();
|
|
2248
2363
|
console.log('To use voice in your agent:');
|
|
2249
2364
|
console.log(color.cyan(`
|
|
@@ -2307,7 +2422,7 @@ keysCmd
|
|
|
2307
2422
|
});
|
|
2308
2423
|
// ── Approve command ───────────────────────────────────────────
|
|
2309
2424
|
const approveCmd = program.command('approve').description('Manage command approvals');
|
|
2310
|
-
// Singleton for CLI
|
|
2425
|
+
// Singleton for CLI - in real usage this would be loaded from daemon state
|
|
2311
2426
|
const approvalManager = new approval_1.ApprovalManager();
|
|
2312
2427
|
approveCmd
|
|
2313
2428
|
.command('list')
|
|
@@ -2439,7 +2554,7 @@ a2aCmd
|
|
|
2439
2554
|
const { oadToAgentCard } = require('./protocols/a2a');
|
|
2440
2555
|
const oad = loadOADFile();
|
|
2441
2556
|
if (!oad) {
|
|
2442
|
-
console.log(`${icon.error} No agent.yaml found`);
|
|
2557
|
+
console.log(`${icon.error} No oad.yaml or agent.yaml found`);
|
|
2443
2558
|
return;
|
|
2444
2559
|
}
|
|
2445
2560
|
const card = oadToAgentCard(oad, 'http://localhost:3001');
|
|
@@ -2479,7 +2594,7 @@ a2aCmd
|
|
|
2479
2594
|
function loadOADFile() {
|
|
2480
2595
|
const fs = require('fs');
|
|
2481
2596
|
const yaml = require('js-yaml');
|
|
2482
|
-
for (const name of ['agent.yaml', 'agent.yml']) {
|
|
2597
|
+
for (const name of ['oad.yaml', 'agent.yaml', 'agent.yml']) {
|
|
2483
2598
|
if (fs.existsSync(name)) {
|
|
2484
2599
|
return yaml.load(fs.readFileSync(name, 'utf-8'));
|
|
2485
2600
|
}
|
|
@@ -2487,7 +2602,7 @@ function loadOADFile() {
|
|
|
2487
2602
|
return null;
|
|
2488
2603
|
}
|
|
2489
2604
|
// ── MCP Server Commands ────────────────────────────────────
|
|
2490
|
-
const mcpCmd = program.command('mcp').description('MCP server commands
|
|
2605
|
+
const mcpCmd = program.command('mcp').description('MCP server commands - expose agent as MCP tools');
|
|
2491
2606
|
mcpCmd
|
|
2492
2607
|
.command('serve')
|
|
2493
2608
|
.option('--http <port>', 'Start HTTP+SSE mode on given port')
|
|
@@ -2516,7 +2631,7 @@ mcpCmd
|
|
|
2516
2631
|
console.log(`${icon.info} Tools: ${server.getToolCount()}`);
|
|
2517
2632
|
}
|
|
2518
2633
|
else {
|
|
2519
|
-
console.error(`${icon.success} MCP server (stdio) started
|
|
2634
|
+
console.error(`${icon.success} MCP server (stdio) started - ${server.getToolCount()} tools`);
|
|
2520
2635
|
await server.serveStdio();
|
|
2521
2636
|
}
|
|
2522
2637
|
});
|
|
@@ -2565,7 +2680,7 @@ mcpCmd
|
|
|
2565
2680
|
console.log(`${icon.info} Tools: ${server.getToolCount()}`);
|
|
2566
2681
|
}
|
|
2567
2682
|
else {
|
|
2568
|
-
console.error(`${icon.success} MCP server ${color.cyan(name)} (stdio)
|
|
2683
|
+
console.error(`${icon.success} MCP server ${color.cyan(name)} (stdio) - ${server.getToolCount()} tools`);
|
|
2569
2684
|
await server.serveStdio();
|
|
2570
2685
|
}
|
|
2571
2686
|
});
|
package/dist/core/runtime.js
CHANGED
|
@@ -74,25 +74,39 @@ class AgentRuntime {
|
|
|
74
74
|
evolveScheduler = null;
|
|
75
75
|
async loadConfig(filePath) {
|
|
76
76
|
const fs = require('fs');
|
|
77
|
+
const path = require('path');
|
|
78
|
+
// 如果指定文件不存在,尝试 fallback
|
|
77
79
|
if (!fs.existsSync(filePath)) {
|
|
78
|
-
//
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
80
|
+
// 如果发现旧的 agent.yaml,提示迁移
|
|
81
|
+
if (filePath === 'oad.yaml' && fs.existsSync('agent.yaml')) {
|
|
82
|
+
this.logger.warn('⚠️ 发现 agent.yaml 但未找到 oad.yaml。建议运行 `opc migrate` 统一为 oad.yaml。');
|
|
83
|
+
this.logger.info('暂时使用 agent.yaml 加载配置...');
|
|
84
|
+
filePath = 'agent.yaml';
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
// Auto-create a minimal oad.yaml with auto-detect provider
|
|
88
|
+
const yaml = require('js-yaml');
|
|
89
|
+
const defaultOAD = {
|
|
90
|
+
apiVersion: 'opc/v1',
|
|
91
|
+
kind: 'Agent',
|
|
92
|
+
metadata: { name: 'my-agent', version: '1.0.0', description: 'OPC Agent' },
|
|
93
|
+
spec: {
|
|
94
|
+
model: 'auto',
|
|
95
|
+
provider: { default: 'auto' },
|
|
96
|
+
systemPrompt: 'You are a helpful AI assistant.',
|
|
97
|
+
channels: [{ type: 'web', config: { port: 3000 } }],
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
fs.writeFileSync(filePath, yaml.dump(defaultOAD, { lineWidth: 120 }));
|
|
101
|
+
this.logger.info('Created default oad.yaml (no config file found)');
|
|
102
|
+
}
|
|
93
103
|
}
|
|
94
104
|
this.config = (0, config_1.loadOAD)(filePath);
|
|
95
105
|
this.logger.info('Config loaded', { name: this.config.metadata.name });
|
|
106
|
+
// 如果同时存在 agent.yaml 和 oad.yaml,提示用户清理
|
|
107
|
+
if (fs.existsSync('agent.yaml') && fs.existsSync('oad.yaml')) {
|
|
108
|
+
this.logger.warn('⚠️ 同时存在 agent.yaml 和 oad.yaml。建议删除 agent.yaml,统一使用 oad.yaml。');
|
|
109
|
+
}
|
|
96
110
|
return this.config;
|
|
97
111
|
}
|
|
98
112
|
setHistoryLimit(limit) {
|
|
@@ -128,6 +142,14 @@ class AgentRuntime {
|
|
|
128
142
|
const cfg = config ?? this.config;
|
|
129
143
|
if (!cfg)
|
|
130
144
|
throw new Error('No config loaded. Call loadConfig() first.');
|
|
145
|
+
// 检查 API key 是否为占位符,启动时警告
|
|
146
|
+
const apiKey = process.env.OPC_LLM_API_KEY;
|
|
147
|
+
const cfgProvider = cfg.spec.provider?.default;
|
|
148
|
+
if (cfgProvider !== 'ollama' && cfgProvider !== 'auto') {
|
|
149
|
+
if (!apiKey || apiKey === 'your-api-key-here') {
|
|
150
|
+
this.logger.warn('⚠️ API Key 未配置或仍是占位符。请编辑 .env 文件设置 OPC_LLM_API_KEY。');
|
|
151
|
+
}
|
|
152
|
+
}
|
|
131
153
|
let memory;
|
|
132
154
|
const memCfg = cfg.spec.memory;
|
|
133
155
|
if (memCfg && typeof memCfg.longTerm === 'object' && memCfg.longTerm.provider === 'deepbrain') {
|