myaiforone 1.0.0

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 (315) hide show
  1. package/README.md +113 -0
  2. package/agents/_template/CLAUDE.md +18 -0
  3. package/agents/_template/agent.json +7 -0
  4. package/agents/platform/agentcreator/CLAUDE.md +300 -0
  5. package/agents/platform/appcreator/CLAUDE.md +158 -0
  6. package/agents/platform/gym/CLAUDE.md +486 -0
  7. package/agents/platform/gym/agent.json +40 -0
  8. package/agents/platform/gym/programs/agent-building/program.json +160 -0
  9. package/agents/platform/gym/programs/automations-mastery/program.json +129 -0
  10. package/agents/platform/gym/programs/getting-started/program.json +124 -0
  11. package/agents/platform/gym/programs/mcp-integrations/program.json +116 -0
  12. package/agents/platform/gym/programs/multi-model-strategy/program.json +115 -0
  13. package/agents/platform/gym/programs/prompt-engineering/program.json +136 -0
  14. package/agents/platform/gym/souls/alex.md +12 -0
  15. package/agents/platform/gym/souls/jordan.md +12 -0
  16. package/agents/platform/gym/souls/morgan.md +12 -0
  17. package/agents/platform/gym/souls/riley.md +12 -0
  18. package/agents/platform/gym/souls/sam.md +12 -0
  19. package/agents/platform/hub/CLAUDE.md +372 -0
  20. package/agents/platform/promptcreator/CLAUDE.md +130 -0
  21. package/agents/platform/skillcreator/CLAUDE.md +163 -0
  22. package/bin/cli.js +566 -0
  23. package/config.example.json +310 -0
  24. package/dist/agent-registry.d.ts +32 -0
  25. package/dist/agent-registry.d.ts.map +1 -0
  26. package/dist/agent-registry.js +144 -0
  27. package/dist/agent-registry.js.map +1 -0
  28. package/dist/channels/discord.d.ts +17 -0
  29. package/dist/channels/discord.d.ts.map +1 -0
  30. package/dist/channels/discord.js +114 -0
  31. package/dist/channels/discord.js.map +1 -0
  32. package/dist/channels/imessage.d.ts +23 -0
  33. package/dist/channels/imessage.d.ts.map +1 -0
  34. package/dist/channels/imessage.js +214 -0
  35. package/dist/channels/imessage.js.map +1 -0
  36. package/dist/channels/slack.d.ts +19 -0
  37. package/dist/channels/slack.d.ts.map +1 -0
  38. package/dist/channels/slack.js +167 -0
  39. package/dist/channels/slack.js.map +1 -0
  40. package/dist/channels/telegram.d.ts +19 -0
  41. package/dist/channels/telegram.d.ts.map +1 -0
  42. package/dist/channels/telegram.js +274 -0
  43. package/dist/channels/telegram.js.map +1 -0
  44. package/dist/channels/types.d.ts +44 -0
  45. package/dist/channels/types.d.ts.map +1 -0
  46. package/dist/channels/types.js +18 -0
  47. package/dist/channels/types.js.map +1 -0
  48. package/dist/channels/whatsapp.d.ts +23 -0
  49. package/dist/channels/whatsapp.d.ts.map +1 -0
  50. package/dist/channels/whatsapp.js +189 -0
  51. package/dist/channels/whatsapp.js.map +1 -0
  52. package/dist/config.d.ts +134 -0
  53. package/dist/config.d.ts.map +1 -0
  54. package/dist/config.js +127 -0
  55. package/dist/config.js.map +1 -0
  56. package/dist/cron.d.ts +8 -0
  57. package/dist/cron.d.ts.map +1 -0
  58. package/dist/cron.js +35 -0
  59. package/dist/cron.js.map +1 -0
  60. package/dist/decrypt-keys.d.ts +7 -0
  61. package/dist/decrypt-keys.d.ts.map +1 -0
  62. package/dist/decrypt-keys.js +53 -0
  63. package/dist/decrypt-keys.js.map +1 -0
  64. package/dist/encrypt-keys.d.ts +8 -0
  65. package/dist/encrypt-keys.d.ts.map +1 -0
  66. package/dist/encrypt-keys.js +62 -0
  67. package/dist/encrypt-keys.js.map +1 -0
  68. package/dist/executor.d.ts +31 -0
  69. package/dist/executor.d.ts.map +1 -0
  70. package/dist/executor.js +2009 -0
  71. package/dist/executor.js.map +1 -0
  72. package/dist/gemini-executor.d.ts +27 -0
  73. package/dist/gemini-executor.d.ts.map +1 -0
  74. package/dist/gemini-executor.js +160 -0
  75. package/dist/gemini-executor.js.map +1 -0
  76. package/dist/goals.d.ts +24 -0
  77. package/dist/goals.d.ts.map +1 -0
  78. package/dist/goals.js +189 -0
  79. package/dist/goals.js.map +1 -0
  80. package/dist/gym/activity-digest.d.ts +30 -0
  81. package/dist/gym/activity-digest.d.ts.map +1 -0
  82. package/dist/gym/activity-digest.js +506 -0
  83. package/dist/gym/activity-digest.js.map +1 -0
  84. package/dist/gym/dimension-scorer.d.ts +76 -0
  85. package/dist/gym/dimension-scorer.d.ts.map +1 -0
  86. package/dist/gym/dimension-scorer.js +236 -0
  87. package/dist/gym/dimension-scorer.js.map +1 -0
  88. package/dist/gym/gym-router.d.ts +7 -0
  89. package/dist/gym/gym-router.d.ts.map +1 -0
  90. package/dist/gym/gym-router.js +718 -0
  91. package/dist/gym/gym-router.js.map +1 -0
  92. package/dist/gym/index.d.ts +11 -0
  93. package/dist/gym/index.d.ts.map +1 -0
  94. package/dist/gym/index.js +11 -0
  95. package/dist/gym/index.js.map +1 -0
  96. package/dist/heartbeat.d.ts +21 -0
  97. package/dist/heartbeat.d.ts.map +1 -0
  98. package/dist/heartbeat.js +163 -0
  99. package/dist/heartbeat.js.map +1 -0
  100. package/dist/index.d.ts +2 -0
  101. package/dist/index.d.ts.map +1 -0
  102. package/dist/index.js +254 -0
  103. package/dist/index.js.map +1 -0
  104. package/dist/keystore.d.ts +22 -0
  105. package/dist/keystore.d.ts.map +1 -0
  106. package/dist/keystore.js +178 -0
  107. package/dist/keystore.js.map +1 -0
  108. package/dist/logger.d.ts +9 -0
  109. package/dist/logger.d.ts.map +1 -0
  110. package/dist/logger.js +45 -0
  111. package/dist/logger.js.map +1 -0
  112. package/dist/memory/daily.d.ts +22 -0
  113. package/dist/memory/daily.d.ts.map +1 -0
  114. package/dist/memory/daily.js +82 -0
  115. package/dist/memory/daily.js.map +1 -0
  116. package/dist/memory/embeddings.d.ts +15 -0
  117. package/dist/memory/embeddings.d.ts.map +1 -0
  118. package/dist/memory/embeddings.js +154 -0
  119. package/dist/memory/embeddings.js.map +1 -0
  120. package/dist/memory/index.d.ts +32 -0
  121. package/dist/memory/index.d.ts.map +1 -0
  122. package/dist/memory/index.js +159 -0
  123. package/dist/memory/index.js.map +1 -0
  124. package/dist/memory/search.d.ts +21 -0
  125. package/dist/memory/search.d.ts.map +1 -0
  126. package/dist/memory/search.js +77 -0
  127. package/dist/memory/search.js.map +1 -0
  128. package/dist/memory/store.d.ts +23 -0
  129. package/dist/memory/store.d.ts.map +1 -0
  130. package/dist/memory/store.js +144 -0
  131. package/dist/memory/store.js.map +1 -0
  132. package/dist/ollama-executor.d.ts +17 -0
  133. package/dist/ollama-executor.d.ts.map +1 -0
  134. package/dist/ollama-executor.js +112 -0
  135. package/dist/ollama-executor.js.map +1 -0
  136. package/dist/openai-executor.d.ts +38 -0
  137. package/dist/openai-executor.d.ts.map +1 -0
  138. package/dist/openai-executor.js +197 -0
  139. package/dist/openai-executor.js.map +1 -0
  140. package/dist/router.d.ts +11 -0
  141. package/dist/router.d.ts.map +1 -0
  142. package/dist/router.js +185 -0
  143. package/dist/router.js.map +1 -0
  144. package/dist/test-message.d.ts +2 -0
  145. package/dist/test-message.d.ts.map +1 -0
  146. package/dist/test-message.js +60 -0
  147. package/dist/test-message.js.map +1 -0
  148. package/dist/utils/imsg-db-reader.d.ts +24 -0
  149. package/dist/utils/imsg-db-reader.d.ts.map +1 -0
  150. package/dist/utils/imsg-db-reader.js +92 -0
  151. package/dist/utils/imsg-db-reader.js.map +1 -0
  152. package/dist/utils/imsg-rpc.d.ts +25 -0
  153. package/dist/utils/imsg-rpc.d.ts.map +1 -0
  154. package/dist/utils/imsg-rpc.js +149 -0
  155. package/dist/utils/imsg-rpc.js.map +1 -0
  156. package/dist/utils/message-formatter.d.ts +3 -0
  157. package/dist/utils/message-formatter.d.ts.map +1 -0
  158. package/dist/utils/message-formatter.js +69 -0
  159. package/dist/utils/message-formatter.js.map +1 -0
  160. package/dist/web-ui.d.ts +12 -0
  161. package/dist/web-ui.d.ts.map +1 -0
  162. package/dist/web-ui.js +5784 -0
  163. package/dist/web-ui.js.map +1 -0
  164. package/dist/whatsapp-chats.d.ts +2 -0
  165. package/dist/whatsapp-chats.d.ts.map +1 -0
  166. package/dist/whatsapp-chats.js +76 -0
  167. package/dist/whatsapp-chats.js.map +1 -0
  168. package/dist/whatsapp-login.d.ts +2 -0
  169. package/dist/whatsapp-login.d.ts.map +1 -0
  170. package/dist/whatsapp-login.js +90 -0
  171. package/dist/whatsapp-login.js.map +1 -0
  172. package/dist/wiki-sync.d.ts +21 -0
  173. package/dist/wiki-sync.d.ts.map +1 -0
  174. package/dist/wiki-sync.js +147 -0
  175. package/dist/wiki-sync.js.map +1 -0
  176. package/docs/AddNewAgentGuide.md +100 -0
  177. package/docs/AddNewMcpGuide.md +72 -0
  178. package/docs/Architecture.md +795 -0
  179. package/docs/CLAUDE-AI-SETUP.md +166 -0
  180. package/docs/Setup.md +297 -0
  181. package/docs/ai-gym-architecture.md +1040 -0
  182. package/docs/ai-gym-build-plan.md +343 -0
  183. package/docs/ai-gym-onboarding.md +122 -0
  184. package/docs/appcreator_plan.md +348 -0
  185. package/docs/platform-mcp-audit.md +320 -0
  186. package/docs/server-deployment-plan.md +503 -0
  187. package/docs/superpowers/plans/2026-03-25-marketplace.md +1281 -0
  188. package/docs/superpowers/specs/2026-03-25-marketplace-design.md +287 -0
  189. package/docs/user-guide.md +2016 -0
  190. package/mcp-catalog.json +628 -0
  191. package/package.json +63 -0
  192. package/public/MyAIforOne-logomark-512.svg +16 -0
  193. package/public/MyAIforOne-logomark-transparent.svg +15 -0
  194. package/public/activity.html +314 -0
  195. package/public/admin.html +1674 -0
  196. package/public/agent-dashboard.html +670 -0
  197. package/public/api-docs.html +1106 -0
  198. package/public/automations.html +722 -0
  199. package/public/canvas.css +223 -0
  200. package/public/canvas.js +588 -0
  201. package/public/changelog.html +231 -0
  202. package/public/gym.html +2766 -0
  203. package/public/home.html +1930 -0
  204. package/public/index.html +2809 -0
  205. package/public/lab.html +1643 -0
  206. package/public/library.html +1442 -0
  207. package/public/marketplace.html +1101 -0
  208. package/public/mcp-docs.html +441 -0
  209. package/public/mini.html +390 -0
  210. package/public/monitor.html +584 -0
  211. package/public/org.html +4304 -0
  212. package/public/projects.html +734 -0
  213. package/public/settings.html +645 -0
  214. package/public/tasks.html +932 -0
  215. package/public/trainers/alex.svg +12 -0
  216. package/public/trainers/jordan.svg +12 -0
  217. package/public/trainers/morgan.svg +12 -0
  218. package/public/trainers/riley.svg +12 -0
  219. package/public/trainers/sam.svg +12 -0
  220. package/public/user-guide.html +218 -0
  221. package/registry/agents.json +3 -0
  222. package/registry/apps.json +20 -0
  223. package/registry/installed-drafts.json +3 -0
  224. package/registry/mcps.json +1084 -0
  225. package/registry/prompts/personal/mcp-test-prompt.md +6 -0
  226. package/registry/prompts/personal/memory-recall.md +6 -0
  227. package/registry/prompts/platform/brainstorm.md +15 -0
  228. package/registry/prompts/platform/code-review.md +16 -0
  229. package/registry/prompts/platform/explain.md +16 -0
  230. package/registry/prompts.json +58 -0
  231. package/registry/skills/external/brainstorming.md +5 -0
  232. package/registry/skills/external/code-review.md +40 -0
  233. package/registry/skills/external/frontend-patterns.md +642 -0
  234. package/registry/skills/external/frontend-slides.md +184 -0
  235. package/registry/skills/external/systematic-debugging.md +5 -0
  236. package/registry/skills/external/tdd.md +328 -0
  237. package/registry/skills/external/verification-before-completion.md +5 -0
  238. package/registry/skills/external/writing-plans.md +5 -0
  239. package/registry/skills/platform/ai41_app_build.md +930 -0
  240. package/registry/skills/platform/ai41_app_deploy.md +168 -0
  241. package/registry/skills/platform/ai41_app_orchestrator.md +239 -0
  242. package/registry/skills/platform/ai41_app_patterns.md +359 -0
  243. package/registry/skills/platform/ai41_app_register.md +85 -0
  244. package/registry/skills/platform/ai41_app_scaffold.md +421 -0
  245. package/registry/skills/platform/ai41_app_verify.md +107 -0
  246. package/registry/skills/platform/opProjectCreate.md +239 -0
  247. package/registry/skills/platform/op_devbrowser.md +136 -0
  248. package/registry/skills/platform/sop_brandguidelines.md +103 -0
  249. package/registry/skills/platform/sop_docx.md +117 -0
  250. package/registry/skills/platform/sop_frontenddesign.md +44 -0
  251. package/registry/skills/platform/sop_frontenddesign_v2.md +659 -0
  252. package/registry/skills/platform/sop_mcpbuilder.md +133 -0
  253. package/registry/skills/platform/sop_pdf.md +172 -0
  254. package/registry/skills/platform/sop_pptx.md +133 -0
  255. package/registry/skills/platform/sop_skillcreator.md +104 -0
  256. package/registry/skills/platform/sop_themefactory.md +128 -0
  257. package/registry/skills/platform/sop_webapptesting.md +75 -0
  258. package/registry/skills/platform/sop_webartifactsbuilder.md +97 -0
  259. package/registry/skills/platform/sop_xlsx.md +134 -0
  260. package/registry/skills.json +1055 -0
  261. package/scripts/discover-chats.sh +11 -0
  262. package/scripts/install-service-windows.ps1 +87 -0
  263. package/scripts/install-service.sh +52 -0
  264. package/scripts/seed-registry.ts +195 -0
  265. package/scripts/test-send.sh +5 -0
  266. package/scripts/tray-indicator.ps1 +35 -0
  267. package/scripts/uninstall-service-windows.ps1 +23 -0
  268. package/scripts/uninstall-service.sh +15 -0
  269. package/scripts/xbar-myagent.5s.sh +32 -0
  270. package/server/mcp-server/dist/index.d.ts +11 -0
  271. package/server/mcp-server/dist/index.js +1332 -0
  272. package/server/mcp-server/dist/lib/api-client.d.ts +165 -0
  273. package/server/mcp-server/dist/lib/api-client.js +241 -0
  274. package/server/mcp-server/index.ts +1545 -0
  275. package/server/mcp-server/lib/api-client.ts +366 -0
  276. package/server/mcp-server/tsconfig.json +14 -0
  277. package/src/agent-registry.ts +180 -0
  278. package/src/channels/discord.ts +129 -0
  279. package/src/channels/imessage.ts +261 -0
  280. package/src/channels/slack.ts +208 -0
  281. package/src/channels/telegram.ts +307 -0
  282. package/src/channels/types.ts +62 -0
  283. package/src/channels/whatsapp.ts +227 -0
  284. package/src/config.ts +281 -0
  285. package/src/cron.ts +43 -0
  286. package/src/decrypt-keys.ts +60 -0
  287. package/src/encrypt-keys.ts +70 -0
  288. package/src/executor.ts +2190 -0
  289. package/src/gemini-executor.ts +212 -0
  290. package/src/goals.ts +240 -0
  291. package/src/gym/activity-digest.ts +546 -0
  292. package/src/gym/dimension-scorer.ts +297 -0
  293. package/src/gym/gym-router.ts +801 -0
  294. package/src/gym/index.ts +19 -0
  295. package/src/heartbeat.ts +220 -0
  296. package/src/index.ts +275 -0
  297. package/src/keystore.ts +190 -0
  298. package/src/logger.ts +51 -0
  299. package/src/memory/daily.ts +101 -0
  300. package/src/memory/embeddings.ts +185 -0
  301. package/src/memory/index.ts +218 -0
  302. package/src/memory/search.ts +124 -0
  303. package/src/memory/store.ts +189 -0
  304. package/src/ollama-executor.ts +126 -0
  305. package/src/openai-executor.ts +259 -0
  306. package/src/router.ts +230 -0
  307. package/src/test-message.ts +72 -0
  308. package/src/utils/imsg-db-reader.ts +109 -0
  309. package/src/utils/imsg-rpc.ts +178 -0
  310. package/src/utils/message-formatter.ts +90 -0
  311. package/src/web-ui.ts +5778 -0
  312. package/src/whatsapp-chats.ts +91 -0
  313. package/src/whatsapp-login.ts +110 -0
  314. package/src/wiki-sync.ts +199 -0
  315. package/tsconfig.json +19 -0
