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
package/src/config.ts ADDED
@@ -0,0 +1,281 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { resolve, join } from "node:path";
4
+ import type { LogLevel } from "./logger.js";
5
+
6
+ export interface RouteMatch {
7
+ type: "chat_id" | "chat_guid" | "chat_identifier" | "channel_id" | "jid";
8
+ value: string | number;
9
+ }
10
+
11
+ export interface RoutePermissions {
12
+ allowFrom: string[];
13
+ requireMention: boolean;
14
+ }
15
+
16
+ export interface RouteConfig {
17
+ channel: string;
18
+ match: RouteMatch;
19
+ permissions: RoutePermissions;
20
+ }
21
+
22
+ // ─── MCP Server definitions ──────────────────────────────────────────
23
+
24
+ export interface McpServerStdio {
25
+ type: "stdio";
26
+ command: string;
27
+ args?: string[];
28
+ env?: Record<string, string>;
29
+ }
30
+
31
+ export interface McpServerHttp {
32
+ type: "http" | "sse";
33
+ url: string;
34
+ headers?: Record<string, string>;
35
+ }
36
+
37
+ export type McpServerConfig = McpServerStdio | McpServerHttp;
38
+
39
+ // ─── Cron job config ─────────────────────────────────────────────────
40
+
41
+ export interface CronJobConfig {
42
+ schedule: string; // cron expression (e.g., "0 9 * * *")
43
+ message: string; // message to send to the agent
44
+ channel: string; // which channel to reply on
45
+ chatId: string; // which chat to reply in
46
+ enabled?: boolean; // default true — set false to pause
47
+ }
48
+
49
+ // ─── Goals config ───────────────────────────────────────────────────
50
+
51
+ export interface GoalConfig {
52
+ id: string;
53
+ enabled: boolean;
54
+ description: string;
55
+ successCriteria?: string;
56
+ instructions?: string;
57
+ heartbeat: string; // cron expression
58
+ budget?: { maxDailyUsd: number };
59
+ reportTo?: string | string[]; // "channel:chatId" or array of them e.g. ["telegram:-5112439418", "slack:C0ALHTDD6JF"]
60
+ }
61
+
62
+ // ─── Agent config ────────────────────────────────────────────────────
63
+
64
+ export interface AgentConfig {
65
+ name: string;
66
+ description: string;
67
+ agentHome?: string; // agent's own folder (memory, skills, keys, storage)
68
+ workspace: string; // project/codebase the agent works on
69
+ claudeMd: string;
70
+ memoryDir: string;
71
+ skills?: string[]; // shared skills from ~/.claude/commands/
72
+ agentSkills?: string[]; // agent-specific skills from agent's skills/ folder
73
+ mcps?: string[];
74
+ prompts?: string[]; // prompt templates available to this agent
75
+ persistent?: boolean;
76
+ perSenderSessions?: boolean;
77
+ streaming?: boolean;
78
+ advancedMemory?: boolean;
79
+ org?: Array<{
80
+ organization: string;
81
+ function: string;
82
+ title: string;
83
+ reportsTo?: string;
84
+ }>;
85
+ autonomousCapable?: boolean;
86
+ autoCommit: boolean;
87
+ autoCommitBranch: string;
88
+ allowedTools: string[];
89
+ claudeAccount?: string; // which account this agent uses (key from service.claudeAccounts)
90
+ mentionAliases?: string[];
91
+ routes: RouteConfig[];
92
+ timeout?: number;
93
+ cron?: CronJobConfig[];
94
+ goals?: GoalConfig[];
95
+ wiki?: boolean; // Enable wiki learning — agent saves learned facts to learned.md
96
+ wikiSync?: {
97
+ enabled: boolean;
98
+ schedule: string; // cron expression (e.g., "0 0 * * *" = daily midnight)
99
+ };
100
+ subAgents?: string[] | "*"; // Group agent: list of agent IDs to delegate to, or "*" for all
101
+ platformAgent?: boolean; // DEPRECATED — use agentClass instead
102
+ agentClass?: "standard" | "platform" | "builder" | "gym"; // standard (default), platform (Lab creators), builder (app developer agents), gym (AI Gym agents)
103
+ executor?: string; // "claude" (default) or "ollama:modelname" (e.g., "ollama:gemma2")
104
+ }
105
+
106
+ export interface ChannelConfig {
107
+ enabled: boolean;
108
+ driver: string;
109
+ config: Record<string, unknown>;
110
+ }
111
+
112
+ // ─── Web UI config ───────────────────────────────────────────────────
113
+
114
+ export interface WebUIConfig {
115
+ enabled: boolean;
116
+ port: number;
117
+ webhookSecret?: string;
118
+ }
119
+
120
+ export interface ServiceConfig {
121
+ logLevel: LogLevel;
122
+ logFile?: string;
123
+ pairingCode?: string;
124
+ personalAgentsDir?: string; // Override path to PersonalAgents dir (default: ~/Desktop/MyAIforOne Drive/PersonalAgents)
125
+ personalRegistryDir?: string; // Override path to PersonalRegistry dir (default: ~/Desktop/MyAIforOne Drive/PersonalRegistry)
126
+ webUI?: WebUIConfig;
127
+ claudeAccounts?: Record<string, string>; // name → config dir path, e.g. {"main": "~/.claude"}
128
+ defaultClaudeAccount?: string; // account name to use when agent has no claudeAccount set (e.g. "main")
129
+ multiModelEnabled?: boolean; // false = claude only, true = enables alternative models
130
+ platformDefaultExecutor?: string; // "claude" (default) or "ollama:gemma2" etc.
131
+ ollamaBaseUrl?: string; // default: "http://localhost:11434"
132
+ providerKeys?: Record<string, string>; // provider API keys: { openai: "sk-...", xai: "xai-...", google: "AIza...", groq: "gsk_...", together: "...", mistral: "..." }
133
+ gymEnabled?: boolean; // false = gym hidden, true = gym active
134
+ aibriefingEnabled?: boolean; // false = no AI briefing feed, true = weekly AI news via web search
135
+ gymOnlyMode?: boolean;
136
+ }
137
+
138
+ export interface AppConfig {
139
+ service: ServiceConfig;
140
+ channels: Record<string, ChannelConfig>;
141
+ agents: Record<string, AgentConfig>;
142
+ mcps?: Record<string, McpServerConfig>;
143
+ defaultAgent: string | null;
144
+ defaultSkills?: string[]; // skills given to every agent automatically
145
+ defaultMcps?: string[]; // MCPs given to every agent automatically
146
+ defaultPrompts?: string[]; // prompt templates given to every agent automatically
147
+ promptTrigger?: string; // character used to invoke prompt templates (default: "!")
148
+ saas?: {
149
+ baseUrl: string;
150
+ apiKey: string;
151
+ };
152
+ }
153
+
154
+ export function loadConfig(configPath: string): AppConfig {
155
+ const fullPath = resolve(configPath);
156
+ const raw = readFileSync(fullPath, "utf-8");
157
+ const config = JSON.parse(raw) as AppConfig;
158
+
159
+ // Ensure required top-level keys exist (default to empty — app can start for setup)
160
+ if (!config.agents) config.agents = {};
161
+ if (!config.channels) config.channels = {};
162
+
163
+ // Normalize each agent's paths and ensure routes is always an array
164
+ const agentIds = Object.keys(config.agents);
165
+ for (const id of agentIds) {
166
+ const agent = config.agents[id];
167
+ // Routes are optional — agents without channel routes are still reachable via web UI
168
+ if (!agent.routes) agent.routes = [];
169
+ if (!agent.workspace) {
170
+ throw new Error(`Agent "${id}" must have a workspace path`);
171
+ }
172
+ if (!agent.claudeMd) {
173
+ throw new Error(`Agent "${id}" must have a claudeMd path`);
174
+ }
175
+
176
+ // Resolve ~ in paths (works on macOS and Windows)
177
+ const home = homedir();
178
+ const resolveTilde = (p: string) =>
179
+ p.startsWith("~") ? p.replace("~", home) : p;
180
+ agent.workspace = resolveTilde(agent.workspace);
181
+ if (agent.agentHome) {
182
+ agent.agentHome = resolveTilde(agent.agentHome);
183
+ // Auto-derive claudeMd and memoryDir from agentHome only if not explicitly set
184
+ if (!agent.claudeMd) {
185
+ agent.claudeMd = join(agent.agentHome, "CLAUDE.md");
186
+ }
187
+ if (!agent.memoryDir) {
188
+ agent.memoryDir = join(agent.agentHome, "memory");
189
+ }
190
+ }
191
+ agent.claudeMd = resolveTilde(agent.claudeMd);
192
+ agent.memoryDir = resolveTilde(agent.memoryDir);
193
+ // Derive agentHome from memoryDir if not set
194
+ if (!agent.agentHome) {
195
+ agent.agentHome = resolve(resolveTilde(agent.memoryDir), "..");
196
+ }
197
+
198
+ // Validate MCP references
199
+ if (agent.mcps && agent.mcps.length > 0) {
200
+ if (!config.mcps || Object.keys(config.mcps).length === 0) {
201
+ throw new Error(`Agent "${id}" references MCPs but no "mcps" registry is defined in config`);
202
+ }
203
+ for (const mcpName of agent.mcps) {
204
+ if (!config.mcps[mcpName]) {
205
+ throw new Error(`Agent "${id}" references MCP "${mcpName}" which is not defined in config.mcps`);
206
+ }
207
+ }
208
+ }
209
+
210
+ // Set defaults
211
+ agent.autoCommit = agent.autoCommit ?? false;
212
+ agent.autoCommitBranch = agent.autoCommitBranch ?? "main";
213
+ agent.allowedTools = agent.allowedTools ?? ["Read", "Edit", "Write", "Glob", "Grep", "Bash"];
214
+ agent.timeout = agent.timeout ?? 14_400_000;
215
+ }
216
+
217
+ // Validate MCP definitions
218
+ if (config.mcps) {
219
+ for (const [mcpId, mcp] of Object.entries(config.mcps)) {
220
+ if (mcp.type === "stdio") {
221
+ if (!mcp.command) throw new Error(`MCP "${mcpId}" (stdio) must have a "command" field`);
222
+ } else if (mcp.type === "http" || mcp.type === "sse") {
223
+ if (!(mcp as McpServerHttp).url) throw new Error(`MCP "${mcpId}" (${mcp.type}) must have a "url" field`);
224
+ } else {
225
+ throw new Error(`MCP "${mcpId}" has unknown type "${(mcp as any).type}" — must be "stdio", "http", or "sse"`);
226
+ }
227
+ }
228
+ }
229
+
230
+ // Defaults
231
+ config.service = config.service ?? { logLevel: "info" };
232
+ config.service.logLevel = config.service.logLevel ?? "info";
233
+ config.defaultAgent = config.defaultAgent ?? null;
234
+
235
+ const home = homedir();
236
+ const driveRoot = resolve(home, "Desktop", "MyAIforOne Drive");
237
+
238
+ // Resolve personalAgentsDir (expand ~)
239
+ if (config.service.personalAgentsDir) {
240
+ config.service.personalAgentsDir = config.service.personalAgentsDir.startsWith("~")
241
+ ? config.service.personalAgentsDir.replace("~", home)
242
+ : config.service.personalAgentsDir;
243
+ }
244
+ _personalAgentsDir = config.service.personalAgentsDir || resolve(driveRoot, "PersonalAgents");
245
+
246
+ // Resolve personalRegistryDir (expand ~)
247
+ if (config.service.personalRegistryDir) {
248
+ config.service.personalRegistryDir = config.service.personalRegistryDir.startsWith("~")
249
+ ? config.service.personalRegistryDir.replace("~", home)
250
+ : config.service.personalRegistryDir;
251
+ }
252
+ _personalRegistryDir = config.service.personalRegistryDir || resolve(driveRoot, "PersonalRegistry");
253
+
254
+ // Inject default account hint into claudeAccounts map so executor can pick it up
255
+ if (config.service.defaultClaudeAccount && config.service.claudeAccounts) {
256
+ (config.service.claudeAccounts as any)._defaultAccount = config.service.defaultClaudeAccount;
257
+ }
258
+
259
+ return config;
260
+ }
261
+
262
+ /** Resolved PersonalAgents directory — set during config loading, used by executor and keystore */
263
+ let _personalAgentsDir: string | null = null;
264
+ /** Resolved PersonalRegistry directory — set during config loading, used by web-ui */
265
+ let _personalRegistryDir: string | null = null;
266
+
267
+ /** Resolve the PersonalAgents directory from config, falling back to ~/Desktop/MyAIforOne Drive/PersonalAgents */
268
+ export function getPersonalAgentsDir(config?: AppConfig): string {
269
+ if (_personalAgentsDir) return _personalAgentsDir;
270
+ const driveRoot = resolve(homedir(), "Desktop", "MyAIforOne Drive");
271
+ if (config) return config.service.personalAgentsDir || resolve(driveRoot, "PersonalAgents");
272
+ return resolve(driveRoot, "PersonalAgents");
273
+ }
274
+
275
+ /** Resolve the PersonalRegistry directory from config, falling back to ~/Desktop/MyAIforOne Drive/PersonalRegistry */
276
+ export function getPersonalRegistryDir(config?: AppConfig): string {
277
+ if (_personalRegistryDir) return _personalRegistryDir;
278
+ const driveRoot = resolve(homedir(), "Desktop", "MyAIforOne Drive");
279
+ if (config) return config.service.personalRegistryDir || resolve(driveRoot, "PersonalRegistry");
280
+ return resolve(driveRoot, "PersonalRegistry");
281
+ }
package/src/cron.ts ADDED
@@ -0,0 +1,43 @@
1
+ import cron from "node-cron";
2
+ import type { AppConfig, CronJobConfig } from "./config.js";
3
+ import { log } from "./logger.js";
4
+
5
+ interface CronHandler {
6
+ (agentId: string, message: string, channel: string, chatId: string): Promise<void>;
7
+ }
8
+
9
+ const activeTasks: cron.ScheduledTask[] = [];
10
+
11
+ export function startCronJobs(config: AppConfig, handler: CronHandler): void {
12
+ for (const [agentId, agent] of Object.entries(config.agents)) {
13
+ if (!agent.cron?.length) continue;
14
+
15
+ for (const job of agent.cron) {
16
+ if (job.enabled === false) continue;
17
+
18
+ if (!cron.validate(job.schedule)) {
19
+ log.error(`Invalid cron schedule for ${agentId}: "${job.schedule}"`);
20
+ continue;
21
+ }
22
+
23
+ const task = cron.schedule(job.schedule, async () => {
24
+ log.info(`Cron fired for ${agentId}: "${job.message.slice(0, 60)}"`);
25
+ try {
26
+ await handler(agentId, job.message, job.channel, job.chatId);
27
+ } catch (err) {
28
+ log.error(`Cron job failed for ${agentId}: ${err}`);
29
+ }
30
+ });
31
+
32
+ activeTasks.push(task);
33
+ log.info(`Cron scheduled for ${agentId}: "${job.schedule}" → "${job.message.slice(0, 60)}"`);
34
+ }
35
+ }
36
+ }
37
+
38
+ export function stopCronJobs(): void {
39
+ for (const task of activeTasks) {
40
+ task.stop();
41
+ }
42
+ activeTasks.length = 0;
43
+ }
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Decrypt all MCP key .env.enc files back to .env.
4
+ * Usage: npm run decrypt-keys
5
+ */
6
+
7
+ import { resolve, dirname, join } from "node:path";
8
+ import { fileURLToPath } from "node:url";
9
+ import { readdirSync, existsSync } from "node:fs";
10
+ import { createInterface } from "node:readline";
11
+ import { decryptDir } from "./keystore.js";
12
+
13
+ const __dirname = dirname(fileURLToPath(import.meta.url));
14
+ const baseDir = resolve(__dirname, "..");
15
+
16
+ async function main() {
17
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
18
+ const ask = (q: string): Promise<string> => new Promise(r => rl.question(q, r));
19
+
20
+ const password = await ask("Master password: ");
21
+ if (!password) {
22
+ console.log("No password provided. Aborting.");
23
+ rl.close();
24
+ return;
25
+ }
26
+
27
+ let total = 0;
28
+
29
+ // Decrypt shared keys
30
+ const sharedDir = join(baseDir, "data", "mcp-keys");
31
+ if (existsSync(sharedDir)) {
32
+ const count = decryptDir(sharedDir, password);
33
+ total += count;
34
+ console.log(`Shared keys: ${count} files decrypted`);
35
+ }
36
+
37
+ // Decrypt agent-level keys
38
+ const personalBase = join(process.env.HOME || process.env.USERPROFILE || "", "Desktop", "MyAIforOne Drive", "PersonalAgents");
39
+ if (existsSync(personalBase)) {
40
+ const walkDirs = (dir: string) => {
41
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
42
+ if (entry.isDirectory()) {
43
+ const mcpKeysDir = join(dir, entry.name, "mcp-keys");
44
+ if (existsSync(mcpKeysDir)) {
45
+ const count = decryptDir(mcpKeysDir, password);
46
+ total += count;
47
+ if (count > 0) console.log(`${entry.name}: ${count} files decrypted`);
48
+ }
49
+ walkDirs(join(dir, entry.name));
50
+ }
51
+ }
52
+ };
53
+ walkDirs(personalBase);
54
+ }
55
+
56
+ console.log(`\nDone. ${total} key files decrypted.`);
57
+ rl.close();
58
+ }
59
+
60
+ main().catch(console.error);
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Encrypt all MCP key .env files.
4
+ * Usage: npm run encrypt-keys
5
+ * Prompts for master password, encrypts all .env → .env.enc
6
+ */
7
+
8
+ import { resolve, dirname, join } from "node:path";
9
+ import { fileURLToPath } from "node:url";
10
+ import { readdirSync, existsSync } from "node:fs";
11
+ import { createInterface } from "node:readline";
12
+ import { encryptDir } from "./keystore.js";
13
+
14
+ const __dirname = dirname(fileURLToPath(import.meta.url));
15
+ const baseDir = resolve(__dirname, "..");
16
+
17
+ async function main() {
18
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
19
+ const ask = (q: string): Promise<string> => new Promise(r => rl.question(q, r));
20
+
21
+ const password = await ask("Master password: ");
22
+ if (!password) {
23
+ console.log("No password provided. Aborting.");
24
+ rl.close();
25
+ return;
26
+ }
27
+
28
+ const confirm = await ask("Confirm password: ");
29
+ if (password !== confirm) {
30
+ console.log("Passwords don't match. Aborting.");
31
+ rl.close();
32
+ return;
33
+ }
34
+
35
+ let total = 0;
36
+
37
+ // Encrypt shared keys
38
+ const sharedDir = join(baseDir, "data", "mcp-keys");
39
+ if (existsSync(sharedDir)) {
40
+ const count = encryptDir(sharedDir, password);
41
+ total += count;
42
+ console.log(`Shared keys: ${count} files encrypted`);
43
+ }
44
+
45
+ // Encrypt agent-level keys
46
+ const personalBase = join(process.env.HOME || process.env.USERPROFILE || "", "Desktop", "MyAIforOne Drive", "PersonalAgents");
47
+ if (existsSync(personalBase)) {
48
+ const walkDirs = (dir: string) => {
49
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
50
+ if (entry.isDirectory()) {
51
+ const mcpKeysDir = join(dir, entry.name, "mcp-keys");
52
+ if (existsSync(mcpKeysDir)) {
53
+ const count = encryptDir(mcpKeysDir, password);
54
+ total += count;
55
+ if (count > 0) console.log(`${entry.name}: ${count} files encrypted`);
56
+ }
57
+ // Recurse into org folders
58
+ walkDirs(join(dir, entry.name));
59
+ }
60
+ }
61
+ };
62
+ walkDirs(personalBase);
63
+ }
64
+
65
+ console.log(`\nDone. ${total} key files encrypted.`);
66
+ console.log("Original .env files replaced with stubs. Encrypted data in .env.enc files.");
67
+ rl.close();
68
+ }
69
+
70
+ main().catch(console.error);