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.
- package/README.md +113 -0
- package/agents/_template/CLAUDE.md +18 -0
- package/agents/_template/agent.json +7 -0
- package/agents/platform/agentcreator/CLAUDE.md +300 -0
- package/agents/platform/appcreator/CLAUDE.md +158 -0
- package/agents/platform/gym/CLAUDE.md +486 -0
- package/agents/platform/gym/agent.json +40 -0
- package/agents/platform/gym/programs/agent-building/program.json +160 -0
- package/agents/platform/gym/programs/automations-mastery/program.json +129 -0
- package/agents/platform/gym/programs/getting-started/program.json +124 -0
- package/agents/platform/gym/programs/mcp-integrations/program.json +116 -0
- package/agents/platform/gym/programs/multi-model-strategy/program.json +115 -0
- package/agents/platform/gym/programs/prompt-engineering/program.json +136 -0
- package/agents/platform/gym/souls/alex.md +12 -0
- package/agents/platform/gym/souls/jordan.md +12 -0
- package/agents/platform/gym/souls/morgan.md +12 -0
- package/agents/platform/gym/souls/riley.md +12 -0
- package/agents/platform/gym/souls/sam.md +12 -0
- package/agents/platform/hub/CLAUDE.md +372 -0
- package/agents/platform/promptcreator/CLAUDE.md +130 -0
- package/agents/platform/skillcreator/CLAUDE.md +163 -0
- package/bin/cli.js +566 -0
- package/config.example.json +310 -0
- package/dist/agent-registry.d.ts +32 -0
- package/dist/agent-registry.d.ts.map +1 -0
- package/dist/agent-registry.js +144 -0
- package/dist/agent-registry.js.map +1 -0
- package/dist/channels/discord.d.ts +17 -0
- package/dist/channels/discord.d.ts.map +1 -0
- package/dist/channels/discord.js +114 -0
- package/dist/channels/discord.js.map +1 -0
- package/dist/channels/imessage.d.ts +23 -0
- package/dist/channels/imessage.d.ts.map +1 -0
- package/dist/channels/imessage.js +214 -0
- package/dist/channels/imessage.js.map +1 -0
- package/dist/channels/slack.d.ts +19 -0
- package/dist/channels/slack.d.ts.map +1 -0
- package/dist/channels/slack.js +167 -0
- package/dist/channels/slack.js.map +1 -0
- package/dist/channels/telegram.d.ts +19 -0
- package/dist/channels/telegram.d.ts.map +1 -0
- package/dist/channels/telegram.js +274 -0
- package/dist/channels/telegram.js.map +1 -0
- package/dist/channels/types.d.ts +44 -0
- package/dist/channels/types.d.ts.map +1 -0
- package/dist/channels/types.js +18 -0
- package/dist/channels/types.js.map +1 -0
- package/dist/channels/whatsapp.d.ts +23 -0
- package/dist/channels/whatsapp.d.ts.map +1 -0
- package/dist/channels/whatsapp.js +189 -0
- package/dist/channels/whatsapp.js.map +1 -0
- package/dist/config.d.ts +134 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +127 -0
- package/dist/config.js.map +1 -0
- package/dist/cron.d.ts +8 -0
- package/dist/cron.d.ts.map +1 -0
- package/dist/cron.js +35 -0
- package/dist/cron.js.map +1 -0
- package/dist/decrypt-keys.d.ts +7 -0
- package/dist/decrypt-keys.d.ts.map +1 -0
- package/dist/decrypt-keys.js +53 -0
- package/dist/decrypt-keys.js.map +1 -0
- package/dist/encrypt-keys.d.ts +8 -0
- package/dist/encrypt-keys.d.ts.map +1 -0
- package/dist/encrypt-keys.js +62 -0
- package/dist/encrypt-keys.js.map +1 -0
- package/dist/executor.d.ts +31 -0
- package/dist/executor.d.ts.map +1 -0
- package/dist/executor.js +2009 -0
- package/dist/executor.js.map +1 -0
- package/dist/gemini-executor.d.ts +27 -0
- package/dist/gemini-executor.d.ts.map +1 -0
- package/dist/gemini-executor.js +160 -0
- package/dist/gemini-executor.js.map +1 -0
- package/dist/goals.d.ts +24 -0
- package/dist/goals.d.ts.map +1 -0
- package/dist/goals.js +189 -0
- package/dist/goals.js.map +1 -0
- package/dist/gym/activity-digest.d.ts +30 -0
- package/dist/gym/activity-digest.d.ts.map +1 -0
- package/dist/gym/activity-digest.js +506 -0
- package/dist/gym/activity-digest.js.map +1 -0
- package/dist/gym/dimension-scorer.d.ts +76 -0
- package/dist/gym/dimension-scorer.d.ts.map +1 -0
- package/dist/gym/dimension-scorer.js +236 -0
- package/dist/gym/dimension-scorer.js.map +1 -0
- package/dist/gym/gym-router.d.ts +7 -0
- package/dist/gym/gym-router.d.ts.map +1 -0
- package/dist/gym/gym-router.js +718 -0
- package/dist/gym/gym-router.js.map +1 -0
- package/dist/gym/index.d.ts +11 -0
- package/dist/gym/index.d.ts.map +1 -0
- package/dist/gym/index.js +11 -0
- package/dist/gym/index.js.map +1 -0
- package/dist/heartbeat.d.ts +21 -0
- package/dist/heartbeat.d.ts.map +1 -0
- package/dist/heartbeat.js +163 -0
- package/dist/heartbeat.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +254 -0
- package/dist/index.js.map +1 -0
- package/dist/keystore.d.ts +22 -0
- package/dist/keystore.d.ts.map +1 -0
- package/dist/keystore.js +178 -0
- package/dist/keystore.js.map +1 -0
- package/dist/logger.d.ts +9 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +45 -0
- package/dist/logger.js.map +1 -0
- package/dist/memory/daily.d.ts +22 -0
- package/dist/memory/daily.d.ts.map +1 -0
- package/dist/memory/daily.js +82 -0
- package/dist/memory/daily.js.map +1 -0
- package/dist/memory/embeddings.d.ts +15 -0
- package/dist/memory/embeddings.d.ts.map +1 -0
- package/dist/memory/embeddings.js +154 -0
- package/dist/memory/embeddings.js.map +1 -0
- package/dist/memory/index.d.ts +32 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/index.js +159 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/memory/search.d.ts +21 -0
- package/dist/memory/search.d.ts.map +1 -0
- package/dist/memory/search.js +77 -0
- package/dist/memory/search.js.map +1 -0
- package/dist/memory/store.d.ts +23 -0
- package/dist/memory/store.d.ts.map +1 -0
- package/dist/memory/store.js +144 -0
- package/dist/memory/store.js.map +1 -0
- package/dist/ollama-executor.d.ts +17 -0
- package/dist/ollama-executor.d.ts.map +1 -0
- package/dist/ollama-executor.js +112 -0
- package/dist/ollama-executor.js.map +1 -0
- package/dist/openai-executor.d.ts +38 -0
- package/dist/openai-executor.d.ts.map +1 -0
- package/dist/openai-executor.js +197 -0
- package/dist/openai-executor.js.map +1 -0
- package/dist/router.d.ts +11 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +185 -0
- package/dist/router.js.map +1 -0
- package/dist/test-message.d.ts +2 -0
- package/dist/test-message.d.ts.map +1 -0
- package/dist/test-message.js +60 -0
- package/dist/test-message.js.map +1 -0
- package/dist/utils/imsg-db-reader.d.ts +24 -0
- package/dist/utils/imsg-db-reader.d.ts.map +1 -0
- package/dist/utils/imsg-db-reader.js +92 -0
- package/dist/utils/imsg-db-reader.js.map +1 -0
- package/dist/utils/imsg-rpc.d.ts +25 -0
- package/dist/utils/imsg-rpc.d.ts.map +1 -0
- package/dist/utils/imsg-rpc.js +149 -0
- package/dist/utils/imsg-rpc.js.map +1 -0
- package/dist/utils/message-formatter.d.ts +3 -0
- package/dist/utils/message-formatter.d.ts.map +1 -0
- package/dist/utils/message-formatter.js +69 -0
- package/dist/utils/message-formatter.js.map +1 -0
- package/dist/web-ui.d.ts +12 -0
- package/dist/web-ui.d.ts.map +1 -0
- package/dist/web-ui.js +5784 -0
- package/dist/web-ui.js.map +1 -0
- package/dist/whatsapp-chats.d.ts +2 -0
- package/dist/whatsapp-chats.d.ts.map +1 -0
- package/dist/whatsapp-chats.js +76 -0
- package/dist/whatsapp-chats.js.map +1 -0
- package/dist/whatsapp-login.d.ts +2 -0
- package/dist/whatsapp-login.d.ts.map +1 -0
- package/dist/whatsapp-login.js +90 -0
- package/dist/whatsapp-login.js.map +1 -0
- package/dist/wiki-sync.d.ts +21 -0
- package/dist/wiki-sync.d.ts.map +1 -0
- package/dist/wiki-sync.js +147 -0
- package/dist/wiki-sync.js.map +1 -0
- package/docs/AddNewAgentGuide.md +100 -0
- package/docs/AddNewMcpGuide.md +72 -0
- package/docs/Architecture.md +795 -0
- package/docs/CLAUDE-AI-SETUP.md +166 -0
- package/docs/Setup.md +297 -0
- package/docs/ai-gym-architecture.md +1040 -0
- package/docs/ai-gym-build-plan.md +343 -0
- package/docs/ai-gym-onboarding.md +122 -0
- package/docs/appcreator_plan.md +348 -0
- package/docs/platform-mcp-audit.md +320 -0
- package/docs/server-deployment-plan.md +503 -0
- package/docs/superpowers/plans/2026-03-25-marketplace.md +1281 -0
- package/docs/superpowers/specs/2026-03-25-marketplace-design.md +287 -0
- package/docs/user-guide.md +2016 -0
- package/mcp-catalog.json +628 -0
- package/package.json +63 -0
- package/public/MyAIforOne-logomark-512.svg +16 -0
- package/public/MyAIforOne-logomark-transparent.svg +15 -0
- package/public/activity.html +314 -0
- package/public/admin.html +1674 -0
- package/public/agent-dashboard.html +670 -0
- package/public/api-docs.html +1106 -0
- package/public/automations.html +722 -0
- package/public/canvas.css +223 -0
- package/public/canvas.js +588 -0
- package/public/changelog.html +231 -0
- package/public/gym.html +2766 -0
- package/public/home.html +1930 -0
- package/public/index.html +2809 -0
- package/public/lab.html +1643 -0
- package/public/library.html +1442 -0
- package/public/marketplace.html +1101 -0
- package/public/mcp-docs.html +441 -0
- package/public/mini.html +390 -0
- package/public/monitor.html +584 -0
- package/public/org.html +4304 -0
- package/public/projects.html +734 -0
- package/public/settings.html +645 -0
- package/public/tasks.html +932 -0
- package/public/trainers/alex.svg +12 -0
- package/public/trainers/jordan.svg +12 -0
- package/public/trainers/morgan.svg +12 -0
- package/public/trainers/riley.svg +12 -0
- package/public/trainers/sam.svg +12 -0
- package/public/user-guide.html +218 -0
- package/registry/agents.json +3 -0
- package/registry/apps.json +20 -0
- package/registry/installed-drafts.json +3 -0
- package/registry/mcps.json +1084 -0
- package/registry/prompts/personal/mcp-test-prompt.md +6 -0
- package/registry/prompts/personal/memory-recall.md +6 -0
- package/registry/prompts/platform/brainstorm.md +15 -0
- package/registry/prompts/platform/code-review.md +16 -0
- package/registry/prompts/platform/explain.md +16 -0
- package/registry/prompts.json +58 -0
- package/registry/skills/external/brainstorming.md +5 -0
- package/registry/skills/external/code-review.md +40 -0
- package/registry/skills/external/frontend-patterns.md +642 -0
- package/registry/skills/external/frontend-slides.md +184 -0
- package/registry/skills/external/systematic-debugging.md +5 -0
- package/registry/skills/external/tdd.md +328 -0
- package/registry/skills/external/verification-before-completion.md +5 -0
- package/registry/skills/external/writing-plans.md +5 -0
- package/registry/skills/platform/ai41_app_build.md +930 -0
- package/registry/skills/platform/ai41_app_deploy.md +168 -0
- package/registry/skills/platform/ai41_app_orchestrator.md +239 -0
- package/registry/skills/platform/ai41_app_patterns.md +359 -0
- package/registry/skills/platform/ai41_app_register.md +85 -0
- package/registry/skills/platform/ai41_app_scaffold.md +421 -0
- package/registry/skills/platform/ai41_app_verify.md +107 -0
- package/registry/skills/platform/opProjectCreate.md +239 -0
- package/registry/skills/platform/op_devbrowser.md +136 -0
- package/registry/skills/platform/sop_brandguidelines.md +103 -0
- package/registry/skills/platform/sop_docx.md +117 -0
- package/registry/skills/platform/sop_frontenddesign.md +44 -0
- package/registry/skills/platform/sop_frontenddesign_v2.md +659 -0
- package/registry/skills/platform/sop_mcpbuilder.md +133 -0
- package/registry/skills/platform/sop_pdf.md +172 -0
- package/registry/skills/platform/sop_pptx.md +133 -0
- package/registry/skills/platform/sop_skillcreator.md +104 -0
- package/registry/skills/platform/sop_themefactory.md +128 -0
- package/registry/skills/platform/sop_webapptesting.md +75 -0
- package/registry/skills/platform/sop_webartifactsbuilder.md +97 -0
- package/registry/skills/platform/sop_xlsx.md +134 -0
- package/registry/skills.json +1055 -0
- package/scripts/discover-chats.sh +11 -0
- package/scripts/install-service-windows.ps1 +87 -0
- package/scripts/install-service.sh +52 -0
- package/scripts/seed-registry.ts +195 -0
- package/scripts/test-send.sh +5 -0
- package/scripts/tray-indicator.ps1 +35 -0
- package/scripts/uninstall-service-windows.ps1 +23 -0
- package/scripts/uninstall-service.sh +15 -0
- package/scripts/xbar-myagent.5s.sh +32 -0
- package/server/mcp-server/dist/index.d.ts +11 -0
- package/server/mcp-server/dist/index.js +1332 -0
- package/server/mcp-server/dist/lib/api-client.d.ts +165 -0
- package/server/mcp-server/dist/lib/api-client.js +241 -0
- package/server/mcp-server/index.ts +1545 -0
- package/server/mcp-server/lib/api-client.ts +366 -0
- package/server/mcp-server/tsconfig.json +14 -0
- package/src/agent-registry.ts +180 -0
- package/src/channels/discord.ts +129 -0
- package/src/channels/imessage.ts +261 -0
- package/src/channels/slack.ts +208 -0
- package/src/channels/telegram.ts +307 -0
- package/src/channels/types.ts +62 -0
- package/src/channels/whatsapp.ts +227 -0
- package/src/config.ts +281 -0
- package/src/cron.ts +43 -0
- package/src/decrypt-keys.ts +60 -0
- package/src/encrypt-keys.ts +70 -0
- package/src/executor.ts +2190 -0
- package/src/gemini-executor.ts +212 -0
- package/src/goals.ts +240 -0
- package/src/gym/activity-digest.ts +546 -0
- package/src/gym/dimension-scorer.ts +297 -0
- package/src/gym/gym-router.ts +801 -0
- package/src/gym/index.ts +19 -0
- package/src/heartbeat.ts +220 -0
- package/src/index.ts +275 -0
- package/src/keystore.ts +190 -0
- package/src/logger.ts +51 -0
- package/src/memory/daily.ts +101 -0
- package/src/memory/embeddings.ts +185 -0
- package/src/memory/index.ts +218 -0
- package/src/memory/search.ts +124 -0
- package/src/memory/store.ts +189 -0
- package/src/ollama-executor.ts +126 -0
- package/src/openai-executor.ts +259 -0
- package/src/router.ts +230 -0
- package/src/test-message.ts +72 -0
- package/src/utils/imsg-db-reader.ts +109 -0
- package/src/utils/imsg-rpc.ts +178 -0
- package/src/utils/message-formatter.ts +90 -0
- package/src/web-ui.ts +5778 -0
- package/src/whatsapp-chats.ts +91 -0
- package/src/whatsapp-login.ts +110 -0
- package/src/wiki-sync.ts +199 -0
- 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
|
+
}
|