@@ -0,0 +1,722 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>MyAIforOne — Automations</title>
7
+ <link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500;600&family=Syne:wght@600;700;800&display=swap" rel="stylesheet">
8
+ <style>
9
+ *,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
10
+ :root{
11
+ --bg-deep:#060a13;--bg-surface:rgba(12,18,33,0.92);--bg-card:rgba(16,22,40,0.85);
12
+ --bg-input:rgba(0,0,0,0.35);--border-dim:rgba(56,189,248,0.08);--border-glow:rgba(56,189,248,0.18);
13
+ --border-active:rgba(56,189,248,0.45);--text-primary:rgba(255,255,255,0.92);
14
+ --text-secondary:rgba(255,255,255,0.68);--text-muted:rgba(148,163,184,0.55);
15
+ --accent:#22d3ee;--accent-bg:rgba(6,182,212,0.15);
16
+ --purple:rgba(139,92,246,0.7);--purple-bg:rgba(139,92,246,0.12);
17
+ --green:#4ade80;--green-bg:rgba(74,222,128,0.1);
18
+ --amber:#fbbf24;--amber-bg:rgba(251,191,36,0.1);
19
+ --red:#f87171;--red-bg:rgba(248,113,113,0.1);
20
+ --shadow:0 2px 12px rgba(0,0,0,0.3);--radius:12px;
21
+ --font-sans:'DM Sans',system-ui,sans-serif;--font-mono:'IBM Plex Mono',monospace;--font-display:'Syne',sans-serif;
22
+ }
23
+ [data-theme="light"]{
24
+ --bg-deep:#f4f6f9;--bg-surface:rgba(255,255,255,0.95);--bg-card:rgba(255,255,255,0.9);
25
+ --bg-input:rgba(0,0,0,0.04);--border-dim:rgba(0,0,0,0.08);--border-glow:rgba(14,116,144,0.18);
26
+ --border-active:rgba(14,116,144,0.45);--text-primary:rgba(15,23,42,0.92);
27
+ --text-secondary:rgba(51,65,85,0.8);--text-muted:rgba(100,116,139,0.6);
28
+ --accent:#0891b2;--accent-bg:rgba(14,116,144,0.08);
29
+ --purple:rgba(109,40,217,0.75);--purple-bg:rgba(139,92,246,0.08);
30
+ --green:#16a34a;--green-bg:rgba(22,163,74,0.08);
31
+ --amber:#d97706;--amber-bg:rgba(217,119,6,0.08);
32
+ --red:#dc2626;--red-bg:rgba(220,38,38,0.06);
33
+ --shadow:0 1px 8px rgba(0,0,0,0.06);
34
+ }
35
+ html,body{width:100%;height:100%;overflow:hidden;background:var(--bg-deep);font-family:var(--font-sans);color:var(--text-primary);transition:background .3s,color .3s}
36
+ .app{display:flex;flex-direction:column;height:100vh}
37
+
38
+ /* Header */
39
+ .header{
40
+ padding:12px 24px;border-bottom:1px solid var(--border-dim);
41
+ display:flex;align-items:center;gap:12px;
42
+ background:var(--bg-surface);backdrop-filter:blur(20px);flex-shrink:0;
43
+ }
44
+ .logo{font-family:var(--font-display);font-size:16px;font-weight:700;color:var(--accent)}
45
+ .header-spacer{flex:1}
46
+ .nav-link{
47
+ font-size:12px;color:var(--text-muted);text-decoration:none;
48
+ padding:4px 10px;border-radius:6px;border:1px solid var(--border-dim);transition:all .2s;
49
+ }
50
+ .nav-link:hover{border-color:var(--border-glow);color:var(--text-secondary)}
51
+ .nav-link.active{color:var(--accent);border-color:var(--accent)}
52
+ .theme-toggle,.gear-btn{
53
+ background:none;border:1px solid var(--border-dim);border-radius:6px;
54
+ padding:4px 8px;cursor:pointer;font-size:14px;color:var(--text-muted);transition:all .2s;
55
+ text-decoration:none;}
56
+ .theme-toggle:hover,.gear-btn:hover{border-color:var(--border-glow);color:var(--text-secondary)}
57
+ .docs-dropdown{position:relative;display:inline-block}
58
+ .docs-menu{display:none;position:absolute;top:40px;right:0;min-width:160px;background:var(--bg-surface);border:1px solid var(--border-glow);border-radius:8px;box-shadow:0 8px 24px rgba(0,0,0,0.4);z-index:200;padding:4px;backdrop-filter:blur(20px)}
59
+ .docs-menu.open{display:block}
60
+ .docs-menu a{display:flex;align-items:center;gap:8px;padding:7px 12px;font-size:12px;font-weight:500;color:var(--text-secondary);text-decoration:none;border-radius:6px;transition:all .15s}
61
+ .docs-menu a:hover{background:var(--accent-bg);color:var(--accent)}
62
+ .docs-menu a .dm-icon{font-size:14px;width:18px;text-align:center}
63
+ .sub-nav{display:flex;align-items:center;gap:2px;padding:6px 24px;background:var(--bg-surface);border-bottom:1px solid var(--border-dim);flex-shrink:0}
64
+ .sub-nav-link{font-family:var(--font-sans);font-size:12px;font-weight:500;color:var(--text-muted);text-decoration:none;padding:4px 10px;border-radius:5px;transition:color .2s}
65
+ .sub-nav-link:hover{color:var(--text-secondary)}
66
+ .sub-nav-link.active{color:var(--accent)}
67
+
68
+ /* Toolbar */
69
+ .toolbar{
70
+ padding:12px 24px;border-bottom:1px solid var(--border-dim);
71
+ display:flex;align-items:center;gap:12px;flex-shrink:0;flex-wrap:wrap;
72
+ }
73
+ .filter-select{
74
+ padding:6px 12px;border-radius:8px;border:1px solid var(--border-dim);
75
+ background:var(--bg-input);color:var(--text-primary);font-family:var(--font-sans);font-size:12px;
76
+ cursor:pointer;transition:border-color .2s;
77
+ }
78
+ .filter-select:focus{outline:none;border-color:var(--border-active)}
79
+
80
+ /* Tabs */
81
+ .tab-bar{display:flex;gap:0;margin-right:auto}
82
+ .tab-btn{
83
+ padding:6px 18px;font-size:13px;font-weight:600;font-family:var(--font-sans);
84
+ background:none;border:1px solid var(--border-dim);color:var(--text-muted);
85
+ cursor:pointer;transition:all .2s;
86
+ }
87
+ .tab-btn:first-child{border-radius:8px 0 0 8px}
88
+ .tab-btn:last-child{border-radius:0 8px 8px 0}
89
+ .tab-btn.active{color:var(--accent);border-color:var(--accent);background:var(--accent-bg)}
90
+
91
+ /* Create buttons */
92
+ .btn-create{
93
+ padding:6px 14px;border-radius:8px;border:1px solid var(--accent);
94
+ background:var(--accent-bg);color:var(--accent);font-family:var(--font-sans);
95
+ font-size:12px;font-weight:600;cursor:pointer;transition:all .2s;
96
+ }
97
+ .btn-create:hover{background:var(--accent);color:#000}
98
+
99
+ /* Content area */
100
+ .content{flex:1;overflow-y:auto;padding:12px 24px}
101
+ .content::-webkit-scrollbar{width:4px}
102
+ .content::-webkit-scrollbar-thumb{background:var(--border-glow);border-radius:2px}
103
+
104
+ /* Cards */
105
+ .auto-card{
106
+ padding:14px 18px;margin-bottom:8px;border-radius:10px;
107
+ background:var(--bg-card);border:1px solid var(--border-dim);
108
+ transition:all .2s;
109
+ }
110
+ .auto-card:hover{border-color:var(--border-glow)}
111
+ .card-top{display:flex;align-items:center;gap:10px;margin-bottom:8px;flex-wrap:wrap}
112
+ .card-agent{
113
+ font-size:13px;font-weight:600;color:var(--accent);text-decoration:none;
114
+ cursor:pointer;
115
+ }
116
+ .card-agent:hover{text-decoration:underline}
117
+ .badge{
118
+ font-size:10px;padding:2px 8px;border-radius:4px;font-weight:600;
119
+ }
120
+ .badge-active{background:var(--green-bg);color:var(--green)}
121
+ .badge-paused{background:var(--amber-bg);color:var(--amber)}
122
+ .badge-label{background:var(--purple-bg);color:var(--purple)}
123
+ .card-spacer{flex:1}
124
+ .card-detail{font-size:12px;color:var(--text-secondary);line-height:1.6;margin-bottom:8px}
125
+ .card-detail span{color:var(--text-muted);font-size:11px}
126
+ .card-actions{display:flex;gap:6px;flex-wrap:wrap}
127
+ .btn-sm{
128
+ padding:4px 10px;border-radius:6px;border:1px solid var(--border-dim);
129
+ background:none;color:var(--text-secondary);font-family:var(--font-sans);
130
+ font-size:11px;cursor:pointer;transition:all .2s;
131
+ }
132
+ .btn-sm:hover{border-color:var(--border-glow);color:var(--text-primary)}
133
+ .btn-sm.danger{color:var(--red);border-color:rgba(248,113,113,0.2)}
134
+ .btn-sm.danger:hover{background:var(--red-bg);border-color:var(--red)}
135
+
136
+ /* History section */
137
+ .history-toggle{
138
+ font-size:11px;color:var(--text-muted);cursor:pointer;
139
+ margin-top:8px;display:inline-block;
140
+ }
141
+ .history-toggle:hover{color:var(--accent)}
142
+ .history-section{
143
+ max-height:0;overflow:hidden;transition:max-height .3s ease;
144
+ }
145
+ .history-section.open{max-height:600px;overflow-y:auto;margin-top:8px;padding-top:8px;border-top:1px solid var(--border-dim)}
146
+ .history-row{
147
+ padding:6px 10px;margin-bottom:4px;border-radius:6px;
148
+ background:var(--bg-input);font-size:11px;color:var(--text-secondary);
149
+ display:flex;gap:10px;align-items:center;flex-wrap:wrap;cursor:pointer;
150
+ }
151
+ .history-row:hover{background:var(--border-dim)}
152
+ .history-detail{
153
+ max-height:0;overflow:hidden;transition:max-height .3s ease;
154
+ font-size:11px;color:var(--text-muted);padding:0 10px;
155
+ line-height:1.5;white-space:pre-wrap;word-break:break-word;
156
+ }
157
+ .history-detail.open{max-height:400px;overflow-y:auto;padding:6px 10px;margin-bottom:4px;
158
+ background:var(--bg-input);border-radius:0 0 6px 6px;border-top:1px solid var(--border-dim)}
159
+
160
+ .no-data{text-align:center;padding:40px;color:var(--text-muted);font-size:14px}
161
+ .loading{text-align:center;padding:40px;color:var(--text-muted);font-size:13px}
162
+ .result-count{font-size:11px;color:var(--text-muted)}
163
+
164
+ /* Modal */
165
+ .modal-overlay{
166
+ display:none;position:fixed;inset:0;background:rgba(0,0,0,0.6);
167
+ backdrop-filter:blur(4px);z-index:100;align-items:center;justify-content:center;
168
+ }
169
+ .modal-overlay.open{display:flex}
170
+ .modal{
171
+ background:var(--bg-surface);border:1px solid var(--border-dim);border-radius:14px;
172
+ padding:24px;width:90%;max-width:520px;max-height:85vh;overflow-y:auto;
173
+ box-shadow:var(--shadow);
174
+ }
175
+ .modal h3{font-family:var(--font-display);font-size:16px;font-weight:700;color:var(--accent);margin-bottom:16px}
176
+ .modal-field{margin-bottom:12px}
177
+ .modal-field label{display:block;font-size:11px;color:var(--text-muted);margin-bottom:4px;font-weight:600}
178
+ .modal-field input,.modal-field select,.modal-field textarea{
179
+ width:100%;padding:8px 12px;border-radius:8px;border:1px solid var(--border-dim);
180
+ background:var(--bg-input);color:var(--text-primary);font-family:var(--font-sans);
181
+ font-size:13px;transition:border-color .2s;
182
+ }
183
+ .modal-field input:focus,.modal-field select:focus,.modal-field textarea:focus{outline:none;border-color:var(--border-active)}
184
+ .modal-field textarea{resize:vertical;min-height:60px;font-family:var(--font-mono);font-size:12px}
185
+ .modal-field .checkbox-row{display:flex;align-items:center;gap:8px}
186
+ .modal-field .checkbox-row input{width:auto}
187
+ .modal-actions{display:flex;gap:8px;justify-content:flex-end;margin-top:16px}
188
+ .btn-modal{
189
+ padding:8px 18px;border-radius:8px;font-family:var(--font-sans);font-size:13px;
190
+ font-weight:600;cursor:pointer;transition:all .2s;
191
+ }
192
+ .btn-modal.primary{background:var(--accent);color:#000;border:none}
193
+ .btn-modal.primary:hover{opacity:0.85}
194
+ .btn-modal.secondary{background:none;border:1px solid var(--border-dim);color:var(--text-secondary)}
195
+ .btn-modal.secondary:hover{border-color:var(--border-glow)}
196
+ </style>
197
+ </head>
198
+ <body>
199
+ <div class="app">
200
+ <div class="header">
201
+ <span class="logo">MyAIforOne</span>
202
+ <div class="header-spacer"></div>
203
+ <a class="nav-link" href="/">Home</a>
204
+ <a class="nav-link active" href="/org">Agents</a>
205
+ <a class="nav-link" href="/ui">Chat</a>
206
+ <a class="nav-link" href="/library">Library</a>
207
+ <a class="nav-link" href="/marketplace">Marketplace</a>
208
+ <a class="nav-link" href="/lab">Lab</a>
209
+ <div class="docs-dropdown" id="docsDropdown">
210
+ <button class="gear-btn" onclick="document.getElementById('docsMenu').classList.toggle('open')" title="Documentation">&#9776;</button>
211
+ <div class="docs-menu" id="docsMenu">
212
+ <a href="/api-docs#api" target="_blank"><span class="dm-icon">&#9881;</span> API Docs</a>
213
+ <a href="/api-docs#mcp" target="_blank"><span class="dm-icon">&#9830;</span> MCP Tools</a>
214
+ <a href="/changelog"><span class="dm-icon">&#x29C9;</span> Changelog</a>
215
+ <a href="/user-guide"><span class="dm-icon">&#x2709;</span> User Guide</a>
216
+ </div>
217
+ </div>
218
+ <a href="/admin" class="gear-btn" title="Admin" style="font-size:11px;font-weight:700;letter-spacing:.03em;padding:0 10px;width:auto">Admin</a>
219
+ <a href="/user-guide" class="gear-btn" title="User Guide" style="font-size:11px;font-weight:700;letter-spacing:.03em;padding:0 10px;width:auto">User Guide</a>
220
+ <button class="theme-toggle" onclick="toggleTheme()" id="themeBtn">🌙</button>
221
+ </div>
222
+ <div class="sub-nav">
223
+ <a class="sub-nav-link" href="/org">Teams</a>
224
+ <a class="sub-nav-link" href="/tasks">Tasks</a>
225
+ <a class="sub-nav-link" href="/projects">Projects</a>
226
+ <a class="sub-nav-link active" href="/automations">Automations</a>
227
+ </div>
228
+
229
+ <div class="toolbar">
230
+ <div class="tab-bar">
231
+ <button class="tab-btn active" data-tab="goals" onclick="switchTab('goals')">Goals</button>
232
+ <button class="tab-btn" data-tab="schedules" onclick="switchTab('schedules')">Schedules</button>
233
+ </div>
234
+ <select class="filter-select" id="agentFilter" onchange="renderCurrent()">
235
+ <option value="">All Agents</option>
236
+ </select>
237
+ <select class="filter-select" id="statusFilter" onchange="renderCurrent()">
238
+ <option value="">All Status</option>
239
+ <option value="active">Active</option>
240
+ <option value="paused">Paused</option>
241
+ </select>
242
+ <span class="result-count" id="resultCount"></span>
243
+ <div style="flex:1"></div>
244
+ <button class="btn-create" id="createBtn" onclick="openCreateModal()">+ New Goal</button>
245
+ </div>
246
+
247
+ <div class="content" id="content">
248
+ <div class="loading">Loading automations...</div>
249
+ </div>
250
+ </div>
251
+
252
+ <!-- Create Goal Modal -->
253
+ <div class="modal-overlay" id="goalModal">
254
+ <div class="modal">
255
+ <h3>New Goal</h3>
256
+ <div class="modal-field">
257
+ <label>Agent</label>
258
+ <select id="gm_agent"></select>
259
+ </div>
260
+ <div class="modal-field">
261
+ <label>Goal ID</label>
262
+ <input id="gm_id" placeholder="e.g. daily-review">
263
+ </div>
264
+ <div class="modal-field">
265
+ <label>Description</label>
266
+ <input id="gm_desc" placeholder="What this goal does">
267
+ </div>
268
+ <div class="modal-field">
269
+ <label>Success Criteria</label>
270
+ <textarea id="gm_criteria" placeholder="How to determine success"></textarea>
271
+ </div>
272
+ <div class="modal-field">
273
+ <label>Instructions</label>
274
+ <textarea id="gm_instructions" placeholder="Detailed instructions for the agent"></textarea>
275
+ </div>
276
+ <div class="modal-field">
277
+ <label>Heartbeat (cron expression)</label>
278
+ <input id="gm_heartbeat" placeholder="0 9 * * * (every day at 9am)">
279
+ </div>
280
+ <div class="modal-field">
281
+ <label>Budget (max daily USD)</label>
282
+ <input id="gm_budget" type="number" step="0.01" placeholder="1.00">
283
+ </div>
284
+ <div class="modal-field">
285
+ <label>Report To</label>
286
+ <input id="gm_reportTo" placeholder="Channel or agent to report to">
287
+ </div>
288
+ <div class="modal-field">
289
+ <div class="checkbox-row">
290
+ <input type="checkbox" id="gm_enabled" checked>
291
+ <label for="gm_enabled" style="margin:0">Enabled</label>
292
+ </div>
293
+ </div>
294
+ <div class="modal-actions">
295
+ <button class="btn-modal secondary" onclick="closeModal('goalModal')">Cancel</button>
296
+ <button class="btn-modal primary" onclick="createGoal()">Create Goal</button>
297
+ </div>
298
+ </div>
299
+ </div>
300
+
301
+ <!-- Create Schedule Modal -->
302
+ <div class="modal-overlay" id="scheduleModal">
303
+ <div class="modal">
304
+ <h3>New Schedule</h3>
305
+ <div class="modal-field">
306
+ <label>Agent</label>
307
+ <select id="sm_agent"></select>
308
+ </div>
309
+ <div class="modal-field">
310
+ <label>Schedule (cron expression)</label>
311
+ <input id="sm_schedule" placeholder="*/30 * * * * (every 30 min)">
312
+ </div>
313
+ <div class="modal-field">
314
+ <label>Message</label>
315
+ <textarea id="sm_message" placeholder="Message to send to the agent"></textarea>
316
+ </div>
317
+ <div class="modal-field">
318
+ <label>Channel</label>
319
+ <select id="sm_channel">
320
+ <option value="telegram">Telegram</option>
321
+ <option value="slack">Slack</option>
322
+ <option value="imessage">iMessage</option>
323
+ <option value="web">Web</option>
324
+ </select>
325
+ </div>
326
+ <div class="modal-field">
327
+ <label>Chat ID</label>
328
+ <input id="sm_chatId" placeholder="Chat/channel ID">
329
+ </div>
330
+ <div class="modal-field">
331
+ <div class="checkbox-row">
332
+ <input type="checkbox" id="sm_enabled" checked>
333
+ <label for="sm_enabled" style="margin:0">Enabled</label>
334
+ </div>
335
+ </div>
336
+ <div class="modal-actions">
337
+ <button class="btn-modal secondary" onclick="closeModal('scheduleModal')">Cancel</button>
338
+ <button class="btn-modal primary" onclick="createSchedule()">Create Schedule</button>
339
+ </div>
340
+ </div>
341
+ </div>
342
+
343
+ <script>
344
+ let agents = [];
345
+ let goals = [];
346
+ let crons = [];
347
+ let currentTab = 'goals';
348
+ let historyCache = {};
349
+
350
+ async function init() {
351
+ try {
352
+ const res = await fetch('/api/dashboard');
353
+ const data = await res.json();
354
+ agents = data.agents || [];
355
+ const sorted = agents.slice().sort((a, b) => a.name.localeCompare(b.name));
356
+
357
+ for (const sel of [document.getElementById('agentFilter'), document.getElementById('gm_agent'), document.getElementById('sm_agent')]) {
358
+ for (const a of sorted) {
359
+ const opt = document.createElement('option');
360
+ opt.value = a.id;
361
+ opt.textContent = a.name + (a.aliases?.[0] ? ` (${a.aliases[0]})` : '');
362
+ sel.appendChild(opt);
363
+ }
364
+ }
365
+ } catch {}
366
+ await fetchAutomations();
367
+ }
368
+
369
+ async function fetchAutomations() {
370
+ try {
371
+ const res = await fetch('/api/automations');
372
+ const data = await res.json();
373
+ goals = data.goals || [];
374
+ crons = data.crons || [];
375
+ renderCurrent();
376
+ } catch {
377
+ document.getElementById('content').innerHTML = '<div class="no-data">Failed to load automations</div>';
378
+ }
379
+ }
380
+
381
+ function switchTab(tab) {
382
+ currentTab = tab;
383
+ document.querySelectorAll('.tab-btn').forEach(b => b.classList.toggle('active', b.dataset.tab === tab));
384
+ document.getElementById('createBtn').textContent = tab === 'goals' ? '+ New Goal' : '+ New Schedule';
385
+ renderCurrent();
386
+ }
387
+
388
+ function renderCurrent() {
389
+ if (currentTab === 'goals') renderGoals();
390
+ else renderSchedules();
391
+ }
392
+
393
+ function applyFilters(items) {
394
+ const agentId = document.getElementById('agentFilter').value;
395
+ const status = document.getElementById('statusFilter').value;
396
+ let filtered = items;
397
+ if (agentId) filtered = filtered.filter(i => i.agentId === agentId);
398
+ if (status === 'active') filtered = filtered.filter(i => i.enabled !== false);
399
+ if (status === 'paused') filtered = filtered.filter(i => i.enabled === false);
400
+ return filtered;
401
+ }
402
+
403
+ function renderGoals() {
404
+ const filtered = applyFilters(goals);
405
+ document.getElementById('resultCount').textContent = filtered.length + ' goal' + (filtered.length !== 1 ? 's' : '');
406
+
407
+ if (!filtered.length) {
408
+ document.getElementById('content').innerHTML = '<div class="no-data">No goals found</div>';
409
+ return;
410
+ }
411
+
412
+ const html = filtered.map((g, i) => {
413
+ const active = g.enabled !== false;
414
+ const hb = g.heartbeat || 'N/A';
415
+ const budget = g.budget != null ? '$' + (typeof g.budget === 'object' ? g.budget.maxDailyUsd : g.budget) : 'N/A';
416
+ const uid = 'goal_' + g.agentId + '_' + g.id;
417
+ return `<div class="auto-card" id="${uid}">
418
+ <div class="card-top">
419
+ <a class="card-agent" href="/ui#${g.agentId}">${esc(g.agentName || g.agentId)}</a>
420
+ <span class="badge badge-label">${esc(g.id)}</span>
421
+ <span class="badge ${active ? 'badge-active' : 'badge-paused'}">${active ? 'Active' : 'Paused'}</span>
422
+ <span class="card-spacer"></span>
423
+ <div class="card-actions">
424
+ <button class="btn-sm" onclick="triggerGoal('${g.agentId}','${g.id}')">Trigger Now</button>
425
+ <button class="btn-sm" onclick="toggleGoal('${g.agentId}','${g.id}')">${active ? 'Pause' : 'Resume'}</button>
426
+ <button class="btn-sm danger" onclick="deleteGoal('${g.agentId}','${g.id}')">Delete</button>
427
+ <a class="btn-sm" href="/org?edit=${g.agentId}&tab=goals" style="text-decoration:none">&#9881; Config</a>
428
+ </div>
429
+ </div>
430
+ <div class="card-detail">
431
+ ${g.description ? esc(g.description) + '<br>' : ''}
432
+ <span>Heartbeat:</span> ${esc(hb)} &nbsp; <span>Budget:</span> ${esc(budget)} &nbsp; <span>Report to:</span> ${esc(g.reportTo || 'N/A')}
433
+ </div>
434
+ <span class="history-toggle" onclick="toggleHistory('${uid}','goal','${g.agentId}','${g.id}')">&#9654; Run History</span>
435
+ <div class="history-section" id="${uid}_hist"></div>
436
+ </div>`;
437
+ }).join('');
438
+
439
+ document.getElementById('content').innerHTML = html;
440
+ }
441
+
442
+ function renderSchedules() {
443
+ const filtered = applyFilters(crons);
444
+ document.getElementById('resultCount').textContent = filtered.length + ' schedule' + (filtered.length !== 1 ? 's' : '');
445
+
446
+ if (!filtered.length) {
447
+ document.getElementById('content').innerHTML = '<div class="no-data">No schedules found</div>';
448
+ return;
449
+ }
450
+
451
+ const html = filtered.map((c, i) => {
452
+ const active = c.enabled !== false;
453
+ const msg = (c.message || '').slice(0, 80) + ((c.message || '').length > 80 ? '...' : '');
454
+ const uid = 'cron_' + c.agentId + '_' + c.index;
455
+ return `<div class="auto-card" id="${uid}">
456
+ <div class="card-top">
457
+ <a class="card-agent" href="/ui#${c.agentId}">${esc(c.agentName || c.agentId)}</a>
458
+ <span class="badge badge-label" style="font-family:var(--font-mono)">${esc(c.schedule || '')}</span>
459
+ <span class="badge ${active ? 'badge-active' : 'badge-paused'}">${active ? 'Active' : 'Paused'}</span>
460
+ ${c.channel ? `<span class="badge" style="background:var(--purple-bg);color:var(--purple)">${esc(c.channel)}</span>` : ''}
461
+ <span class="card-spacer"></span>
462
+ <div class="card-actions">
463
+ <button class="btn-sm" onclick="triggerCron('${c.agentId}',${c.index})">Trigger Now</button>
464
+ <button class="btn-sm" onclick="toggleCron('${c.agentId}',${c.index})">${active ? 'Pause' : 'Resume'}</button>
465
+ <button class="btn-sm danger" onclick="deleteCron('${c.agentId}',${c.index})">Delete</button>
466
+ <a class="btn-sm" href="/org?edit=${c.agentId}&tab=schedules" style="text-decoration:none">&#9881; Config</a>
467
+ </div>
468
+ </div>
469
+ <div class="card-detail">
470
+ <span>Message:</span> ${esc(msg)}<br>
471
+ ${c.chatId ? '<span>Chat ID:</span> ' + esc(c.chatId) : ''}
472
+ </div>
473
+ <span class="history-toggle" onclick="toggleHistory('${uid}','cron','${c.agentId}','${c.index}')">&#9654; Run History</span>
474
+ <div class="history-section" id="${uid}_hist"></div>
475
+ </div>`;
476
+ }).join('');
477
+
478
+ document.getElementById('content').innerHTML = html;
479
+ }
480
+
481
+ async function toggleHistory(uid, type, agentId, itemId) {
482
+ const el = document.getElementById(uid + '_hist');
483
+ if (el.classList.contains('open')) {
484
+ el.classList.remove('open');
485
+ return;
486
+ }
487
+
488
+ const cacheKey = type + '_' + agentId + '_' + itemId;
489
+ if (historyCache[cacheKey]) {
490
+ el.innerHTML = historyCache[cacheKey];
491
+ el.classList.add('open');
492
+ return;
493
+ }
494
+
495
+ el.innerHTML = '<div style="padding:8px;font-size:11px;color:var(--text-muted)">Loading...</div>';
496
+ el.classList.add('open');
497
+
498
+ try {
499
+ const url = type === 'goal'
500
+ ? `/api/agents/${agentId}/goals/${itemId}/history`
501
+ : `/api/agents/${agentId}/cron/${itemId}/history`;
502
+ const res = await fetch(url);
503
+ const data = await res.json();
504
+ const history = data.history || [];
505
+
506
+ if (!history.length) {
507
+ const h = '<div style="padding:8px;font-size:11px;color:var(--text-muted)">No runs yet</div>';
508
+ historyCache[cacheKey] = h;
509
+ el.innerHTML = h;
510
+ return;
511
+ }
512
+
513
+ let html;
514
+ if (type === 'goal') {
515
+ html = history.map((r, i) => {
516
+ const rid = uid + '_run_' + i;
517
+ const statusColor = r.status === 'executed' ? 'var(--green)' : r.status === 'error' ? 'var(--red)' : 'var(--amber)';
518
+ return `<div class="history-row" onclick="document.getElementById('${rid}').classList.toggle('open')">
519
+ <span style="color:var(--text-muted)">${fmtTime(r.ts)}</span>
520
+ <span style="color:${statusColor};font-weight:600">${esc(r.status || 'unknown')}</span>
521
+ ${r.cost != null ? `<span>$${Number(r.cost).toFixed(4)}</span>` : ''}
522
+ ${r.responseLength != null ? `<span>${r.responseLength} chars</span>` : ''}
523
+ </div>
524
+ <div class="history-detail" id="${rid}">${esc(JSON.stringify(r, null, 2))}</div>`;
525
+ }).join('');
526
+ } else {
527
+ html = history.map((r, i) => {
528
+ const rid = uid + '_run_' + i;
529
+ const resp = (r.response || '').slice(0, 120) + ((r.response || '').length > 120 ? '...' : '');
530
+ return `<div class="history-row" onclick="document.getElementById('${rid}').classList.toggle('open')">
531
+ <span style="color:var(--text-muted)">${fmtTime(r.ts)}</span>
532
+ <span>${esc((r.text || '').slice(0, 60))}</span>
533
+ </div>
534
+ <div class="history-detail" id="${rid}"><strong>Message:</strong> ${esc(r.text || '')}\n\n<strong>Response:</strong> ${esc(r.response || '')}</div>`;
535
+ }).join('');
536
+ }
537
+
538
+ historyCache[cacheKey] = html;
539
+ el.innerHTML = html;
540
+ } catch {
541
+ el.innerHTML = '<div style="padding:8px;font-size:11px;color:var(--red)">Failed to load history</div>';
542
+ }
543
+ }
544
+
545
+ // Goal actions
546
+ async function triggerGoal(agentId, goalId) {
547
+ try {
548
+ await fetch(`/api/agents/${agentId}/goals/${goalId}/trigger`, { method: 'POST' });
549
+ flash('Goal triggered');
550
+ } catch { flash('Failed to trigger', true); }
551
+ }
552
+
553
+ async function toggleGoal(agentId, goalId) {
554
+ try {
555
+ const res = await fetch(`/api/agents/${agentId}/goals/${goalId}/toggle`, { method: 'POST' });
556
+ const data = await res.json();
557
+ const g = goals.find(g => g.agentId === agentId && g.id === goalId);
558
+ if (g) g.enabled = data.enabled;
559
+ renderCurrent();
560
+ } catch { flash('Failed to toggle', true); }
561
+ }
562
+
563
+ async function deleteGoal(agentId, goalId) {
564
+ if (!confirm('Delete goal "' + goalId + '"? This cannot be undone.')) return;
565
+ try {
566
+ await fetch(`/api/agents/${agentId}/goals/${goalId}`, { method: 'DELETE' });
567
+ goals = goals.filter(g => !(g.agentId === agentId && g.id === goalId));
568
+ renderCurrent();
569
+ flash('Goal deleted');
570
+ } catch { flash('Failed to delete', true); }
571
+ }
572
+
573
+ // Cron actions
574
+ async function triggerCron(agentId, index) {
575
+ try {
576
+ await fetch(`/api/agents/${agentId}/cron/${index}/trigger`, { method: 'POST' });
577
+ flash('Schedule triggered');
578
+ } catch { flash('Failed to trigger', true); }
579
+ }
580
+
581
+ async function toggleCron(agentId, index) {
582
+ try {
583
+ const res = await fetch(`/api/agents/${agentId}/cron/${index}/toggle`, { method: 'POST' });
584
+ const data = await res.json();
585
+ const c = crons.find(c => c.agentId === agentId && c.index === index);
586
+ if (c) c.enabled = data.enabled;
587
+ renderCurrent();
588
+ } catch { flash('Failed to toggle', true); }
589
+ }
590
+
591
+ async function deleteCron(agentId, index) {
592
+ if (!confirm('Delete this schedule? This cannot be undone.')) return;
593
+ try {
594
+ await fetch(`/api/agents/${agentId}/cron/${index}`, { method: 'DELETE' });
595
+ crons = crons.filter(c => !(c.agentId === agentId && c.index === index));
596
+ renderCurrent();
597
+ flash('Schedule deleted');
598
+ } catch { flash('Failed to delete', true); }
599
+ }
600
+
601
+ // Create
602
+ function openCreateModal() {
603
+ if (currentTab === 'goals') {
604
+ document.getElementById('goalModal').classList.add('open');
605
+ } else {
606
+ document.getElementById('scheduleModal').classList.add('open');
607
+ }
608
+ }
609
+
610
+ function closeModal(id) {
611
+ document.getElementById(id).classList.remove('open');
612
+ }
613
+
614
+ async function createGoal() {
615
+ const agentId = document.getElementById('gm_agent').value;
616
+ if (!agentId) { flash('Select an agent', true); return; }
617
+ const body = {
618
+ id: document.getElementById('gm_id').value.trim(),
619
+ description: document.getElementById('gm_desc').value.trim(),
620
+ successCriteria: document.getElementById('gm_criteria').value.trim(),
621
+ instructions: document.getElementById('gm_instructions').value.trim(),
622
+ heartbeat: document.getElementById('gm_heartbeat').value.trim(),
623
+ budget: { maxDailyUsd: parseFloat(document.getElementById('gm_budget').value) || 1 },
624
+ reportTo: document.getElementById('gm_reportTo').value.trim(),
625
+ enabled: document.getElementById('gm_enabled').checked,
626
+ };
627
+ if (!body.id) { flash('Goal ID is required', true); return; }
628
+
629
+ try {
630
+ await fetch(`/api/agents/${agentId}/goals`, {
631
+ method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body)
632
+ });
633
+ closeModal('goalModal');
634
+ await fetchAutomations();
635
+ flash('Goal created');
636
+ } catch { flash('Failed to create goal', true); }
637
+ }
638
+
639
+ async function createSchedule() {
640
+ const agentId = document.getElementById('sm_agent').value;
641
+ if (!agentId) { flash('Select an agent', true); return; }
642
+ const body = {
643
+ schedule: document.getElementById('sm_schedule').value.trim(),
644
+ message: document.getElementById('sm_message').value.trim(),
645
+ channel: document.getElementById('sm_channel').value,
646
+ chatId: document.getElementById('sm_chatId').value.trim(),
647
+ enabled: document.getElementById('sm_enabled').checked,
648
+ };
649
+ if (!body.schedule) { flash('Schedule expression is required', true); return; }
650
+
651
+ try {
652
+ await fetch(`/api/agents/${agentId}/cron`, {
653
+ method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body)
654
+ });
655
+ closeModal('scheduleModal');
656
+ await fetchAutomations();
657
+ flash('Schedule created');
658
+ } catch { flash('Failed to create schedule', true); }
659
+ }
660
+
661
+ // Helpers
662
+ function esc(s) {
663
+ return (s || '').toString().replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
664
+ }
665
+
666
+ function fmtTime(ts) {
667
+ if (!ts) return '';
668
+ try {
669
+ const d = new Date(ts);
670
+ const now = new Date();
671
+ const day = d.toDateString() === now.toDateString() ? 'Today'
672
+ : d.toDateString() === new Date(now - 86400000).toDateString() ? 'Yesterday'
673
+ : d.toLocaleDateString([], { month: 'short', day: 'numeric' });
674
+ return day + ' ' + d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
675
+ } catch { return ''; }
676
+ }
677
+
678
+ function flash(msg, isError) {
679
+ const el = document.createElement('div');
680
+ el.textContent = msg;
681
+ Object.assign(el.style, {
682
+ position: 'fixed', bottom: '20px', left: '50%', transform: 'translateX(-50%)',
683
+ padding: '8px 20px', borderRadius: '8px', fontSize: '13px', fontWeight: '600',
684
+ background: isError ? 'var(--red-bg)' : 'var(--green-bg)',
685
+ color: isError ? 'var(--red)' : 'var(--green)',
686
+ border: '1px solid ' + (isError ? 'rgba(248,113,113,0.3)' : 'rgba(74,222,128,0.3)'),
687
+ zIndex: '200', transition: 'opacity 0.3s',
688
+ });
689
+ document.body.appendChild(el);
690
+ setTimeout(() => { el.style.opacity = '0'; setTimeout(() => el.remove(), 300); }, 2000);
691
+ }
692
+
693
+ function toggleTheme() {
694
+ const html = document.documentElement;
695
+ const isLight = html.getAttribute('data-theme') === 'light';
696
+ html.setAttribute('data-theme', isLight ? '' : 'light');
697
+ document.getElementById('themeBtn').textContent = isLight ? '🌙' : '☀️';
698
+ localStorage.setItem('theme', isLight ? 'dark' : 'light');
699
+ }
700
+
701
+ // Close modals on overlay click
702
+ document.querySelectorAll('.modal-overlay').forEach(el => {
703
+ el.addEventListener('click', e => { if (e.target === el) el.classList.remove('open'); });
704
+ });
705
+
706
+ // Load saved theme
707
+ (function() {
708
+ const saved = localStorage.getItem('theme');
709
+ if (saved === 'light') {
710
+ document.documentElement.setAttribute('data-theme', 'light');
711
+ setTimeout(() => { document.getElementById('themeBtn').textContent = '☀️'; }, 50);
712
+ }
713
+ })();
714
+
715
+ init();
716
+ document.addEventListener('click', function(e){
717
+ const dd = document.getElementById('docsDropdown');
718
+ if(dd && !dd.contains(e.target)) document.getElementById('docsMenu')?.classList.remove('open');
719
+ });
720
+ </script>
721
+ </body>
722
+ </html>