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.
Files changed (245) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +20 -20
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +14 -14
  3. package/.github/PULL_REQUEST_TEMPLATE.md +13 -13
  4. package/CHANGELOG.md +48 -48
  5. package/CONTRIBUTING.md +36 -36
  6. package/README.zh-CN.md +497 -497
  7. package/dist/channels/wechat.js +6 -6
  8. package/dist/deploy/index.js +56 -56
  9. package/dist/studio/server.js +30 -1
  10. package/dist/studio-ui/index.html +230 -10
  11. package/dist/ui/components.js +105 -105
  12. package/examples/README.md +22 -22
  13. package/examples/basic-agent.ts +90 -90
  14. package/examples/brain-integration.ts +71 -71
  15. package/examples/multi-channel.ts +74 -74
  16. package/fix-sidebar.mjs +188 -188
  17. package/install.ps1 +154 -154
  18. package/install.sh +164 -164
  19. package/package.json +1 -1
  20. package/scripts/install.ps1 +31 -31
  21. package/scripts/install.sh +40 -40
  22. package/serve-studio.js +13 -13
  23. package/serve-test.js +25 -25
  24. package/src/channels/dingtalk.ts +46 -46
  25. package/src/channels/email.ts +351 -351
  26. package/src/channels/feishu.ts +349 -349
  27. package/src/channels/googlechat.ts +42 -42
  28. package/src/channels/imessage.ts +31 -31
  29. package/src/channels/irc.ts +82 -82
  30. package/src/channels/line.ts +32 -32
  31. package/src/channels/matrix.ts +33 -33
  32. package/src/channels/mattermost.ts +57 -57
  33. package/src/channels/msteams.ts +32 -32
  34. package/src/channels/nostr.ts +32 -32
  35. package/src/channels/qq.ts +33 -33
  36. package/src/channels/signal.ts +32 -32
  37. package/src/channels/sms.ts +33 -33
  38. package/src/channels/telegram.ts +616 -616
  39. package/src/channels/twitch.ts +65 -65
  40. package/src/channels/voice-call.ts +100 -100
  41. package/src/channels/websocket.ts +399 -399
  42. package/src/channels/wechat.ts +329 -329
  43. package/src/channels/whatsapp.ts +32 -32
  44. package/src/cli/chat.ts +99 -99
  45. package/src/cli/setup.ts +314 -314
  46. package/src/core/agent.ts +476 -476
  47. package/src/core/api-server.ts +277 -277
  48. package/src/core/audio.ts +98 -98
  49. package/src/core/collaboration.ts +275 -275
  50. package/src/core/context-discovery.ts +85 -85
  51. package/src/core/context-refs.ts +140 -140
  52. package/src/core/gateway.ts +106 -106
  53. package/src/core/heartbeat.ts +51 -51
  54. package/src/core/hooks.ts +105 -105
  55. package/src/core/ide-bridge.ts +133 -133
  56. package/src/core/node-network.ts +86 -86
  57. package/src/core/profiles.ts +122 -122
  58. package/src/core/scheduler.ts +187 -187
  59. package/src/core/session-manager.ts +137 -137
  60. package/src/core/subagent.ts +98 -98
  61. package/src/core/vision.ts +180 -180
  62. package/src/core/workflow-graph.ts +365 -365
  63. package/src/daemon.ts +96 -96
  64. package/src/deploy/index.ts +255 -255
  65. package/src/doctor.ts +156 -156
  66. package/src/eval/index.ts +211 -211
  67. package/src/eval/suites/basic.json +16 -16
  68. package/src/eval/suites/memory.json +12 -12
  69. package/src/eval/suites/safety.json +14 -14
  70. package/src/hub/brain-seed.ts +54 -54
  71. package/src/hub/client.ts +60 -60
  72. package/src/mcp/servers/calculator-mcp.ts +65 -65
  73. package/src/mcp/servers/crypto-mcp.ts +73 -73
  74. package/src/mcp/servers/database-mcp.ts +72 -72
  75. package/src/mcp/servers/datetime-mcp.ts +69 -69
  76. package/src/mcp/servers/filesystem.ts +66 -66
  77. package/src/mcp/servers/github-mcp.ts +58 -58
  78. package/src/mcp/servers/index.ts +63 -63
  79. package/src/mcp/servers/json-mcp.ts +102 -102
  80. package/src/mcp/servers/memory-mcp.ts +56 -56
  81. package/src/mcp/servers/regex-mcp.ts +53 -53
  82. package/src/mcp/servers/web-mcp.ts +49 -49
  83. package/src/memory/context-compressor.ts +189 -189
  84. package/src/memory/seed-loader.ts +212 -212
  85. package/src/memory/user-profiler.ts +215 -215
  86. package/src/plugins/content-filter.ts +23 -23
  87. package/src/plugins/logger.ts +18 -18
  88. package/src/plugins/rate-limiter.ts +38 -38
  89. package/src/protocols/a2a/client.ts +132 -132
  90. package/src/protocols/a2a/index.ts +8 -8
  91. package/src/protocols/a2a/server.ts +333 -333
  92. package/src/protocols/a2a/types.ts +88 -88
  93. package/src/protocols/a2a/utils.ts +50 -50
  94. package/src/protocols/agui/client.ts +83 -83
  95. package/src/protocols/agui/index.ts +4 -4
  96. package/src/protocols/agui/server.ts +218 -218
  97. package/src/protocols/agui/types.ts +153 -153
  98. package/src/protocols/index.ts +2 -2
  99. package/src/protocols/mcp/agent-tools.ts +134 -134
  100. package/src/protocols/mcp/index.ts +8 -8
  101. package/src/protocols/mcp/server.ts +262 -262
  102. package/src/protocols/mcp/types.ts +69 -69
  103. package/src/providers/index.ts +632 -632
  104. package/src/publish/index.ts +376 -376
  105. package/src/scheduler/cron-engine.ts +191 -191
  106. package/src/scheduler/index.ts +2 -2
  107. package/src/schema/oad.ts +217 -217
  108. package/src/security/approval.ts +131 -131
  109. package/src/security/approvals.ts +143 -143
  110. package/src/security/elevated.ts +105 -105
  111. package/src/security/guardrails.ts +248 -248
  112. package/src/security/index.ts +9 -9
  113. package/src/security/keys.ts +87 -87
  114. package/src/security/secrets.ts +129 -129
  115. package/src/skills/builtin/index.ts +408 -408
  116. package/src/skills/marketplace.ts +113 -113
  117. package/src/skills/types.ts +42 -42
  118. package/src/studio/server.ts +31 -1
  119. package/src/studio/templates-data.ts +178 -178
  120. package/src/studio-ui/index.html +230 -10
  121. package/src/telemetry/index.ts +324 -324
  122. package/src/tools/builtin/browser.ts +299 -299
  123. package/src/tools/builtin/datetime.ts +41 -41
  124. package/src/tools/builtin/file.ts +107 -107
  125. package/src/tools/builtin/home-assistant.ts +116 -116
  126. package/src/tools/builtin/rl-tools.ts +243 -243
  127. package/src/tools/builtin/shell.ts +43 -43
  128. package/src/tools/builtin/vision.ts +64 -64
  129. package/src/tools/builtin/web-search.ts +126 -126
  130. package/src/tools/builtin/web.ts +35 -35
  131. package/src/tools/document-processor.ts +213 -213
  132. package/src/tools/image-generator.ts +150 -150
  133. package/src/tools/integrations/calendar.ts +73 -73
  134. package/src/tools/integrations/code-exec.ts +39 -39
  135. package/src/tools/integrations/csv-analyzer.ts +92 -92
  136. package/src/tools/integrations/database.ts +44 -44
  137. package/src/tools/integrations/email-send.ts +76 -76
  138. package/src/tools/integrations/git-tool.ts +42 -42
  139. package/src/tools/integrations/github-tool.ts +76 -76
  140. package/src/tools/integrations/image-gen.ts +56 -56
  141. package/src/tools/integrations/index.ts +92 -92
  142. package/src/tools/integrations/jira.ts +83 -83
  143. package/src/tools/integrations/notion.ts +71 -71
  144. package/src/tools/integrations/npm-tool.ts +48 -48
  145. package/src/tools/integrations/pdf-reader.ts +58 -58
  146. package/src/tools/integrations/slack.ts +65 -65
  147. package/src/tools/integrations/summarizer.ts +49 -49
  148. package/src/tools/integrations/translator.ts +48 -48
  149. package/src/tools/integrations/trello.ts +60 -60
  150. package/src/tools/integrations/vector-search.ts +42 -42
  151. package/src/tools/integrations/web-scraper.ts +47 -47
  152. package/src/tools/integrations/web-search.ts +58 -58
  153. package/src/tools/integrations/webhook.ts +38 -38
  154. package/src/tools/mcp-client.ts +131 -131
  155. package/src/tools/web-scraper.ts +179 -179
  156. package/src/tools/web-search.ts +180 -180
  157. package/src/ui/components.ts +127 -127
  158. package/srv-out.txt +1 -1
  159. package/templates/ecommerce-assistant/README.md +45 -45
  160. package/templates/ecommerce-assistant/oad.yaml +47 -47
  161. package/templates/tech-support/README.md +43 -43
  162. package/templates/tech-support/oad.yaml +45 -45
  163. package/test-agent/Dockerfile +9 -9
  164. package/test-agent/README.md +50 -50
  165. package/test-agent/agent.yaml +23 -23
  166. package/test-agent/docker-compose.yml +11 -11
  167. package/test-agent/oad.yaml +31 -31
  168. package/test-agent/package-lock.json +1492 -1492
  169. package/test-agent/package.json +17 -17
  170. package/test-agent/src/index.ts +24 -24
  171. package/test-agent/src/skills/echo.ts +15 -15
  172. package/test-agent/tsconfig.json +24 -24
  173. package/test-full.js +43 -43
  174. package/test-sidebar.js +22 -22
  175. package/test-studio3.js +75 -75
  176. package/test-studio4.js +41 -41
  177. package/tests/a2a-protocol.test.ts +285 -285
  178. package/tests/agui-protocol.test.ts +246 -246
  179. package/tests/api-server.test.ts +148 -148
  180. package/tests/approvals.test.ts +89 -89
  181. package/tests/audio.test.ts +40 -40
  182. package/tests/brain-seed-extended.test.ts +490 -490
  183. package/tests/brain-seed.test.ts +239 -239
  184. package/tests/browser.test.ts +179 -179
  185. package/tests/channels/discord.test.ts +79 -79
  186. package/tests/channels/email.test.ts +148 -148
  187. package/tests/channels/feishu.test.ts +123 -123
  188. package/tests/channels/telegram.test.ts +129 -129
  189. package/tests/channels/websocket.test.ts +53 -53
  190. package/tests/channels/wechat.test.ts +170 -170
  191. package/tests/channels-extra.test.ts +45 -45
  192. package/tests/chat-cli.test.ts +160 -160
  193. package/tests/cli.test.ts +46 -46
  194. package/tests/context-compressor.test.ts +172 -172
  195. package/tests/context-refs.test.ts +121 -121
  196. package/tests/cron-engine.test.ts +101 -101
  197. package/tests/daemon.test.ts +135 -135
  198. package/tests/deepbrain-wire.test.ts +234 -234
  199. package/tests/deploy-and-dag.test.ts +196 -196
  200. package/tests/doctor.test.ts +38 -38
  201. package/tests/document-processor.test.ts +69 -69
  202. package/tests/e2e-nocode.test.ts +442 -442
  203. package/tests/elevated.test.ts +69 -69
  204. package/tests/eval.test.ts +173 -173
  205. package/tests/gateway.test.ts +63 -63
  206. package/tests/guardrails.test.ts +177 -177
  207. package/tests/home-assistant.test.ts +40 -40
  208. package/tests/hooks.test.ts +79 -79
  209. package/tests/ide-bridge.test.ts +38 -38
  210. package/tests/image-generator.test.ts +84 -84
  211. package/tests/init-role.test.ts +124 -124
  212. package/tests/integrations.test.ts +249 -249
  213. package/tests/mcp-client.test.ts +92 -92
  214. package/tests/mcp-server.test.ts +178 -178
  215. package/tests/mcp-servers.test.ts +260 -260
  216. package/tests/node-network.test.ts +74 -74
  217. package/tests/plugin-a2a-enhanced.test.ts +230 -230
  218. package/tests/profiles.test.ts +61 -61
  219. package/tests/publish.test.ts +231 -231
  220. package/tests/rl-tools.test.ts +93 -93
  221. package/tests/sandbox-manager.test.ts +46 -46
  222. package/tests/scheduler.test.ts +200 -200
  223. package/tests/secrets.test.ts +107 -107
  224. package/tests/security-enhanced.test.ts +233 -233
  225. package/tests/settings-api.test.ts +148 -148
  226. package/tests/setup.test.ts +73 -73
  227. package/tests/subagent.test.ts +193 -193
  228. package/tests/telegram-discord.test.ts +60 -60
  229. package/tests/telemetry.test.ts +186 -186
  230. package/tests/user-profiler.test.ts +169 -169
  231. package/tests/v090-features.test.ts +254 -254
  232. package/tests/vision.test.ts +61 -61
  233. package/tests/voice-call.test.ts +47 -47
  234. package/tests/voice-enhanced.test.ts +169 -169
  235. package/tests/voice-interaction.test.ts +38 -38
  236. package/tests/web-search.test.ts +155 -155
  237. package/tests/workflow-graph.test.ts +279 -279
  238. package/tutorial/customer-service-agent/README.md +612 -612
  239. package/tutorial/customer-service-agent/SOUL.md +26 -26
  240. package/tutorial/customer-service-agent/agent.yaml +63 -63
  241. package/tutorial/customer-service-agent/package.json +19 -19
  242. package/tutorial/customer-service-agent/src/index.ts +69 -69
  243. package/tutorial/customer-service-agent/src/skills/faq.ts +27 -27
  244. package/tutorial/customer-service-agent/src/skills/ticket.ts +22 -22
  245. 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.');