opc-agent 4.0.44 → 4.1.1
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/dist/channels/wechat.js +6 -6
- package/dist/cli.js +2 -2
- package/dist/core/runtime.js +18 -0
- package/dist/deploy/index.js +56 -56
- package/dist/providers/index.js +39 -13
- package/dist/studio/server.js +211 -20
- package/dist/studio-ui/index.html +279 -24
- 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/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 +2 -2
- 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 +18 -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 +156 -156
- 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/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 -608
- 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 +209 -22
- package/src/studio/templates-data.ts +178 -178
- package/src/studio-ui/index.html +279 -24
- 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
|
@@ -1,113 +1,113 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Skill Marketplace Manager
|
|
3
|
-
* Handles skill installation, uninstallation, and runtime discovery.
|
|
4
|
-
*/
|
|
5
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
|
|
6
|
-
import { join } from 'path';
|
|
7
|
-
import * as os from 'os';
|
|
8
|
-
import type { MarketplaceSkill, InstalledSkill, SkillInstallResult, SkillCategory } from './types';
|
|
9
|
-
import { BUILTIN_SKILLS } from './builtin/index';
|
|
10
|
-
|
|
11
|
-
export class SkillMarketplace {
|
|
12
|
-
private installedSkills: Map<string, InstalledSkill> = new Map();
|
|
13
|
-
private configPath: string;
|
|
14
|
-
|
|
15
|
-
constructor() {
|
|
16
|
-
const dir = join(os.homedir(), '.opc');
|
|
17
|
-
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
18
|
-
this.configPath = join(dir, 'skills-installed.json');
|
|
19
|
-
this.loadInstalled();
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
private loadInstalled(): void {
|
|
23
|
-
if (existsSync(this.configPath)) {
|
|
24
|
-
try {
|
|
25
|
-
const data = JSON.parse(readFileSync(this.configPath, 'utf-8'));
|
|
26
|
-
if (Array.isArray(data)) {
|
|
27
|
-
data.forEach((s: InstalledSkill) => this.installedSkills.set(s.skillId, s));
|
|
28
|
-
}
|
|
29
|
-
} catch { /* ignore corrupted file */ }
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
private saveInstalled(): void {
|
|
34
|
-
const data = Array.from(this.installedSkills.values());
|
|
35
|
-
writeFileSync(this.configPath, JSON.stringify(data, null, 2));
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/** Get all available skills with installation status */
|
|
39
|
-
listAll(category?: string, search?: string): (MarketplaceSkill & { installed: boolean })[] {
|
|
40
|
-
let skills = BUILTIN_SKILLS;
|
|
41
|
-
if (category) {
|
|
42
|
-
skills = skills.filter(s => s.category === category);
|
|
43
|
-
}
|
|
44
|
-
if (search) {
|
|
45
|
-
const q = search.toLowerCase();
|
|
46
|
-
skills = skills.filter(s =>
|
|
47
|
-
s.name.toLowerCase().includes(q) ||
|
|
48
|
-
s.nameZh.includes(q) ||
|
|
49
|
-
s.description.toLowerCase().includes(q) ||
|
|
50
|
-
s.descriptionZh.includes(q)
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
return skills.map(s => ({
|
|
54
|
-
...s,
|
|
55
|
-
installed: this.installedSkills.has(s.id),
|
|
56
|
-
}));
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/** Get a single skill by ID */
|
|
60
|
-
getSkill(id: string): (MarketplaceSkill & { installed: boolean }) | null {
|
|
61
|
-
const skill = BUILTIN_SKILLS.find(s => s.id === id);
|
|
62
|
-
if (!skill) return null;
|
|
63
|
-
return { ...skill, installed: this.installedSkills.has(id) };
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/** Install a skill */
|
|
67
|
-
install(id: string, config?: Record<string, any>): SkillInstallResult {
|
|
68
|
-
const skill = BUILTIN_SKILLS.find(s => s.id === id);
|
|
69
|
-
if (!skill) return { success: false, skillId: id, message: 'Skill not found' };
|
|
70
|
-
if (this.installedSkills.has(id)) return { success: true, skillId: id, message: 'Already installed' };
|
|
71
|
-
|
|
72
|
-
this.installedSkills.set(id, {
|
|
73
|
-
skillId: id,
|
|
74
|
-
installedAt: new Date().toISOString(),
|
|
75
|
-
config,
|
|
76
|
-
});
|
|
77
|
-
this.saveInstalled();
|
|
78
|
-
return { success: true, skillId: id, message: 'Installed successfully' };
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/** Uninstall a skill */
|
|
82
|
-
uninstall(id: string): SkillInstallResult {
|
|
83
|
-
if (!this.installedSkills.has(id)) {
|
|
84
|
-
return { success: false, skillId: id, message: 'Skill not installed' };
|
|
85
|
-
}
|
|
86
|
-
this.installedSkills.delete(id);
|
|
87
|
-
this.saveInstalled();
|
|
88
|
-
return { success: true, skillId: id, message: 'Uninstalled successfully' };
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/** Get installed skills with full details */
|
|
92
|
-
getInstalled(): (MarketplaceSkill & { installed: boolean; installedAt: string })[] {
|
|
93
|
-
return Array.from(this.installedSkills.values())
|
|
94
|
-
.map(inst => {
|
|
95
|
-
const skill = BUILTIN_SKILLS.find(s => s.id === inst.skillId);
|
|
96
|
-
if (!skill) return null;
|
|
97
|
-
return { ...skill, installed: true, installedAt: inst.installedAt };
|
|
98
|
-
})
|
|
99
|
-
.filter(Boolean) as any;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/** Get system prompts for all installed skills (for agent runtime) */
|
|
103
|
-
getInstalledSystemPrompts(): string[] {
|
|
104
|
-
return this.getInstalled()
|
|
105
|
-
.filter(s => s.systemPrompt)
|
|
106
|
-
.map(s => `[${s.icon} ${s.nameZh}] ${s.systemPrompt}`);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/** Get all tool names from installed skills */
|
|
110
|
-
getInstalledToolNames(): string[] {
|
|
111
|
-
return this.getInstalled().flatMap(s => s.tools);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Skill Marketplace Manager
|
|
3
|
+
* Handles skill installation, uninstallation, and runtime discovery.
|
|
4
|
+
*/
|
|
5
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
|
|
6
|
+
import { join } from 'path';
|
|
7
|
+
import * as os from 'os';
|
|
8
|
+
import type { MarketplaceSkill, InstalledSkill, SkillInstallResult, SkillCategory } from './types';
|
|
9
|
+
import { BUILTIN_SKILLS } from './builtin/index';
|
|
10
|
+
|
|
11
|
+
export class SkillMarketplace {
|
|
12
|
+
private installedSkills: Map<string, InstalledSkill> = new Map();
|
|
13
|
+
private configPath: string;
|
|
14
|
+
|
|
15
|
+
constructor() {
|
|
16
|
+
const dir = join(os.homedir(), '.opc');
|
|
17
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
18
|
+
this.configPath = join(dir, 'skills-installed.json');
|
|
19
|
+
this.loadInstalled();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
private loadInstalled(): void {
|
|
23
|
+
if (existsSync(this.configPath)) {
|
|
24
|
+
try {
|
|
25
|
+
const data = JSON.parse(readFileSync(this.configPath, 'utf-8'));
|
|
26
|
+
if (Array.isArray(data)) {
|
|
27
|
+
data.forEach((s: InstalledSkill) => this.installedSkills.set(s.skillId, s));
|
|
28
|
+
}
|
|
29
|
+
} catch { /* ignore corrupted file */ }
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
private saveInstalled(): void {
|
|
34
|
+
const data = Array.from(this.installedSkills.values());
|
|
35
|
+
writeFileSync(this.configPath, JSON.stringify(data, null, 2));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Get all available skills with installation status */
|
|
39
|
+
listAll(category?: string, search?: string): (MarketplaceSkill & { installed: boolean })[] {
|
|
40
|
+
let skills = BUILTIN_SKILLS;
|
|
41
|
+
if (category) {
|
|
42
|
+
skills = skills.filter(s => s.category === category);
|
|
43
|
+
}
|
|
44
|
+
if (search) {
|
|
45
|
+
const q = search.toLowerCase();
|
|
46
|
+
skills = skills.filter(s =>
|
|
47
|
+
s.name.toLowerCase().includes(q) ||
|
|
48
|
+
s.nameZh.includes(q) ||
|
|
49
|
+
s.description.toLowerCase().includes(q) ||
|
|
50
|
+
s.descriptionZh.includes(q)
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
return skills.map(s => ({
|
|
54
|
+
...s,
|
|
55
|
+
installed: this.installedSkills.has(s.id),
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Get a single skill by ID */
|
|
60
|
+
getSkill(id: string): (MarketplaceSkill & { installed: boolean }) | null {
|
|
61
|
+
const skill = BUILTIN_SKILLS.find(s => s.id === id);
|
|
62
|
+
if (!skill) return null;
|
|
63
|
+
return { ...skill, installed: this.installedSkills.has(id) };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Install a skill */
|
|
67
|
+
install(id: string, config?: Record<string, any>): SkillInstallResult {
|
|
68
|
+
const skill = BUILTIN_SKILLS.find(s => s.id === id);
|
|
69
|
+
if (!skill) return { success: false, skillId: id, message: 'Skill not found' };
|
|
70
|
+
if (this.installedSkills.has(id)) return { success: true, skillId: id, message: 'Already installed' };
|
|
71
|
+
|
|
72
|
+
this.installedSkills.set(id, {
|
|
73
|
+
skillId: id,
|
|
74
|
+
installedAt: new Date().toISOString(),
|
|
75
|
+
config,
|
|
76
|
+
});
|
|
77
|
+
this.saveInstalled();
|
|
78
|
+
return { success: true, skillId: id, message: 'Installed successfully' };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Uninstall a skill */
|
|
82
|
+
uninstall(id: string): SkillInstallResult {
|
|
83
|
+
if (!this.installedSkills.has(id)) {
|
|
84
|
+
return { success: false, skillId: id, message: 'Skill not installed' };
|
|
85
|
+
}
|
|
86
|
+
this.installedSkills.delete(id);
|
|
87
|
+
this.saveInstalled();
|
|
88
|
+
return { success: true, skillId: id, message: 'Uninstalled successfully' };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Get installed skills with full details */
|
|
92
|
+
getInstalled(): (MarketplaceSkill & { installed: boolean; installedAt: string })[] {
|
|
93
|
+
return Array.from(this.installedSkills.values())
|
|
94
|
+
.map(inst => {
|
|
95
|
+
const skill = BUILTIN_SKILLS.find(s => s.id === inst.skillId);
|
|
96
|
+
if (!skill) return null;
|
|
97
|
+
return { ...skill, installed: true, installedAt: inst.installedAt };
|
|
98
|
+
})
|
|
99
|
+
.filter(Boolean) as any;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Get system prompts for all installed skills (for agent runtime) */
|
|
103
|
+
getInstalledSystemPrompts(): string[] {
|
|
104
|
+
return this.getInstalled()
|
|
105
|
+
.filter(s => s.systemPrompt)
|
|
106
|
+
.map(s => `[${s.icon} ${s.nameZh}] ${s.systemPrompt}`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Get all tool names from installed skills */
|
|
110
|
+
getInstalledToolNames(): string[] {
|
|
111
|
+
return this.getInstalled().flatMap(s => s.tools);
|
|
112
|
+
}
|
|
113
|
+
}
|
package/src/skills/types.ts
CHANGED
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Skill Marketplace - Type Definitions
|
|
3
|
-
* Defines the structure for installable skills in the OPC Agent marketplace.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export interface MarketplaceSkill {
|
|
7
|
-
id: string;
|
|
8
|
-
name: string;
|
|
9
|
-
nameZh: string;
|
|
10
|
-
description: string;
|
|
11
|
-
descriptionZh: string;
|
|
12
|
-
category: SkillCategory;
|
|
13
|
-
icon: string;
|
|
14
|
-
version: string;
|
|
15
|
-
author: string;
|
|
16
|
-
tools: string[];
|
|
17
|
-
config?: Record<string, any>;
|
|
18
|
-
systemPrompt?: string;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export type SkillCategory = 'productivity' | 'knowledge' | 'creative' | 'developer' | 'lifestyle' | 'business';
|
|
22
|
-
|
|
23
|
-
export interface InstalledSkill {
|
|
24
|
-
skillId: string;
|
|
25
|
-
installedAt: string;
|
|
26
|
-
config?: Record<string, any>;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface SkillInstallResult {
|
|
30
|
-
success: boolean;
|
|
31
|
-
skillId: string;
|
|
32
|
-
message?: string;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export const CATEGORY_LABELS: Record<SkillCategory, { en: string; zh: string }> = {
|
|
36
|
-
productivity: { en: 'Productivity', zh: '效率工具' },
|
|
37
|
-
knowledge: { en: 'Knowledge', zh: '知识工具' },
|
|
38
|
-
creative: { en: 'Creative', zh: '创作工具' },
|
|
39
|
-
developer: { en: 'Developer', zh: '开发工具' },
|
|
40
|
-
lifestyle: { en: 'Lifestyle', zh: '生活工具' },
|
|
41
|
-
business: { en: 'Business', zh: '业务工具' },
|
|
42
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* Skill Marketplace - Type Definitions
|
|
3
|
+
* Defines the structure for installable skills in the OPC Agent marketplace.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface MarketplaceSkill {
|
|
7
|
+
id: string;
|
|
8
|
+
name: string;
|
|
9
|
+
nameZh: string;
|
|
10
|
+
description: string;
|
|
11
|
+
descriptionZh: string;
|
|
12
|
+
category: SkillCategory;
|
|
13
|
+
icon: string;
|
|
14
|
+
version: string;
|
|
15
|
+
author: string;
|
|
16
|
+
tools: string[];
|
|
17
|
+
config?: Record<string, any>;
|
|
18
|
+
systemPrompt?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type SkillCategory = 'productivity' | 'knowledge' | 'creative' | 'developer' | 'lifestyle' | 'business';
|
|
22
|
+
|
|
23
|
+
export interface InstalledSkill {
|
|
24
|
+
skillId: string;
|
|
25
|
+
installedAt: string;
|
|
26
|
+
config?: Record<string, any>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface SkillInstallResult {
|
|
30
|
+
success: boolean;
|
|
31
|
+
skillId: string;
|
|
32
|
+
message?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const CATEGORY_LABELS: Record<SkillCategory, { en: string; zh: string }> = {
|
|
36
|
+
productivity: { en: 'Productivity', zh: '效率工具' },
|
|
37
|
+
knowledge: { en: 'Knowledge', zh: '知识工具' },
|
|
38
|
+
creative: { en: 'Creative', zh: '创作工具' },
|
|
39
|
+
developer: { en: 'Developer', zh: '开发工具' },
|
|
40
|
+
lifestyle: { en: 'Lifestyle', zh: '生活工具' },
|
|
41
|
+
business: { en: 'Business', zh: '业务工具' },
|
|
42
|
+
};
|
package/src/studio/server.ts
CHANGED
|
@@ -181,12 +181,91 @@ class StudioServer {
|
|
|
181
181
|
const industry = url.searchParams.get('industry') || '';
|
|
182
182
|
const search = url.searchParams.get('q') || '';
|
|
183
183
|
data = this.getTemplates(industry, search);
|
|
184
|
+
// Merge with real workstation templates
|
|
185
|
+
try {
|
|
186
|
+
const ws = require('agent-workstation');
|
|
187
|
+
const categories = ws.getCategories();
|
|
188
|
+
const wsTemplates: any[] = [];
|
|
189
|
+
for (const cat of categories) {
|
|
190
|
+
for (const roleName of cat.roles) {
|
|
191
|
+
const role = ws.getRole(cat.name, roleName);
|
|
192
|
+
if (!role) continue;
|
|
193
|
+
let oad: any = {};
|
|
194
|
+
try {
|
|
195
|
+
if (role.files?.['oad.yaml']) {
|
|
196
|
+
const yaml = require('js-yaml');
|
|
197
|
+
oad = yaml.load(role.files['oad.yaml']) || {};
|
|
198
|
+
}
|
|
199
|
+
} catch {}
|
|
200
|
+
const tpl = {
|
|
201
|
+
id: `ws-${cat.name}-${roleName}`,
|
|
202
|
+
name: oad.name || roleName.replace(/-/g, ' ').replace(/\b\w/g, (c: string) => c.toUpperCase()),
|
|
203
|
+
nameZh: oad.nameZh || '',
|
|
204
|
+
icon: oad.icon || '🤖',
|
|
205
|
+
description: oad.description || '',
|
|
206
|
+
descriptionZh: oad.descriptionZh || '',
|
|
207
|
+
industry: cat.name,
|
|
208
|
+
industryZh: cat.name,
|
|
209
|
+
tags: [cat.name, 'workstation'],
|
|
210
|
+
suggestedModel: 'auto',
|
|
211
|
+
systemPrompt: oad.systemPrompt || role.files?.['brain-seed.md'] || '',
|
|
212
|
+
source: 'workstation',
|
|
213
|
+
ego: oad.ego || null,
|
|
214
|
+
mission: oad.mission || null,
|
|
215
|
+
skills: oad.skills || [],
|
|
216
|
+
};
|
|
217
|
+
if (!search || tpl.name.toLowerCase().includes(search.toLowerCase()) || tpl.nameZh.includes(search)) {
|
|
218
|
+
if (!industry || tpl.industry === industry) {
|
|
219
|
+
wsTemplates.push(tpl);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
data.templates = [...data.templates, ...wsTemplates];
|
|
225
|
+
// Add workstation industries to list
|
|
226
|
+
const existingIds = new Set(data.industries.map((i: any) => i.id));
|
|
227
|
+
for (const cat of categories) {
|
|
228
|
+
if (!existingIds.has(cat.name)) {
|
|
229
|
+
data.industries.push({ id: cat.name, name: cat.name, nameZh: cat.name });
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
} catch (wsErr: any) {
|
|
233
|
+
// workstation not available, use built-in templates only
|
|
234
|
+
}
|
|
184
235
|
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
|
|
185
236
|
res.end(JSON.stringify(data));
|
|
186
237
|
return;
|
|
187
238
|
}
|
|
188
239
|
if (route.match(/^templates\/[^/]+$/) && req.method === 'GET') {
|
|
189
240
|
const tplId = route.split('/')[1];
|
|
241
|
+
// Check workstation first
|
|
242
|
+
if (tplId.startsWith('ws-')) {
|
|
243
|
+
const parts = tplId.replace('ws-', '').split('-');
|
|
244
|
+
const catName = parts[0];
|
|
245
|
+
const roleName = parts.slice(1).join('-');
|
|
246
|
+
try {
|
|
247
|
+
const ws = require('agent-workstation');
|
|
248
|
+
const role = ws.getRole(catName, roleName);
|
|
249
|
+
if (role) {
|
|
250
|
+
let oad: any = {};
|
|
251
|
+
try {
|
|
252
|
+
if (role.files?.['oad.yaml']) {
|
|
253
|
+
const yaml = require('js-yaml');
|
|
254
|
+
oad = yaml.load(role.files['oad.yaml']) || {};
|
|
255
|
+
}
|
|
256
|
+
} catch {}
|
|
257
|
+
data = {
|
|
258
|
+
id: tplId, name: oad.name || roleName, source: 'workstation',
|
|
259
|
+
category: catName, role: roleName, files: role.files,
|
|
260
|
+
ego: oad.ego, mission: oad.mission, skills: oad.skills,
|
|
261
|
+
systemPrompt: oad.systemPrompt || role.files?.['brain-seed.md'] || '',
|
|
262
|
+
};
|
|
263
|
+
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
|
|
264
|
+
res.end(JSON.stringify(data));
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
} catch {}
|
|
268
|
+
}
|
|
190
269
|
data = this.getTemplateById(tplId);
|
|
191
270
|
res.writeHead(data.error ? 404 : 200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
|
|
192
271
|
res.end(JSON.stringify(data));
|
|
@@ -494,6 +573,65 @@ class StudioServer {
|
|
|
494
573
|
return;
|
|
495
574
|
}
|
|
496
575
|
|
|
576
|
+
// === Global config API (reads/writes ~/.opc/config.json) ===
|
|
577
|
+
if (route === 'config' && req.method === 'GET') {
|
|
578
|
+
data = loadSettingsConfig();
|
|
579
|
+
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
|
|
580
|
+
res.end(JSON.stringify(data));
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
if (route === 'config' && req.method === 'PUT') {
|
|
584
|
+
const body = JSON.parse(await this.readBody(req));
|
|
585
|
+
const cfg = loadSettingsConfig();
|
|
586
|
+
Object.assign(cfg, body);
|
|
587
|
+
saveSettingsConfig(cfg);
|
|
588
|
+
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
|
|
589
|
+
res.end(JSON.stringify({ success: true, config: cfg }));
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// === Models API (real agentkits integration) ===
|
|
594
|
+
if (route === 'models' && req.method === 'GET') {
|
|
595
|
+
try {
|
|
596
|
+
const ak = await import('agentkits');
|
|
597
|
+
const providers = ak.listLLMProviders();
|
|
598
|
+
data = { providers };
|
|
599
|
+
} catch (e: any) {
|
|
600
|
+
data = { providers: [], error: 'agentkits not available: ' + e.message };
|
|
601
|
+
}
|
|
602
|
+
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
|
|
603
|
+
res.end(JSON.stringify(data));
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// === Memory stats API (real deepbrain integration) ===
|
|
608
|
+
if (route === 'memory/stats' && req.method === 'GET') {
|
|
609
|
+
try {
|
|
610
|
+
const { Brain } = require('deepbrain');
|
|
611
|
+
const oad = this.loadOAD();
|
|
612
|
+
const dbPath = oad?.spec?.memory?.longTerm?.database || './data/brain.db';
|
|
613
|
+
const brain = new Brain({ database: dbPath, embedding_provider: 'ollama' });
|
|
614
|
+
await brain.connect();
|
|
615
|
+
const stats = await brain.stats();
|
|
616
|
+
await brain.disconnect();
|
|
617
|
+
data = { connected: true, ...stats };
|
|
618
|
+
} catch {
|
|
619
|
+
data = { connected: false, pages: 0, chunks: 0, error: 'DeepBrain not installed or not configured. Install with: npm i deepbrain' };
|
|
620
|
+
}
|
|
621
|
+
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
|
|
622
|
+
res.end(JSON.stringify(data));
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
if (route.match(/^memory\/[^/]+$/) && req.method === 'GET') {
|
|
626
|
+
const agentId = route.split('/')[1];
|
|
627
|
+
if (agentId !== 'stats' && agentId !== 'list' && agentId !== 'search') {
|
|
628
|
+
data = this.getAgentMemory(agentId);
|
|
629
|
+
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
|
|
630
|
+
res.end(JSON.stringify(data));
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
497
635
|
switch (route) {
|
|
498
636
|
case 'modules':
|
|
499
637
|
data = await this.getModulesStatus();
|
|
@@ -647,11 +785,41 @@ class StudioServer {
|
|
|
647
785
|
}
|
|
648
786
|
|
|
649
787
|
private listAgents(): { agents: any[] } {
|
|
788
|
+
// 1. Load Studio-created agents from ~/.opc/agents/*.json
|
|
650
789
|
const dir = this.getAgentsDir();
|
|
651
790
|
const files = readdirSync(dir).filter(f => f.endsWith('.json'));
|
|
652
791
|
const agents = files.map(f => {
|
|
653
792
|
try { return JSON.parse(readFileSync(join(dir, f), 'utf-8')); } catch { return null; }
|
|
654
|
-
}).filter(Boolean)
|
|
793
|
+
}).filter(Boolean);
|
|
794
|
+
|
|
795
|
+
// 2. Also detect current working directory agent (oad.yaml)
|
|
796
|
+
const seenIds = new Set(agents.map((a: any) => a.id));
|
|
797
|
+
const oadPath = join(this.config.agentDir, 'oad.yaml');
|
|
798
|
+
if (existsSync(oadPath)) {
|
|
799
|
+
try {
|
|
800
|
+
const oadRaw = readFileSync(oadPath, 'utf-8');
|
|
801
|
+
const yamlMod = require('js-yaml');
|
|
802
|
+
const oad = yamlMod.load(oadRaw) as any;
|
|
803
|
+
const name = oad?.name || oad?.metadata?.name || 'My Agent';
|
|
804
|
+
const id = oad?.id || name.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '');
|
|
805
|
+
if (!seenIds.has(id)) {
|
|
806
|
+
agents.push({
|
|
807
|
+
id,
|
|
808
|
+
name,
|
|
809
|
+
description: oad?.description || oad?.spec?.description || '',
|
|
810
|
+
icon: oad?.icon || '🤖',
|
|
811
|
+
emoji: oad?.icon || '🤖',
|
|
812
|
+
status: 'running',
|
|
813
|
+
source: 'oad.yaml',
|
|
814
|
+
model: oad?.spec?.model || oad?.spec?.provider?.model || 'auto',
|
|
815
|
+
created: new Date().toISOString(),
|
|
816
|
+
updated: new Date().toISOString(),
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
} catch { /* ignore parse errors */ }
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
agents.sort((a: any, b: any) => new Date(b.updated).getTime() - new Date(a.updated).getTime());
|
|
655
823
|
return { agents };
|
|
656
824
|
}
|
|
657
825
|
|
|
@@ -708,8 +876,22 @@ class StudioServer {
|
|
|
708
876
|
}
|
|
709
877
|
|
|
710
878
|
private async handleAgentChat(req: IncomingMessage, res: ServerResponse, agentId: string): Promise<void> {
|
|
711
|
-
|
|
712
|
-
|
|
879
|
+
let body: any;
|
|
880
|
+
try {
|
|
881
|
+
body = JSON.parse(await this.readBody(req));
|
|
882
|
+
} catch {
|
|
883
|
+
res.writeHead(400, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
|
|
884
|
+
res.end(JSON.stringify({ error: 'Invalid JSON body' }));
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
// Accept both { messages: [...] } and { message: "...", history: [...] }
|
|
889
|
+
let messages: any[] = body.messages || [];
|
|
890
|
+
if (body.message) {
|
|
891
|
+
// Frontend sends { message, history }
|
|
892
|
+
messages = [...(body.history || []), { role: 'user', content: body.message }];
|
|
893
|
+
}
|
|
894
|
+
|
|
713
895
|
const agent = this.getAgentById(agentId);
|
|
714
896
|
if (agent.error) {
|
|
715
897
|
res.writeHead(404, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
|
|
@@ -721,8 +903,8 @@ class StudioServer {
|
|
|
721
903
|
agent.messageCount = (agent.messageCount || 0) + 1;
|
|
722
904
|
agent.lastActive = new Date().toISOString();
|
|
723
905
|
agent.updated = new Date().toISOString();
|
|
724
|
-
const
|
|
725
|
-
writeFileSync(
|
|
906
|
+
const agentFilePath = join(this.getAgentsDir(), `${agentId}.json`);
|
|
907
|
+
writeFileSync(agentFilePath, JSON.stringify(agent, null, 2));
|
|
726
908
|
|
|
727
909
|
// SSE streaming response
|
|
728
910
|
res.writeHead(200, {
|
|
@@ -732,31 +914,30 @@ class StudioServer {
|
|
|
732
914
|
'Access-Control-Allow-Origin': '*',
|
|
733
915
|
});
|
|
734
916
|
|
|
735
|
-
const allMsgs = [{ role: 'system', content: agent.systemPrompt }, ...messages];
|
|
736
|
-
const lastMsg = allMsgs[allMsgs.length - 1]?.content || '';
|
|
737
|
-
|
|
738
917
|
// Use createProvider directly to call LLM
|
|
739
918
|
try {
|
|
740
919
|
const { createProvider } = require('../providers');
|
|
741
|
-
//
|
|
920
|
+
// Determine provider: agent config > OAD yaml > env > auto
|
|
742
921
|
let providerName = agent.provider || process.env.OPC_LLM_PROVIDER;
|
|
743
922
|
if (!providerName) {
|
|
744
|
-
// Try reading from oad.yaml
|
|
745
923
|
try {
|
|
746
|
-
const
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
924
|
+
for (const fname of ['oad.yaml', 'agent.yaml']) {
|
|
925
|
+
const oadPath = join(this.config.agentDir, fname);
|
|
926
|
+
if (existsSync(oadPath)) {
|
|
927
|
+
const yaml = require('js-yaml');
|
|
928
|
+
const oad = yaml.load(readFileSync(oadPath, 'utf-8'));
|
|
929
|
+
providerName = oad?.spec?.provider?.default;
|
|
930
|
+
if (providerName) break;
|
|
931
|
+
}
|
|
751
932
|
}
|
|
752
933
|
} catch {}
|
|
753
934
|
}
|
|
754
|
-
providerName = providerName || '
|
|
935
|
+
providerName = providerName || 'auto';
|
|
755
936
|
const provider = createProvider(providerName, agent.model);
|
|
756
|
-
|
|
937
|
+
|
|
757
938
|
let fullText = '';
|
|
758
939
|
try {
|
|
759
|
-
for await (const chunk of provider.chatStream(
|
|
940
|
+
for await (const chunk of provider.chatStream(messages, agent.systemPrompt)) {
|
|
760
941
|
const sseData = JSON.stringify({
|
|
761
942
|
choices: [{ delta: { content: chunk }, index: 0 }],
|
|
762
943
|
});
|
|
@@ -765,16 +946,22 @@ class StudioServer {
|
|
|
765
946
|
}
|
|
766
947
|
} catch (streamErr: any) {
|
|
767
948
|
if (!fullText) {
|
|
768
|
-
|
|
769
|
-
|
|
949
|
+
const errData = JSON.stringify({
|
|
950
|
+
choices: [{ delta: { content: `⚠️ LLM Error: ${streamErr.message}` }, index: 0 }],
|
|
951
|
+
});
|
|
770
952
|
res.write(`data: ${errData}\n\n`);
|
|
771
953
|
}
|
|
772
954
|
}
|
|
773
955
|
res.write('data: [DONE]\n\n');
|
|
774
956
|
res.end();
|
|
775
957
|
} catch (err: any) {
|
|
776
|
-
//
|
|
777
|
-
|
|
958
|
+
// Provider creation failed — send error as SSE so frontend can display it
|
|
959
|
+
const errData = JSON.stringify({
|
|
960
|
+
choices: [{ delta: { content: `⚠️ Provider error: ${err.message}\n\nTip: Install Claude CLI (npm i -g @anthropic-ai/claude-code) or set OPENAI_API_KEY.` }, index: 0 }],
|
|
961
|
+
});
|
|
962
|
+
res.write(`data: ${errData}\n\n`);
|
|
963
|
+
res.write('data: [DONE]\n\n');
|
|
964
|
+
res.end();
|
|
778
965
|
}
|
|
779
966
|
}
|
|
780
967
|
|