opc-agent 4.1.2 → 4.1.4

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 (745) hide show
  1. package/dist/analytics/index.d.ts.map +1 -0
  2. package/dist/analytics/index.js.map +1 -0
  3. package/dist/channels/dingtalk.d.ts.map +1 -0
  4. package/dist/channels/dingtalk.js.map +1 -0
  5. package/dist/channels/discord.d.ts.map +1 -0
  6. package/dist/channels/discord.js.map +1 -0
  7. package/dist/channels/email.d.ts.map +1 -0
  8. package/dist/channels/email.js.map +1 -0
  9. package/dist/channels/feishu.d.ts.map +1 -0
  10. package/dist/channels/feishu.js.map +1 -0
  11. package/dist/channels/googlechat.d.ts.map +1 -0
  12. package/dist/channels/googlechat.js.map +1 -0
  13. package/dist/channels/imessage.d.ts.map +1 -0
  14. package/dist/channels/imessage.js.map +1 -0
  15. package/dist/channels/index.d.ts.map +1 -0
  16. package/dist/channels/index.js.map +1 -0
  17. package/dist/channels/irc.d.ts.map +1 -0
  18. package/dist/channels/irc.js.map +1 -0
  19. package/dist/channels/line.d.ts.map +1 -0
  20. package/dist/channels/line.js.map +1 -0
  21. package/dist/channels/matrix.d.ts.map +1 -0
  22. package/dist/channels/matrix.js.map +1 -0
  23. package/dist/channels/mattermost.d.ts.map +1 -0
  24. package/dist/channels/mattermost.js.map +1 -0
  25. package/dist/channels/msteams.d.ts.map +1 -0
  26. package/dist/channels/msteams.js.map +1 -0
  27. package/dist/channels/nostr.d.ts.map +1 -0
  28. package/dist/channels/nostr.js.map +1 -0
  29. package/dist/channels/qq.d.ts.map +1 -0
  30. package/dist/channels/qq.js.map +1 -0
  31. package/dist/channels/signal.d.ts.map +1 -0
  32. package/dist/channels/signal.js.map +1 -0
  33. package/dist/channels/slack.d.ts.map +1 -0
  34. package/dist/channels/slack.js.map +1 -0
  35. package/dist/channels/sms.d.ts.map +1 -0
  36. package/dist/channels/sms.js.map +1 -0
  37. package/dist/channels/telegram.d.ts.map +1 -0
  38. package/dist/channels/telegram.js.map +1 -0
  39. package/dist/channels/twitch.d.ts.map +1 -0
  40. package/dist/channels/twitch.js.map +1 -0
  41. package/dist/channels/voice-call.d.ts.map +1 -0
  42. package/dist/channels/voice-call.js.map +1 -0
  43. package/dist/channels/voice.d.ts.map +1 -0
  44. package/dist/channels/voice.js.map +1 -0
  45. package/dist/channels/web.d.ts.map +1 -0
  46. package/dist/channels/web.js.map +1 -0
  47. package/dist/channels/webhook.d.ts.map +1 -0
  48. package/dist/channels/webhook.js.map +1 -0
  49. package/dist/channels/websocket.d.ts.map +1 -0
  50. package/dist/channels/websocket.js.map +1 -0
  51. package/dist/channels/wechat.d.ts.map +1 -0
  52. package/dist/channels/wechat.js.map +1 -0
  53. package/dist/channels/whatsapp.d.ts.map +1 -0
  54. package/dist/channels/whatsapp.js.map +1 -0
  55. package/dist/cli/chat.d.ts.map +1 -0
  56. package/dist/cli/chat.js.map +1 -0
  57. package/dist/cli/setup.d.ts.map +1 -0
  58. package/dist/cli/setup.js.map +1 -0
  59. package/dist/cli.d.ts.map +1 -0
  60. package/dist/cli.js +108 -11
  61. package/dist/cli.js.map +1 -0
  62. package/dist/core/a2a.d.ts.map +1 -0
  63. package/dist/core/a2a.js.map +1 -0
  64. package/dist/core/agent.d.ts.map +1 -0
  65. package/dist/core/agent.js.map +1 -0
  66. package/dist/core/analytics-engine.d.ts.map +1 -0
  67. package/dist/core/analytics-engine.js.map +1 -0
  68. package/dist/core/api-server.d.ts.map +1 -0
  69. package/dist/core/api-server.js.map +1 -0
  70. package/dist/core/audio.d.ts.map +1 -0
  71. package/dist/core/audio.js.map +1 -0
  72. package/dist/core/auth.d.ts.map +1 -0
  73. package/dist/core/auth.js.map +1 -0
  74. package/dist/core/cache.d.ts.map +1 -0
  75. package/dist/core/cache.js.map +1 -0
  76. package/dist/core/collaboration.d.ts.map +1 -0
  77. package/dist/core/collaboration.js.map +1 -0
  78. package/dist/core/compose.d.ts.map +1 -0
  79. package/dist/core/compose.js.map +1 -0
  80. package/dist/core/config.d.ts.map +1 -0
  81. package/dist/core/config.js.map +1 -0
  82. package/dist/core/context-discovery.d.ts.map +1 -0
  83. package/dist/core/context-discovery.js.map +1 -0
  84. package/dist/core/context-refs.d.ts.map +1 -0
  85. package/dist/core/context-refs.js.map +1 -0
  86. package/dist/core/errors.d.ts.map +1 -0
  87. package/dist/core/errors.js.map +1 -0
  88. package/dist/core/gateway.d.ts.map +1 -0
  89. package/dist/core/gateway.js.map +1 -0
  90. package/dist/core/heartbeat.d.ts.map +1 -0
  91. package/dist/core/heartbeat.js.map +1 -0
  92. package/dist/core/hitl.d.ts.map +1 -0
  93. package/dist/core/hitl.js.map +1 -0
  94. package/dist/core/hooks.d.ts.map +1 -0
  95. package/dist/core/hooks.js.map +1 -0
  96. package/dist/core/ide-bridge.d.ts.map +1 -0
  97. package/dist/core/ide-bridge.js.map +1 -0
  98. package/dist/core/knowledge.d.ts.map +1 -0
  99. package/dist/core/knowledge.js.map +1 -0
  100. package/dist/core/logger.d.ts.map +1 -0
  101. package/dist/core/logger.js.map +1 -0
  102. package/dist/core/model-recommender.d.ts +40 -0
  103. package/dist/core/model-recommender.d.ts.map +1 -0
  104. package/dist/core/model-recommender.js +186 -0
  105. package/dist/core/model-recommender.js.map +1 -0
  106. package/dist/core/node-network.d.ts.map +1 -0
  107. package/dist/core/node-network.js.map +1 -0
  108. package/dist/core/orchestrator.d.ts.map +1 -0
  109. package/dist/core/orchestrator.js.map +1 -0
  110. package/dist/core/performance.d.ts.map +1 -0
  111. package/dist/core/performance.js.map +1 -0
  112. package/dist/core/profiles.d.ts.map +1 -0
  113. package/dist/core/profiles.js.map +1 -0
  114. package/dist/core/rate-limiter.d.ts.map +1 -0
  115. package/dist/core/rate-limiter.js.map +1 -0
  116. package/dist/core/room.d.ts.map +1 -0
  117. package/dist/core/room.js.map +1 -0
  118. package/dist/core/runtime.d.ts.map +1 -0
  119. package/dist/core/runtime.js.map +1 -0
  120. package/dist/core/sandbox.d.ts.map +1 -0
  121. package/dist/core/sandbox.js.map +1 -0
  122. package/dist/core/scheduler.d.ts.map +1 -0
  123. package/dist/core/scheduler.js.map +1 -0
  124. package/dist/core/security.d.ts.map +1 -0
  125. package/dist/core/security.js.map +1 -0
  126. package/dist/core/session-manager.d.ts.map +1 -0
  127. package/dist/core/session-manager.js.map +1 -0
  128. package/dist/core/streaming.d.ts.map +1 -0
  129. package/dist/core/streaming.js.map +1 -0
  130. package/dist/core/subagent.d.ts.map +1 -0
  131. package/dist/core/subagent.js.map +1 -0
  132. package/dist/core/types.d.ts.map +1 -0
  133. package/dist/core/types.js.map +1 -0
  134. package/dist/core/versioning.d.ts.map +1 -0
  135. package/dist/core/versioning.js.map +1 -0
  136. package/dist/core/vision.d.ts.map +1 -0
  137. package/dist/core/vision.js.map +1 -0
  138. package/dist/core/watch.d.ts.map +1 -0
  139. package/dist/core/watch.js.map +1 -0
  140. package/dist/core/workflow-graph.d.ts.map +1 -0
  141. package/dist/core/workflow-graph.js.map +1 -0
  142. package/dist/core/workflow.d.ts.map +1 -0
  143. package/dist/core/workflow.js.map +1 -0
  144. package/dist/daemon.d.ts.map +1 -0
  145. package/dist/daemon.js.map +1 -0
  146. package/dist/deploy/hermes.d.ts.map +1 -0
  147. package/dist/deploy/hermes.js.map +1 -0
  148. package/dist/deploy/index.d.ts.map +1 -0
  149. package/dist/deploy/index.js.map +1 -0
  150. package/dist/deploy/openclaw.d.ts.map +1 -0
  151. package/dist/deploy/openclaw.js.map +1 -0
  152. package/dist/doctor.d.ts.map +1 -0
  153. package/dist/doctor.js.map +1 -0
  154. package/dist/eval/index.d.ts.map +1 -0
  155. package/dist/eval/index.js.map +1 -0
  156. package/dist/hub/brain-seed.d.ts.map +1 -0
  157. package/dist/hub/brain-seed.js.map +1 -0
  158. package/dist/hub/client.d.ts.map +1 -0
  159. package/dist/hub/client.js.map +1 -0
  160. package/dist/i18n/index.d.ts.map +1 -0
  161. package/dist/i18n/index.js.map +1 -0
  162. package/dist/index.d.ts +2 -0
  163. package/dist/index.d.ts.map +1 -0
  164. package/dist/index.js +11 -5
  165. package/dist/index.js.map +1 -0
  166. package/dist/mcp/servers/calculator-mcp.d.ts.map +1 -0
  167. package/dist/mcp/servers/calculator-mcp.js.map +1 -0
  168. package/dist/mcp/servers/crypto-mcp.d.ts.map +1 -0
  169. package/dist/mcp/servers/crypto-mcp.js.map +1 -0
  170. package/dist/mcp/servers/database-mcp.d.ts.map +1 -0
  171. package/dist/mcp/servers/database-mcp.js.map +1 -0
  172. package/dist/mcp/servers/datetime-mcp.d.ts.map +1 -0
  173. package/dist/mcp/servers/datetime-mcp.js.map +1 -0
  174. package/dist/mcp/servers/filesystem.d.ts.map +1 -0
  175. package/dist/mcp/servers/filesystem.js.map +1 -0
  176. package/dist/mcp/servers/github-mcp.d.ts.map +1 -0
  177. package/dist/mcp/servers/github-mcp.js.map +1 -0
  178. package/dist/mcp/servers/index.d.ts.map +1 -0
  179. package/dist/mcp/servers/index.js.map +1 -0
  180. package/dist/mcp/servers/json-mcp.d.ts.map +1 -0
  181. package/dist/mcp/servers/json-mcp.js.map +1 -0
  182. package/dist/mcp/servers/memory-mcp.d.ts.map +1 -0
  183. package/dist/mcp/servers/memory-mcp.js.map +1 -0
  184. package/dist/mcp/servers/regex-mcp.d.ts.map +1 -0
  185. package/dist/mcp/servers/regex-mcp.js.map +1 -0
  186. package/dist/mcp/servers/web-mcp.d.ts.map +1 -0
  187. package/dist/mcp/servers/web-mcp.js.map +1 -0
  188. package/dist/memory/context-compressor.d.ts.map +1 -0
  189. package/dist/memory/context-compressor.js.map +1 -0
  190. package/dist/memory/deepbrain.d.ts.map +1 -0
  191. package/dist/memory/deepbrain.js.map +1 -0
  192. package/dist/memory/index.d.ts.map +1 -0
  193. package/dist/memory/index.js.map +1 -0
  194. package/dist/memory/seed-loader.d.ts.map +1 -0
  195. package/dist/memory/seed-loader.js.map +1 -0
  196. package/dist/memory/user-profiler.d.ts.map +1 -0
  197. package/dist/memory/user-profiler.js.map +1 -0
  198. package/dist/plugins/content-filter.d.ts.map +1 -0
  199. package/dist/plugins/content-filter.js.map +1 -0
  200. package/dist/plugins/index.d.ts.map +1 -0
  201. package/dist/plugins/index.js.map +1 -0
  202. package/dist/plugins/logger.d.ts.map +1 -0
  203. package/dist/plugins/logger.js.map +1 -0
  204. package/dist/plugins/rate-limiter.d.ts.map +1 -0
  205. package/dist/plugins/rate-limiter.js.map +1 -0
  206. package/dist/protocols/a2a/client.d.ts.map +1 -0
  207. package/dist/protocols/a2a/client.js.map +1 -0
  208. package/dist/protocols/a2a/index.d.ts.map +1 -0
  209. package/dist/protocols/a2a/index.js.map +1 -0
  210. package/dist/protocols/a2a/server.d.ts.map +1 -0
  211. package/dist/protocols/a2a/server.js.map +1 -0
  212. package/dist/protocols/a2a/types.d.ts.map +1 -0
  213. package/dist/protocols/a2a/types.js.map +1 -0
  214. package/dist/protocols/a2a/utils.d.ts.map +1 -0
  215. package/dist/protocols/a2a/utils.js.map +1 -0
  216. package/dist/protocols/agui/client.d.ts.map +1 -0
  217. package/dist/protocols/agui/client.js.map +1 -0
  218. package/dist/protocols/agui/index.d.ts.map +1 -0
  219. package/dist/protocols/agui/index.js.map +1 -0
  220. package/dist/protocols/agui/server.d.ts.map +1 -0
  221. package/dist/protocols/agui/server.js.map +1 -0
  222. package/dist/protocols/agui/types.d.ts.map +1 -0
  223. package/dist/protocols/agui/types.js.map +1 -0
  224. package/dist/protocols/index.d.ts.map +1 -0
  225. package/dist/protocols/index.js.map +1 -0
  226. package/dist/protocols/mcp/agent-tools.d.ts.map +1 -0
  227. package/dist/protocols/mcp/agent-tools.js.map +1 -0
  228. package/dist/protocols/mcp/index.d.ts.map +1 -0
  229. package/dist/protocols/mcp/index.js.map +1 -0
  230. package/dist/protocols/mcp/server.d.ts.map +1 -0
  231. package/dist/protocols/mcp/server.js.map +1 -0
  232. package/dist/protocols/mcp/types.d.ts.map +1 -0
  233. package/dist/protocols/mcp/types.js.map +1 -0
  234. package/dist/providers/index.d.ts.map +1 -0
  235. package/dist/providers/index.js.map +1 -0
  236. package/dist/publish/index.d.ts.map +1 -0
  237. package/dist/publish/index.js.map +1 -0
  238. package/dist/scheduler/cron-engine.d.ts.map +1 -0
  239. package/dist/scheduler/cron-engine.js.map +1 -0
  240. package/dist/scheduler/index.d.ts.map +1 -0
  241. package/dist/scheduler/index.js.map +1 -0
  242. package/dist/schema/oad.d.ts.map +1 -0
  243. package/dist/schema/oad.js.map +1 -0
  244. package/dist/security/approval.d.ts.map +1 -0
  245. package/dist/security/approval.js.map +1 -0
  246. package/dist/security/approvals.d.ts.map +1 -0
  247. package/dist/security/approvals.js.map +1 -0
  248. package/dist/security/elevated.d.ts.map +1 -0
  249. package/dist/security/elevated.js.map +1 -0
  250. package/dist/security/guardrails.d.ts.map +1 -0
  251. package/dist/security/guardrails.js.map +1 -0
  252. package/dist/security/index.d.ts.map +1 -0
  253. package/dist/security/index.js.map +1 -0
  254. package/dist/security/keys.d.ts.map +1 -0
  255. package/dist/security/keys.js.map +1 -0
  256. package/dist/security/secrets.d.ts.map +1 -0
  257. package/dist/security/secrets.js.map +1 -0
  258. package/dist/skills/auto-learn.d.ts.map +1 -0
  259. package/dist/skills/auto-learn.js.map +1 -0
  260. package/dist/skills/base.d.ts.map +1 -0
  261. package/dist/skills/base.js.map +1 -0
  262. package/dist/skills/builtin/index.d.ts.map +1 -0
  263. package/dist/skills/builtin/index.js.map +1 -0
  264. package/dist/skills/document.d.ts.map +1 -0
  265. package/dist/skills/document.js.map +1 -0
  266. package/dist/skills/http.d.ts.map +1 -0
  267. package/dist/skills/http.js.map +1 -0
  268. package/dist/skills/index.d.ts.map +1 -0
  269. package/dist/skills/index.js.map +1 -0
  270. package/dist/skills/marketplace.d.ts.map +1 -0
  271. package/dist/skills/marketplace.js.map +1 -0
  272. package/dist/skills/scheduler.d.ts.map +1 -0
  273. package/dist/skills/scheduler.js.map +1 -0
  274. package/dist/skills/types.d.ts.map +1 -0
  275. package/dist/skills/types.js.map +1 -0
  276. package/dist/skills/webhook-trigger.d.ts.map +1 -0
  277. package/dist/skills/webhook-trigger.js.map +1 -0
  278. package/dist/studio/server.d.ts.map +1 -0
  279. package/dist/studio/server.js.map +1 -0
  280. package/dist/studio/templates-data.d.ts.map +1 -0
  281. package/dist/studio/templates-data.js.map +1 -0
  282. package/dist/telemetry/index.d.ts.map +1 -0
  283. package/dist/telemetry/index.js.map +1 -0
  284. package/dist/templates/code-reviewer.d.ts.map +1 -0
  285. package/dist/templates/code-reviewer.js.map +1 -0
  286. package/dist/templates/content-writer.d.ts.map +1 -0
  287. package/dist/templates/content-writer.js.map +1 -0
  288. package/dist/templates/customer-service.d.ts.map +1 -0
  289. package/dist/templates/customer-service.js.map +1 -0
  290. package/dist/templates/data-analyst.d.ts.map +1 -0
  291. package/dist/templates/data-analyst.js.map +1 -0
  292. package/dist/templates/executive-assistant.d.ts.map +1 -0
  293. package/dist/templates/executive-assistant.js.map +1 -0
  294. package/dist/templates/financial-advisor.d.ts.map +1 -0
  295. package/dist/templates/financial-advisor.js.map +1 -0
  296. package/dist/templates/hr-recruiter.d.ts.map +1 -0
  297. package/dist/templates/hr-recruiter.js.map +1 -0
  298. package/dist/templates/knowledge-base.d.ts.map +1 -0
  299. package/dist/templates/knowledge-base.js.map +1 -0
  300. package/dist/templates/legal-assistant.d.ts.map +1 -0
  301. package/dist/templates/legal-assistant.js.map +1 -0
  302. package/dist/templates/project-manager.d.ts.map +1 -0
  303. package/dist/templates/project-manager.js.map +1 -0
  304. package/dist/templates/sales-assistant.d.ts.map +1 -0
  305. package/dist/templates/sales-assistant.js.map +1 -0
  306. package/dist/templates/teacher.d.ts.map +1 -0
  307. package/dist/templates/teacher.js.map +1 -0
  308. package/dist/testing/index.d.ts.map +1 -0
  309. package/dist/testing/index.js.map +1 -0
  310. package/dist/tools/builtin/browser.d.ts.map +1 -0
  311. package/dist/tools/builtin/browser.js.map +1 -0
  312. package/dist/tools/builtin/datetime.d.ts.map +1 -0
  313. package/dist/tools/builtin/datetime.js.map +1 -0
  314. package/dist/tools/builtin/file.d.ts.map +1 -0
  315. package/dist/tools/builtin/file.js.map +1 -0
  316. package/dist/tools/builtin/home-assistant.d.ts.map +1 -0
  317. package/dist/tools/builtin/home-assistant.js.map +1 -0
  318. package/dist/tools/builtin/index.d.ts.map +1 -0
  319. package/dist/tools/builtin/index.js.map +1 -0
  320. package/dist/tools/builtin/rl-tools.d.ts.map +1 -0
  321. package/dist/tools/builtin/rl-tools.js.map +1 -0
  322. package/dist/tools/builtin/shell.d.ts.map +1 -0
  323. package/dist/tools/builtin/shell.js.map +1 -0
  324. package/dist/tools/builtin/vision.d.ts.map +1 -0
  325. package/dist/tools/builtin/vision.js.map +1 -0
  326. package/dist/tools/builtin/web-search.d.ts.map +1 -0
  327. package/dist/tools/builtin/web-search.js.map +1 -0
  328. package/dist/tools/builtin/web.d.ts.map +1 -0
  329. package/dist/tools/builtin/web.js.map +1 -0
  330. package/dist/tools/calculator.d.ts.map +1 -0
  331. package/dist/tools/calculator.js.map +1 -0
  332. package/dist/tools/datetime.d.ts.map +1 -0
  333. package/dist/tools/datetime.js.map +1 -0
  334. package/dist/tools/document-processor.d.ts.map +1 -0
  335. package/dist/tools/document-processor.js.map +1 -0
  336. package/dist/tools/gateway.d.ts.map +1 -0
  337. package/dist/tools/gateway.js.map +1 -0
  338. package/dist/tools/image-generator.d.ts.map +1 -0
  339. package/dist/tools/image-generator.js.map +1 -0
  340. package/dist/tools/integrations/calendar.d.ts.map +1 -0
  341. package/dist/tools/integrations/calendar.js.map +1 -0
  342. package/dist/tools/integrations/code-exec.d.ts.map +1 -0
  343. package/dist/tools/integrations/code-exec.js.map +1 -0
  344. package/dist/tools/integrations/csv-analyzer.d.ts.map +1 -0
  345. package/dist/tools/integrations/csv-analyzer.js.map +1 -0
  346. package/dist/tools/integrations/database.d.ts.map +1 -0
  347. package/dist/tools/integrations/database.js.map +1 -0
  348. package/dist/tools/integrations/email-send.d.ts.map +1 -0
  349. package/dist/tools/integrations/email-send.js.map +1 -0
  350. package/dist/tools/integrations/git-tool.d.ts.map +1 -0
  351. package/dist/tools/integrations/git-tool.js.map +1 -0
  352. package/dist/tools/integrations/github-tool.d.ts.map +1 -0
  353. package/dist/tools/integrations/github-tool.js.map +1 -0
  354. package/dist/tools/integrations/image-gen.d.ts.map +1 -0
  355. package/dist/tools/integrations/image-gen.js.map +1 -0
  356. package/dist/tools/integrations/index.d.ts.map +1 -0
  357. package/dist/tools/integrations/index.js.map +1 -0
  358. package/dist/tools/integrations/jira.d.ts.map +1 -0
  359. package/dist/tools/integrations/jira.js.map +1 -0
  360. package/dist/tools/integrations/notion.d.ts.map +1 -0
  361. package/dist/tools/integrations/notion.js.map +1 -0
  362. package/dist/tools/integrations/npm-tool.d.ts.map +1 -0
  363. package/dist/tools/integrations/npm-tool.js.map +1 -0
  364. package/dist/tools/integrations/pdf-reader.d.ts.map +1 -0
  365. package/dist/tools/integrations/pdf-reader.js.map +1 -0
  366. package/dist/tools/integrations/slack.d.ts.map +1 -0
  367. package/dist/tools/integrations/slack.js.map +1 -0
  368. package/dist/tools/integrations/summarizer.d.ts.map +1 -0
  369. package/dist/tools/integrations/summarizer.js.map +1 -0
  370. package/dist/tools/integrations/translator.d.ts.map +1 -0
  371. package/dist/tools/integrations/translator.js.map +1 -0
  372. package/dist/tools/integrations/trello.d.ts.map +1 -0
  373. package/dist/tools/integrations/trello.js.map +1 -0
  374. package/dist/tools/integrations/vector-search.d.ts.map +1 -0
  375. package/dist/tools/integrations/vector-search.js.map +1 -0
  376. package/dist/tools/integrations/web-scraper.d.ts.map +1 -0
  377. package/dist/tools/integrations/web-scraper.js.map +1 -0
  378. package/dist/tools/integrations/web-search.d.ts.map +1 -0
  379. package/dist/tools/integrations/web-search.js.map +1 -0
  380. package/dist/tools/integrations/webhook.d.ts.map +1 -0
  381. package/dist/tools/integrations/webhook.js.map +1 -0
  382. package/dist/tools/json-transform.d.ts.map +1 -0
  383. package/dist/tools/json-transform.js.map +1 -0
  384. package/dist/tools/mcp-client.d.ts.map +1 -0
  385. package/dist/tools/mcp-client.js.map +1 -0
  386. package/dist/tools/mcp.d.ts.map +1 -0
  387. package/dist/tools/mcp.js.map +1 -0
  388. package/dist/tools/text-analysis.d.ts.map +1 -0
  389. package/dist/tools/text-analysis.js.map +1 -0
  390. package/dist/tools/web-scraper.d.ts.map +1 -0
  391. package/dist/tools/web-scraper.js.map +1 -0
  392. package/dist/tools/web-search.d.ts.map +1 -0
  393. package/dist/tools/web-search.js.map +1 -0
  394. package/dist/traces/index.d.ts.map +1 -0
  395. package/dist/traces/index.js.map +1 -0
  396. package/dist/ui/components.d.ts.map +1 -0
  397. package/dist/ui/components.js.map +1 -0
  398. package/models.json +164 -0
  399. package/package.json +1 -1
  400. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -20
  401. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -14
  402. package/.github/PULL_REQUEST_TEMPLATE.md +0 -13
  403. package/.github/workflows/ci.yml +0 -24
  404. package/USABILITY-ISSUES.md +0 -73
  405. package/docs/.vitepress/config.ts +0 -103
  406. package/docs/api/cli.md +0 -48
  407. package/docs/api/oad-schema.md +0 -64
  408. package/docs/api/sdk.md +0 -80
  409. package/docs/guide/concepts.md +0 -51
  410. package/docs/guide/configuration.md +0 -79
  411. package/docs/guide/deployment.md +0 -42
  412. package/docs/guide/getting-started.md +0 -44
  413. package/docs/guide/templates.md +0 -28
  414. package/docs/guide/testing.md +0 -84
  415. package/docs/index.md +0 -27
  416. package/docs/zh/api/cli.md +0 -54
  417. package/docs/zh/api/oad-schema.md +0 -87
  418. package/docs/zh/api/sdk.md +0 -102
  419. package/docs/zh/guide/concepts.md +0 -104
  420. package/docs/zh/guide/configuration.md +0 -135
  421. package/docs/zh/guide/deployment.md +0 -81
  422. package/docs/zh/guide/getting-started.md +0 -82
  423. package/docs/zh/guide/templates.md +0 -84
  424. package/docs/zh/guide/testing.md +0 -88
  425. package/docs/zh/index.md +0 -27
  426. package/fix-sidebar.mjs +0 -188
  427. package/serve-studio.js +0 -13
  428. package/serve-test.js +0 -25
  429. package/src/analytics/index.ts +0 -66
  430. package/src/channels/dingtalk.ts +0 -46
  431. package/src/channels/discord.ts +0 -192
  432. package/src/channels/email.ts +0 -351
  433. package/src/channels/feishu.ts +0 -349
  434. package/src/channels/googlechat.ts +0 -42
  435. package/src/channels/imessage.ts +0 -32
  436. package/src/channels/index.ts +0 -15
  437. package/src/channels/irc.ts +0 -82
  438. package/src/channels/line.ts +0 -33
  439. package/src/channels/matrix.ts +0 -34
  440. package/src/channels/mattermost.ts +0 -57
  441. package/src/channels/msteams.ts +0 -33
  442. package/src/channels/nostr.ts +0 -33
  443. package/src/channels/qq.ts +0 -34
  444. package/src/channels/signal.ts +0 -33
  445. package/src/channels/slack.ts +0 -217
  446. package/src/channels/sms.ts +0 -34
  447. package/src/channels/telegram.ts +0 -616
  448. package/src/channels/twitch.ts +0 -65
  449. package/src/channels/voice-call.ts +0 -100
  450. package/src/channels/voice.ts +0 -471
  451. package/src/channels/web.ts +0 -638
  452. package/src/channels/webhook.ts +0 -199
  453. package/src/channels/websocket.ts +0 -399
  454. package/src/channels/wechat.ts +0 -329
  455. package/src/channels/whatsapp.ts +0 -33
  456. package/src/cli/chat.ts +0 -99
  457. package/src/cli/setup.ts +0 -314
  458. package/src/cli.ts +0 -2826
  459. package/src/core/a2a.ts +0 -203
  460. package/src/core/agent.ts +0 -476
  461. package/src/core/analytics-engine.ts +0 -186
  462. package/src/core/api-server.ts +0 -277
  463. package/src/core/audio.ts +0 -98
  464. package/src/core/auth.ts +0 -57
  465. package/src/core/cache.ts +0 -141
  466. package/src/core/collaboration.ts +0 -275
  467. package/src/core/compose.ts +0 -77
  468. package/src/core/config.ts +0 -14
  469. package/src/core/context-discovery.ts +0 -85
  470. package/src/core/context-refs.ts +0 -140
  471. package/src/core/errors.ts +0 -148
  472. package/src/core/gateway.ts +0 -106
  473. package/src/core/heartbeat.ts +0 -51
  474. package/src/core/hitl.ts +0 -138
  475. package/src/core/hooks.ts +0 -105
  476. package/src/core/ide-bridge.ts +0 -133
  477. package/src/core/knowledge.ts +0 -255
  478. package/src/core/logger.ts +0 -57
  479. package/src/core/node-network.ts +0 -86
  480. package/src/core/orchestrator.ts +0 -215
  481. package/src/core/performance.ts +0 -187
  482. package/src/core/profiles.ts +0 -122
  483. package/src/core/rate-limiter.ts +0 -128
  484. package/src/core/room.ts +0 -109
  485. package/src/core/runtime.ts +0 -435
  486. package/src/core/sandbox.ts +0 -344
  487. package/src/core/scheduler.ts +0 -187
  488. package/src/core/security.ts +0 -171
  489. package/src/core/session-manager.ts +0 -137
  490. package/src/core/streaming.ts +0 -195
  491. package/src/core/subagent.ts +0 -98
  492. package/src/core/types.ts +0 -68
  493. package/src/core/versioning.ts +0 -106
  494. package/src/core/vision.ts +0 -180
  495. package/src/core/watch.ts +0 -178
  496. package/src/core/workflow-graph.ts +0 -365
  497. package/src/core/workflow.ts +0 -235
  498. package/src/daemon.ts +0 -96
  499. package/src/deploy/hermes.ts +0 -156
  500. package/src/deploy/index.ts +0 -255
  501. package/src/deploy/openclaw.ts +0 -190
  502. package/src/doctor.ts +0 -243
  503. package/src/eval/index.ts +0 -211
  504. package/src/eval/suites/basic.json +0 -16
  505. package/src/eval/suites/memory.json +0 -12
  506. package/src/eval/suites/safety.json +0 -14
  507. package/src/hub/brain-seed.ts +0 -54
  508. package/src/hub/client.ts +0 -60
  509. package/src/i18n/index.ts +0 -216
  510. package/src/index.ts +0 -283
  511. package/src/mcp/servers/calculator-mcp.ts +0 -65
  512. package/src/mcp/servers/crypto-mcp.ts +0 -73
  513. package/src/mcp/servers/database-mcp.ts +0 -72
  514. package/src/mcp/servers/datetime-mcp.ts +0 -69
  515. package/src/mcp/servers/filesystem.ts +0 -66
  516. package/src/mcp/servers/github-mcp.ts +0 -58
  517. package/src/mcp/servers/index.ts +0 -63
  518. package/src/mcp/servers/json-mcp.ts +0 -102
  519. package/src/mcp/servers/memory-mcp.ts +0 -56
  520. package/src/mcp/servers/regex-mcp.ts +0 -53
  521. package/src/mcp/servers/web-mcp.ts +0 -49
  522. package/src/memory/context-compressor.ts +0 -189
  523. package/src/memory/deepbrain.ts +0 -202
  524. package/src/memory/index.ts +0 -41
  525. package/src/memory/seed-loader.ts +0 -212
  526. package/src/memory/user-profiler.ts +0 -215
  527. package/src/plugins/content-filter.ts +0 -23
  528. package/src/plugins/index.ts +0 -339
  529. package/src/plugins/logger.ts +0 -18
  530. package/src/plugins/rate-limiter.ts +0 -38
  531. package/src/protocols/a2a/client.ts +0 -132
  532. package/src/protocols/a2a/index.ts +0 -8
  533. package/src/protocols/a2a/server.ts +0 -333
  534. package/src/protocols/a2a/types.ts +0 -88
  535. package/src/protocols/a2a/utils.ts +0 -50
  536. package/src/protocols/agui/client.ts +0 -83
  537. package/src/protocols/agui/index.ts +0 -4
  538. package/src/protocols/agui/server.ts +0 -218
  539. package/src/protocols/agui/types.ts +0 -153
  540. package/src/protocols/index.ts +0 -2
  541. package/src/protocols/mcp/agent-tools.ts +0 -134
  542. package/src/protocols/mcp/index.ts +0 -8
  543. package/src/protocols/mcp/server.ts +0 -262
  544. package/src/protocols/mcp/types.ts +0 -69
  545. package/src/providers/index.ts +0 -632
  546. package/src/publish/index.ts +0 -376
  547. package/src/scheduler/cron-engine.ts +0 -191
  548. package/src/scheduler/index.ts +0 -2
  549. package/src/schema/oad.ts +0 -217
  550. package/src/security/approval.ts +0 -131
  551. package/src/security/approvals.ts +0 -143
  552. package/src/security/elevated.ts +0 -105
  553. package/src/security/guardrails.ts +0 -248
  554. package/src/security/index.ts +0 -9
  555. package/src/security/keys.ts +0 -87
  556. package/src/security/secrets.ts +0 -129
  557. package/src/skills/auto-learn.ts +0 -262
  558. package/src/skills/base.ts +0 -16
  559. package/src/skills/builtin/index.ts +0 -408
  560. package/src/skills/document.ts +0 -100
  561. package/src/skills/http.ts +0 -35
  562. package/src/skills/index.ts +0 -27
  563. package/src/skills/marketplace.ts +0 -113
  564. package/src/skills/scheduler.ts +0 -80
  565. package/src/skills/types.ts +0 -42
  566. package/src/skills/webhook-trigger.ts +0 -59
  567. package/src/studio/server.ts +0 -1791
  568. package/src/studio/templates-data.ts +0 -178
  569. package/src/studio-ui/index.html +0 -3076
  570. package/src/telemetry/index.ts +0 -324
  571. package/src/templates/code-reviewer.ts +0 -30
  572. package/src/templates/content-writer.ts +0 -58
  573. package/src/templates/customer-service.ts +0 -76
  574. package/src/templates/data-analyst.ts +0 -66
  575. package/src/templates/executive-assistant.ts +0 -71
  576. package/src/templates/financial-advisor.ts +0 -60
  577. package/src/templates/hr-recruiter.ts +0 -58
  578. package/src/templates/knowledge-base.ts +0 -27
  579. package/src/templates/legal-assistant.ts +0 -71
  580. package/src/templates/project-manager.ts +0 -58
  581. package/src/templates/sales-assistant.ts +0 -75
  582. package/src/templates/teacher.ts +0 -75
  583. package/src/testing/index.ts +0 -181
  584. package/src/tools/builtin/browser.ts +0 -299
  585. package/src/tools/builtin/datetime.ts +0 -41
  586. package/src/tools/builtin/file.ts +0 -107
  587. package/src/tools/builtin/home-assistant.ts +0 -116
  588. package/src/tools/builtin/index.ts +0 -37
  589. package/src/tools/builtin/rl-tools.ts +0 -243
  590. package/src/tools/builtin/shell.ts +0 -43
  591. package/src/tools/builtin/vision.ts +0 -64
  592. package/src/tools/builtin/web-search.ts +0 -126
  593. package/src/tools/builtin/web.ts +0 -35
  594. package/src/tools/calculator.ts +0 -73
  595. package/src/tools/datetime.ts +0 -149
  596. package/src/tools/document-processor.ts +0 -213
  597. package/src/tools/gateway.ts +0 -220
  598. package/src/tools/image-generator.ts +0 -150
  599. package/src/tools/integrations/calendar.ts +0 -73
  600. package/src/tools/integrations/code-exec.ts +0 -39
  601. package/src/tools/integrations/csv-analyzer.ts +0 -92
  602. package/src/tools/integrations/database.ts +0 -44
  603. package/src/tools/integrations/email-send.ts +0 -76
  604. package/src/tools/integrations/git-tool.ts +0 -42
  605. package/src/tools/integrations/github-tool.ts +0 -76
  606. package/src/tools/integrations/image-gen.ts +0 -56
  607. package/src/tools/integrations/index.ts +0 -92
  608. package/src/tools/integrations/jira.ts +0 -83
  609. package/src/tools/integrations/notion.ts +0 -71
  610. package/src/tools/integrations/npm-tool.ts +0 -48
  611. package/src/tools/integrations/pdf-reader.ts +0 -58
  612. package/src/tools/integrations/slack.ts +0 -65
  613. package/src/tools/integrations/summarizer.ts +0 -49
  614. package/src/tools/integrations/translator.ts +0 -48
  615. package/src/tools/integrations/trello.ts +0 -60
  616. package/src/tools/integrations/vector-search.ts +0 -42
  617. package/src/tools/integrations/web-scraper.ts +0 -47
  618. package/src/tools/integrations/web-search.ts +0 -58
  619. package/src/tools/integrations/webhook.ts +0 -38
  620. package/src/tools/json-transform.ts +0 -187
  621. package/src/tools/mcp-client.ts +0 -131
  622. package/src/tools/mcp.ts +0 -76
  623. package/src/tools/text-analysis.ts +0 -116
  624. package/src/tools/web-scraper.ts +0 -179
  625. package/src/tools/web-search.ts +0 -180
  626. package/src/traces/index.ts +0 -132
  627. package/src/types/agent-workstation.d.ts +0 -2
  628. package/src/ui/components.ts +0 -127
  629. package/srv-err.txt +0 -0
  630. package/srv-out.txt +0 -1
  631. package/test-agent/Dockerfile +0 -9
  632. package/test-agent/README.md +0 -50
  633. package/test-agent/agent.yaml +0 -23
  634. package/test-agent/docker-compose.yml +0 -11
  635. package/test-agent/oad.yaml +0 -31
  636. package/test-agent/package-lock.json +0 -1492
  637. package/test-agent/package.json +0 -18
  638. package/test-agent/src/index.ts +0 -24
  639. package/test-agent/src/skills/echo.ts +0 -15
  640. package/test-agent/tsconfig.json +0 -25
  641. package/test-full.js +0 -43
  642. package/test-sidebar.js +0 -22
  643. package/test-studio3.js +0 -75
  644. package/test-studio4.js +0 -41
  645. package/tests/a2a-protocol.test.ts +0 -285
  646. package/tests/a2a.test.ts +0 -66
  647. package/tests/agent.test.ts +0 -72
  648. package/tests/agui-protocol.test.ts +0 -246
  649. package/tests/analytics.test.ts +0 -50
  650. package/tests/api-server.test.ts +0 -148
  651. package/tests/approvals.test.ts +0 -89
  652. package/tests/audio.test.ts +0 -40
  653. package/tests/auto-learn.test.ts +0 -105
  654. package/tests/brain-seed-extended.test.ts +0 -490
  655. package/tests/brain-seed.test.ts +0 -239
  656. package/tests/browser.test.ts +0 -179
  657. package/tests/builtin-tools.test.ts +0 -83
  658. package/tests/channel.test.ts +0 -39
  659. package/tests/channels/discord.test.ts +0 -79
  660. package/tests/channels/email.test.ts +0 -148
  661. package/tests/channels/feishu.test.ts +0 -123
  662. package/tests/channels/telegram.test.ts +0 -129
  663. package/tests/channels/websocket.test.ts +0 -53
  664. package/tests/channels/wechat.test.ts +0 -170
  665. package/tests/channels-extra.test.ts +0 -45
  666. package/tests/chat-cli.test.ts +0 -160
  667. package/tests/cli.test.ts +0 -46
  668. package/tests/collaboration.test.ts +0 -319
  669. package/tests/context-compressor.test.ts +0 -172
  670. package/tests/context-refs.test.ts +0 -121
  671. package/tests/cron-engine.test.ts +0 -101
  672. package/tests/daemon.test.ts +0 -135
  673. package/tests/deepbrain-wire.test.ts +0 -234
  674. package/tests/deploy-and-dag.test.ts +0 -196
  675. package/tests/doctor.test.ts +0 -38
  676. package/tests/document-processor.test.ts +0 -69
  677. package/tests/e2e-nocode.test.ts +0 -442
  678. package/tests/e2e.test.ts +0 -134
  679. package/tests/elevated.test.ts +0 -69
  680. package/tests/errors.test.ts +0 -83
  681. package/tests/eval.test.ts +0 -173
  682. package/tests/gateway.test.ts +0 -63
  683. package/tests/guardrails.test.ts +0 -177
  684. package/tests/hitl.test.ts +0 -71
  685. package/tests/home-assistant.test.ts +0 -40
  686. package/tests/hooks.test.ts +0 -79
  687. package/tests/i18n.test.ts +0 -41
  688. package/tests/ide-bridge.test.ts +0 -38
  689. package/tests/image-generator.test.ts +0 -84
  690. package/tests/init-role.test.ts +0 -124
  691. package/tests/integrations.test.ts +0 -249
  692. package/tests/mcp-client.test.ts +0 -92
  693. package/tests/mcp-server.test.ts +0 -178
  694. package/tests/mcp-servers.test.ts +0 -260
  695. package/tests/mcp.test.ts +0 -54
  696. package/tests/node-network.test.ts +0 -74
  697. package/tests/oad.test.ts +0 -68
  698. package/tests/performance.test.ts +0 -115
  699. package/tests/plugin-a2a-enhanced.test.ts +0 -230
  700. package/tests/plugin.test.ts +0 -74
  701. package/tests/profiles.test.ts +0 -61
  702. package/tests/publish.test.ts +0 -231
  703. package/tests/rl-tools.test.ts +0 -93
  704. package/tests/room.test.ts +0 -106
  705. package/tests/runtime.test.ts +0 -42
  706. package/tests/sandbox-manager.test.ts +0 -46
  707. package/tests/sandbox.test.ts +0 -46
  708. package/tests/scheduler.test.ts +0 -200
  709. package/tests/secrets.test.ts +0 -107
  710. package/tests/security-enhanced.test.ts +0 -233
  711. package/tests/security.test.ts +0 -60
  712. package/tests/settings-api.test.ts +0 -148
  713. package/tests/setup.test.ts +0 -73
  714. package/tests/skill-learner.test.ts +0 -161
  715. package/tests/streaming.test.ts +0 -109
  716. package/tests/studio.test.ts +0 -402
  717. package/tests/subagent.test.ts +0 -193
  718. package/tests/telegram-discord.test.ts +0 -60
  719. package/tests/telemetry.test.ts +0 -186
  720. package/tests/templates.test.ts +0 -77
  721. package/tests/tools/builtin-extended.test.ts +0 -138
  722. package/tests/user-profiler.test.ts +0 -169
  723. package/tests/v070.test.ts +0 -76
  724. package/tests/v090-features.test.ts +0 -254
  725. package/tests/versioning.test.ts +0 -75
  726. package/tests/vision.test.ts +0 -61
  727. package/tests/voice-call.test.ts +0 -47
  728. package/tests/voice-enhanced.test.ts +0 -169
  729. package/tests/voice-interaction.test.ts +0 -38
  730. package/tests/voice.test.ts +0 -61
  731. package/tests/web-search.test.ts +0 -155
  732. package/tests/webhook.test.ts +0 -29
  733. package/tests/workflow-graph.test.ts +0 -279
  734. package/tests/workflow.test.ts +0 -143
  735. package/tmp-js-test.js +0 -1532
  736. package/tmp-sc.js +0 -1716
  737. package/tutorial/customer-service-agent/README.md +0 -612
  738. package/tutorial/customer-service-agent/SOUL.md +0 -26
  739. package/tutorial/customer-service-agent/agent.yaml +0 -63
  740. package/tutorial/customer-service-agent/package.json +0 -19
  741. package/tutorial/customer-service-agent/src/index.ts +0 -69
  742. package/tutorial/customer-service-agent/src/skills/faq.ts +0 -27
  743. package/tutorial/customer-service-agent/src/skills/ticket.ts +0 -22
  744. package/tutorial/customer-service-agent/tsconfig.json +0 -14
  745. package/vitest.config.ts +0 -9
