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,189 @@
1
+ /**
2
+ * Vector store — JSON default, auto-upgrades to SQLite if better-sqlite3 is available.
3
+ * Stores text chunks with their embedding vectors for semantic search.
4
+ */
5
+
6
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
7
+ import { join, dirname } from "node:path";
8
+ import { log } from "../logger.js";
9
+
10
+ export interface MemoryChunk {
11
+ id: string;
12
+ text: string;
13
+ vector: number[];
14
+ source: string; // "daily", "context", "conversation", "manual"
15
+ timestamp: string; // ISO date
16
+ metadata?: Record<string, string>;
17
+ }
18
+
19
+ export interface VectorStore {
20
+ name: string;
21
+ add(chunk: MemoryChunk): Promise<void>;
22
+ addBatch(chunks: MemoryChunk[]): Promise<void>;
23
+ getAll(): Promise<MemoryChunk[]>;
24
+ remove(id: string): Promise<void>;
25
+ clear(): Promise<void>;
26
+ count(): Promise<number>;
27
+ }
28
+
29
+ // ─── JSON Store (default) ────────────────────────────────────────────
30
+
31
+ class JSONStore implements VectorStore {
32
+ name = "json";
33
+ private filePath: string;
34
+ private chunks: MemoryChunk[] = [];
35
+ private loaded = false;
36
+
37
+ constructor(memoryDir: string) {
38
+ mkdirSync(memoryDir, { recursive: true });
39
+ this.filePath = join(memoryDir, "vectors.json");
40
+ }
41
+
42
+ private load(): void {
43
+ if (this.loaded) return;
44
+ if (existsSync(this.filePath)) {
45
+ try {
46
+ this.chunks = JSON.parse(readFileSync(this.filePath, "utf-8"));
47
+ } catch {
48
+ this.chunks = [];
49
+ }
50
+ }
51
+ this.loaded = true;
52
+ }
53
+
54
+ private save(): void {
55
+ writeFileSync(this.filePath, JSON.stringify(this.chunks));
56
+ }
57
+
58
+ async add(chunk: MemoryChunk): Promise<void> {
59
+ this.load();
60
+ // Replace if same ID exists
61
+ this.chunks = this.chunks.filter(c => c.id !== chunk.id);
62
+ this.chunks.push(chunk);
63
+ this.save();
64
+ }
65
+
66
+ async addBatch(chunks: MemoryChunk[]): Promise<void> {
67
+ this.load();
68
+ const newIds = new Set(chunks.map(c => c.id));
69
+ this.chunks = this.chunks.filter(c => !newIds.has(c.id));
70
+ this.chunks.push(...chunks);
71
+ this.save();
72
+ }
73
+
74
+ async getAll(): Promise<MemoryChunk[]> {
75
+ this.load();
76
+ return [...this.chunks];
77
+ }
78
+
79
+ async remove(id: string): Promise<void> {
80
+ this.load();
81
+ this.chunks = this.chunks.filter(c => c.id !== id);
82
+ this.save();
83
+ }
84
+
85
+ async clear(): Promise<void> {
86
+ this.chunks = [];
87
+ this.loaded = true;
88
+ this.save();
89
+ }
90
+
91
+ async count(): Promise<number> {
92
+ this.load();
93
+ return this.chunks.length;
94
+ }
95
+ }
96
+
97
+ // ─── SQLite Store (auto-upgrade if available) ────────────────────────
98
+
99
+ async function trySQLiteStore(memoryDir: string): Promise<VectorStore | null> {
100
+ try {
101
+ // Dynamic import — only works if better-sqlite3 is installed
102
+ // Use createRequire to avoid TypeScript module resolution errors
103
+ const { createRequire } = await import("node:module");
104
+ const require = createRequire(import.meta.url);
105
+ const Database = require("better-sqlite3");
106
+
107
+ const dbPath = join(memoryDir, "vectors.sqlite");
108
+ const db = new Database(dbPath);
109
+
110
+ // Create table
111
+ db.exec(`
112
+ CREATE TABLE IF NOT EXISTS chunks (
113
+ id TEXT PRIMARY KEY,
114
+ text TEXT NOT NULL,
115
+ vector TEXT NOT NULL,
116
+ source TEXT NOT NULL,
117
+ timestamp TEXT NOT NULL,
118
+ metadata TEXT
119
+ )
120
+ `);
121
+
122
+ const store: VectorStore = {
123
+ name: "sqlite",
124
+
125
+ async add(chunk: MemoryChunk) {
126
+ db.prepare(`
127
+ INSERT OR REPLACE INTO chunks (id, text, vector, source, timestamp, metadata)
128
+ VALUES (?, ?, ?, ?, ?, ?)
129
+ `).run(
130
+ chunk.id, chunk.text, JSON.stringify(chunk.vector),
131
+ chunk.source, chunk.timestamp, chunk.metadata ? JSON.stringify(chunk.metadata) : null
132
+ );
133
+ },
134
+
135
+ async addBatch(chunks: MemoryChunk[]) {
136
+ const stmt = db.prepare(`
137
+ INSERT OR REPLACE INTO chunks (id, text, vector, source, timestamp, metadata)
138
+ VALUES (?, ?, ?, ?, ?, ?)
139
+ `);
140
+ const tx = db.transaction((items: MemoryChunk[]) => {
141
+ for (const c of items) {
142
+ stmt.run(c.id, c.text, JSON.stringify(c.vector), c.source, c.timestamp,
143
+ c.metadata ? JSON.stringify(c.metadata) : null);
144
+ }
145
+ });
146
+ tx(chunks);
147
+ },
148
+
149
+ async getAll(): Promise<MemoryChunk[]> {
150
+ const rows = db.prepare("SELECT * FROM chunks").all() as any[];
151
+ return rows.map(r => ({
152
+ id: r.id, text: r.text, vector: JSON.parse(r.vector),
153
+ source: r.source, timestamp: r.timestamp,
154
+ metadata: r.metadata ? JSON.parse(r.metadata) : undefined,
155
+ }));
156
+ },
157
+
158
+ async remove(id: string) {
159
+ db.prepare("DELETE FROM chunks WHERE id = ?").run(id);
160
+ },
161
+
162
+ async clear() {
163
+ db.exec("DELETE FROM chunks");
164
+ },
165
+
166
+ async count(): Promise<number> {
167
+ const row = db.prepare("SELECT COUNT(*) as cnt FROM chunks").get() as any;
168
+ return row.cnt;
169
+ },
170
+ };
171
+
172
+ log.info("Memory store: SQLite (better-sqlite3)");
173
+ return store;
174
+ } catch {
175
+ return null;
176
+ }
177
+ }
178
+
179
+ // ─── Store factory ───────────────────────────────────────────────────
180
+
181
+ export async function createStore(memoryDir: string): Promise<VectorStore> {
182
+ // Try SQLite first
183
+ const sqlite = await trySQLiteStore(memoryDir);
184
+ if (sqlite) return sqlite;
185
+
186
+ // Fall back to JSON
187
+ log.info("Memory store: JSON (install better-sqlite3 for faster search at scale)");
188
+ return new JSONStore(memoryDir);
189
+ }
@@ -0,0 +1,126 @@
1
+ import { log } from "./logger.js";
2
+
3
+ export interface OllamaOptions {
4
+ model: string; // e.g., "gemma2", "llama3.1"
5
+ systemPrompt: string;
6
+ message: string;
7
+ baseUrl?: string; // default: "http://localhost:11434"
8
+ timeout?: number; // default: 300000 (5 min)
9
+ }
10
+
11
+ export async function executeOllama(opts: OllamaOptions): Promise<string> {
12
+ const baseUrl = opts.baseUrl || "http://localhost:11434";
13
+ const timeout = opts.timeout || 300_000;
14
+
15
+ log.info(`[Ollama] Executing ${opts.model}: ${opts.message.slice(0, 200)}`);
16
+
17
+ const controller = new AbortController();
18
+ const timer = setTimeout(() => controller.abort(), timeout);
19
+
20
+ try {
21
+ const res = await fetch(`${baseUrl}/api/chat`, {
22
+ method: "POST",
23
+ headers: { "Content-Type": "application/json" },
24
+ body: JSON.stringify({
25
+ model: opts.model,
26
+ messages: [
27
+ { role: "system", content: opts.systemPrompt },
28
+ { role: "user", content: opts.message },
29
+ ],
30
+ stream: false,
31
+ }),
32
+ signal: controller.signal,
33
+ });
34
+
35
+ if (!res.ok) {
36
+ const errText = await res.text();
37
+ log.error(`[Ollama] Error ${res.status}: ${errText}`);
38
+ throw new Error(`Ollama API error ${res.status}: ${errText}`);
39
+ }
40
+
41
+ const data = await res.json() as { message?: { content?: string }; error?: string };
42
+ if (data.error) {
43
+ throw new Error(`Ollama error: ${data.error}`);
44
+ }
45
+
46
+ const response = data.message?.content || "";
47
+ log.info(`[Ollama] Response from ${opts.model}: ${response.slice(0, 200)}`);
48
+ return response;
49
+ } finally {
50
+ clearTimeout(timer);
51
+ }
52
+ }
53
+
54
+ export async function* streamOllama(opts: OllamaOptions): AsyncGenerator<string> {
55
+ const baseUrl = opts.baseUrl || "http://localhost:11434";
56
+ const timeout = opts.timeout || 300_000;
57
+
58
+ log.info(`[Ollama] Streaming ${opts.model}: ${opts.message.slice(0, 200)}`);
59
+
60
+ const controller = new AbortController();
61
+ const timer = setTimeout(() => controller.abort(), timeout);
62
+
63
+ try {
64
+ const res = await fetch(`${baseUrl}/api/chat`, {
65
+ method: "POST",
66
+ headers: { "Content-Type": "application/json" },
67
+ body: JSON.stringify({
68
+ model: opts.model,
69
+ messages: [
70
+ { role: "system", content: opts.systemPrompt },
71
+ { role: "user", content: opts.message },
72
+ ],
73
+ stream: true,
74
+ }),
75
+ signal: controller.signal,
76
+ });
77
+
78
+ if (!res.ok) {
79
+ const errText = await res.text();
80
+ throw new Error(`Ollama API error ${res.status}: ${errText}`);
81
+ }
82
+
83
+ const reader = res.body?.getReader();
84
+ if (!reader) throw new Error("No response body");
85
+
86
+ const decoder = new TextDecoder();
87
+ while (true) {
88
+ const { done, value } = await reader.read();
89
+ if (done) break;
90
+ const chunk = decoder.decode(value, { stream: true });
91
+ // Ollama streams newline-delimited JSON
92
+ for (const line of chunk.split("\n")) {
93
+ if (!line.trim()) continue;
94
+ try {
95
+ const parsed = JSON.parse(line) as { message?: { content?: string }; done?: boolean };
96
+ if (parsed.message?.content) {
97
+ yield parsed.message.content;
98
+ }
99
+ } catch { /* skip unparseable lines */ }
100
+ }
101
+ }
102
+ } finally {
103
+ clearTimeout(timer);
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Check if Ollama is running and the model is available
109
+ */
110
+ export async function checkOllamaHealth(baseUrl?: string, model?: string): Promise<{ ok: boolean; error?: string }> {
111
+ const url = baseUrl || "http://localhost:11434";
112
+ try {
113
+ const res = await fetch(`${url}/api/tags`);
114
+ if (!res.ok) return { ok: false, error: `Ollama not responding (${res.status})` };
115
+ if (model) {
116
+ const data = await res.json() as { models?: Array<{ name: string }> };
117
+ const models = data.models?.map(m => m.name.replace(/:latest$/, "")) || [];
118
+ if (!models.some(m => m === model || m.startsWith(model + ":"))) {
119
+ return { ok: false, error: `Model "${model}" not found. Available: ${models.join(", ")}` };
120
+ }
121
+ }
122
+ return { ok: true };
123
+ } catch (err) {
124
+ return { ok: false, error: `Ollama not running at ${url}: ${err}` };
125
+ }
126
+ }
@@ -0,0 +1,259 @@
1
+ /**
2
+ * OpenAI-compatible executor — handles OpenAI, xAI (Grok), Groq, Together, Mistral,
3
+ * and any other provider that implements the OpenAI Chat Completions API.
4
+ *
5
+ * Executor format: "openai:gpt-4o", "grok:grok-3", "groq:llama-3.3-70b-versatile",
6
+ * "together:meta-llama/Llama-3.3-70B-Instruct", "mistral:mistral-large-latest"
7
+ */
8
+
9
+ import { log } from "./logger.js";
10
+
11
+ // ─── Provider registry ─────────────────────────────────────────
12
+ export interface ProviderConfig {
13
+ name: string;
14
+ baseUrl: string;
15
+ keyField: string; // field name in providerKeys, e.g., "openai"
16
+ modelsEndpoint?: string; // path to list models (default: /models)
17
+ }
18
+
19
+ export const PROVIDERS: Record<string, ProviderConfig> = {
20
+ openai: {
21
+ name: "OpenAI",
22
+ baseUrl: "https://api.openai.com/v1",
23
+ keyField: "openai",
24
+ },
25
+ grok: {
26
+ name: "xAI (Grok)",
27
+ baseUrl: "https://api.x.ai/v1",
28
+ keyField: "xai",
29
+ },
30
+ groq: {
31
+ name: "Groq",
32
+ baseUrl: "https://api.groq.com/openai/v1",
33
+ keyField: "groq",
34
+ },
35
+ together: {
36
+ name: "Together",
37
+ baseUrl: "https://api.together.xyz/v1",
38
+ keyField: "together",
39
+ },
40
+ mistral: {
41
+ name: "Mistral",
42
+ baseUrl: "https://api.mistral.ai/v1",
43
+ keyField: "mistral",
44
+ },
45
+ };
46
+
47
+ // ─── Options ────────────────────────────────────────────────────
48
+ export interface OpenAICompatOptions {
49
+ provider: string; // "openai", "grok", "groq", "together", "mistral"
50
+ model: string; // "gpt-4o", "grok-3", "llama-3.3-70b-versatile", etc.
51
+ apiKey: string;
52
+ systemPrompt: string;
53
+ message: string;
54
+ baseUrl?: string; // override provider default
55
+ timeout?: number; // default: 300000 (5 min)
56
+ temperature?: number; // default: 0.7
57
+ maxTokens?: number; // default: 4096
58
+ }
59
+
60
+ // ─── Response types ─────────────────────────────────────────────
61
+ interface ChatCompletionResponse {
62
+ id: string;
63
+ choices: Array<{
64
+ message: { role: string; content: string | null };
65
+ finish_reason: string;
66
+ }>;
67
+ usage?: { prompt_tokens: number; completion_tokens: number; total_tokens: number };
68
+ error?: { message: string; type: string; code?: string };
69
+ }
70
+
71
+ interface ChatCompletionChunk {
72
+ choices: Array<{
73
+ delta: { content?: string; role?: string };
74
+ finish_reason: string | null;
75
+ }>;
76
+ }
77
+
78
+ // ─── Execute (non-streaming) ────────────────────────────────────
79
+ export async function executeOpenAICompat(opts: OpenAICompatOptions): Promise<string> {
80
+ const providerConfig = PROVIDERS[opts.provider];
81
+ const baseUrl = opts.baseUrl || providerConfig?.baseUrl;
82
+ if (!baseUrl) throw new Error(`Unknown provider: ${opts.provider}`);
83
+
84
+ const timeout = opts.timeout || 300_000;
85
+ const providerName = providerConfig?.name || opts.provider;
86
+
87
+ log.info(`[${providerName}] Executing ${opts.model}: ${opts.message.slice(0, 200)}`);
88
+
89
+ const controller = new AbortController();
90
+ const timer = setTimeout(() => controller.abort(), timeout);
91
+
92
+ try {
93
+ const res = await fetch(`${baseUrl}/chat/completions`, {
94
+ method: "POST",
95
+ headers: {
96
+ "Content-Type": "application/json",
97
+ "Authorization": `Bearer ${opts.apiKey}`,
98
+ },
99
+ body: JSON.stringify({
100
+ model: opts.model,
101
+ messages: [
102
+ { role: "system", content: opts.systemPrompt },
103
+ { role: "user", content: opts.message },
104
+ ],
105
+ temperature: opts.temperature ?? 0.7,
106
+ max_tokens: opts.maxTokens ?? 4096,
107
+ stream: false,
108
+ }),
109
+ signal: controller.signal,
110
+ });
111
+
112
+ if (!res.ok) {
113
+ const errText = await res.text();
114
+ log.error(`[${providerName}] Error ${res.status}: ${errText}`);
115
+
116
+ // Parse structured error if possible
117
+ try {
118
+ const errJson = JSON.parse(errText);
119
+ const errMsg = errJson.error?.message || errJson.message || errText;
120
+ throw new Error(`${providerName} API error ${res.status}: ${errMsg}`);
121
+ } catch (e) {
122
+ if (e instanceof Error && e.message.startsWith(providerName)) throw e;
123
+ throw new Error(`${providerName} API error ${res.status}: ${errText}`);
124
+ }
125
+ }
126
+
127
+ const data = await res.json() as ChatCompletionResponse;
128
+
129
+ if (data.error) {
130
+ throw new Error(`${providerName} error: ${data.error.message}`);
131
+ }
132
+
133
+ const response = data.choices?.[0]?.message?.content || "";
134
+
135
+ if (data.usage) {
136
+ log.debug(`[${providerName}] ${opts.model} usage: ${data.usage.prompt_tokens}+${data.usage.completion_tokens} tokens`);
137
+ }
138
+
139
+ log.info(`[${providerName}] Response from ${opts.model}: ${response.slice(0, 200)}`);
140
+ return response;
141
+ } finally {
142
+ clearTimeout(timer);
143
+ }
144
+ }
145
+
146
+ // ─── Stream ─────────────────────────────────────────────────────
147
+ export async function* streamOpenAICompat(opts: OpenAICompatOptions): AsyncGenerator<string> {
148
+ const providerConfig = PROVIDERS[opts.provider];
149
+ const baseUrl = opts.baseUrl || providerConfig?.baseUrl;
150
+ if (!baseUrl) throw new Error(`Unknown provider: ${opts.provider}`);
151
+
152
+ const timeout = opts.timeout || 300_000;
153
+ const providerName = providerConfig?.name || opts.provider;
154
+
155
+ log.info(`[${providerName}] Streaming ${opts.model}: ${opts.message.slice(0, 200)}`);
156
+
157
+ const controller = new AbortController();
158
+ const timer = setTimeout(() => controller.abort(), timeout);
159
+
160
+ try {
161
+ const res = await fetch(`${baseUrl}/chat/completions`, {
162
+ method: "POST",
163
+ headers: {
164
+ "Content-Type": "application/json",
165
+ "Authorization": `Bearer ${opts.apiKey}`,
166
+ },
167
+ body: JSON.stringify({
168
+ model: opts.model,
169
+ messages: [
170
+ { role: "system", content: opts.systemPrompt },
171
+ { role: "user", content: opts.message },
172
+ ],
173
+ temperature: opts.temperature ?? 0.7,
174
+ max_tokens: opts.maxTokens ?? 4096,
175
+ stream: true,
176
+ }),
177
+ signal: controller.signal,
178
+ });
179
+
180
+ if (!res.ok) {
181
+ const errText = await res.text();
182
+ throw new Error(`${providerName} API error ${res.status}: ${errText}`);
183
+ }
184
+
185
+ const reader = res.body?.getReader();
186
+ if (!reader) throw new Error("No response body");
187
+
188
+ const decoder = new TextDecoder();
189
+ let buffer = "";
190
+
191
+ while (true) {
192
+ const { done, value } = await reader.read();
193
+ if (done) break;
194
+
195
+ buffer += decoder.decode(value, { stream: true });
196
+ const lines = buffer.split("\n");
197
+ buffer = lines.pop() || ""; // keep incomplete last line
198
+
199
+ for (const line of lines) {
200
+ const trimmed = line.trim();
201
+ if (!trimmed || trimmed === "data: [DONE]") continue;
202
+ if (!trimmed.startsWith("data: ")) continue;
203
+
204
+ const jsonStr = trimmed.slice(6);
205
+ try {
206
+ const chunk = JSON.parse(jsonStr) as ChatCompletionChunk;
207
+ const content = chunk.choices?.[0]?.delta?.content;
208
+ if (content) {
209
+ yield content;
210
+ }
211
+ } catch { /* skip unparseable lines */ }
212
+ }
213
+ }
214
+ } finally {
215
+ clearTimeout(timer);
216
+ }
217
+ }
218
+
219
+ // ─── Health check ───────────────────────────────────────────────
220
+ export async function checkOpenAICompatHealth(
221
+ provider: string,
222
+ apiKey: string,
223
+ baseUrl?: string,
224
+ ): Promise<{ ok: boolean; error?: string; models?: string[] }> {
225
+ const providerConfig = PROVIDERS[provider];
226
+ const url = baseUrl || providerConfig?.baseUrl;
227
+ if (!url) return { ok: false, error: `Unknown provider: ${provider}` };
228
+
229
+ const providerName = providerConfig?.name || provider;
230
+
231
+ try {
232
+ const res = await fetch(`${url}/models`, {
233
+ headers: { "Authorization": `Bearer ${apiKey}` },
234
+ signal: AbortSignal.timeout(10_000),
235
+ });
236
+
237
+ if (res.status === 401 || res.status === 403) {
238
+ return { ok: false, error: `${providerName}: Invalid API key` };
239
+ }
240
+
241
+ if (!res.ok) {
242
+ return { ok: false, error: `${providerName}: API returned ${res.status}` };
243
+ }
244
+
245
+ const data = await res.json() as { data?: Array<{ id: string }> };
246
+ const models = data.data?.map(m => m.id) || [];
247
+ return { ok: true, models: models.slice(0, 20) };
248
+ } catch (err) {
249
+ return { ok: false, error: `${providerName}: ${err}` };
250
+ }
251
+ }
252
+
253
+ /**
254
+ * Resolve provider prefix to config.
255
+ * Returns null if the prefix is not an OpenAI-compatible provider.
256
+ */
257
+ export function resolveProvider(prefix: string): ProviderConfig | null {
258
+ return PROVIDERS[prefix] || null;
259
+ }