opc-agent 4.1.0 → 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/deploy/index.js +56 -56
- 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/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/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/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 -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
|
@@ -1,74 +1,74 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* OPC Agent Example: Multi-Channel Setup
|
|
3
|
-
*
|
|
4
|
-
* Shows how to configure Web + Telegram channels for an agent.
|
|
5
|
-
* Note: This is a configuration demo — actual channel connections
|
|
6
|
-
* require valid tokens/endpoints.
|
|
7
|
-
*
|
|
8
|
-
* Run: npx tsx examples/multi-channel.ts
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import {
|
|
12
|
-
BaseAgent,
|
|
13
|
-
AgentRuntime,
|
|
14
|
-
WebChannel,
|
|
15
|
-
TelegramChannel,
|
|
16
|
-
InMemoryStore,
|
|
17
|
-
} from 'opc-agent';
|
|
18
|
-
|
|
19
|
-
async function main() {
|
|
20
|
-
console.log('🌐 OPC Agent — Multi-Channel Demo\n');
|
|
21
|
-
|
|
22
|
-
try {
|
|
23
|
-
const agent = new BaseAgent({
|
|
24
|
-
name: 'multi-channel-agent',
|
|
25
|
-
description: 'Agent that works across multiple channels',
|
|
26
|
-
version: '1.0.0',
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
const memory = new InMemoryStore();
|
|
30
|
-
const runtime = new AgentRuntime(agent, { memory });
|
|
31
|
-
|
|
32
|
-
// Channel 1: Web (HTTP endpoint)
|
|
33
|
-
const webChannel = new WebChannel({
|
|
34
|
-
port: 3000,
|
|
35
|
-
path: '/chat',
|
|
36
|
-
});
|
|
37
|
-
console.log('📡 Web channel configured: http://localhost:3000/chat');
|
|
38
|
-
|
|
39
|
-
// Channel 2: Telegram (requires BOT_TOKEN)
|
|
40
|
-
const telegramToken = process.env.TELEGRAM_BOT_TOKEN;
|
|
41
|
-
if (telegramToken) {
|
|
42
|
-
const telegramChannel = new TelegramChannel({
|
|
43
|
-
token: telegramToken,
|
|
44
|
-
});
|
|
45
|
-
runtime.addChannel(telegramChannel);
|
|
46
|
-
console.log('📱 Telegram channel configured');
|
|
47
|
-
} else {
|
|
48
|
-
console.log('📱 Telegram: skipped (set TELEGRAM_BOT_TOKEN to enable)');
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Register web channel
|
|
52
|
-
runtime.addChannel(webChannel);
|
|
53
|
-
|
|
54
|
-
console.log('\n✅ Agent configured with channels:');
|
|
55
|
-
console.log(' • Web: HTTP REST endpoint');
|
|
56
|
-
console.log(' • Telegram: Bot API (optional)');
|
|
57
|
-
console.log('\nAvailable channels in OPC Agent:');
|
|
58
|
-
console.log(' WebChannel, TelegramChannel, WebSocketChannel,');
|
|
59
|
-
console.log(' SlackChannel, DiscordChannel, FeishuChannel,');
|
|
60
|
-
console.log(' EmailChannel, WeChatChannel, VoiceChannel,');
|
|
61
|
-
console.log(' WebhookChannel\n');
|
|
62
|
-
|
|
63
|
-
// In production you would call: await runtime.start();
|
|
64
|
-
console.log('💡 To start: await runtime.start()');
|
|
65
|
-
} catch (e: any) {
|
|
66
|
-
console.error(`❌ Error: ${e.message}`);
|
|
67
|
-
process.exit(1);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
main().catch(e => {
|
|
72
|
-
console.error(`\n❌ Error: ${e.message}\n`);
|
|
73
|
-
process.exit(1);
|
|
74
|
-
});
|
|
1
|
+
/**
|
|
2
|
+
* OPC Agent Example: Multi-Channel Setup
|
|
3
|
+
*
|
|
4
|
+
* Shows how to configure Web + Telegram channels for an agent.
|
|
5
|
+
* Note: This is a configuration demo — actual channel connections
|
|
6
|
+
* require valid tokens/endpoints.
|
|
7
|
+
*
|
|
8
|
+
* Run: npx tsx examples/multi-channel.ts
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
BaseAgent,
|
|
13
|
+
AgentRuntime,
|
|
14
|
+
WebChannel,
|
|
15
|
+
TelegramChannel,
|
|
16
|
+
InMemoryStore,
|
|
17
|
+
} from 'opc-agent';
|
|
18
|
+
|
|
19
|
+
async function main() {
|
|
20
|
+
console.log('🌐 OPC Agent — Multi-Channel Demo\n');
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
const agent = new BaseAgent({
|
|
24
|
+
name: 'multi-channel-agent',
|
|
25
|
+
description: 'Agent that works across multiple channels',
|
|
26
|
+
version: '1.0.0',
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const memory = new InMemoryStore();
|
|
30
|
+
const runtime = new AgentRuntime(agent, { memory });
|
|
31
|
+
|
|
32
|
+
// Channel 1: Web (HTTP endpoint)
|
|
33
|
+
const webChannel = new WebChannel({
|
|
34
|
+
port: 3000,
|
|
35
|
+
path: '/chat',
|
|
36
|
+
});
|
|
37
|
+
console.log('📡 Web channel configured: http://localhost:3000/chat');
|
|
38
|
+
|
|
39
|
+
// Channel 2: Telegram (requires BOT_TOKEN)
|
|
40
|
+
const telegramToken = process.env.TELEGRAM_BOT_TOKEN;
|
|
41
|
+
if (telegramToken) {
|
|
42
|
+
const telegramChannel = new TelegramChannel({
|
|
43
|
+
token: telegramToken,
|
|
44
|
+
});
|
|
45
|
+
runtime.addChannel(telegramChannel);
|
|
46
|
+
console.log('📱 Telegram channel configured');
|
|
47
|
+
} else {
|
|
48
|
+
console.log('📱 Telegram: skipped (set TELEGRAM_BOT_TOKEN to enable)');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Register web channel
|
|
52
|
+
runtime.addChannel(webChannel);
|
|
53
|
+
|
|
54
|
+
console.log('\n✅ Agent configured with channels:');
|
|
55
|
+
console.log(' • Web: HTTP REST endpoint');
|
|
56
|
+
console.log(' • Telegram: Bot API (optional)');
|
|
57
|
+
console.log('\nAvailable channels in OPC Agent:');
|
|
58
|
+
console.log(' WebChannel, TelegramChannel, WebSocketChannel,');
|
|
59
|
+
console.log(' SlackChannel, DiscordChannel, FeishuChannel,');
|
|
60
|
+
console.log(' EmailChannel, WeChatChannel, VoiceChannel,');
|
|
61
|
+
console.log(' WebhookChannel\n');
|
|
62
|
+
|
|
63
|
+
// In production you would call: await runtime.start();
|
|
64
|
+
console.log('💡 To start: await runtime.start()');
|
|
65
|
+
} catch (e: any) {
|
|
66
|
+
console.error(`❌ Error: ${e.message}`);
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
main().catch(e => {
|
|
72
|
+
console.error(`\n❌ Error: ${e.message}\n`);
|
|
73
|
+
process.exit(1);
|
|
74
|
+
});
|
package/fix-sidebar.mjs
CHANGED
|
@@ -1,188 +1,188 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync } from 'fs';
|
|
2
|
-
|
|
3
|
-
const file = 'C:\\Users\\mingjwan\\tmp-opc-clone\\src\\studio-ui\\index.html';
|
|
4
|
-
let html = readFileSync(file, 'utf-8');
|
|
5
|
-
|
|
6
|
-
// 1. Add CSS classes before the closing </style>
|
|
7
|
-
const newCSS = `
|
|
8
|
-
/* Sidebar restructure */
|
|
9
|
-
.sidebar-section-title { font-size: 11px; letter-spacing: 1px; color: var(--text-dim); margin: 20px 12px 8px; text-transform: uppercase; font-weight: 600; }
|
|
10
|
-
.sidebar-divider { height: 1px; background: var(--border); margin: 8px 12px; }
|
|
11
|
-
.agent-list-container { overflow-y: auto; flex: 1; min-height: 0; }
|
|
12
|
-
.agent-list-item {
|
|
13
|
-
display: flex; align-items: center; gap: 10px; padding: 10px 16px; border-radius: 12px;
|
|
14
|
-
cursor: pointer; color: var(--text-muted); transition: all 0.2s ease; font-size: 14px; margin-bottom: 2px; position: relative;
|
|
15
|
-
}
|
|
16
|
-
.agent-list-item:hover { background: var(--bg-hover); color: var(--text); transform: translateX(4px); }
|
|
17
|
-
.agent-list-item.active { background: var(--accent-light); color: #fff; font-weight: 600; box-shadow: var(--glow-sm); border: 1px solid var(--border); }
|
|
18
|
-
.agent-list-item .agent-icon { width: 24px; text-align: center; font-size: 16px; }
|
|
19
|
-
.agent-list-item .agent-name { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
20
|
-
.agent-list-item .status-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
|
|
21
|
-
.agent-list-item .status-dot.online { background: var(--green); box-shadow: 0 0 6px var(--green); }
|
|
22
|
-
.agent-list-item .status-dot.offline { background: var(--text-dim); }
|
|
23
|
-
.agent-list-item .status-dot.error { background: var(--red); box-shadow: 0 0 6px var(--red); }
|
|
24
|
-
.sidebar-bottom { margin-top: auto; flex-shrink: 0; }
|
|
25
|
-
.sidebar-nav { display: flex; flex-direction: column; flex: 1; min-height: 0; overflow: hidden; }
|
|
26
|
-
`;
|
|
27
|
-
|
|
28
|
-
html = html.replace(' </style>', newCSS + ' </style>');
|
|
29
|
-
|
|
30
|
-
// 2. Replace sidebar nav content
|
|
31
|
-
const oldSidebar = ` <div class="sidebar-nav">
|
|
32
|
-
<div class="nav-item active" data-page="dashboard" onclick="navigate('dashboard')">
|
|
33
|
-
<span class="icon">🏠</span> Dashboard
|
|
34
|
-
</div>
|
|
35
|
-
<div class="nav-item" data-page="chat" onclick="openLastChat()">
|
|
36
|
-
<span class="icon">💬</span> Chat
|
|
37
|
-
</div>
|
|
38
|
-
<div class="nav-item" data-page="templates" onclick="navigate('templates')">
|
|
39
|
-
<span class="icon">👤</span> Templates
|
|
40
|
-
</div>
|
|
41
|
-
<div class="nav-item" data-page="skills" onclick="navigate('skills')">
|
|
42
|
-
<span class="icon">🧩</span> Skills Market
|
|
43
|
-
</div>
|
|
44
|
-
<div class="nav-item" data-page="create" onclick="navigate('create')">
|
|
45
|
-
<span class="icon">✨</span> Create Agent
|
|
46
|
-
</div>
|
|
47
|
-
<div class="nav-item" data-page="settings" onclick="currentSettingsTab='models';navigate('settings')">
|
|
48
|
-
<span class="icon">🤖</span> Models
|
|
49
|
-
</div>
|
|
50
|
-
<div class="nav-item" data-page="settings" onclick="currentSettingsTab='channels';navigate('settings')">
|
|
51
|
-
<span class="icon">📡</span> Channels
|
|
52
|
-
</div>
|
|
53
|
-
<div class="nav-item" data-page="settings" onclick="currentSettingsTab='memory';navigate('settings')">
|
|
54
|
-
<span class="icon">🧠</span> Memory
|
|
55
|
-
</div>
|
|
56
|
-
<div class="nav-item" data-page="settings" onclick="navigate('settings')">
|
|
57
|
-
<span class="icon">⚙️</span> Settings
|
|
58
|
-
</div>
|
|
59
|
-
<div class="nav-item" data-page="schedules" onclick="navigate('schedules')">
|
|
60
|
-
<span class="icon">⏰</span> Schedules
|
|
61
|
-
</div>
|
|
62
|
-
</div>`;
|
|
63
|
-
|
|
64
|
-
const newSidebar = ` <div class="sidebar-nav">
|
|
65
|
-
<!-- Section 1: My Agents -->
|
|
66
|
-
<div class="sidebar-section-title">🤖 我的 Agent</div>
|
|
67
|
-
<div class="agent-list-container" id="sidebar-agent-list">
|
|
68
|
-
<div style="padding: 12px 16px; color: var(--text-dim); font-size: 13px;">加载中...</div>
|
|
69
|
-
</div>
|
|
70
|
-
|
|
71
|
-
<!-- Section 2: Create -->
|
|
72
|
-
<div class="sidebar-divider"></div>
|
|
73
|
-
<div class="nav-item" data-page="create" onclick="navigate('create')">
|
|
74
|
-
<span class="icon">➕</span> 新建 Agent
|
|
75
|
-
</div>
|
|
76
|
-
|
|
77
|
-
<!-- Section 3: Global Config -->
|
|
78
|
-
<div class="sidebar-bottom">
|
|
79
|
-
<div class="sidebar-divider"></div>
|
|
80
|
-
<div class="sidebar-section-title">⚙️ 全局配置</div>
|
|
81
|
-
<div class="nav-item" data-page="global-runtime" onclick="navigate('global-runtime')">
|
|
82
|
-
<span class="icon">🚀</span> Runtime
|
|
83
|
-
</div>
|
|
84
|
-
<div class="nav-item" data-page="global-models" onclick="navigate('global-models')">
|
|
85
|
-
<span class="icon">🧠</span> Models
|
|
86
|
-
</div>
|
|
87
|
-
<div class="nav-item" data-page="global-memory" onclick="navigate('global-memory')">
|
|
88
|
-
<span class="icon">💾</span> Memory
|
|
89
|
-
</div>
|
|
90
|
-
<div class="nav-item" data-page="global-templates" onclick="navigate('global-templates')">
|
|
91
|
-
<span class="icon">📋</span> Templates
|
|
92
|
-
</div>
|
|
93
|
-
</div>
|
|
94
|
-
</div>`;
|
|
95
|
-
|
|
96
|
-
html = html.replace(oldSidebar, newSidebar);
|
|
97
|
-
|
|
98
|
-
// 3. Add JavaScript - extend navigate() and add loadSidebarAgents()
|
|
99
|
-
// Insert global-* navigation handling into navigate function
|
|
100
|
-
const oldNavigate = ` if (page === 'settings') { showSettings(currentSettingsTab || 'models'); }`;
|
|
101
|
-
const newNavigate = ` if (page === 'settings') { showSettings(currentSettingsTab || 'models'); }
|
|
102
|
-
if (page === 'global-runtime') { currentSettingsTab='status'; showSettings('status'); showPage('settings'); return; }
|
|
103
|
-
if (page === 'global-models') { currentSettingsTab='models'; showSettings('models'); showPage('settings'); return; }
|
|
104
|
-
if (page === 'global-memory') { currentSettingsTab='memory'; showSettings('memory'); showPage('settings'); return; }
|
|
105
|
-
if (page === 'global-templates') { navigate('templates'); return; }`;
|
|
106
|
-
|
|
107
|
-
html = html.replace(oldNavigate, newNavigate);
|
|
108
|
-
|
|
109
|
-
// Add loadSidebarAgents function and navigateToAgent before the navigate function
|
|
110
|
-
const navFuncMarker = ` // === Navigation ===`;
|
|
111
|
-
const sidebarJS = ` // === Sidebar Agents ===
|
|
112
|
-
let selectedAgentId = null;
|
|
113
|
-
|
|
114
|
-
async function loadSidebarAgents() {
|
|
115
|
-
try {
|
|
116
|
-
const res = await fetch('/api/agents');
|
|
117
|
-
const data = await res.json();
|
|
118
|
-
const agents = data.agents || data || [];
|
|
119
|
-
const container = document.getElementById('sidebar-agent-list');
|
|
120
|
-
if (!agents.length) {
|
|
121
|
-
container.innerHTML = '<div style="padding: 12px 16px; color: var(--text-dim); font-size: 13px;">暂无 Agent</div>';
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
container.innerHTML = agents.map(a => {
|
|
125
|
-
const status = (a.status || 'offline').toLowerCase();
|
|
126
|
-
const icon = a.emoji || a.icon || '🤖';
|
|
127
|
-
const name = a.name || a.id;
|
|
128
|
-
return \`<div class="agent-list-item\${selectedAgentId === a.id ? ' active' : ''}" data-agent-id="\${a.id}" onclick="navigateToAgent('\${a.id}')">
|
|
129
|
-
<span class="agent-icon">\${icon}</span>
|
|
130
|
-
<span class="agent-name">\${name}</span>
|
|
131
|
-
<span class="status-dot \${status}"></span>
|
|
132
|
-
</div>\`;
|
|
133
|
-
}).join('');
|
|
134
|
-
} catch(e) {
|
|
135
|
-
console.error('Failed to load sidebar agents:', e);
|
|
136
|
-
const container = document.getElementById('sidebar-agent-list');
|
|
137
|
-
if (container) container.innerHTML = '<div style="padding: 12px 16px; color: var(--text-dim); font-size: 13px;">加载失败</div>';
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function navigateToAgent(agentId) {
|
|
142
|
-
selectedAgentId = agentId;
|
|
143
|
-
// Update sidebar active state
|
|
144
|
-
document.querySelectorAll('.agent-list-item').forEach(el => el.classList.remove('active'));
|
|
145
|
-
const item = document.querySelector(\`.agent-list-item[data-agent-id="\${agentId}"]\`);
|
|
146
|
-
if (item) item.classList.add('active');
|
|
147
|
-
// For now, navigate to dashboard with agent context
|
|
148
|
-
navigate('dashboard');
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
${navFuncMarker}`;
|
|
152
|
-
|
|
153
|
-
html = html.replace(navFuncMarker, sidebarJS);
|
|
154
|
-
|
|
155
|
-
// Also update nav-item active highlighting to support global-* pages
|
|
156
|
-
const oldNavActive = ` document.querySelectorAll('.nav-item').forEach(n => n.classList.remove('active'));
|
|
157
|
-
const navItem = document.querySelector(\`.nav-item[data-page="\${page}"]\`);
|
|
158
|
-
if (navItem) navItem.classList.add('active');`;
|
|
159
|
-
|
|
160
|
-
const newNavActive = ` document.querySelectorAll('.nav-item').forEach(n => n.classList.remove('active'));
|
|
161
|
-
document.querySelectorAll('.agent-list-item').forEach(n => n.classList.remove('active'));
|
|
162
|
-
const navItem = document.querySelector(\`.nav-item[data-page="\${page}"]\`);
|
|
163
|
-
if (navItem) navItem.classList.add('active');`;
|
|
164
|
-
|
|
165
|
-
html = html.replace(oldNavActive, newNavActive);
|
|
166
|
-
|
|
167
|
-
// Add loadSidebarAgents() call on page load - find DOMContentLoaded or init
|
|
168
|
-
// Look for where loadAgents is first called
|
|
169
|
-
const initMarker = `if (page === 'dashboard') { loadAgents(); loadHealthDashboard(); }`;
|
|
170
|
-
// We'll add the sidebar load call at the end of the navigate function's dashboard case isn't ideal.
|
|
171
|
-
// Let's find where the app initializes
|
|
172
|
-
const hashNav = `location.hash = \`/\${page}\`;`;
|
|
173
|
-
|
|
174
|
-
// Better: add it to the initial page load. Find where hash routing happens.
|
|
175
|
-
// Search for DOMContentLoaded or window.onload
|
|
176
|
-
const loadMatch = html.match(/(?:DOMContentLoaded|window\.onload|loadAgents\(\);\s*loadHealthDashboard)/);
|
|
177
|
-
|
|
178
|
-
// Just add it right after the navigate function definition area, in the init block
|
|
179
|
-
// Find "navigate('dashboard')" at the bottom (initial route)
|
|
180
|
-
const initRoute = html.indexOf("navigate('dashboard')");
|
|
181
|
-
// Let's just add a call in the dashboard load
|
|
182
|
-
html = html.replace(
|
|
183
|
-
`if (page === 'dashboard') { loadAgents(); loadHealthDashboard(); }`,
|
|
184
|
-
`if (page === 'dashboard') { loadAgents(); loadHealthDashboard(); loadSidebarAgents(); }`
|
|
185
|
-
);
|
|
186
|
-
|
|
187
|
-
writeFileSync(file, html, 'utf-8');
|
|
188
|
-
console.log('Done! Sidebar restructured.');
|
|
1
|
+
import { readFileSync, writeFileSync } from 'fs';
|
|
2
|
+
|
|
3
|
+
const file = 'C:\\Users\\mingjwan\\tmp-opc-clone\\src\\studio-ui\\index.html';
|
|
4
|
+
let html = readFileSync(file, 'utf-8');
|
|
5
|
+
|
|
6
|
+
// 1. Add CSS classes before the closing </style>
|
|
7
|
+
const newCSS = `
|
|
8
|
+
/* Sidebar restructure */
|
|
9
|
+
.sidebar-section-title { font-size: 11px; letter-spacing: 1px; color: var(--text-dim); margin: 20px 12px 8px; text-transform: uppercase; font-weight: 600; }
|
|
10
|
+
.sidebar-divider { height: 1px; background: var(--border); margin: 8px 12px; }
|
|
11
|
+
.agent-list-container { overflow-y: auto; flex: 1; min-height: 0; }
|
|
12
|
+
.agent-list-item {
|
|
13
|
+
display: flex; align-items: center; gap: 10px; padding: 10px 16px; border-radius: 12px;
|
|
14
|
+
cursor: pointer; color: var(--text-muted); transition: all 0.2s ease; font-size: 14px; margin-bottom: 2px; position: relative;
|
|
15
|
+
}
|
|
16
|
+
.agent-list-item:hover { background: var(--bg-hover); color: var(--text); transform: translateX(4px); }
|
|
17
|
+
.agent-list-item.active { background: var(--accent-light); color: #fff; font-weight: 600; box-shadow: var(--glow-sm); border: 1px solid var(--border); }
|
|
18
|
+
.agent-list-item .agent-icon { width: 24px; text-align: center; font-size: 16px; }
|
|
19
|
+
.agent-list-item .agent-name { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
20
|
+
.agent-list-item .status-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
|
|
21
|
+
.agent-list-item .status-dot.online { background: var(--green); box-shadow: 0 0 6px var(--green); }
|
|
22
|
+
.agent-list-item .status-dot.offline { background: var(--text-dim); }
|
|
23
|
+
.agent-list-item .status-dot.error { background: var(--red); box-shadow: 0 0 6px var(--red); }
|
|
24
|
+
.sidebar-bottom { margin-top: auto; flex-shrink: 0; }
|
|
25
|
+
.sidebar-nav { display: flex; flex-direction: column; flex: 1; min-height: 0; overflow: hidden; }
|
|
26
|
+
`;
|
|
27
|
+
|
|
28
|
+
html = html.replace(' </style>', newCSS + ' </style>');
|
|
29
|
+
|
|
30
|
+
// 2. Replace sidebar nav content
|
|
31
|
+
const oldSidebar = ` <div class="sidebar-nav">
|
|
32
|
+
<div class="nav-item active" data-page="dashboard" onclick="navigate('dashboard')">
|
|
33
|
+
<span class="icon">🏠</span> Dashboard
|
|
34
|
+
</div>
|
|
35
|
+
<div class="nav-item" data-page="chat" onclick="openLastChat()">
|
|
36
|
+
<span class="icon">💬</span> Chat
|
|
37
|
+
</div>
|
|
38
|
+
<div class="nav-item" data-page="templates" onclick="navigate('templates')">
|
|
39
|
+
<span class="icon">👤</span> Templates
|
|
40
|
+
</div>
|
|
41
|
+
<div class="nav-item" data-page="skills" onclick="navigate('skills')">
|
|
42
|
+
<span class="icon">🧩</span> Skills Market
|
|
43
|
+
</div>
|
|
44
|
+
<div class="nav-item" data-page="create" onclick="navigate('create')">
|
|
45
|
+
<span class="icon">✨</span> Create Agent
|
|
46
|
+
</div>
|
|
47
|
+
<div class="nav-item" data-page="settings" onclick="currentSettingsTab='models';navigate('settings')">
|
|
48
|
+
<span class="icon">🤖</span> Models
|
|
49
|
+
</div>
|
|
50
|
+
<div class="nav-item" data-page="settings" onclick="currentSettingsTab='channels';navigate('settings')">
|
|
51
|
+
<span class="icon">📡</span> Channels
|
|
52
|
+
</div>
|
|
53
|
+
<div class="nav-item" data-page="settings" onclick="currentSettingsTab='memory';navigate('settings')">
|
|
54
|
+
<span class="icon">🧠</span> Memory
|
|
55
|
+
</div>
|
|
56
|
+
<div class="nav-item" data-page="settings" onclick="navigate('settings')">
|
|
57
|
+
<span class="icon">⚙️</span> Settings
|
|
58
|
+
</div>
|
|
59
|
+
<div class="nav-item" data-page="schedules" onclick="navigate('schedules')">
|
|
60
|
+
<span class="icon">⏰</span> Schedules
|
|
61
|
+
</div>
|
|
62
|
+
</div>`;
|
|
63
|
+
|
|
64
|
+
const newSidebar = ` <div class="sidebar-nav">
|
|
65
|
+
<!-- Section 1: My Agents -->
|
|
66
|
+
<div class="sidebar-section-title">🤖 我的 Agent</div>
|
|
67
|
+
<div class="agent-list-container" id="sidebar-agent-list">
|
|
68
|
+
<div style="padding: 12px 16px; color: var(--text-dim); font-size: 13px;">加载中...</div>
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
<!-- Section 2: Create -->
|
|
72
|
+
<div class="sidebar-divider"></div>
|
|
73
|
+
<div class="nav-item" data-page="create" onclick="navigate('create')">
|
|
74
|
+
<span class="icon">➕</span> 新建 Agent
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
<!-- Section 3: Global Config -->
|
|
78
|
+
<div class="sidebar-bottom">
|
|
79
|
+
<div class="sidebar-divider"></div>
|
|
80
|
+
<div class="sidebar-section-title">⚙️ 全局配置</div>
|
|
81
|
+
<div class="nav-item" data-page="global-runtime" onclick="navigate('global-runtime')">
|
|
82
|
+
<span class="icon">🚀</span> Runtime
|
|
83
|
+
</div>
|
|
84
|
+
<div class="nav-item" data-page="global-models" onclick="navigate('global-models')">
|
|
85
|
+
<span class="icon">🧠</span> Models
|
|
86
|
+
</div>
|
|
87
|
+
<div class="nav-item" data-page="global-memory" onclick="navigate('global-memory')">
|
|
88
|
+
<span class="icon">💾</span> Memory
|
|
89
|
+
</div>
|
|
90
|
+
<div class="nav-item" data-page="global-templates" onclick="navigate('global-templates')">
|
|
91
|
+
<span class="icon">📋</span> Templates
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
</div>`;
|
|
95
|
+
|
|
96
|
+
html = html.replace(oldSidebar, newSidebar);
|
|
97
|
+
|
|
98
|
+
// 3. Add JavaScript - extend navigate() and add loadSidebarAgents()
|
|
99
|
+
// Insert global-* navigation handling into navigate function
|
|
100
|
+
const oldNavigate = ` if (page === 'settings') { showSettings(currentSettingsTab || 'models'); }`;
|
|
101
|
+
const newNavigate = ` if (page === 'settings') { showSettings(currentSettingsTab || 'models'); }
|
|
102
|
+
if (page === 'global-runtime') { currentSettingsTab='status'; showSettings('status'); showPage('settings'); return; }
|
|
103
|
+
if (page === 'global-models') { currentSettingsTab='models'; showSettings('models'); showPage('settings'); return; }
|
|
104
|
+
if (page === 'global-memory') { currentSettingsTab='memory'; showSettings('memory'); showPage('settings'); return; }
|
|
105
|
+
if (page === 'global-templates') { navigate('templates'); return; }`;
|
|
106
|
+
|
|
107
|
+
html = html.replace(oldNavigate, newNavigate);
|
|
108
|
+
|
|
109
|
+
// Add loadSidebarAgents function and navigateToAgent before the navigate function
|
|
110
|
+
const navFuncMarker = ` // === Navigation ===`;
|
|
111
|
+
const sidebarJS = ` // === Sidebar Agents ===
|
|
112
|
+
let selectedAgentId = null;
|
|
113
|
+
|
|
114
|
+
async function loadSidebarAgents() {
|
|
115
|
+
try {
|
|
116
|
+
const res = await fetch('/api/agents');
|
|
117
|
+
const data = await res.json();
|
|
118
|
+
const agents = data.agents || data || [];
|
|
119
|
+
const container = document.getElementById('sidebar-agent-list');
|
|
120
|
+
if (!agents.length) {
|
|
121
|
+
container.innerHTML = '<div style="padding: 12px 16px; color: var(--text-dim); font-size: 13px;">暂无 Agent</div>';
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
container.innerHTML = agents.map(a => {
|
|
125
|
+
const status = (a.status || 'offline').toLowerCase();
|
|
126
|
+
const icon = a.emoji || a.icon || '🤖';
|
|
127
|
+
const name = a.name || a.id;
|
|
128
|
+
return \`<div class="agent-list-item\${selectedAgentId === a.id ? ' active' : ''}" data-agent-id="\${a.id}" onclick="navigateToAgent('\${a.id}')">
|
|
129
|
+
<span class="agent-icon">\${icon}</span>
|
|
130
|
+
<span class="agent-name">\${name}</span>
|
|
131
|
+
<span class="status-dot \${status}"></span>
|
|
132
|
+
</div>\`;
|
|
133
|
+
}).join('');
|
|
134
|
+
} catch(e) {
|
|
135
|
+
console.error('Failed to load sidebar agents:', e);
|
|
136
|
+
const container = document.getElementById('sidebar-agent-list');
|
|
137
|
+
if (container) container.innerHTML = '<div style="padding: 12px 16px; color: var(--text-dim); font-size: 13px;">加载失败</div>';
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function navigateToAgent(agentId) {
|
|
142
|
+
selectedAgentId = agentId;
|
|
143
|
+
// Update sidebar active state
|
|
144
|
+
document.querySelectorAll('.agent-list-item').forEach(el => el.classList.remove('active'));
|
|
145
|
+
const item = document.querySelector(\`.agent-list-item[data-agent-id="\${agentId}"]\`);
|
|
146
|
+
if (item) item.classList.add('active');
|
|
147
|
+
// For now, navigate to dashboard with agent context
|
|
148
|
+
navigate('dashboard');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
${navFuncMarker}`;
|
|
152
|
+
|
|
153
|
+
html = html.replace(navFuncMarker, sidebarJS);
|
|
154
|
+
|
|
155
|
+
// Also update nav-item active highlighting to support global-* pages
|
|
156
|
+
const oldNavActive = ` document.querySelectorAll('.nav-item').forEach(n => n.classList.remove('active'));
|
|
157
|
+
const navItem = document.querySelector(\`.nav-item[data-page="\${page}"]\`);
|
|
158
|
+
if (navItem) navItem.classList.add('active');`;
|
|
159
|
+
|
|
160
|
+
const newNavActive = ` document.querySelectorAll('.nav-item').forEach(n => n.classList.remove('active'));
|
|
161
|
+
document.querySelectorAll('.agent-list-item').forEach(n => n.classList.remove('active'));
|
|
162
|
+
const navItem = document.querySelector(\`.nav-item[data-page="\${page}"]\`);
|
|
163
|
+
if (navItem) navItem.classList.add('active');`;
|
|
164
|
+
|
|
165
|
+
html = html.replace(oldNavActive, newNavActive);
|
|
166
|
+
|
|
167
|
+
// Add loadSidebarAgents() call on page load - find DOMContentLoaded or init
|
|
168
|
+
// Look for where loadAgents is first called
|
|
169
|
+
const initMarker = `if (page === 'dashboard') { loadAgents(); loadHealthDashboard(); }`;
|
|
170
|
+
// We'll add the sidebar load call at the end of the navigate function's dashboard case isn't ideal.
|
|
171
|
+
// Let's find where the app initializes
|
|
172
|
+
const hashNav = `location.hash = \`/\${page}\`;`;
|
|
173
|
+
|
|
174
|
+
// Better: add it to the initial page load. Find where hash routing happens.
|
|
175
|
+
// Search for DOMContentLoaded or window.onload
|
|
176
|
+
const loadMatch = html.match(/(?:DOMContentLoaded|window\.onload|loadAgents\(\);\s*loadHealthDashboard)/);
|
|
177
|
+
|
|
178
|
+
// Just add it right after the navigate function definition area, in the init block
|
|
179
|
+
// Find "navigate('dashboard')" at the bottom (initial route)
|
|
180
|
+
const initRoute = html.indexOf("navigate('dashboard')");
|
|
181
|
+
// Let's just add a call in the dashboard load
|
|
182
|
+
html = html.replace(
|
|
183
|
+
`if (page === 'dashboard') { loadAgents(); loadHealthDashboard(); }`,
|
|
184
|
+
`if (page === 'dashboard') { loadAgents(); loadHealthDashboard(); loadSidebarAgents(); }`
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
writeFileSync(file, html, 'utf-8');
|
|
188
|
+
console.log('Done! Sidebar restructured.');
|