package/tmp-sc.js DELETED
@@ -1,1716 +0,0 @@
1
-
2
- // === Debug: catch all JS errors ===
3
- window.onerror = function(msg, url, line, col, err) {
4
- console.error('JS ERROR:', msg, 'at line', line, ':', col);
5
- const d = document.createElement('div');
6
- d.style.cssText = 'position:fixed;bottom:0;left:0;right:0;background:red;color:white;padding:8px;z-index:9999;font-size:12px;';
7
- d.textContent = 'JS Error: ' + msg + ' (line ' + line + ')';
8
- document.body.appendChild(d);
9
- };
10
- // === State ===
11
- let templates = [];
12
- let industries = [];
13
- let agents = [];
14
- let selectedTemplate = null;
15
- let currentAgent = null;
16
- let chatMessages = [];
17
- let wizardStep = 1;
18
- let selectedIndustry = '';
19
- let deleteTargetId = null;
20
-
21
- const API = '';
22
-
23
- // === Init ===
24
- async function init() {
25
- await Promise.all([loadTemplates(), loadAgents()]);
26
- loadSidebarGroups();
27
- handleRoute();
28
- window.addEventListener('popstate', handleRoute);
29
- checkFirstRun();
30
- }
31
-
32
- function handleRoute() {
33
- const path = location.hash.slice(1) || '/dashboard';
34
- const parts = path.split('/').filter(Boolean);
35
- if (parts[0] === 'chat' && parts[1]) {
36
- openChat(parts[1]);
37
- } else if (parts[0] === 'agent' && parts[1]) {
38
- loadSidebarAgents().then(() => navigateToAgent(parts[1]));
39
- } else if (parts[0] === 'settings') {
40
- if (parts[1]) currentSettingsTab = parts[1];
41
- navigate('settings');
42
- } else if (parts[0] === 'memory' && parts[1]) {
43
- openMemoryPage(parts[1]);
44
- } else if (parts[0] === 'create') {
45
- if (parts[1]) {
46
- // pre-select template
47
- selectedTemplate = templates.find(t => t.id === parts[1]) || null;
48
- if (selectedTemplate) {
49
- wizardStep = 2;
50
- }
51
- }
52
- showPage('create');
53
- renderWizard();
54
- } else {
55
- navigate(parts[0] || 'dashboard');
56
- }
57
- }
58
-
59
- // === API ===
60
- async function loadTemplates() {
61
- try {
62
- const res = await fetch(`${API}/api/templates`);
63
- const data = await res.json();
64
- templates = data.templates || [];
65
- industries = data.industries || [];
66
- renderIndustryChips();
67
- renderTemplates();
68
- } catch(e) { console.error('Failed to load templates:', e); }
69
- }
70
-
71
- async function loadAgents() {
72
- try {
73
- const res = await fetch(`${API}/api/agents`);
74
- const data = await res.json();
75
- agents = data.agents || [];
76
- renderAgents();
77
- } catch(e) { console.error('Failed to load agents:', e); }
78
- }
79
-
80
- // === Sidebar Agents ===
81
- let selectedAgentId = null;
82
-
83
- async function loadSidebarAgents() {
84
- try {
85
- const res = await fetch('/api/agents');
86
- const data = await res.json();
87
- const agents = data.agents || data || [];
88
- window._sidebarAgents = agents;
89
- const container = document.getElementById('sidebar-agent-list');
90
- if (!agents.length) {
91
- container.innerHTML = '<div style="padding: 12px 16px; color: var(--text-dim); font-size: 13px;">暂无 Agent</div>';
92
- return;
93
- }
94
- container.innerHTML = agents.map(a => {
95
- const status = (a.status || 'offline').toLowerCase();
96
- const icon = a.emoji || a.icon || '🤖';
97
- const name = a.name || a.id;
98
- return `<div class="agent-list-item${selectedAgentId === a.id ? ' active' : ''}" data-agent-id="${a.id}" onclick="navigateToAgent('${a.id}')">
99
- <span class="agent-icon">${icon}</span>
100
- <span class="agent-name">${name}</span>
101
- <span class="status-dot ${status}"></span>
102
- </div>`;
103
- }).join('');
104
- } catch(e) {
105
- console.error('Failed to load sidebar agents:', e);
106
- const container = document.getElementById('sidebar-agent-list');
107
- if (container) container.innerHTML = '<div style="padding: 12px 16px; color: var(--text-dim); font-size: 13px;">加载失败</div>';
108
- }
109
- }
110
-
111
- // --- Collaboration Groups ---
112
- let selectedPattern = 'debate';
113
- function selectPattern(pat) {
114
- selectedPattern = pat;
115
- document.querySelectorAll('.pattern-card').forEach(c => c.classList.remove('active'));
116
- const el = document.getElementById('pat-' + pat);
117
- if (el) el.classList.add('active');
118
- }
119
- async function loadGroupAgentSelect() {
120
- try {
121
- const res = await fetch('/api/agents');
122
- const data = await res.json();
123
- const agents = data.agents || data || [];
124
- const container = document.getElementById('group-agent-select');
125
- if (!agents.length) {
126
- container.innerHTML = '<p style="color:var(--text-muted);font-size:13px;">请先创建 Agent,再拉入群组</p>';
127
- return;
128
- }
129
- container.innerHTML = agents.map(a => `<label style="display:flex;align-items:center;gap:8px;padding:6px 0;cursor:pointer;"><input type="checkbox" class="group-agent-cb" value="${a.id}"> <span>${a.templateIcon || a.icon || '🤖'}</span> <span style="font-size:14px;">${a.name}</span></label>`).join('');
130
- } catch(e) { console.error('loadGroupAgentSelect error', e); }
131
- }
132
- async function createGroup() {
133
- const name = document.getElementById('group-name').value.trim();
134
- if (!name) { alert('请输入群组名称'); return; }
135
- const members = [...document.querySelectorAll('.group-agent-cb:checked')].map(cb => cb.value);
136
- if (members.length < 2) { alert('至少选择 2 个 Agent'); return; }
137
- // TODO: POST to /api/groups
138
- alert('群组 "' + name + '" 创建成功!模式: ' + selectedPattern + ', 成员: ' + members.length + ' 个 Agent');
139
- navigate('dashboard');
140
- }
141
- async function loadSidebarGroups() {
142
- // TODO: fetch /api/groups and render
143
- const container = document.getElementById('groups-list');
144
- if (container) container.innerHTML = '<div style="padding:6px 16px;color:var(--text-dim);font-size:12px;">暂无群组</div>';
145
- }
146
-
147
- function navigateToAgent(agentId) {
148
- selectedAgentId = agentId;
149
- // Update sidebar active state
150
- document.querySelectorAll('.agent-list-item').forEach(el => el.classList.remove('active'));
151
- document.querySelectorAll('.nav-item').forEach(n => n.classList.remove('active'));
152
- const item = document.querySelector(`.agent-list-item[data-agent-id="${agentId}"]`);
153
- if (item) item.classList.add('active');
154
-
155
- // Find agent data
156
- const agent = (window._sidebarAgents || []).find(a => a.id === agentId) || { id: agentId, name: agentId };
157
- document.getElementById('agent-detail-icon').textContent = agent.emoji || agent.icon || '🤖';
158
- document.getElementById('agent-detail-name').textContent = agent.name || agentId;
159
- const statusDot = document.getElementById('agent-detail-status');
160
- const status = (agent.status || 'offline').toLowerCase();
161
- statusDot.className = 'status-dot ' + status;
162
-
163
- // Reset to chat view
164
- document.getElementById('agent-chat-view').style.display = '';
165
- document.getElementById('agent-settings-view').style.display = 'none';
166
- document.getElementById('agent-detail-toggle').classList.remove('active');
167
- document.getElementById('agent-chat-messages').innerHTML = document.querySelector('.agent-chat-welcome').outerHTML || '<div class="agent-chat-welcome"><div style="font-size:48px;margin-bottom:16px">💬</div><div style="font-size:18px;font-weight:600;margin-bottom:8px">开始对话</div><div style="color:var(--text-muted);font-size:14px">向你的 Agent 发送第一条消息</div></div>';
168
- document.getElementById('agent-chat-input').value = '';
169
-
170
- // Show agent detail page
171
- document.querySelectorAll('.page, .chat-container').forEach(p => { p.classList.remove('active'); p.style.display = ''; });
172
- document.getElementById('page-agent-detail').classList.add('active');
173
- location.hash = `/agent/${agentId}`;
174
- toggleSidebar(false);
175
- }
176
-
177
- function toggleAgentSettings() {
178
- const chatView = document.getElementById('agent-chat-view');
179
- const settingsView = document.getElementById('agent-settings-view');
180
- const toggleBtn = document.getElementById('agent-detail-toggle');
181
- const showSettings = chatView.style.display !== 'none';
182
- chatView.style.display = showSettings ? 'none' : '';
183
- settingsView.style.display = showSettings ? '' : 'none';
184
- toggleBtn.classList.toggle('active', showSettings);
185
- }
186
-
187
- function switchAgentTab(tab) {
188
- document.querySelectorAll('.agent-tab').forEach(t => t.classList.remove('active'));
189
- document.querySelector(`.agent-tab[data-atab="${tab}"]`)?.classList.add('active');
190
- document.querySelectorAll('.agent-tab-panel').forEach(p => p.classList.remove('active'));
191
- document.getElementById(`atab-${tab}`)?.classList.add('active');
192
- }
193
-
194
- let agentChatHistory = [];
195
-
196
- async function sendAgentChat() {
197
- const input = document.getElementById('agent-chat-input');
198
- const msg = input.value.trim();
199
- if (!msg || !selectedAgentId) return;
200
- input.value = '';
201
- input.style.height = 'auto';
202
-
203
- const messagesEl = document.getElementById('agent-chat-messages');
204
- // Remove welcome screen
205
- const welcome = messagesEl.querySelector('.agent-chat-welcome');
206
- if (welcome) welcome.remove();
207
-
208
- // Add user message
209
- const userDiv = document.createElement('div');
210
- userDiv.className = 'agent-chat-msg user';
211
- userDiv.textContent = msg;
212
- messagesEl.appendChild(userDiv);
213
- messagesEl.scrollTop = messagesEl.scrollHeight;
214
-
215
- agentChatHistory.push({ role: 'user', content: msg });
216
-
217
- // Send to API
218
- try {
219
- const res = await fetch(`/api/agents/${selectedAgentId}/chat`, {
220
- method: 'POST',
221
- headers: { 'Content-Type': 'application/json' },
222
- body: JSON.stringify({ message: msg, history: agentChatHistory })
223
- });
224
- const data = await res.json();
225
- const reply = data.reply || data.message || data.content || JSON.stringify(data);
226
- const assistantDiv = document.createElement('div');
227
- assistantDiv.className = 'agent-chat-msg assistant';
228
- assistantDiv.textContent = reply;
229
- messagesEl.appendChild(assistantDiv);
230
- agentChatHistory.push({ role: 'assistant', content: reply });
231
- } catch(e) {
232
- const errDiv = document.createElement('div');
233
- errDiv.className = 'agent-chat-msg assistant';
234
- errDiv.style.borderColor = 'var(--red)';
235
- errDiv.textContent = `⚠️ 发送失败: ${e.message}`;
236
- messagesEl.appendChild(errDiv);
237
- }
238
- messagesEl.scrollTop = messagesEl.scrollHeight;
239
- }
240
-
241
- function handleAgentChatKey(e) {
242
- if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendAgentChat(); }
243
- // Auto-resize textarea
244
- e.target.style.height = 'auto';
245
- e.target.style.height = Math.min(e.target.scrollHeight, 120) + 'px';
246
- }
247
-
248
- // === Navigation ===
249
- function navigate(page) {
250
- document.querySelectorAll('.page, .chat-container').forEach(p => { p.classList.remove('active'); p.style.display = ''; });
251
- document.querySelectorAll('.nav-item').forEach(n => n.classList.remove('active'));
252
- const navItem = document.querySelector(`.nav-item[data-page="${page}"]`);
253
- if (navItem) navItem.classList.add('active');
254
-
255
- if (page === 'dashboard') { loadAgents(); loadHealthDashboard(); loadSidebarAgents(); }
256
- if (page === 'create') { renderWizard(); renderWizardTemplates(); }
257
- if (page === 'settings') { showSettings(currentSettingsTab || 'models'); }
258
- if (page === 'global-runtime') { currentSettingsTab='status'; showSettings('status'); showPage('settings'); return; }
259
- if (page === 'global-models') { currentSettingsTab='models'; showSettings('models'); showPage('settings'); return; }
260
- if (page === 'global-memory') { currentSettingsTab='memory'; showSettings('memory'); showPage('settings'); return; }
261
- if (page === 'global-templates') { navigate('templates'); return; }
262
- if (page === 'create-group') { loadGroupAgentSelect(); }
263
- if (page === 'schedules') { loadSchedules(); }
264
- if (page === 'skills') { loadSkillsMarketplace(); }
265
-
266
- showPage(page);
267
- location.hash = `/${page}`;
268
- toggleSidebar(false);
269
- }
270
-
271
- function showPage(page) {
272
- document.querySelectorAll('.page, .chat-container').forEach(p => { p.classList.remove('active'); });
273
- const el = document.getElementById(`page-${page}`);
274
- if (el) el.classList.add('active');
275
- }
276
-
277
- function toggleSidebar(open) {
278
- document.querySelector('.sidebar').classList.toggle('open', open);
279
- document.querySelector('.sidebar-overlay').classList.toggle('show', open);
280
- }
281
-
282
- // === Templates Rendering ===
283
- function renderIndustryChips() {
284
- const html = `<span class="chip active" onclick="filterByIndustry('')">All</span>` +
285
- industries.map(i => `<span class="chip" onclick="filterByIndustry('${i.id}')">${i.nameZh} ${i.name}</span>`).join('');
286
- document.getElementById('industry-chips').innerHTML = html;
287
- document.getElementById('wizard-industry-chips').innerHTML = html.replace(/filterByIndustry/g, 'filterWizardByIndustry');
288
- }
289
-
290
- function filterByIndustry(id) {
291
- selectedIndustry = id;
292
- document.querySelectorAll('#industry-chips .chip').forEach(c => c.classList.remove('active'));
293
- event.target.classList.add('active');
294
- renderTemplates();
295
- }
296
-
297
- function filterWizardByIndustry(id) {
298
- selectedIndustry = id;
299
- document.querySelectorAll('#wizard-industry-chips .chip').forEach(c => c.classList.remove('active'));
300
- event.target.classList.add('active');
301
- renderWizardTemplates();
302
- }
303
-
304
- function filterTemplates() {
305
- renderTemplates();
306
- }
307
-
308
- // === Skills Marketplace ===
309
- let allSkills = [];
310
- let selectedSkillCategory = '';
311
- const SKILL_CATEGORIES = [
312
- { id: '', label: 'All', labelZh: '全部' },
313
- { id: 'productivity', label: 'Productivity', labelZh: '效率' },
314
- { id: 'knowledge', label: 'Knowledge', labelZh: '知识' },
315
- { id: 'creative', label: 'Creative', labelZh: '创作' },
316
- { id: 'developer', label: 'Developer', labelZh: '开发' },
317
- { id: 'lifestyle', label: 'Lifestyle', labelZh: '生活' },
318
- { id: 'business', label: 'Business', labelZh: '业务' },
319
- ];
320
-
321
- async function loadSkillsMarketplace() {
322
- try {
323
- const res = await fetch('/api/skills/marketplace');
324
- allSkills = await res.json();
325
- } catch(e) { console.error('Failed to load skills:', e); allSkills = []; }
326
- renderSkillCategoryChips();
327
- renderSkills();
328
- }
329
-
330
- function renderSkillCategoryChips() {
331
- const el = document.getElementById('skill-category-chips');
332
- if (!el) return;
333
- el.innerHTML = SKILL_CATEGORIES.map(c =>
334
- `<span class="chip ${selectedSkillCategory === c.id ? 'active' : ''}" onclick="selectSkillCategory('${c.id}')">${c.labelZh}</span>`
335
- ).join('');
336
- }
337
-
338
- function selectSkillCategory(cat) {
339
- selectedSkillCategory = cat;
340
- renderSkillCategoryChips();
341
- renderSkills();
342
- }
343
-
344
- function filterSkills() {
345
- renderSkills();
346
- }
347
-
348
- function renderSkills() {
349
- const q = (document.getElementById('skills-search')?.value || '').toLowerCase();
350
- let filtered = allSkills;
351
- if (selectedSkillCategory) {
352
- filtered = filtered.filter(s => s.category === selectedSkillCategory);
353
- }
354
- if (q) {
355
- filtered = filtered.filter(s =>
356
- s.name.toLowerCase().includes(q) || s.nameZh.includes(q) ||
357
- s.description.toLowerCase().includes(q) || s.descriptionZh.includes(q)
358
- );
359
- }
360
- const grid = document.getElementById('skills-grid');
361
- if (!grid) return;
362
- grid.innerHTML = filtered.map(s => `
363
- <div class="card" style="cursor:default;position:relative;">
364
- <div style="font-size:36px;margin-bottom:8px;">${s.icon}</div>
365
- <div style="font-weight:600;font-size:15px;">${s.nameZh}</div>
366
- <div style="font-size:12px;color:var(--text-dim);margin-bottom:4px;">${s.name}</div>
367
- <div style="font-size:13px;color:var(--text-muted);margin-bottom:12px;line-height:1.4;">${s.descriptionZh}</div>
368
- <div style="display:flex;flex-wrap:wrap;gap:4px;margin-bottom:12px;">
369
- ${s.tools.slice(0,3).map(t => `<span style="font-size:11px;padding:2px 6px;background:var(--bg-hover);border-radius:4px;color:var(--text-dim);">${t}</span>`).join('')}
370
- ${s.tools.length > 3 ? `<span style="font-size:11px;color:var(--text-dim);">+${s.tools.length-3}</span>` : ''}
371
- </div>
372
- ${s.installed
373
- ? `<button class="btn" style="width:100%;background:var(--bg-hover);color:var(--text-muted);cursor:pointer;" onclick="uninstallSkill('${s.id}',this)">✓ Installed</button>`
374
- : `<button class="btn btn-primary" style="width:100%;" onclick="installSkill('${s.id}',this)">Install</button>`
375
- }
376
- </div>
377
- `).join('');
378
- }
379
-
380
- async function installSkill(id, btn) {
381
- btn.disabled = true; btn.textContent = 'Installing...';
382
- try {
383
- const res = await fetch(`/api/skills/marketplace/${id}/install`, { method: 'POST' });
384
- const data = await res.json();
385
- if (data.success) {
386
- const skill = allSkills.find(s => s.id === id);
387
- if (skill) skill.installed = true;
388
- renderSkills();
389
- }
390
- } catch(e) { console.error(e); btn.disabled = false; btn.textContent = 'Install'; }
391
- }
392
-
393
- async function uninstallSkill(id, btn) {
394
- btn.disabled = true; btn.textContent = 'Removing...';
395
- try {
396
- const res = await fetch(`/api/skills/marketplace/${id}/uninstall`, { method: 'DELETE' });
397
- const data = await res.json();
398
- if (data.success) {
399
- const skill = allSkills.find(s => s.id === id);
400
- if (skill) skill.installed = false;
401
- renderSkills();
402
- }
403
- } catch(e) { console.error(e); btn.disabled = false; btn.textContent = '✓ Installed'; }
404
- }
405
-
406
- function filterWizardTemplates() {
407
- renderWizardTemplates();
408
- }
409
-
410
- function getFilteredTemplates(searchId) {
411
- const q = (document.getElementById(searchId)?.value || '').toLowerCase();
412
- return templates.filter(t => {
413
- if (selectedIndustry && t.industry !== selectedIndustry) return false;
414
- if (q && !t.name.toLowerCase().includes(q) && !t.nameZh.includes(q) && !t.description.toLowerCase().includes(q)) return false;
415
- return true;
416
- });
417
- }
418
-
419
- function renderTemplates() {
420
- const filtered = getFilteredTemplates('tpl-search');
421
- document.getElementById('templates-grid').innerHTML = filtered.map(t => `
422
- <div class="card tpl-card" onclick="selectTemplateAndCreate('${t.id}')">
423
- <div class="tpl-icon">${t.icon}</div>
424
- <div class="tpl-name">${t.name}</div>
425
- <div style="font-size:13px;color:var(--text-dim);margin-bottom:6px;">${t.nameZh}</div>
426
- <div class="tpl-desc">${t.description}</div>
427
- <div class="tpl-tags">
428
- <span class="tpl-tag">${t.industryZh}</span>
429
- ${t.tags.map(tag => `<span class="tpl-tag">${tag}</span>`).join('')}
430
- </div>
431
- </div>
432
- `).join('');
433
- }
434
-
435
- function renderWizardTemplates() {
436
- const filtered = getFilteredTemplates('wizard-tpl-search');
437
- document.getElementById('wizard-tpl-grid').innerHTML = filtered.map(t => `
438
- <div class="card tpl-card ${selectedTemplate?.id === t.id ? 'selected' : ''}" onclick="selectWizardTemplate('${t.id}')"
439
- style="${selectedTemplate?.id === t.id ? 'border-color:var(--accent);background:var(--accent-light);' : ''}">
440
- <div class="tpl-icon">${t.icon}</div>
441
- <div class="tpl-name">${t.name}</div>
442
- <div style="font-size:12px;color:var(--text-dim);">${t.nameZh} · ${t.industryZh}</div>
443
- </div>
444
- `).join('');
445
- }
446
-
447
- function selectTemplateAndCreate(id) {
448
- selectedTemplate = templates.find(t => t.id === id);
449
- wizardStep = 2;
450
- navigate('create');
451
- }
452
-
453
- function selectWizardTemplate(id) {
454
- selectedTemplate = templates.find(t => t.id === id);
455
- renderWizardTemplates();
456
- // Auto-advance after selection
457
- setTimeout(() => wizardNext(), 300);
458
- }
459
-
460
- // === Wizard ===
461
- function renderWizard() {
462
- for (let i = 1; i <= 3; i++) {
463
- const ws = document.getElementById(`ws-${i}`);
464
- const wp = document.getElementById(`wp-${i}`);
465
- ws.className = 'wizard-step' + (i < wizardStep ? ' done' : i === wizardStep ? ' active' : '');
466
- wp.className = 'wizard-panel' + (i === wizardStep ? ' active' : '');
467
- }
468
- if (wizardStep === 2 && selectedTemplate) {
469
- document.getElementById('agent-name').placeholder = selectedTemplate.name;
470
- document.getElementById('agent-model').value = selectedTemplate.suggestedModel;
471
- }
472
- if (wizardStep === 3) {
473
- renderConfirmCard();
474
- }
475
- }
476
-
477
- function wizardNext() {
478
- if (wizardStep === 1 && !selectedTemplate) { alert('Please select a template first'); return; }
479
- if (wizardStep < 3) { wizardStep++; renderWizard(); }
480
- }
481
-
482
- function wizardBack() {
483
- if (wizardStep > 1) { wizardStep--; renderWizard(); }
484
- }
485
-
486
- function renderConfirmCard() {
487
- const name = document.getElementById('agent-name').value || selectedTemplate?.name || 'My Agent';
488
- const model = document.getElementById('agent-model').value;
489
- const lang = document.getElementById('agent-lang').selectedOptions[0]?.text || 'English';
490
- document.getElementById('confirm-card').innerHTML = `
491
- <div style="display:flex;align-items:center;gap:16px;margin-bottom:16px;">
492
- <span style="font-size:48px;">${selectedTemplate?.icon || '🤖'}</span>
493
- <div>
494
- <div style="font-size:20px;font-weight:700;">${name}</div>
495
- <div style="color:var(--text-muted);font-size:14px;">Based on: ${selectedTemplate?.name || 'Custom'} (${selectedTemplate?.nameZh || ''})</div>
496
- </div>
497
- </div>
498
- <div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;font-size:14px;">
499
- <div><span style="color:var(--text-dim);">Model:</span> ${model}</div>
500
- <div><span style="color:var(--text-dim);">Language:</span> ${lang}</div>
501
- <div style="grid-column:span 2;"><span style="color:var(--text-dim);">Industry:</span> ${selectedTemplate?.industryZh || ''} (${selectedTemplate?.industry || ''})</div>
502
- </div>
503
- `;
504
- }
505
-
506
- async function createAgent() {
507
- const btn = document.getElementById('create-btn');
508
- btn.textContent = '⏳ Creating...';
509
- btn.disabled = true;
510
- try {
511
- const res = await fetch(`${API}/api/agents`, {
512
- method: 'POST',
513
- headers: { 'Content-Type': 'application/json' },
514
- body: JSON.stringify({
515
- name: document.getElementById('agent-name').value || selectedTemplate?.name,
516
- templateId: selectedTemplate?.id,
517
- description: document.getElementById('agent-desc').value,
518
- model: document.getElementById('agent-model').value,
519
- language: document.getElementById('agent-lang').value,
520
- }),
521
- });
522
- const agent = await res.json();
523
- // Reset wizard
524
- wizardStep = 1;
525
- selectedTemplate = null;
526
- document.getElementById('agent-name').value = '';
527
- document.getElementById('agent-desc').value = '';
528
- // Navigate to chat
529
- openChat(agent.id);
530
- } catch(e) {
531
- alert('Failed to create agent: ' + e.message);
532
- }
533
- btn.textContent = '🚀 Create Agent';
534
- btn.disabled = false;
535
- }
536
-
537
- // === Dashboard ===
538
- function renderAgents() {
539
- if (agents.length === 0) {
540
- document.getElementById('agents-list').style.display = 'none';
541
- document.getElementById('agents-empty').style.display = 'block';
542
- return;
543
- }
544
- document.getElementById('agents-list').style.display = '';
545
- document.getElementById('agents-empty').style.display = 'none';
546
- document.getElementById('agents-list').innerHTML = agents.map(a => {
547
- const timeAgo = getTimeAgo(a.lastActive || a.created);
548
- return `
549
- <div class="card agent-card" onclick="openChat('${a.id}')">
550
- <div class="agent-actions">
551
- <button onclick="event.stopPropagation();openDeleteDialog('${a.id}')">🗑️</button>
552
- </div>
553
- <div class="agent-icon">${a.templateIcon || '🤖'}</div>
554
- <div class="agent-name">${a.name}</div>
555
- <div class="agent-template">${a.templateName || 'Custom'}</div>
556
- <div class="agent-stats">
557
- <span>💬 ${a.messageCount || 0} messages</span>
558
- <span>⏰ ${timeAgo}</span>
559
- </div>
560
- </div>
561
- `;
562
- }).join('');
563
- }
564
-
565
- function getTimeAgo(dateStr) {
566
- const diff = Date.now() - new Date(dateStr).getTime();
567
- const mins = Math.floor(diff / 60000);
568
- if (mins < 1) return 'just now';
569
- if (mins < 60) return `${mins}m ago`;
570
- const hours = Math.floor(mins / 60);
571
- if (hours < 24) return `${hours}h ago`;
572
- const days = Math.floor(hours / 24);
573
- return `${days}d ago`;
574
- }
575
-
576
- // === Delete ===
577
- function openDeleteDialog(id) { deleteTargetId = id; document.getElementById('delete-dialog').classList.add('show'); }
578
- function closeDeleteDialog() { deleteTargetId = null; document.getElementById('delete-dialog').classList.remove('show'); }
579
- async function confirmDelete() {
580
- if (!deleteTargetId) return;
581
- await fetch(`${API}/api/agents/${deleteTargetId}`, { method: 'DELETE' });
582
- closeDeleteDialog();
583
- loadAgents();
584
- }
585
-
586
- // === Chat ===
587
- async function openLastChat() {
588
- if (currentAgent) { openChat(currentAgent.id); return; }
589
- const agentsRes = await fetch(`${API}/api/agents`).catch(() => null);
590
- if (agentsRes) {
591
- const data = await agentsRes.json().catch(() => ({}));
592
- const list = data.agents || [];
593
- if (list.length > 0) { openChat(list[0].id); return; }
594
- }
595
- navigate('dashboard');
596
- }
597
-
598
- async function switchChatAgent(agentId) {
599
- if (agentId && agentId !== currentAgent?.id) openChat(agentId);
600
- }
601
-
602
- async function openChat(agentId) {
603
- try {
604
- const res = await fetch(`${API}/api/agents/${agentId}`);
605
- currentAgent = await res.json();
606
- if (currentAgent.error) { navigate('dashboard'); return; }
607
- } catch { navigate('dashboard'); return; }
608
-
609
- // Load history from localStorage, fallback to empty
610
- const stored = localStorage.getItem(`opc-chat-${agentId}`);
611
- chatMessages = stored ? JSON.parse(stored) : [];
612
-
613
- document.getElementById('chat-agent-icon').textContent = currentAgent.templateIcon || '🤖';
614
- document.getElementById('chat-agent-name').textContent = currentAgent.name;
615
- document.getElementById('chat-agent-status').textContent = `${currentAgent.templateName || 'Custom'} · ${currentAgent.model}`;
616
-
617
- // Populate agent selector
618
- const sel = document.getElementById('chat-agent-select');
619
- sel.innerHTML = agents.map(a => `<option value="${a.id}" ${a.id === agentId ? 'selected' : ''}>${a.templateIcon || '🤖'} ${a.name}</option>`).join('');
620
-
621
- // Render messages
622
- const msgEl = document.getElementById('chat-messages');
623
- if (chatMessages.length > 0) {
624
- msgEl.innerHTML = chatMessages.map(m => `
625
- <div class="msg ${m.role}">
626
- <div class="msg-bubble">${m.content.replace(/</g,'&lt;')}</div>
627
- </div>
628
- `).join('');
629
- } else {
630
- msgEl.innerHTML = `
631
- <div class="msg assistant">
632
- <div class="msg-bubble">Hello! I'm ${currentAgent.name}. ${currentAgent.description ? 'I specialize in: ' + currentAgent.description : 'How can I help you today?'}</div>
633
- </div>
634
- `;
635
- }
636
- document.getElementById('chat-input').value = '';
637
-
638
- showPage('chat');
639
- location.hash = `/chat/${agentId}`;
640
- document.querySelectorAll('.nav-item').forEach(n => n.classList.remove('active'));
641
- const chatNav = document.querySelector('.nav-item[data-page="chat"]');
642
- if (chatNav) chatNav.classList.add('active');
643
- msgEl.scrollTop = msgEl.scrollHeight;
644
- document.getElementById('chat-input').focus();
645
- }
646
-
647
- function clearChat() {
648
- if (!currentAgent) return;
649
- chatMessages = [];
650
- localStorage.removeItem(`opc-chat-${currentAgent.id}`);
651
- document.getElementById('chat-messages').innerHTML = `
652
- <div class="msg assistant">
653
- <div class="msg-bubble">Hello! I'm ${currentAgent.name}. How can I help you today?</div>
654
- </div>
655
- `;
656
- }
657
-
658
- async function handleDocUpload(input) {
659
- const file = input.files[0];
660
- if (!file || !currentAgent) return;
661
- input.value = '';
662
-
663
- // Show uploading status in chat
664
- appendMessage('user', `📎 Uploading: ${file.name}`);
665
- const statusEl = appendMessage('assistant', '⏳ Processing document...');
666
-
667
- try {
668
- const formData = new FormData();
669
- formData.append('file', file);
670
-
671
- const res = await fetch(`${API}/api/agents/${currentAgent.id}/upload`, {
672
- method: 'POST',
673
- body: formData,
674
- });
675
-
676
- const data = await res.json();
677
- if (data.error) {
678
- statusEl.textContent = `❌ ${data.error}`;
679
- } else {
680
- statusEl.textContent = `✅ Learned ${data.learnedCount} knowledge chunks from "${file.name}"`;
681
- }
682
- } catch (e) {
683
- statusEl.textContent = `❌ Upload failed: ${e.message}`;
684
- }
685
- }
686
-
687
- async function sendMessage() {
688
- const input = document.getElementById('chat-input');
689
- const text = input.value.trim();
690
- if (!text || !currentAgent) return;
691
-
692
- input.value = '';
693
- chatMessages.push({ role: 'user', content: text });
694
-
695
- // Render user message
696
- appendMessage('user', text);
697
-
698
- // Show typing + streaming indicator
699
- document.getElementById('typing-indicator').classList.add('show');
700
- document.getElementById('streaming-indicator').style.display = 'inline';
701
- const msgContainer = document.getElementById('chat-messages');
702
- msgContainer.scrollTop = msgContainer.scrollHeight;
703
-
704
- try {
705
- const res = await fetch(`${API}/api/agents/${currentAgent.id}/chat`, {
706
- method: 'POST',
707
- headers: { 'Content-Type': 'application/json' },
708
- body: JSON.stringify({ messages: chatMessages }),
709
- });
710
-
711
- document.getElementById('typing-indicator').classList.remove('show');
712
-
713
- if (res.headers.get('content-type')?.includes('text/event-stream')) {
714
- // SSE streaming
715
- const reader = res.body.getReader();
716
- const decoder = new TextDecoder();
717
- let assistantText = '';
718
- const bubbleEl = appendMessage('assistant', '');
719
-
720
- while (true) {
721
- const { done, value } = await reader.read();
722
- if (done) break;
723
- const chunk = decoder.decode(value);
724
- const lines = chunk.split('\n');
725
- for (const line of lines) {
726
- if (line.startsWith('data: ')) {
727
- const data = line.slice(6);
728
- if (data === '[DONE]') break;
729
- try {
730
- const parsed = JSON.parse(data);
731
- const content = parsed.choices?.[0]?.delta?.content || parsed.content || '';
732
- assistantText += content;
733
- bubbleEl.textContent = assistantText;
734
- msgContainer.scrollTop = msgContainer.scrollHeight;
735
- } catch {}
736
- }
737
- }
738
- }
739
- chatMessages.push({ role: 'assistant', content: assistantText });
740
- } else {
741
- const data = await res.json();
742
- const reply = data.response || data.error || 'No response';
743
- appendMessage('assistant', reply);
744
- chatMessages.push({ role: 'assistant', content: reply });
745
- }
746
- // Persist to localStorage
747
- if (currentAgent) {
748
- try { localStorage.setItem(`opc-chat-${currentAgent.id}`, JSON.stringify(chatMessages.slice(-100))); } catch {}
749
- }
750
- } catch(e) {
751
- document.getElementById('typing-indicator').classList.remove('show');
752
- appendMessage('assistant', `Error: ${e.message}`);
753
- } finally {
754
- document.getElementById('streaming-indicator').style.display = 'none';
755
- }
756
- msgContainer.scrollTop = msgContainer.scrollHeight;
757
- }
758
-
759
- function appendMessage(role, text) {
760
- const msgContainer = document.getElementById('chat-messages');
761
- const time = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
762
- const div = document.createElement('div');
763
- div.className = `msg ${role}`;
764
- const bubble = document.createElement('div');
765
- bubble.className = 'msg-bubble';
766
- bubble.textContent = text;
767
- div.appendChild(bubble);
768
- const timeEl = document.createElement('div');
769
- timeEl.className = 'msg-time';
770
- timeEl.textContent = time;
771
- div.appendChild(timeEl);
772
- msgContainer.appendChild(div);
773
- msgContainer.scrollTop = msgContainer.scrollHeight;
774
- return bubble;
775
- }
776
-
777
- // === Memory ===
778
- function openMemory() {
779
- if (currentAgent) openMemoryPage(currentAgent.id);
780
- }
781
-
782
- async function openMemoryPage(agentId) {
783
- showPage('memory');
784
- location.hash = `/memory/${agentId}`;
785
- try {
786
- const res = await fetch(`${API}/api/agents/${agentId}/memory`);
787
- const data = await res.json();
788
- if (data.entries && data.entries.length > 0) {
789
- document.getElementById('memory-empty').style.display = 'none';
790
- document.getElementById('memory-timeline').innerHTML = `
791
- <div class="timeline">
792
- ${data.entries.map(e => `
793
- <div class="timeline-item">
794
- <div class="timeline-date">${new Date(e.timestamp).toLocaleDateString()} ${new Date(e.timestamp).toLocaleTimeString()}</div>
795
- <div class="timeline-content">${e.summary || e.content || 'Learned something new'}</div>
796
- </div>
797
- `).join('')}
798
- </div>
799
- `;
800
- } else {
801
- document.getElementById('memory-empty').style.display = 'block';
802
- document.getElementById('memory-timeline').innerHTML = '';
803
- }
804
- } catch {
805
- document.getElementById('memory-empty').style.display = 'block';
806
- }
807
- }
808
-
809
- function navigateToChat() {
810
- if (currentAgent) openChat(currentAgent.id);
811
- else navigate('dashboard');
812
- }
813
-
814
- // === Settings ===
815
- let currentSettingsTab = 'models';
816
- let currentProvider = null;
817
- let currentChannel = null;
818
- let modelConfig = {};
819
- let statusRefreshTimer = null;
820
-
821
- function showSettings(tab) {
822
- currentSettingsTab = tab;
823
- document.querySelectorAll('.settings-nav-item').forEach(n => n.classList.remove('active'));
824
- document.querySelector(`.settings-nav-item[data-settings="${tab}"]`)?.classList.add('active');
825
- document.querySelectorAll('.settings-panel').forEach(p => p.classList.remove('active'));
826
- document.getElementById(`sp-${tab}`)?.classList.add('active');
827
-
828
- if (tab === 'models') initModelsPanel();
829
- if (tab === 'channels') initChannelsPanel();
830
- if (tab === 'memory') initMemoryPanel();
831
- if (tab === 'role') initRolePanel();
832
- if (tab === 'status') refreshStatus();
833
- if (tab === 'usage') refreshUsage();
834
- if (tab === 'search') initSearchPanel();
835
- }
836
-
837
- function switchModelTab(tab) {
838
- document.querySelectorAll('#sp-models .tab').forEach(t => t.classList.remove('active'));
839
- document.querySelectorAll('#sp-models .tab-panel').forEach(p => p.classList.remove('active'));
840
- if (tab === 'local') {
841
- document.querySelector('#sp-models .tab:first-child').classList.add('active');
842
- document.getElementById('mt-local').classList.add('active');
843
- } else {
844
- document.querySelector('#sp-models .tab:last-child').classList.add('active');
845
- document.getElementById('mt-cloud').classList.add('active');
846
- }
847
- }
848
-
849
- // --- Models Panel ---
850
- async function initModelsPanel() {
851
- try {
852
- const res = await fetch(`${API}/api/settings/models`);
853
- modelConfig = await res.json();
854
- } catch { modelConfig = {}; }
855
- detectOllama();
856
- updateProviderStatuses();
857
- updateModelDropdowns();
858
- }
859
-
860
- async function detectOllama() {
861
- const statusEl = document.getElementById('ollama-status');
862
- const modelsEl = document.getElementById('ollama-models');
863
- const tutorialEl = document.getElementById('ollama-tutorial');
864
- statusEl.innerHTML = '<div style="display:flex;align-items:center;gap:8px;"><span class="status-dot yellow"></span> 正在检测本地 Ollama...</div>';
865
- try {
866
- const res = await fetch(`${API}/api/settings/models/local`);
867
- const data = await res.json();
868
- if (data.running) {
869
- statusEl.innerHTML = '<div style="display:flex;align-items:center;gap:8px;"><span class="status-dot green"></span> <b>Ollama 运行中</b> — 本地模型可用,完全免费</div>';
870
- tutorialEl.style.display = 'none';
871
- if (data.models && data.models.length > 0) {
872
- modelsEl.innerHTML = '<div class="card"><h3 style="font-size:15px;margin-bottom:12px;">已安装的模型</h3>' +
873
- data.models.map(m => {
874
- const size = m.size ? `${(m.size / 1e9).toFixed(1)}GB` : '';
875
- const isChat = modelConfig.chatModel === m.name;
876
- const isEmbed = modelConfig.embeddingModel === m.name;
877
- const badge = isChat ? ' <span style="color:var(--accent);font-size:11px;">● 聊天</span>' : isEmbed ? ' <span style="color:var(--green);font-size:11px;">● 记忆</span>' : '';
878
- return `<div style="display:flex;justify-content:space-between;align-items:center;padding:8px 0;border-bottom:1px solid var(--border);">
879
- <div><span style="font-weight:500;">${m.name}</span>${badge}</div>
880
- <span style="font-size:12px;color:var(--text-dim);">${size}</span>
881
- </div>`;
882
- }).join('') + '</div>';
883
- // Update dropdowns with local models
884
- updateModelDropdowns(data.models);
885
- } else {
886
- modelsEl.innerHTML = '<div class="card"><p style="color:var(--text-muted);font-size:14px;">Ollama 已运行但没有安装任何模型。请在终端运行:<br><code style="background:var(--bg-hover);padding:2px 8px;border-radius:4px;font-family:var(--mono);">ollama pull qwen2.5:7b</code></p></div>';
887
- }
888
- } else {
889
- statusEl.innerHTML = '<div style="display:flex;align-items:center;gap:8px;"><span class="status-dot red"></span> <b>Ollama 未运行</b> — 按照下面的教程安装</div>';
890
- modelsEl.innerHTML = '';
891
- tutorialEl.style.display = 'block';
892
- }
893
- } catch {
894
- statusEl.innerHTML = '<div style="display:flex;align-items:center;gap:8px;"><span class="status-dot red"></span> 无法检测 Ollama</div>';
895
- tutorialEl.style.display = 'block';
896
- }
897
- }
898
-
899
- function updateModelDropdowns(localModels) {
900
- const chatSel = document.getElementById('cfg-chat-model');
901
- const embedSel = document.getElementById('cfg-embed-model');
902
- if (localModels && localModels.length > 0) {
903
- const chatOpts = localModels.map(m => `<option value="${m.name}" ${modelConfig.chatModel === m.name ? 'selected' : ''}>${m.name}${m.name === 'qwen2.5:7b' ? ' ⭐ 推荐' : ''}</option>`).join('');
904
- const embedOpts = localModels.map(m => `<option value="${m.name}" ${modelConfig.embeddingModel === m.name ? 'selected' : ''}>${m.name}${m.name === 'nomic-embed-text' ? ' ⭐ 推荐' : ''}</option>`).join('');
905
- chatSel.innerHTML = chatOpts;
906
- embedSel.innerHTML = embedOpts;
907
- }
908
- // Add cloud models if configured
909
- const providers = modelConfig.providers || {};
910
- const cloudModels = [];
911
- if (providers.openai?.apiKey) cloudModels.push({name:'gpt-4o',label:'GPT-4o (OpenAI)'},{name:'gpt-4o-mini',label:'GPT-4o Mini (OpenAI)'});
912
- if (providers.deepseek?.apiKey) cloudModels.push({name:'deepseek-chat',label:'DeepSeek V3'},{name:'deepseek-reasoner',label:'DeepSeek R1'});
913
- if (providers.anthropic?.apiKey) cloudModels.push({name:'claude-sonnet-4-20250514',label:'Claude Sonnet (Anthropic)'});
914
- if (providers.openrouter?.apiKey) cloudModels.push({name:'openrouter/auto',label:'OpenRouter Auto'});
915
- cloudModels.forEach(m => {
916
- chatSel.innerHTML += `<option value="${m.name}" ${modelConfig.chatModel === m.name ? 'selected' : ''}>${m.label}</option>`;
917
- });
918
- }
919
-
920
- function updateProviderStatuses() {
921
- const providers = modelConfig.providers || {};
922
- ['openai','deepseek','qwen','anthropic','openrouter'].forEach(p => {
923
- const el = document.getElementById(`pv-${p}`);
924
- if (!el) return;
925
- if (providers[p]?.apiKey) {
926
- el.innerHTML = '<span style="color:var(--green);">✅ 已配置</span>';
927
- el.closest('.provider-card')?.classList.add('configured');
928
- } else {
929
- el.innerHTML = '未配置';
930
- el.closest('.provider-card')?.classList.remove('configured');
931
- }
932
- });
933
- }
934
-
935
- async function saveModelAssignment() {
936
- const chatModel = document.getElementById('cfg-chat-model').value;
937
- const embeddingModel = document.getElementById('cfg-embed-model').value;
938
- try {
939
- await fetch(`${API}/api/settings/models`, {
940
- method: 'PUT', headers: {'Content-Type':'application/json'},
941
- body: JSON.stringify({ chatModel, embeddingModel })
942
- });
943
- modelConfig.chatModel = chatModel;
944
- modelConfig.embeddingModel = embeddingModel;
945
- } catch {}
946
- }
947
-
948
- // --- Provider Dialog ---
949
- const PROVIDER_INFO = {
950
- openai: { name: 'OpenAI', desc: '需要 OpenAI 账号。获取 Key: platform.openai.com/api-keys', placeholder: 'sk-...' },
951
- deepseek: { name: 'DeepSeek', desc: '国产大模型,性价比极高。获取 Key: platform.deepseek.com', placeholder: 'sk-...' },
952
- qwen: { name: '通义千问', desc: '阿里云大模型。获取 Key: dashscope.console.aliyun.com', placeholder: 'sk-...' },
953
- anthropic: { name: 'Anthropic', desc: 'Claude 系列模型。获取 Key: console.anthropic.com', placeholder: 'sk-ant-...' },
954
- openrouter: { name: 'OpenRouter', desc: '100+ 模型聚合平台。获取 Key: openrouter.ai/keys', placeholder: 'sk-or-...' },
955
- };
956
-
957
- function configureProvider(provider) {
958
- currentProvider = provider;
959
- const info = PROVIDER_INFO[provider] || {};
960
- document.getElementById('pd-title').textContent = `配置 ${info.name || provider}`;
961
- document.getElementById('pd-desc').textContent = info.desc || '';
962
- document.getElementById('pd-apikey').placeholder = info.placeholder || 'API Key';
963
- document.getElementById('pd-apikey').value = modelConfig.providers?.[provider]?.apiKey || '';
964
- document.getElementById('pd-baseurl').value = modelConfig.providers?.[provider]?.baseUrl || '';
965
- document.getElementById('pd-test-result').innerHTML = '';
966
- document.getElementById('pd-baseurl-group').style.display = (provider === 'qwen' || provider === 'openrouter') ? 'block' : 'none';
967
- document.getElementById('provider-dialog').classList.add('show');
968
- }
969
- function closeProviderDialog() { document.getElementById('provider-dialog').classList.remove('show'); currentProvider = null; }
970
-
971
- async function testProvider() {
972
- const apiKey = document.getElementById('pd-apikey').value.trim();
973
- const baseUrl = document.getElementById('pd-baseurl').value.trim();
974
- const resultEl = document.getElementById('pd-test-result');
975
- if (!apiKey) { resultEl.innerHTML = '<span style="color:var(--yellow);">请先填入 API Key</span>'; return; }
976
- resultEl.innerHTML = '<span style="color:var(--text-muted);">⏳ 测试中...</span>';
977
- try {
978
- const res = await fetch(`${API}/api/settings/models/test`, {
979
- method: 'POST', headers: {'Content-Type':'application/json'},
980
- body: JSON.stringify({ provider: currentProvider, apiKey, baseUrl: baseUrl || undefined })
981
- });
982
- const data = await res.json();
983
- resultEl.innerHTML = data.success ? '<span style="color:var(--green);">✅ 连接成功!</span>' : `<span style="color:var(--red);">❌ 连接失败 (${data.error || data.statusCode})</span>`;
984
- } catch(e) {
985
- resultEl.innerHTML = `<span style="color:var(--red);">❌ 网络错误</span>`;
986
- }
987
- }
988
-
989
- async function saveProvider() {
990
- const apiKey = document.getElementById('pd-apikey').value.trim();
991
- const baseUrl = document.getElementById('pd-baseurl').value.trim();
992
- if (!modelConfig.providers) modelConfig.providers = {};
993
- modelConfig.providers[currentProvider] = { apiKey, baseUrl: baseUrl || undefined };
994
- try {
995
- await fetch(`${API}/api/settings/models`, {
996
- method: 'PUT', headers: {'Content-Type':'application/json'},
997
- body: JSON.stringify({ providers: modelConfig.providers })
998
- });
999
- } catch {}
1000
- updateProviderStatuses();
1001
- updateModelDropdowns();
1002
- closeProviderDialog();
1003
- }
1004
-
1005
- // --- Channels Panel ---
1006
- const CHANNELS = [
1007
- { id: 'telegram', name: 'Telegram', icon: '✈️', fields: [{key:'botToken',label:'Bot Token',placeholder:'123456:ABC-DEF...',help:'从 @BotFather 获取。<a href="https://t.me/botfather" target="_blank">打开 BotFather →</a>'}] },
1008
- { id: 'wechat', name: '微信', icon: '💬', fields: [], comingSoon: true },
1009
- { id: 'feishu', name: '飞书', icon: '🐦', fields: [{key:'appId',label:'App ID',placeholder:'cli_...'},{key:'appSecret',label:'App Secret',placeholder:'',type:'password'}] },
1010
- { id: 'discord', name: 'Discord', icon: '🎮', fields: [{key:'botToken',label:'Bot Token',placeholder:'',type:'password'}] },
1011
- { id: 'slack', name: 'Slack', icon: '💼', fields: [{key:'botToken',label:'Bot Token',placeholder:'xoxb-...',type:'password'}] },
1012
- { id: 'email', name: 'Email', icon: '📧', fields: [{key:'imapHost',label:'IMAP Host',placeholder:'imap.gmail.com'},{key:'smtpHost',label:'SMTP Host',placeholder:'smtp.gmail.com'},{key:'email',label:'Email',placeholder:'agent@example.com'},{key:'password',label:'Password',placeholder:'',type:'password'}] },
1013
- { id: 'web', name: 'Web', icon: '🌐', fields: [], alwaysOn: true },
1014
- { id: 'whatsapp', name: 'WhatsApp', icon: '📱', fields: [{key:'phoneId',label:'Phone Number ID',placeholder:''},{key:'accessToken',label:'Access Token',placeholder:'',type:'password'}] },
1015
- ];
1016
-
1017
- let channelConfigs = {};
1018
-
1019
- async function initChannelsPanel() {
1020
- try {
1021
- const res = await fetch(`${API}/api/settings/channels`);
1022
- channelConfigs = await res.json();
1023
- } catch { channelConfigs = {}; }
1024
- renderChannels();
1025
- }
1026
-
1027
- function renderChannels() {
1028
- document.getElementById('channels-grid').innerHTML = CHANNELS.map(ch => {
1029
- const cfg = channelConfigs[ch.id] || {};
1030
- const connected = ch.alwaysOn || (cfg && Object.keys(cfg).some(k => k !== 'updated' && cfg[k]));
1031
- const statusDot = ch.comingSoon ? 'yellow' : connected ? 'green' : 'red';
1032
- const statusText = ch.comingSoon ? '即将支持' : connected ? '已连接' : '未配置';
1033
- return `<div class="card channel-card" onclick="${ch.comingSoon ? '' : `configureChannel('${ch.id}')`}" style="${ch.comingSoon ? 'opacity:0.6;cursor:default;' : ''}">
1034
- <div class="ch-icon">${ch.icon}</div>
1035
- <div class="ch-info">
1036
- <div class="ch-name">${ch.name}</div>
1037
- <div class="ch-status"><span class="status-dot ${statusDot}"></span> ${statusText}</div>
1038
- </div>
1039
- ${!ch.comingSoon && !ch.alwaysOn ? '<span style="color:var(--text-dim);font-size:18px;">›</span>' : ''}
1040
- ${ch.alwaysOn ? '<span style="font-size:12px;color:var(--green);">默认开启</span>' : ''}
1041
- </div>`;
1042
- }).join('');
1043
- }
1044
-
1045
- function configureChannel(chId) {
1046
- const ch = CHANNELS.find(c => c.id === chId);
1047
- if (!ch || ch.comingSoon) return;
1048
- if (ch.alwaysOn) return;
1049
- currentChannel = chId;
1050
- const cfg = channelConfigs[chId] || {};
1051
- document.getElementById('cd-title').textContent = `配置 ${ch.name}`;
1052
- document.getElementById('cd-desc').textContent = '';
1053
- document.getElementById('cd-fields').innerHTML = ch.fields.map(f =>
1054
- `<div class="form-group">
1055
- <label class="label">${f.label}</label>
1056
- <input class="input" id="cf-${f.key}" type="${f.type || 'text'}" placeholder="${f.placeholder || ''}" value="${cfg[f.key] || ''}">
1057
- ${f.help ? `<p style="font-size:12px;color:var(--text-dim);margin-top:4px;">${f.help}</p>` : ''}
1058
- </div>`
1059
- ).join('');
1060
- document.getElementById('channel-dialog').classList.add('show');
1061
- }
1062
- function closeChannelDialog() { document.getElementById('channel-dialog').classList.remove('show'); currentChannel = null; }
1063
-
1064
- async function saveChannel() {
1065
- const ch = CHANNELS.find(c => c.id === currentChannel);
1066
- if (!ch) return;
1067
- const cfg = {};
1068
- ch.fields.forEach(f => { cfg[f.key] = document.getElementById(`cf-${f.key}`)?.value?.trim() || ''; });
1069
- try {
1070
- await fetch(`${API}/api/settings/channels/${currentChannel}`, {
1071
- method: 'PUT', headers: {'Content-Type':'application/json'},
1072
- body: JSON.stringify(cfg)
1073
- });
1074
- channelConfigs[currentChannel] = cfg;
1075
- } catch {}
1076
- renderChannels();
1077
- closeChannelDialog();
1078
- }
1079
-
1080
- // --- Memory Panel (DeepBrain iframe) ---
1081
- async function initMemoryPanel() {
1082
- const container = document.getElementById('memory-module-frame');
1083
- const running = await checkModulePort(4001);
1084
- if (running) {
1085
- container.innerHTML = `<div class="module-frame-container"><iframe src="http://localhost:4001" title="DeepBrain 记忆管理"></iframe></div>`;
1086
- } else {
1087
- container.innerHTML = `<div class="card module-frame-fallback">
1088
- <div class="mf-icon">🧠</div>
1089
- <h3 style="margin-bottom:8px;">DeepBrain 未运行</h3>
1090
- <p style="color:var(--text-muted);font-size:14px;margin-bottom:16px;">记忆管理由 DeepBrain 模块提供(端口 4001)</p>
1091
- <a href="http://localhost:4001" target="_blank" class="btn btn-primary">🔗 打开记忆管理</a>
1092
- <p style="color:var(--text-dim);font-size:12px;margin-top:12px;">如果按钮无法打开,请先启动 DeepBrain 服务</p>
1093
- </div>`;
1094
- }
1095
- }
1096
-
1097
- // --- Role Panel (Workstation iframe) ---
1098
- async function initRolePanel() {
1099
- const container = document.getElementById('role-module-frame');
1100
- const running = await checkModulePort(4003);
1101
- if (running) {
1102
- container.innerHTML = `<div class="module-frame-container"><iframe src="http://localhost:4003" title="Workstation 角色编辑"></iframe></div>`;
1103
- } else {
1104
- container.innerHTML = `<div class="card module-frame-fallback">
1105
- <div class="mf-icon">👤</div>
1106
- <h3 style="margin-bottom:8px;">Workstation 未运行</h3>
1107
- <p style="color:var(--text-muted);font-size:14px;margin-bottom:16px;">角色编辑由 Workstation 模块提供(端口 4003)</p>
1108
- <a href="http://localhost:4003" target="_blank" class="btn btn-primary">🔗 打开角色编辑</a>
1109
- <p style="color:var(--text-dim);font-size:12px;margin-top:12px;">如果按钮无法打开,请先启动 Workstation 服务</p>
1110
- </div>`;
1111
- }
1112
- }
1113
-
1114
- async function checkModulePort(port) {
1115
- try {
1116
- const res = await fetch(`${API}/api/modules`);
1117
- const data = await res.json();
1118
- const mod = (data.modules || []).find(m => m.port === port);
1119
- return mod?.running || false;
1120
- } catch { return false; }
1121
- }
1122
-
1123
- // --- Status Panel ---
1124
- async function refreshStatus() {
1125
- try {
1126
- const res = await fetch(`${API}/api/settings/status`);
1127
- const data = await res.json();
1128
-
1129
- // Overview cards
1130
- const upHrs = Math.floor(data.uptime / 3600);
1131
- const upMins = Math.floor((data.uptime % 3600) / 60);
1132
- const memMB = Math.round((data.memory?.rss || 0) / 1048576);
1133
-
1134
- document.getElementById('status-overview').innerHTML = `
1135
- <div class="card-grid" style="margin-bottom:16px;">
1136
- <div class="card stat-card">
1137
- <div style="display:flex;align-items:center;justify-content:center;gap:8px;margin-bottom:8px;">
1138
- <span class="status-dot green"></span><span style="font-size:14px;font-weight:600;">运行中</span>
1139
- </div>
1140
- <div class="stat-value">${upHrs}h ${upMins}m</div>
1141
- <div class="stat-label">运行时间</div>
1142
- </div>
1143
- <div class="card stat-card">
1144
- <div class="stat-value">${memMB} MB</div>
1145
- <div class="stat-label">内存占用</div>
1146
- </div>
1147
- <div class="card stat-card">
1148
- <div class="stat-value">${(data.modules || []).filter(m => m.running).length}/${(data.modules || []).length}</div>
1149
- <div class="stat-label">模块在线</div>
1150
- </div>
1151
- </div>
1152
- <div class="card" style="margin-bottom:16px;">
1153
- <h3 style="font-size:15px;margin-bottom:12px;">模块状态</h3>
1154
- ${(data.modules || []).map(m => `<div style="display:flex;align-items:center;gap:10px;padding:6px 0;">
1155
- <span class="status-dot ${m.running ? 'green' : 'red'}"></span>
1156
- <span>${m.icon} ${m.name}</span>
1157
- <span style="color:var(--text-dim);font-size:12px;margin-left:auto;">:${m.port}</span>
1158
- </div>`).join('')}
1159
- </div>
1160
- `;
1161
-
1162
- // Logs
1163
- const logsEl = document.getElementById('status-logs');
1164
- if (data.logs && data.logs.length > 0) {
1165
- logsEl.textContent = data.logs.join('\n');
1166
- logsEl.scrollTop = logsEl.scrollHeight;
1167
- } else {
1168
- logsEl.textContent = '暂无日志。Agent 运行后日志会显示在这里。';
1169
- }
1170
- } catch {
1171
- document.getElementById('status-overview').innerHTML = '<div class="card"><p style="color:var(--text-muted);">无法获取状态信息</p></div>';
1172
- }
1173
- }
1174
-
1175
- // --- Usage Panel ---
1176
- async function refreshUsage() {
1177
- try {
1178
- const res = await fetch(`${API}/api/settings/usage`);
1179
- const data = await res.json();
1180
- const totalTokens = data.totalTokens || 0;
1181
- const totalCost = data.totalCost || 0;
1182
- const byModel = data.byModel || {};
1183
- const daily = data.daily || [];
1184
-
1185
- document.getElementById('usage-stats').innerHTML = `
1186
- <div class="card-grid" style="margin-bottom:24px;">
1187
- <div class="card stat-card">
1188
- <div class="stat-value">${totalTokens > 1000 ? (totalTokens/1000).toFixed(1) + 'K' : totalTokens}</div>
1189
- <div class="stat-label">总 Token 消耗</div>
1190
- </div>
1191
- <div class="card stat-card">
1192
- <div class="stat-value">$${totalCost.toFixed(4)}</div>
1193
- <div class="stat-label">估算费用</div>
1194
- </div>
1195
- <div class="card stat-card">
1196
- <div class="stat-value">${Object.keys(byModel).length || 0}</div>
1197
- <div class="stat-label">使用模型数</div>
1198
- </div>
1199
- </div>
1200
- ${Object.keys(byModel).length > 0 ? `
1201
- <div class="card" style="margin-bottom:16px;">
1202
- <h3 style="font-size:15px;margin-bottom:12px;">按模型分布</h3>
1203
- ${Object.entries(byModel).map(([m, v]) => `<div style="display:flex;justify-content:space-between;padding:6px 0;border-bottom:1px solid var(--border);">
1204
- <span style="font-size:14px;">${m}</span>
1205
- <span style="font-size:13px;color:var(--text-muted);">${v.tokens || 0} tokens · $${(v.cost || 0).toFixed(4)}</span>
1206
- </div>`).join('')}
1207
- </div>
1208
- ` : ''}
1209
- ${totalTokens === 0 ? `
1210
- <div class="card" style="text-align:center;padding:40px;">
1211
- <div style="font-size:36px;margin-bottom:12px;">📊</div>
1212
- <p style="color:var(--text-muted);">还没有使用记录。开始和 Agent 聊天后,用量数据会自动记录在这里。</p>
1213
- ${modelConfig.mode === 'local' || !modelConfig.mode ? '<p style="color:var(--green);font-size:13px;margin-top:8px;">💡 使用本地模型完全免费,不产生费用</p>' : ''}
1214
- </div>
1215
- ` : ''}
1216
- `;
1217
- } catch {
1218
- document.getElementById('usage-stats').innerHTML = '<div class="card"><p style="color:var(--text-muted);">无法获取用量数据</p></div>';
1219
- }
1220
- }
1221
-
1222
- // === Web Search Settings ===
1223
- async function initSearchPanel() {
1224
- try {
1225
- const res = await fetch(`${API}/api/settings/search`);
1226
- const cfg = await res.json();
1227
- document.getElementById('search-enabled').checked = cfg.enabled !== false;
1228
- document.getElementById('search-engine').value = cfg.defaultEngine || 'duckduckgo';
1229
- updateSearchEngineUI(cfg.defaultEngine || 'duckduckgo');
1230
- if (cfg.engines) {
1231
- const eng = cfg.engines[cfg.defaultEngine];
1232
- if (eng?.apiKey) document.getElementById('search-apikey').value = eng.apiKey;
1233
- if (eng?.baseUrl) document.getElementById('search-baseurl').value = eng.baseUrl;
1234
- }
1235
- } catch { /* defaults are fine */ }
1236
- }
1237
-
1238
- function updateSearchEngineUI(engine) {
1239
- const needsKey = ['brave', 'google'].includes(engine);
1240
- const needsUrl = engine === 'searxng';
1241
- document.getElementById('search-apikey-group').style.display = needsKey ? '' : 'none';
1242
- document.getElementById('search-baseurl-group').style.display = needsUrl ? '' : 'none';
1243
- if (engine === 'brave') document.getElementById('search-apikey-label').textContent = 'Brave Search API Key';
1244
- if (engine === 'google') document.getElementById('search-apikey-label').textContent = 'Google API Key:CX';
1245
- }
1246
-
1247
- async function updateSearchConfig() {
1248
- const engine = document.getElementById('search-engine').value;
1249
- updateSearchEngineUI(engine);
1250
- const cfg = {
1251
- enabled: document.getElementById('search-enabled').checked,
1252
- defaultEngine: engine,
1253
- engines: {}
1254
- };
1255
- cfg.engines[engine] = { enabled: true };
1256
- const apiKey = document.getElementById('search-apikey').value;
1257
- const baseUrl = document.getElementById('search-baseurl').value;
1258
- if (apiKey) cfg.engines[engine].apiKey = apiKey;
1259
- if (baseUrl) cfg.engines[engine].baseUrl = baseUrl;
1260
- cfg.engines.duckduckgo = { enabled: true };
1261
- try {
1262
- await fetch(`${API}/api/settings/search`, {
1263
- method: 'PUT', headers: { 'Content-Type': 'application/json' },
1264
- body: JSON.stringify(cfg)
1265
- });
1266
- } catch { /* silent */ }
1267
- }
1268
-
1269
- async function testSearch() {
1270
- const el = document.getElementById('search-test-result');
1271
- el.innerHTML = '<span style="color:var(--yellow);">🔍 正在搜索...</span>';
1272
- try {
1273
- const res = await fetch(`${API}/api/settings/search/test`, {
1274
- method: 'POST', headers: { 'Content-Type': 'application/json' },
1275
- body: JSON.stringify({ query: 'hello world test' })
1276
- });
1277
- const data = await res.json();
1278
- if (data.success && data.results?.length) {
1279
- el.innerHTML = `<span style="color:var(--green);">✅ 搜索成功!找到 ${data.results.length} 条结果</span><br>` +
1280
- data.results.map(r => `<div style="margin-top:8px;font-size:12px;"><a href="${r.url}" target="_blank">${r.title}</a><br><span style="color:var(--text-muted);">${r.snippet?.slice(0,100)}</span></div>`).join('');
1281
- } else {
1282
- el.innerHTML = `<span style="color:var(--red);">❌ ${data.error || '未找到结果'}</span>`;
1283
- }
1284
- } catch (e) {
1285
- el.innerHTML = `<span style="color:var(--red);">❌ 测试失败: ${e.message}</span>`;
1286
- }
1287
- }
1288
-
1289
- // === Health Dashboard ===
1290
- async function loadHealthDashboard() {
1291
- const el = document.getElementById('health-section');
1292
- if (!el) return;
1293
- try {
1294
- const [modRes, ollamaRes] = await Promise.all([
1295
- fetch(`${API}/api/modules`),
1296
- fetch(`${API}/api/settings/models/local`),
1297
- ]);
1298
- const modData = await modRes.json();
1299
- const ollamaData = await ollamaRes.json();
1300
- const modules = modData.modules || [];
1301
- const runningCount = modules.filter(m => m.running).length;
1302
- el.innerHTML = `
1303
- <div style="display:flex;gap:12px;flex-wrap:wrap;margin-bottom:12px;">
1304
- ${modules.map(m => `
1305
- <div class="card" style="flex:1;min-width:140px;display:flex;align-items:center;gap:10px;padding:12px 14px;">
1306
- <span class="status-dot ${m.running ? 'green' : 'red'}"></span>
1307
- <span style="font-size:13px;">${m.icon} ${m.name}</span>
1308
- <span style="font-size:11px;color:var(--text-dim);margin-left:auto;">:${m.port}</span>
1309
- </div>
1310
- `).join('')}
1311
- <div class="card" style="flex:1;min-width:140px;display:flex;align-items:center;gap:10px;padding:12px 14px;">
1312
- <span class="status-dot ${ollamaData.running ? 'green' : 'red'}"></span>
1313
- <span style="font-size:13px;">🦙 Ollama</span>
1314
- <span style="font-size:11px;color:var(--text-dim);margin-left:auto;">${ollamaData.running ? (ollamaData.models?.length || 0) + ' models' : 'offline'}</span>
1315
- </div>
1316
- </div>
1317
- `;
1318
- } catch {
1319
- el.innerHTML = '';
1320
- }
1321
- }
1322
-
1323
- // === First Run Wizard ===
1324
- let frStep = 1;
1325
- let frSelectedTemplate = null;
1326
- let frCreatedAgentId = null;
1327
-
1328
- async function checkFirstRun() {
1329
- // Skip first-run wizard — agents are configured via init
1330
- return;
1331
- }
1332
-
1333
- function showFirstRunWizard(data) {
1334
- frStep = 1;
1335
- const overlay = document.getElementById('first-run-overlay');
1336
- overlay.style.display = 'flex';
1337
- frRenderStep();
1338
- if (data?.ollamaDetected) {
1339
- const statusEl = document.getElementById('fr-ollama-status');
1340
- if (statusEl) {
1341
- statusEl.innerHTML = `<div style="display:flex;align-items:center;gap:8px;color:var(--green);"><span class="status-dot green"></span> <b>Ollama detected!</b> ${data.ollamaModels?.length ? data.ollamaModels.length + ' models available.' : ''} Local AI is free.</div>`;
1342
- const choiceEl = document.getElementById('fr-model-choice');
1343
- if (choiceEl) choiceEl.style.display = 'block';
1344
- const sel = document.getElementById('fr-model-select');
1345
- if (sel && data.ollamaModels?.length) {
1346
- sel.innerHTML = data.ollamaModels.map(m => `<option value="${m.name}">${m.name} (local)</option>`).join('') + '<option value="gpt-4o-mini">GPT-4o Mini (cloud)</option>';
1347
- }
1348
- }
1349
- } else {
1350
- detectFrOllama();
1351
- }
1352
- }
1353
-
1354
- async function detectFrOllama() {
1355
- try {
1356
- const res = await fetch(`${API}/api/settings/models/local`);
1357
- const data = await res.json();
1358
- const statusEl = document.getElementById('fr-ollama-status');
1359
- const choiceEl = document.getElementById('fr-model-choice');
1360
- if (!statusEl) return;
1361
- if (data.running) {
1362
- statusEl.innerHTML = `<div style="display:flex;align-items:center;gap:8px;color:var(--green);"><span class="status-dot green"></span> <b>Ollama running</b> — free local models available!</div>`;
1363
- if (choiceEl) choiceEl.style.display = 'block';
1364
- const sel = document.getElementById('fr-model-select');
1365
- if (sel && data.models?.length) {
1366
- sel.innerHTML = data.models.map(m => `<option value="${m.name}">${m.name} (local)</option>`).join('') + '<option value="gpt-4o-mini">GPT-4o Mini (cloud)</option>';
1367
- }
1368
- } else {
1369
- statusEl.innerHTML = `<div style="display:flex;align-items:center;gap:8px;"><span class="status-dot red"></span> Ollama not detected — you can use cloud models or <a href="https://ollama.com" target="_blank">install Ollama</a> for free local AI.</div>`;
1370
- if (choiceEl) choiceEl.style.display = 'block';
1371
- }
1372
- } catch {}
1373
- }
1374
-
1375
- function frRenderStep() {
1376
- for (let i = 1; i <= 4; i++) {
1377
- const stepEl = document.getElementById(`fr-step-${i}`);
1378
- const panelEl = document.getElementById(`fr-panel-${i}`);
1379
- if (stepEl) stepEl.className = 'wizard-step' + (i < frStep ? ' done' : i === frStep ? ' active' : '');
1380
- if (panelEl) panelEl.className = 'wizard-panel' + (i === frStep ? ' active' : '');
1381
- }
1382
- }
1383
-
1384
- function frNext() {
1385
- if (frStep === 3 && !frSelectedTemplate) {
1386
- frSelectedTemplate = 'customer-service';
1387
- }
1388
- if (frStep === 3) {
1389
- frStep = 4;
1390
- frRenderStep();
1391
- frCreateAgent();
1392
- return;
1393
- }
1394
- if (frStep < 4) { frStep++; frRenderStep(); }
1395
- }
1396
-
1397
- function frBack() {
1398
- if (frStep > 1) { frStep--; frRenderStep(); }
1399
- }
1400
-
1401
- function frSelectTemplate(id) {
1402
- frSelectedTemplate = id;
1403
- document.querySelectorAll('#fr-template-list .card').forEach(c => {
1404
- c.style.borderColor = '';
1405
- c.style.background = '';
1406
- });
1407
- const el = document.getElementById(`fr-tpl-${id}`);
1408
- if (el) { el.style.borderColor = 'var(--accent)'; el.style.background = 'var(--accent-light)'; }
1409
- }
1410
-
1411
- async function frCreateAgent() {
1412
- const model = document.getElementById('fr-model-select')?.value || 'qwen2.5:7b';
1413
- try {
1414
- // Save first-run complete
1415
- await fetch(`${API}/api/first-run/complete`, {
1416
- method: 'POST',
1417
- headers: { 'Content-Type': 'application/json' },
1418
- body: JSON.stringify({ templateId: frSelectedTemplate, model }),
1419
- });
1420
- // Create the agent
1421
- const res = await fetch(`${API}/api/agents`, {
1422
- method: 'POST',
1423
- headers: { 'Content-Type': 'application/json' },
1424
- body: JSON.stringify({ name: '', templateId: frSelectedTemplate || 'customer-service', model }),
1425
- });
1426
- const agent = await res.json();
1427
- frCreatedAgentId = agent.id;
1428
- document.getElementById('fr-creating').style.display = 'none';
1429
- document.getElementById('fr-done').style.display = 'block';
1430
- await loadAgents();
1431
- } catch(e) {
1432
- document.getElementById('fr-creating').innerHTML = `<div style="color:var(--red);">Error: ${e.message}</div>`;
1433
- }
1434
- }
1435
-
1436
- function frFinish() {
1437
- document.getElementById('first-run-overlay').style.display = 'none';
1438
- if (frCreatedAgentId) openChat(frCreatedAgentId);
1439
- else navigate('dashboard');
1440
- }
1441
-
1442
- // === Drag & drop document upload ===
1443
- const chatArea = document.getElementById('chat-messages');
1444
- if (chatArea) {
1445
- chatArea.addEventListener('dragover', (e) => { e.preventDefault(); chatArea.style.outline = '2px dashed var(--primary)'; });
1446
- chatArea.addEventListener('dragleave', () => { chatArea.style.outline = ''; });
1447
- chatArea.addEventListener('drop', (e) => {
1448
- e.preventDefault();
1449
- chatArea.style.outline = '';
1450
- const file = e.dataTransfer?.files?.[0];
1451
- if (file) {
1452
- const dt = new DataTransfer();
1453
- dt.items.add(file);
1454
- const inp = document.getElementById('doc-upload-input');
1455
- inp.files = dt.files;
1456
- handleDocUpload(inp);
1457
- }
1458
- });
1459
- }
1460
-
1461
- // === Start ===
1462
- init();
1463
-
1464
- // =============================================
1465
- // === Schedules Management ===
1466
- // =============================================
1467
- let editingScheduleId = null;
1468
-
1469
- async function loadSchedules() {
1470
- try {
1471
- const res = await fetch('/api/schedules');
1472
- const tasks = await res.json();
1473
- const list = Array.isArray(tasks) ? tasks : [];
1474
- renderSchedules(list);
1475
- } catch(e) {
1476
- console.error('Failed to load schedules:', e);
1477
- renderSchedules([]);
1478
- }
1479
- }
1480
-
1481
- function renderSchedules(tasks) {
1482
- const listEl = document.getElementById('schedules-list');
1483
- const emptyEl = document.getElementById('schedules-empty');
1484
- if (!tasks.length) {
1485
- listEl.innerHTML = '';
1486
- emptyEl.style.display = '';
1487
- return;
1488
- }
1489
- emptyEl.style.display = 'none';
1490
- listEl.innerHTML = tasks.map(t => `
1491
- <div class="card" style="margin-bottom:12px;display:flex;align-items:center;gap:16px;">
1492
- <div style="font-size:28px;">⏰</div>
1493
- <div style="flex:1;">
1494
- <div style="font-size:15px;font-weight:600;">${esc(t.name)}</div>
1495
- <div style="font-size:12px;color:var(--text-muted);">${esc(t.description || '')}</div>
1496
- <div style="font-size:11px;color:var(--text-dim);margin-top:4px;">
1497
- ${esc(t.schedule)} · ${t.outputChannel || 'web'} · Next: ${t.nextRun ? new Date(t.nextRun).toLocaleString() : 'N/A'}
1498
- </div>
1499
- </div>
1500
- <div style="display:flex;gap:8px;align-items:center;">
1501
- <label style="position:relative;display:inline-block;width:40px;height:22px;cursor:pointer;">
1502
- <input type="checkbox" ${t.enabled ? 'checked' : ''} onchange="toggleSchedule('${t.id}', this.checked)" style="opacity:0;width:0;height:0;">
1503
- <span style="position:absolute;inset:0;border-radius:11px;background:${t.enabled ? 'var(--green)' : 'var(--border)'};transition:0.3s;"></span>
1504
- <span style="position:absolute;top:2px;left:${t.enabled ? '20px' : '2px'};width:18px;height:18px;border-radius:50%;background:white;transition:0.3s;"></span>
1505
- </label>
1506
- <button class="btn btn-sm btn-secondary" onclick="runScheduleNow('${t.id}')" title="Run now">▶️</button>
1507
- <button class="btn btn-sm btn-secondary" onclick="editSchedule('${t.id}')" title="Edit">✏️</button>
1508
- <button class="btn btn-sm btn-danger" onclick="deleteSchedule('${t.id}')" title="Delete">🗑</button>
1509
- </div>
1510
- </div>
1511
- `).join('');
1512
- }
1513
-
1514
- function esc(s) { const d = document.createElement('div'); d.textContent = s; return d.innerHTML; }
1515
-
1516
- function showScheduleForm(task) {
1517
- editingScheduleId = task ? task.id : null;
1518
- document.getElementById('schedule-form').style.display = '';
1519
- document.getElementById('schedule-form-title').textContent = task ? 'Edit Task' : 'New Scheduled Task';
1520
- document.getElementById('sched-name').value = task ? task.name : '';
1521
- document.getElementById('sched-frequency').value = task ? task.frequency : 'daily';
1522
- document.getElementById('sched-time').value = task ? (task.time || '09:00') : '09:00';
1523
- document.getElementById('sched-cron').value = task ? task.schedule : '';
1524
- document.getElementById('sched-desc').value = task ? task.description : '';
1525
- document.getElementById('sched-channel').value = task ? task.outputChannel : 'web';
1526
- onSchedFreqChange();
1527
- }
1528
-
1529
- function hideScheduleForm() {
1530
- document.getElementById('schedule-form').style.display = 'none';
1531
- editingScheduleId = null;
1532
- }
1533
-
1534
- function onSchedFreqChange() {
1535
- const freq = document.getElementById('sched-frequency').value;
1536
- document.getElementById('sched-time-group').style.display = freq === 'custom' ? 'none' : '';
1537
- document.getElementById('sched-cron-group').style.display = freq === 'custom' ? '' : 'none';
1538
- }
1539
-
1540
- async function saveSchedule() {
1541
- const data = {
1542
- name: document.getElementById('sched-name').value.trim(),
1543
- frequency: document.getElementById('sched-frequency').value,
1544
- time: document.getElementById('sched-time').value,
1545
- schedule: document.getElementById('sched-frequency').value === 'custom' ? document.getElementById('sched-cron').value.trim() : '',
1546
- description: document.getElementById('sched-desc').value.trim(),
1547
- outputChannel: document.getElementById('sched-channel').value,
1548
- enabled: true,
1549
- };
1550
- if (!data.name) { alert('Task name is required'); return; }
1551
- try {
1552
- if (editingScheduleId) {
1553
- await fetch(`/api/schedules/${editingScheduleId}`, { method: 'PUT', headers: {'Content-Type':'application/json'}, body: JSON.stringify(data) });
1554
- } else {
1555
- await fetch('/api/schedules', { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify(data) });
1556
- }
1557
- hideScheduleForm();
1558
- loadSchedules();
1559
- } catch(e) { alert('Failed to save: ' + e.message); }
1560
- }
1561
-
1562
- async function toggleSchedule(id, enabled) {
1563
- await fetch(`/api/schedules/${id}`, { method: 'PUT', headers: {'Content-Type':'application/json'}, body: JSON.stringify({ enabled }) });
1564
- loadSchedules();
1565
- }
1566
-
1567
- async function deleteSchedule(id) {
1568
- if (!confirm('Delete this task?')) return;
1569
- await fetch(`/api/schedules/${id}`, { method: 'DELETE' });
1570
- loadSchedules();
1571
- }
1572
-
1573
- async function runScheduleNow(id) {
1574
- await fetch(`/api/schedules/${id}/run`, { method: 'POST' });
1575
- alert('Task executed!');
1576
- loadSchedules();
1577
- }
1578
-
1579
- async function editSchedule(id) {
1580
- const res = await fetch('/api/schedules');
1581
- const tasks = await res.json();
1582
- const list = Array.isArray(tasks) ? tasks : [];
1583
- const task = list.find(t => t.id === id);
1584
- if (task) showScheduleForm(task);
1585
- }
1586
-
1587
- // =============================================
1588
- // === Voice Interaction ===
1589
- // =============================================
1590
- let voiceRecognition = null;
1591
- let isRecording = false;
1592
-
1593
- function toggleVoiceInput() {
1594
- if (isRecording) {
1595
- stopVoiceInput();
1596
- } else {
1597
- startVoiceInput();
1598
- }
1599
- }
1600
-
1601
- function startVoiceInput() {
1602
- const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
1603
- if (!SpeechRecognition) {
1604
- alert('Speech recognition is not supported in this browser. Try Chrome.');
1605
- return;
1606
- }
1607
- voiceRecognition = new SpeechRecognition();
1608
- voiceRecognition.continuous = false;
1609
- voiceRecognition.interimResults = true;
1610
- voiceRecognition.lang = navigator.language || 'en-US';
1611
-
1612
- voiceRecognition.onstart = () => {
1613
- isRecording = true;
1614
- const btn = document.getElementById('voice-btn');
1615
- btn.style.background = 'var(--red)';
1616
- btn.style.color = 'white';
1617
- btn.style.borderColor = 'var(--red)';
1618
- btn.textContent = '⏹';
1619
- };
1620
-
1621
- voiceRecognition.onresult = (event) => {
1622
- let transcript = '';
1623
- for (let i = event.resultIndex; i < event.results.length; i++) {
1624
- transcript += event.results[i][0].transcript;
1625
- }
1626
- document.getElementById('chat-input').value = transcript;
1627
- };
1628
-
1629
- voiceRecognition.onend = () => {
1630
- isRecording = false;
1631
- const btn = document.getElementById('voice-btn');
1632
- btn.style.background = 'transparent';
1633
- btn.style.color = '';
1634
- btn.style.borderColor = 'var(--border)';
1635
- btn.textContent = '🎤';
1636
- // Auto-send if we got text
1637
- const input = document.getElementById('chat-input');
1638
- if (input.value.trim()) {
1639
- sendMessage();
1640
- }
1641
- };
1642
-
1643
- voiceRecognition.onerror = (event) => {
1644
- console.error('Speech recognition error:', event.error);
1645
- isRecording = false;
1646
- const btn = document.getElementById('voice-btn');
1647
- btn.style.background = 'transparent';
1648
- btn.style.color = '';
1649
- btn.style.borderColor = 'var(--border)';
1650
- btn.textContent = '🎤';
1651
- };
1652
-
1653
- voiceRecognition.start();
1654
- }
1655
-
1656
- function stopVoiceInput() {
1657
- if (voiceRecognition) {
1658
- voiceRecognition.stop();
1659
- }
1660
- }
1661
-
1662
- function speakText(text) {
1663
- if (!window.speechSynthesis) return;
1664
- window.speechSynthesis.cancel();
1665
- const utterance = new SpeechSynthesisUtterance(text);
1666
- utterance.lang = navigator.language || 'en-US';
1667
- utterance.rate = 1.0;
1668
- window.speechSynthesis.speak(utterance);
1669
- }
1670
-
1671
- // Patch message rendering to add TTS button to assistant messages
1672
- const _origAppendMsg = typeof appendMessage === 'function' ? appendMessage : null;
1673
- if (typeof window._patchedMsgRender === 'undefined') {
1674
- window._patchedMsgRender = true;
1675
- const observer = new MutationObserver((mutations) => {
1676
- for (const m of mutations) {
1677
- for (const node of m.addedNodes) {
1678
- if (node.nodeType === 1 && node.classList?.contains('msg') && node.classList?.contains('assistant')) {
1679
- const bubble = node.querySelector('.msg-bubble');
1680
- if (bubble && !node.querySelector('.tts-btn')) {
1681
- const btn = document.createElement('button');
1682
- btn.className = 'tts-btn';
1683
- btn.textContent = '🔊';
1684
- btn.title = 'Read aloud';
1685
- btn.style.cssText = 'background:none;border:1px solid var(--border);border-radius:50%;padding:4px 6px;cursor:pointer;font-size:14px;margin-top:4px;color:var(--text-muted);';
1686
- btn.onclick = () => speakText(bubble.textContent);
1687
- node.appendChild(btn);
1688
- }
1689
- }
1690
- }
1691
- }
1692
- });
1693
- const chatMsgs = document.getElementById('chat-messages');
1694
- if (chatMsgs) observer.observe(chatMsgs, { childList: true });
1695
- }
1696
-
1697
- // =============================================
1698
- // === Image Generation Config ===
1699
- // =============================================
1700
- async function saveImageGenConfig() {
1701
- const data = {
1702
- openaiApiKey: document.getElementById('ig-openai-key').value.trim(),
1703
- sdApiUrl: document.getElementById('ig-sd-url').value.trim(),
1704
- replicateApiKey: document.getElementById('ig-replicate-key').value.trim(),
1705
- };
1706
- try {
1707
- await fetch('/api/image-gen/config', { method: 'PUT', headers: {'Content-Type':'application/json'}, body: JSON.stringify(data) });
1708
- document.getElementById('ig-status').textContent = '✅ Configuration saved!';
1709
- document.getElementById('ig-status').style.color = 'var(--green)';
1710
- } catch(e) {
1711
- document.getElementById('ig-status').textContent = '❌ Failed: ' + e.message;
1712
- document.getElementById('ig-status').style.color = 'var(--red)';
1713
- }
1714
- }
1715
-
1716
-