@johpaz/hive 1.1.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/CONTRIBUTING.md +44 -0
- package/README.md +310 -0
- package/package.json +96 -0
- package/packages/cli/package.json +28 -0
- package/packages/cli/src/commands/agent-run.ts +168 -0
- package/packages/cli/src/commands/agents.ts +398 -0
- package/packages/cli/src/commands/chat.ts +142 -0
- package/packages/cli/src/commands/config.ts +50 -0
- package/packages/cli/src/commands/cron.ts +161 -0
- package/packages/cli/src/commands/dev.ts +95 -0
- package/packages/cli/src/commands/doctor.ts +133 -0
- package/packages/cli/src/commands/gateway.ts +443 -0
- package/packages/cli/src/commands/logs.ts +57 -0
- package/packages/cli/src/commands/mcp.ts +175 -0
- package/packages/cli/src/commands/message.ts +77 -0
- package/packages/cli/src/commands/onboard.ts +1868 -0
- package/packages/cli/src/commands/security.ts +144 -0
- package/packages/cli/src/commands/service.ts +50 -0
- package/packages/cli/src/commands/sessions.ts +116 -0
- package/packages/cli/src/commands/skills.ts +187 -0
- package/packages/cli/src/commands/update.ts +25 -0
- package/packages/cli/src/index.ts +185 -0
- package/packages/cli/src/utils/token.ts +6 -0
- package/packages/code-bridge/README.md +78 -0
- package/packages/code-bridge/package.json +18 -0
- package/packages/code-bridge/src/index.ts +95 -0
- package/packages/code-bridge/src/process-manager.ts +212 -0
- package/packages/code-bridge/src/schemas.ts +133 -0
- package/packages/core/package.json +46 -0
- package/packages/core/src/agent/agent-loop.ts +369 -0
- package/packages/core/src/agent/compaction.ts +140 -0
- package/packages/core/src/agent/context-compiler.ts +378 -0
- package/packages/core/src/agent/context-guard.ts +91 -0
- package/packages/core/src/agent/context.ts +138 -0
- package/packages/core/src/agent/conversation-store.ts +198 -0
- package/packages/core/src/agent/curator.ts +158 -0
- package/packages/core/src/agent/hooks.ts +166 -0
- package/packages/core/src/agent/index.ts +116 -0
- package/packages/core/src/agent/llm-client.ts +503 -0
- package/packages/core/src/agent/native-tools.ts +505 -0
- package/packages/core/src/agent/prompt-builder.ts +532 -0
- package/packages/core/src/agent/providers/index.ts +167 -0
- package/packages/core/src/agent/providers.ts +1 -0
- package/packages/core/src/agent/reflector.ts +170 -0
- package/packages/core/src/agent/service.ts +64 -0
- package/packages/core/src/agent/stuck-loop.ts +133 -0
- package/packages/core/src/agent/supervisor.ts +39 -0
- package/packages/core/src/agent/tracer.ts +102 -0
- package/packages/core/src/agent/workspace.ts +110 -0
- package/packages/core/src/canvas/canvas-manager.test.ts +161 -0
- package/packages/core/src/canvas/canvas-manager.ts +319 -0
- package/packages/core/src/canvas/canvas-tools.ts +420 -0
- package/packages/core/src/canvas/emitter.ts +115 -0
- package/packages/core/src/canvas/index.ts +2 -0
- package/packages/core/src/channels/base.ts +138 -0
- package/packages/core/src/channels/discord.ts +260 -0
- package/packages/core/src/channels/index.ts +7 -0
- package/packages/core/src/channels/manager.ts +383 -0
- package/packages/core/src/channels/slack.ts +287 -0
- package/packages/core/src/channels/telegram.ts +502 -0
- package/packages/core/src/channels/webchat.ts +128 -0
- package/packages/core/src/channels/whatsapp.ts +375 -0
- package/packages/core/src/config/index.ts +12 -0
- package/packages/core/src/config/loader.ts +529 -0
- package/packages/core/src/events/event-bus.ts +169 -0
- package/packages/core/src/gateway/index.ts +5 -0
- package/packages/core/src/gateway/initializer.ts +290 -0
- package/packages/core/src/gateway/lane-queue.ts +169 -0
- package/packages/core/src/gateway/resolver.ts +108 -0
- package/packages/core/src/gateway/router.ts +124 -0
- package/packages/core/src/gateway/server.ts +3317 -0
- package/packages/core/src/gateway/session.ts +95 -0
- package/packages/core/src/gateway/slash-commands.ts +192 -0
- package/packages/core/src/heartbeat/index.ts +157 -0
- package/packages/core/src/index.ts +19 -0
- package/packages/core/src/integrations/catalog.ts +286 -0
- package/packages/core/src/integrations/env.ts +64 -0
- package/packages/core/src/integrations/index.ts +2 -0
- package/packages/core/src/memory/index.ts +1 -0
- package/packages/core/src/memory/notes.ts +68 -0
- package/packages/core/src/plugins/api.ts +128 -0
- package/packages/core/src/plugins/index.ts +2 -0
- package/packages/core/src/plugins/loader.ts +365 -0
- package/packages/core/src/resilience/circuit-breaker.ts +225 -0
- package/packages/core/src/security/google-chat.ts +269 -0
- package/packages/core/src/security/index.ts +192 -0
- package/packages/core/src/security/pairing.ts +250 -0
- package/packages/core/src/security/rate-limit.ts +270 -0
- package/packages/core/src/security/signal.ts +321 -0
- package/packages/core/src/state/store.ts +312 -0
- package/packages/core/src/storage/bun-sqlite-store.ts +188 -0
- package/packages/core/src/storage/crypto.ts +101 -0
- package/packages/core/src/storage/db-context.ts +333 -0
- package/packages/core/src/storage/onboarding.ts +1087 -0
- package/packages/core/src/storage/schema.ts +541 -0
- package/packages/core/src/storage/seed.ts +571 -0
- package/packages/core/src/storage/sqlite.ts +387 -0
- package/packages/core/src/storage/usage.ts +212 -0
- package/packages/core/src/tools/bridge-events.ts +74 -0
- package/packages/core/src/tools/browser.ts +275 -0
- package/packages/core/src/tools/codebridge.ts +421 -0
- package/packages/core/src/tools/coordinator-tools.ts +179 -0
- package/packages/core/src/tools/cron.ts +611 -0
- package/packages/core/src/tools/exec.ts +140 -0
- package/packages/core/src/tools/fs.ts +364 -0
- package/packages/core/src/tools/index.ts +12 -0
- package/packages/core/src/tools/memory.ts +176 -0
- package/packages/core/src/tools/notify.ts +113 -0
- package/packages/core/src/tools/project-management.ts +376 -0
- package/packages/core/src/tools/project.ts +375 -0
- package/packages/core/src/tools/read.ts +158 -0
- package/packages/core/src/tools/web.ts +436 -0
- package/packages/core/src/tools/workspace.ts +171 -0
- package/packages/core/src/utils/benchmark.ts +80 -0
- package/packages/core/src/utils/crypto.ts +73 -0
- package/packages/core/src/utils/date.ts +42 -0
- package/packages/core/src/utils/index.ts +4 -0
- package/packages/core/src/utils/logger.ts +388 -0
- package/packages/core/src/utils/retry.ts +70 -0
- package/packages/core/src/voice/index.ts +583 -0
- package/packages/core/tsconfig.json +9 -0
- package/packages/mcp/package.json +26 -0
- package/packages/mcp/src/config.ts +13 -0
- package/packages/mcp/src/index.ts +1 -0
- package/packages/mcp/src/logger.ts +42 -0
- package/packages/mcp/src/manager.ts +434 -0
- package/packages/mcp/src/transports/index.ts +67 -0
- package/packages/mcp/src/transports/sse.ts +241 -0
- package/packages/mcp/src/transports/websocket.ts +159 -0
- package/packages/skills/package.json +21 -0
- package/packages/skills/src/bundled/agent_management/SKILL.md +24 -0
- package/packages/skills/src/bundled/browser_automation/SKILL.md +30 -0
- package/packages/skills/src/bundled/context_compact/SKILL.md +35 -0
- package/packages/skills/src/bundled/cron_manager/SKILL.md +52 -0
- package/packages/skills/src/bundled/file_manager/SKILL.md +76 -0
- package/packages/skills/src/bundled/http_client/SKILL.md +24 -0
- package/packages/skills/src/bundled/memory/SKILL.md +42 -0
- package/packages/skills/src/bundled/project_management/SKILL.md +26 -0
- package/packages/skills/src/bundled/shell/SKILL.md +43 -0
- package/packages/skills/src/bundled/system_notify/SKILL.md +52 -0
- package/packages/skills/src/bundled/voice/SKILL.md +25 -0
- package/packages/skills/src/bundled/web_search/SKILL.md +29 -0
- package/packages/skills/src/index.ts +1 -0
- package/packages/skills/src/loader.ts +282 -0
- package/packages/tools/package.json +43 -0
- package/packages/tools/src/browser/browser.test.ts +111 -0
- package/packages/tools/src/browser/index.ts +272 -0
- package/packages/tools/src/canvas/index.ts +220 -0
- package/packages/tools/src/cron/cron.test.ts +164 -0
- package/packages/tools/src/cron/index.ts +304 -0
- package/packages/tools/src/filesystem/filesystem.test.ts +240 -0
- package/packages/tools/src/filesystem/index.ts +379 -0
- package/packages/tools/src/git/index.ts +239 -0
- package/packages/tools/src/index.ts +4 -0
- package/packages/tools/src/shell/detect-env.ts +70 -0
- package/packages/tools/tsconfig.json +9 -0
|
@@ -0,0 +1,1087 @@
|
|
|
1
|
+
import { logger } from "../utils/logger.ts";
|
|
2
|
+
import { getDb, initializeDatabase } from "./sqlite";
|
|
3
|
+
import { encryptApiKey, encryptConfig, decryptApiKey, decryptConfig } from "./crypto";
|
|
4
|
+
import { seedAllData } from "./seed";
|
|
5
|
+
|
|
6
|
+
export interface OnboardingSection {
|
|
7
|
+
step: "user" | "skills" | "ethics" | "tools" | "provider" | "model" | "channel" | "codebridge" | "mcp" | "agent" | "complete";
|
|
8
|
+
userId: string;
|
|
9
|
+
data: Record<string, unknown>;
|
|
10
|
+
completedAt?: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const log = logger.child("onboarding");
|
|
14
|
+
|
|
15
|
+
export function initOnboardingDb(): void {
|
|
16
|
+
try {
|
|
17
|
+
initializeDatabase();
|
|
18
|
+
|
|
19
|
+
// Verificar si la DB ya tiene datos antes de hacer seed
|
|
20
|
+
const db = getDb();
|
|
21
|
+
const userCount = db.query("SELECT COUNT(*) as count FROM users").get() as { count: number };
|
|
22
|
+
|
|
23
|
+
if (userCount.count > 0) {
|
|
24
|
+
log.info("✅ DB ya inicializada con " + userCount.count + " usuario(s). Saltando seed.");
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
log.info("🌱 Ejecutando seed de datos...");
|
|
29
|
+
seedAllData();
|
|
30
|
+
log.info("✅ Seed completado correctamente.");
|
|
31
|
+
} catch (e) {
|
|
32
|
+
log.error("⚠️ Fallo al inicializar/poblar la DB:", { error: (e as Error).message });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function saveUserProfile(data: {
|
|
37
|
+
userId?: string;
|
|
38
|
+
userName?: string;
|
|
39
|
+
userLanguage?: string;
|
|
40
|
+
userTimezone?: string;
|
|
41
|
+
userOccupation?: string;
|
|
42
|
+
userNotes?: string;
|
|
43
|
+
agentName?: string;
|
|
44
|
+
agentId?: string;
|
|
45
|
+
agentDescription?: string;
|
|
46
|
+
agentTone?: string;
|
|
47
|
+
channelUserId?: string;
|
|
48
|
+
}): string {
|
|
49
|
+
try {
|
|
50
|
+
const db = getDb();
|
|
51
|
+
let finalUserId = data.userId;
|
|
52
|
+
|
|
53
|
+
if (!finalUserId) {
|
|
54
|
+
// 1️⃣ Dejar que SQLite genere el ID automáticamente con randomblob(16)
|
|
55
|
+
const result = db.query(`
|
|
56
|
+
INSERT INTO users (name, language, timezone, occupation, notes)
|
|
57
|
+
VALUES (?, ?, ?, ?, ?) RETURNING id
|
|
58
|
+
`).get(
|
|
59
|
+
data.userName || null,
|
|
60
|
+
data.userLanguage || null,
|
|
61
|
+
data.userTimezone || null,
|
|
62
|
+
data.userOccupation || null,
|
|
63
|
+
data.userNotes || null
|
|
64
|
+
) as { id: string };
|
|
65
|
+
finalUserId = result.id;
|
|
66
|
+
log.info("✅ User created with auto-generated ID", { userId: finalUserId });
|
|
67
|
+
} else {
|
|
68
|
+
// 1️⃣ Upsert con ID explícito (flujo web o actualización)
|
|
69
|
+
db.query(`
|
|
70
|
+
INSERT INTO users (id, name, language, timezone, occupation, notes)
|
|
71
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
72
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
73
|
+
name = COALESCE(excluded.name, name),
|
|
74
|
+
language = COALESCE(excluded.language, language),
|
|
75
|
+
timezone = COALESCE(excluded.timezone, timezone),
|
|
76
|
+
occupation = COALESCE(excluded.occupation, occupation),
|
|
77
|
+
notes = COALESCE(excluded.notes, notes)
|
|
78
|
+
`).run(
|
|
79
|
+
finalUserId,
|
|
80
|
+
data.userName || null,
|
|
81
|
+
data.userLanguage || null,
|
|
82
|
+
data.userTimezone || null,
|
|
83
|
+
data.userOccupation || null,
|
|
84
|
+
data.userNotes || null
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// 2️⃣ Crear identidad base para webchat (sesión única)
|
|
89
|
+
if (data.channelUserId) {
|
|
90
|
+
db.query(`
|
|
91
|
+
INSERT OR REPLACE INTO user_identities (user_id, channel, channel_user_id)
|
|
92
|
+
VALUES (?, 'webchat', ?)
|
|
93
|
+
`).run(finalUserId, data.channelUserId);
|
|
94
|
+
log.info("✅ User identity created for webchat", { userId: finalUserId });
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// 3️⃣ Crear o actualizar agente
|
|
98
|
+
if (data.agentId && data.agentName) {
|
|
99
|
+
const systemPrompt = buildAgentSystemPrompt(data.agentName, data.agentDescription, data.agentTone || "friendly")
|
|
100
|
+
db.query(`
|
|
101
|
+
INSERT INTO agents
|
|
102
|
+
(id, user_id, name, description, tone, system_prompt, status, is_coordinator)
|
|
103
|
+
VALUES (?, ?, ?, ?, ?, ?, 'idle', 1)
|
|
104
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
105
|
+
user_id = COALESCE(excluded.user_id, user_id),
|
|
106
|
+
name = COALESCE(excluded.name, name),
|
|
107
|
+
description = COALESCE(excluded.description, description),
|
|
108
|
+
tone = COALESCE(excluded.tone, tone),
|
|
109
|
+
system_prompt = excluded.system_prompt
|
|
110
|
+
`).run(
|
|
111
|
+
data.agentId,
|
|
112
|
+
finalUserId,
|
|
113
|
+
data.agentName,
|
|
114
|
+
data.agentDescription || null,
|
|
115
|
+
data.agentTone || null,
|
|
116
|
+
systemPrompt
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return finalUserId;
|
|
121
|
+
} catch (e) {
|
|
122
|
+
log.error("⚠️ Error saving user profile:", { error: (e as Error).message });
|
|
123
|
+
throw e;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function activateSkills(userId: string, skillIds: string[]): void {
|
|
128
|
+
try {
|
|
129
|
+
const db = getDb();
|
|
130
|
+
// Activar skills seleccionadas
|
|
131
|
+
for (const skillId of skillIds) {
|
|
132
|
+
db.query(`UPDATE skills SET active = 1, enabled = 1 WHERE id = ?`).run(skillId);
|
|
133
|
+
}
|
|
134
|
+
log.info("✅ Skills activadas:", { skillIds: skillIds.join(", ") });
|
|
135
|
+
} catch (e) {
|
|
136
|
+
log.error("⚠️ Error activating skills:", { error: (e as Error).message });
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export function activateEthics(userId: string, ethicsId: string): void {
|
|
141
|
+
try {
|
|
142
|
+
const db = getDb();
|
|
143
|
+
// Activar el ethics seleccionado
|
|
144
|
+
db.query(`UPDATE ethics SET active = 1 WHERE id = ?`).run(ethicsId);
|
|
145
|
+
// Desactivar los demás
|
|
146
|
+
db.query(`UPDATE ethics SET active = 0 WHERE id != ?`).run(ethicsId);
|
|
147
|
+
log.info("✅ Ethics activado:", { ethicsId });
|
|
148
|
+
} catch (e) {
|
|
149
|
+
log.error("⚠️ Error activating ethics:", { error: (e as Error).message });
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function activateTools(userId: string, toolIds: string[]): void {
|
|
154
|
+
try {
|
|
155
|
+
const db = getDb();
|
|
156
|
+
// Activar tools seleccionadas
|
|
157
|
+
for (const toolId of toolIds) {
|
|
158
|
+
db.query(`UPDATE tools SET active = 1, enabled = 1 WHERE id = ?`).run(toolId);
|
|
159
|
+
}
|
|
160
|
+
log.info("✅ Tools activadas:", { toolIds: toolIds.join(", ") });
|
|
161
|
+
} catch (e) {
|
|
162
|
+
log.error("⚠️ Error activating tools:", { error: (e as Error).message });
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export async function saveProviderConfig(data: {
|
|
167
|
+
userId: string;
|
|
168
|
+
provider: string;
|
|
169
|
+
model: string;
|
|
170
|
+
apiKey?: string;
|
|
171
|
+
baseUrl?: string;
|
|
172
|
+
}): Promise<void> {
|
|
173
|
+
try {
|
|
174
|
+
const db = getDb();
|
|
175
|
+
|
|
176
|
+
let apiKeyEncrypted = null;
|
|
177
|
+
let apiKeyIv = null;
|
|
178
|
+
|
|
179
|
+
if (data.apiKey) {
|
|
180
|
+
const encrypted = await encryptApiKey(data.apiKey);
|
|
181
|
+
apiKeyEncrypted = encrypted.encrypted;
|
|
182
|
+
apiKeyIv = encrypted.iv;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// 1️⃣ Primero: Actualizar provider global con API key del usuario
|
|
186
|
+
db.query(`
|
|
187
|
+
UPDATE providers SET
|
|
188
|
+
api_key_encrypted = ?,
|
|
189
|
+
api_key_iv = ?,
|
|
190
|
+
base_url = ?,
|
|
191
|
+
enabled = 1,
|
|
192
|
+
active = 1
|
|
193
|
+
WHERE id = ?
|
|
194
|
+
`).run(apiKeyEncrypted, apiKeyIv, data.baseUrl || null, data.provider);
|
|
195
|
+
|
|
196
|
+
log.info("✅ Provider actualizado:", { provider: data.provider });
|
|
197
|
+
|
|
198
|
+
// 2️⃣ Segundo: Activar el modelo seleccionado
|
|
199
|
+
db.query(`
|
|
200
|
+
UPDATE models SET enabled = 1, active = 1
|
|
201
|
+
WHERE id = ?
|
|
202
|
+
`).run(data.model);
|
|
203
|
+
|
|
204
|
+
log.info("✅ Model activado:", { model: data.model });
|
|
205
|
+
} catch (e) {
|
|
206
|
+
log.error("⚠️ Error saving provider:", { error: (e as Error).message });
|
|
207
|
+
throw e;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export function activateCodeBridge(userId: string, codeBridgeConfig: { id: string; enabled: boolean; port?: number }[]): void {
|
|
212
|
+
try {
|
|
213
|
+
const db = getDb();
|
|
214
|
+
// 7️⃣ Séptimo: Configurar Code Bridge CLIs seleccionados
|
|
215
|
+
for (const cb of codeBridgeConfig) {
|
|
216
|
+
db.query(`
|
|
217
|
+
UPDATE code_bridge SET enabled = ?, active = ?, port = ?, user_id = ?
|
|
218
|
+
WHERE id = ?
|
|
219
|
+
`).run(cb.enabled ? 1 : 0, cb.enabled ? 1 : 0, cb.port || 18791, userId, cb.id);
|
|
220
|
+
}
|
|
221
|
+
log.info("✅ Code Bridge configurado:", { codeBridgeIds: codeBridgeConfig.map(c => c.id).join(", ") });
|
|
222
|
+
} catch (e) {
|
|
223
|
+
log.error("⚠️ Error configuring code bridge:", { error: (e as Error).message });
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export function activateMcpServers(userId: string, mcpIds: string[]): void {
|
|
228
|
+
try {
|
|
229
|
+
const db = getDb();
|
|
230
|
+
// Activar MCP servers seleccionados
|
|
231
|
+
for (const mcpId of mcpIds) {
|
|
232
|
+
db.query(`UPDATE mcp_servers SET active = 1, enabled = 1 WHERE id = ?`).run(mcpId);
|
|
233
|
+
}
|
|
234
|
+
log.info("✅ MCP servers activados:", { mcpIds: mcpIds.join(", ") });
|
|
235
|
+
} catch (e) {
|
|
236
|
+
log.error("⚠️ Error activating MCP servers:", { error: (e as Error).message });
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function buildAgentSystemPrompt(name: string, description: string | undefined, tone: string): string {
|
|
241
|
+
const toneGuide: Record<string, string> = {
|
|
242
|
+
friendly: "Sos cálido, cercano y empático. Usás un lenguaje natural y amigable, como si hablaras con un amigo de confianza.",
|
|
243
|
+
professional: "Sos preciso, claro y formal. Priorizás la exactitud técnica y el lenguaje estructurado.",
|
|
244
|
+
direct: "Sos conciso y al punto. Sin rodeos ni relleno — respondés exactamente lo que se pregunta.",
|
|
245
|
+
casual: "Sos relajado e informal. Podés usar humor moderado y un tono conversacional descontracturado.",
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const toneText = toneGuide[tone] || toneGuide.friendly
|
|
249
|
+
|
|
250
|
+
const lines = [
|
|
251
|
+
`Sos ${name}${description ? `, ${description}` : ""}.`,
|
|
252
|
+
"",
|
|
253
|
+
`TONO Y ESTILO:`,
|
|
254
|
+
toneText,
|
|
255
|
+
"",
|
|
256
|
+
`PRINCIPIOS:`,
|
|
257
|
+
`- Siempre usá las herramientas disponibles antes de pedir información al usuario.`,
|
|
258
|
+
`- Confirmá acciones irreversibles (borrar archivos, cancelar tareas) antes de ejecutarlas.`,
|
|
259
|
+
`- Cuando no puedas completar algo, explicá brevemente por qué y qué alternativas hay.`,
|
|
260
|
+
`- Adaptá tu nivel técnico al contexto del usuario.`,
|
|
261
|
+
]
|
|
262
|
+
|
|
263
|
+
return lines.join("\n")
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export function saveAgentConfig(data: {
|
|
267
|
+
userId: string;
|
|
268
|
+
agentId?: string;
|
|
269
|
+
agentName: string;
|
|
270
|
+
providerId: string;
|
|
271
|
+
modelId: string;
|
|
272
|
+
tone: string;
|
|
273
|
+
description?: string;
|
|
274
|
+
}): string {
|
|
275
|
+
try {
|
|
276
|
+
const db = getDb();
|
|
277
|
+
let finalAgentId = data.agentId;
|
|
278
|
+
|
|
279
|
+
const systemPrompt = buildAgentSystemPrompt(data.agentName, data.description, data.tone)
|
|
280
|
+
|
|
281
|
+
// Si no se pasa agentId, dejar que SQLite lo genere automáticamente
|
|
282
|
+
if (!finalAgentId) {
|
|
283
|
+
const result = db.query(`
|
|
284
|
+
INSERT INTO agents
|
|
285
|
+
(user_id, name, description, tone, system_prompt, provider_id, model_id, status, is_coordinator, enabled)
|
|
286
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, 'idle', 1, 1)
|
|
287
|
+
RETURNING id
|
|
288
|
+
`).get(
|
|
289
|
+
data.userId,
|
|
290
|
+
data.agentName,
|
|
291
|
+
data.description || null,
|
|
292
|
+
data.tone,
|
|
293
|
+
systemPrompt,
|
|
294
|
+
data.providerId || null,
|
|
295
|
+
data.modelId || null
|
|
296
|
+
) as { id: string };
|
|
297
|
+
finalAgentId = result.id;
|
|
298
|
+
log.info("✅ Agent created with auto-generated ID", { agentId: finalAgentId });
|
|
299
|
+
} else {
|
|
300
|
+
// INSERT or UPDATE agent (crea nuevo o actualiza existente)
|
|
301
|
+
db.query(`
|
|
302
|
+
INSERT INTO agents
|
|
303
|
+
(id, user_id, name, description, tone, system_prompt, provider_id, model_id, status, is_coordinator, enabled)
|
|
304
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'idle', 1, 1)
|
|
305
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
306
|
+
user_id = COALESCE(excluded.user_id, user_id),
|
|
307
|
+
name = COALESCE(excluded.name, name),
|
|
308
|
+
description = COALESCE(excluded.description, description),
|
|
309
|
+
tone = COALESCE(excluded.tone, tone),
|
|
310
|
+
system_prompt = excluded.system_prompt,
|
|
311
|
+
provider_id = COALESCE(excluded.provider_id, provider_id),
|
|
312
|
+
model_id = COALESCE(excluded.model_id, model_id),
|
|
313
|
+
status = 'idle',
|
|
314
|
+
enabled = 1,
|
|
315
|
+
is_coordinator = 1
|
|
316
|
+
`).run(
|
|
317
|
+
data.agentId,
|
|
318
|
+
data.userId,
|
|
319
|
+
data.agentName,
|
|
320
|
+
data.description || null,
|
|
321
|
+
data.tone,
|
|
322
|
+
systemPrompt,
|
|
323
|
+
data.providerId || null,
|
|
324
|
+
data.modelId || null
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return finalAgentId;
|
|
329
|
+
} catch (e) {
|
|
330
|
+
log.error("⚠️ Error saving agent:", { error: (e as Error).message });
|
|
331
|
+
throw e;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export async function activateChannel(userId: string, data: {
|
|
336
|
+
channelId: string;
|
|
337
|
+
channelUserId?: string; // For creating user_identity
|
|
338
|
+
config?: Record<string, unknown>;
|
|
339
|
+
}): Promise<void> {
|
|
340
|
+
try {
|
|
341
|
+
const db = getDb();
|
|
342
|
+
|
|
343
|
+
if (data.config && Object.keys(data.config).length > 0) {
|
|
344
|
+
const encrypted = await encryptConfig(data.config);
|
|
345
|
+
db.query(`
|
|
346
|
+
UPDATE channels
|
|
347
|
+
SET user_id = ?, active = 1, enabled = 1, status = 'connected',
|
|
348
|
+
config_encrypted = ?, config_iv = ?
|
|
349
|
+
WHERE id = ?
|
|
350
|
+
`).run(userId, encrypted.encrypted, encrypted.iv, data.channelId);
|
|
351
|
+
} else {
|
|
352
|
+
db.query(`
|
|
353
|
+
UPDATE channels
|
|
354
|
+
SET user_id = ?, active = 1, enabled = 1, status = 'connected'
|
|
355
|
+
WHERE id = ?
|
|
356
|
+
`).run(userId, data.channelId);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Create user_identity for the channel if channelUserId provided
|
|
360
|
+
if (data.channelUserId) {
|
|
361
|
+
const channelType = data.channelId; // webchat, telegram, discord, etc.
|
|
362
|
+
db.query(`
|
|
363
|
+
INSERT OR REPLACE INTO user_identities (user_id, channel, channel_user_id)
|
|
364
|
+
VALUES (?, ?, ?)
|
|
365
|
+
`).run(userId, channelType, data.channelUserId);
|
|
366
|
+
log.info("✅ User identity created", { userId, channel: channelType });
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
log.info("✅ Channel activated:", { channelId: data.channelId, userId });
|
|
370
|
+
} catch (e) {
|
|
371
|
+
log.error("⚠️ Error activating channel:", { error: (e as Error).message });
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
export async function saveVoiceConfig(data: {
|
|
376
|
+
userId: string;
|
|
377
|
+
channelId: string;
|
|
378
|
+
voiceEnabled: boolean;
|
|
379
|
+
sttProvider: string;
|
|
380
|
+
ttsProvider: string;
|
|
381
|
+
sttApiKey?: string;
|
|
382
|
+
ttsApiKey?: string;
|
|
383
|
+
}): Promise<void> {
|
|
384
|
+
try {
|
|
385
|
+
const db = getDb();
|
|
386
|
+
|
|
387
|
+
// Activate STT and TTS models
|
|
388
|
+
db.query(`UPDATE models SET active = 1, enabled = 1 WHERE id = ?`).run(data.sttProvider);
|
|
389
|
+
db.query(`UPDATE models SET active = 1, enabled = 1 WHERE id = ?`).run(data.ttsProvider);
|
|
390
|
+
|
|
391
|
+
// Determine provider IDs based on model IDs
|
|
392
|
+
let sttProviderId = "";
|
|
393
|
+
let ttsProviderId = "";
|
|
394
|
+
|
|
395
|
+
if (data.sttProvider.startsWith("whisper") || data.sttProvider === "distil-whisper-large-v3-en") {
|
|
396
|
+
sttProviderId = "groq";
|
|
397
|
+
} else if (data.sttProvider === "whisper-1") {
|
|
398
|
+
sttProviderId = "openai";
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
if (data.ttsProvider.startsWith("eleven")) {
|
|
402
|
+
ttsProviderId = "elevenlabs";
|
|
403
|
+
} else if (data.ttsProvider.startsWith("tts-") || data.ttsProvider.startsWith("gpt-")) {
|
|
404
|
+
ttsProviderId = "openai";
|
|
405
|
+
} else if (data.ttsProvider.startsWith("gemini")) {
|
|
406
|
+
ttsProviderId = "gemini";
|
|
407
|
+
} else if (data.ttsProvider.startsWith("qwen")) {
|
|
408
|
+
ttsProviderId = "qwen";
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// Save STT API key to provider if provided
|
|
412
|
+
if (data.sttApiKey && sttProviderId) {
|
|
413
|
+
const encrypted = await encryptApiKey(data.sttApiKey);
|
|
414
|
+
db.query(`
|
|
415
|
+
UPDATE providers SET
|
|
416
|
+
api_key_encrypted = ?,
|
|
417
|
+
api_key_iv = ?,
|
|
418
|
+
enabled = 1,
|
|
419
|
+
active = 1
|
|
420
|
+
WHERE id = ?
|
|
421
|
+
`).run(encrypted.encrypted, encrypted.iv, sttProviderId);
|
|
422
|
+
log.info("✅ STT API key guardada en BD (encriptada)", { provider: sttProviderId });
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// Save TTS API key to provider if provided
|
|
426
|
+
if (data.ttsApiKey && ttsProviderId) {
|
|
427
|
+
const encrypted = await encryptApiKey(data.ttsApiKey);
|
|
428
|
+
db.query(`
|
|
429
|
+
UPDATE providers SET
|
|
430
|
+
api_key_encrypted = ?,
|
|
431
|
+
api_key_iv = ?,
|
|
432
|
+
enabled = 1,
|
|
433
|
+
active = 1
|
|
434
|
+
WHERE id = ?
|
|
435
|
+
`).run(encrypted.encrypted, encrypted.iv, ttsProviderId);
|
|
436
|
+
log.info("✅ TTS API key guardada en BD (encriptada)", { provider: ttsProviderId });
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Update channel with voice config
|
|
440
|
+
db.query(`
|
|
441
|
+
UPDATE channels
|
|
442
|
+
SET user_id = ?, voice_enabled = ?, stt_provider = ?, tts_provider = ?
|
|
443
|
+
WHERE id = ?
|
|
444
|
+
`).run(data.userId, data.voiceEnabled ? 1 : 0, data.sttProvider, data.ttsProvider, data.channelId);
|
|
445
|
+
|
|
446
|
+
log.info("✅ Voice config saved:", {
|
|
447
|
+
channelId: data.channelId,
|
|
448
|
+
userId: data.userId,
|
|
449
|
+
sttProvider: data.sttProvider,
|
|
450
|
+
ttsProvider: data.ttsProvider,
|
|
451
|
+
sttProviderId,
|
|
452
|
+
ttsProviderId
|
|
453
|
+
});
|
|
454
|
+
} catch (e) {
|
|
455
|
+
log.error("⚠️ Error saving voice config:", { error: (e as Error).message });
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
export async function saveMcpServer(data: {
|
|
460
|
+
userId: string;
|
|
461
|
+
name: string;
|
|
462
|
+
transport: string;
|
|
463
|
+
command?: string;
|
|
464
|
+
args?: string[];
|
|
465
|
+
env?: Record<string, string>;
|
|
466
|
+
url?: string;
|
|
467
|
+
enabled?: boolean;
|
|
468
|
+
}): Promise<void> {
|
|
469
|
+
try {
|
|
470
|
+
const db = getDb();
|
|
471
|
+
|
|
472
|
+
const mcpId = `${data.userId}:${data.name}`;
|
|
473
|
+
|
|
474
|
+
let envEncrypted = null;
|
|
475
|
+
let envIv = null;
|
|
476
|
+
|
|
477
|
+
if (data.env && Object.keys(data.env).length > 0) {
|
|
478
|
+
const encrypted = await encryptConfig(data.env as Record<string, unknown>);
|
|
479
|
+
envEncrypted = encrypted.encrypted;
|
|
480
|
+
envIv = encrypted.iv;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
db.query(`
|
|
484
|
+
INSERT OR REPLACE INTO mcp_servers
|
|
485
|
+
(id, user_id, name, transport, command, args, env_encrypted, env_iv, url, enabled, builtin)
|
|
486
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0)
|
|
487
|
+
`).run(
|
|
488
|
+
mcpId,
|
|
489
|
+
data.userId,
|
|
490
|
+
data.name,
|
|
491
|
+
data.transport,
|
|
492
|
+
data.command || null,
|
|
493
|
+
JSON.stringify(data.args || []),
|
|
494
|
+
envEncrypted,
|
|
495
|
+
envIv,
|
|
496
|
+
data.url || null,
|
|
497
|
+
data.enabled ? 1 : 0
|
|
498
|
+
);
|
|
499
|
+
|
|
500
|
+
log.info("✅ MCP server saved:", { name: data.name });
|
|
501
|
+
} catch (e) {
|
|
502
|
+
log.error("⚠️ Error saving MCP server:", { error: (e as Error).message });
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
export function saveToolSelection(userId: string, tools: string[]): void {
|
|
507
|
+
try {
|
|
508
|
+
const db = getDb();
|
|
509
|
+
|
|
510
|
+
for (const tool of tools) {
|
|
511
|
+
// Activar la herramienta (ya existe del seed)
|
|
512
|
+
db.query(`
|
|
513
|
+
UPDATE tools SET active = 1, enabled = 1
|
|
514
|
+
WHERE id = ?
|
|
515
|
+
`).run(tool);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
log.info("✅ Tools activadas:", { tools: tools.join(", ") });
|
|
519
|
+
} catch (e) {
|
|
520
|
+
log.error("⚠️ Error saving tools:", { error: (e as Error).message });
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
export function activateProvider(providerId: string): void {
|
|
525
|
+
try {
|
|
526
|
+
const db = getDb();
|
|
527
|
+
db.query(`
|
|
528
|
+
UPDATE providers SET active = 1, enabled = 1
|
|
529
|
+
WHERE id = ?
|
|
530
|
+
`).run(providerId);
|
|
531
|
+
log.info("✅ Provider activado:", { providerId });
|
|
532
|
+
} catch (e) {
|
|
533
|
+
log.error("⚠️ Error activating provider:", { error: (e as Error).message });
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
export function activateModel(modelId: string): void {
|
|
538
|
+
try {
|
|
539
|
+
const db = getDb();
|
|
540
|
+
db.query(`
|
|
541
|
+
UPDATE models SET active = 1, enabled = 1
|
|
542
|
+
WHERE id = ?
|
|
543
|
+
`).run(modelId);
|
|
544
|
+
log.info("✅ Model activado:", { modelId });
|
|
545
|
+
} catch (e) {
|
|
546
|
+
log.error("⚠️ Error activating model:", { error: (e as Error).message });
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
export function activateMcpServer(mcpName: string): void {
|
|
553
|
+
try {
|
|
554
|
+
const db = getDb();
|
|
555
|
+
db.query(`
|
|
556
|
+
UPDATE mcp_servers SET active = 1, enabled = 1
|
|
557
|
+
WHERE id = ?
|
|
558
|
+
`).run(mcpName);
|
|
559
|
+
log.info("✅ MCP server activado:", { mcpName });
|
|
560
|
+
} catch (e) {
|
|
561
|
+
log.error("⚠️ Error activating MCP server:", { error: (e as Error).message });
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
export function deactivateProvider(providerId: string): void {
|
|
566
|
+
try {
|
|
567
|
+
const db = getDb();
|
|
568
|
+
db.query(`
|
|
569
|
+
UPDATE providers SET active = 0, enabled = 0
|
|
570
|
+
WHERE id = ?
|
|
571
|
+
`).run(providerId);
|
|
572
|
+
log.warn("⚠️ Provider desactivado:", { providerId });
|
|
573
|
+
} catch (e) {
|
|
574
|
+
log.error("⚠️ Error deactivating provider:", { error: (e as Error).message });
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
export function deactivateModel(modelId: string): void {
|
|
579
|
+
try {
|
|
580
|
+
const db = getDb();
|
|
581
|
+
db.query(`
|
|
582
|
+
UPDATE models SET active = 0, enabled = 0
|
|
583
|
+
WHERE id = ?
|
|
584
|
+
`).run(modelId);
|
|
585
|
+
log.warn("⚠️ Model desactivado:", { modelId });
|
|
586
|
+
} catch (e) {
|
|
587
|
+
log.error("⚠️ Error deactivating model:", { error: (e as Error).message });
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
export function deactivateChannel(channelType: string): void {
|
|
592
|
+
try {
|
|
593
|
+
const db = getDb();
|
|
594
|
+
db.query(`
|
|
595
|
+
UPDATE channels SET active = 0, enabled = 0
|
|
596
|
+
WHERE id = ?
|
|
597
|
+
`).run(channelType);
|
|
598
|
+
log.warn("⚠️ Channel desactivado:", { channelType });
|
|
599
|
+
} catch (e) {
|
|
600
|
+
log.error("⚠️ Error deactivating channel:", { error: (e as Error).message });
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
export function deactivateMcpServer(mcpName: string): void {
|
|
605
|
+
try {
|
|
606
|
+
const db = getDb();
|
|
607
|
+
db.query(`
|
|
608
|
+
UPDATE mcp_servers SET active = 0, enabled = 0
|
|
609
|
+
WHERE id = ?
|
|
610
|
+
`).run(mcpName);
|
|
611
|
+
log.warn("⚠️ MCP server desactivado:", { mcpName });
|
|
612
|
+
} catch (e) {
|
|
613
|
+
log.error("⚠️ Error deactivating MCP server:", { error: (e as Error).message });
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
export function getAllProviders(): Array<{
|
|
618
|
+
id: string;
|
|
619
|
+
name: string;
|
|
620
|
+
baseUrl: string | null;
|
|
621
|
+
enabled: boolean;
|
|
622
|
+
active: boolean;
|
|
623
|
+
}> {
|
|
624
|
+
try {
|
|
625
|
+
const db = getDb();
|
|
626
|
+
const results = db.query(`
|
|
627
|
+
SELECT id, name, base_url, enabled, active
|
|
628
|
+
FROM providers
|
|
629
|
+
`).all() as Array<{
|
|
630
|
+
id: string;
|
|
631
|
+
name: string;
|
|
632
|
+
base_url: string | null;
|
|
633
|
+
enabled: number;
|
|
634
|
+
active: number;
|
|
635
|
+
}>;
|
|
636
|
+
|
|
637
|
+
return results.map(r => ({
|
|
638
|
+
id: r.id,
|
|
639
|
+
name: r.name,
|
|
640
|
+
baseUrl: r.base_url,
|
|
641
|
+
enabled: r.enabled === 1,
|
|
642
|
+
active: r.active === 1,
|
|
643
|
+
}));
|
|
644
|
+
} catch (e) {
|
|
645
|
+
log.warn("[onboarding] ⚠️ Error getting providers:", (e as Error).message);
|
|
646
|
+
return [];
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
export function getAllModels(): Array<{
|
|
651
|
+
id: string;
|
|
652
|
+
name: string;
|
|
653
|
+
providerId: string;
|
|
654
|
+
contextWindow: number | null;
|
|
655
|
+
capabilities: string | null;
|
|
656
|
+
enabled: boolean;
|
|
657
|
+
active: boolean;
|
|
658
|
+
}> {
|
|
659
|
+
try {
|
|
660
|
+
const db = getDb();
|
|
661
|
+
const results = db.query(`
|
|
662
|
+
SELECT id, name, provider_id, context_window, capabilities, enabled, active
|
|
663
|
+
FROM models
|
|
664
|
+
`).all() as Array<{
|
|
665
|
+
id: string;
|
|
666
|
+
name: string;
|
|
667
|
+
provider_id: string;
|
|
668
|
+
context_window: number | null;
|
|
669
|
+
capabilities: string | null;
|
|
670
|
+
enabled: number;
|
|
671
|
+
active: number;
|
|
672
|
+
}>;
|
|
673
|
+
|
|
674
|
+
return results.map(r => ({
|
|
675
|
+
id: r.id,
|
|
676
|
+
name: r.name,
|
|
677
|
+
providerId: r.provider_id,
|
|
678
|
+
contextWindow: r.context_window,
|
|
679
|
+
capabilities: r.capabilities,
|
|
680
|
+
enabled: r.enabled === 1,
|
|
681
|
+
active: r.active === 1,
|
|
682
|
+
}));
|
|
683
|
+
} catch (e) {
|
|
684
|
+
log.error("⚠️ Error getting models:", { error: (e as Error).message });
|
|
685
|
+
return [];
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
export function getAllEthics(): Array<{
|
|
690
|
+
id: string;
|
|
691
|
+
name: string;
|
|
692
|
+
description: string | null;
|
|
693
|
+
content: string;
|
|
694
|
+
isDefault: boolean;
|
|
695
|
+
active: boolean;
|
|
696
|
+
}> {
|
|
697
|
+
try {
|
|
698
|
+
const db = getDb();
|
|
699
|
+
const results = db.query(`
|
|
700
|
+
SELECT id, name, description, content, is_default, active
|
|
701
|
+
FROM ethics
|
|
702
|
+
`).all() as Array<{
|
|
703
|
+
id: string;
|
|
704
|
+
name: string;
|
|
705
|
+
description: string | null;
|
|
706
|
+
content: string;
|
|
707
|
+
is_default: number;
|
|
708
|
+
active: number;
|
|
709
|
+
}>;
|
|
710
|
+
|
|
711
|
+
return results.map(r => ({
|
|
712
|
+
id: r.id,
|
|
713
|
+
name: r.name,
|
|
714
|
+
description: r.description,
|
|
715
|
+
content: r.content,
|
|
716
|
+
isDefault: r.is_default === 1,
|
|
717
|
+
active: r.active === 1,
|
|
718
|
+
}));
|
|
719
|
+
} catch (e) {
|
|
720
|
+
log.error("⚠️ Error getting ethics:", { error: (e as Error).message });
|
|
721
|
+
return [];
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
export function getAllCodeBridge(): Array<{
|
|
726
|
+
id: string;
|
|
727
|
+
name: string;
|
|
728
|
+
cliCommand: string;
|
|
729
|
+
port: number;
|
|
730
|
+
enabled: boolean;
|
|
731
|
+
active: boolean;
|
|
732
|
+
}> {
|
|
733
|
+
try {
|
|
734
|
+
const db = getDb();
|
|
735
|
+
const results = db.query(`
|
|
736
|
+
SELECT id, name, cli_command, port, enabled, active
|
|
737
|
+
FROM code_bridge
|
|
738
|
+
`).all() as Array<{
|
|
739
|
+
id: string;
|
|
740
|
+
name: string;
|
|
741
|
+
cli_command: string;
|
|
742
|
+
port: number;
|
|
743
|
+
enabled: number;
|
|
744
|
+
active: number;
|
|
745
|
+
}>;
|
|
746
|
+
|
|
747
|
+
return results.map(r => ({
|
|
748
|
+
id: r.id,
|
|
749
|
+
name: r.name,
|
|
750
|
+
cliCommand: r.cli_command,
|
|
751
|
+
port: r.port,
|
|
752
|
+
enabled: r.enabled === 1,
|
|
753
|
+
active: r.active === 1,
|
|
754
|
+
}));
|
|
755
|
+
} catch (e) {
|
|
756
|
+
log.error("⚠️ Error getting code bridge:", { error: (e as Error).message });
|
|
757
|
+
return [];
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
export function getAllSkills(): Array<{
|
|
762
|
+
id: string;
|
|
763
|
+
name: string;
|
|
764
|
+
description: string | null;
|
|
765
|
+
source: string;
|
|
766
|
+
isGlobal: boolean;
|
|
767
|
+
enabled: boolean;
|
|
768
|
+
active: boolean;
|
|
769
|
+
}> {
|
|
770
|
+
try {
|
|
771
|
+
const db = getDb();
|
|
772
|
+
const results = db.query(`
|
|
773
|
+
SELECT id, name, api_key_encrypted, api_key_iv, base_url, enabled
|
|
774
|
+
FROM providers
|
|
775
|
+
`).all() as Array<{
|
|
776
|
+
id: string;
|
|
777
|
+
name: string;
|
|
778
|
+
description: string | null;
|
|
779
|
+
source: string;
|
|
780
|
+
enabled: number;
|
|
781
|
+
active: number;
|
|
782
|
+
}>;
|
|
783
|
+
|
|
784
|
+
return results.map(r => ({
|
|
785
|
+
id: r.id,
|
|
786
|
+
name: r.name,
|
|
787
|
+
description: r.description,
|
|
788
|
+
source: r.source,
|
|
789
|
+
isGlobal: false,
|
|
790
|
+
enabled: r.enabled === 1,
|
|
791
|
+
active: r.active === 1,
|
|
792
|
+
}));
|
|
793
|
+
} catch (e) {
|
|
794
|
+
log.error("⚠️ Error getting skills:", { error: (e as Error).message });
|
|
795
|
+
return [];
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
export function getAllTools(): Array<{
|
|
800
|
+
id: string;
|
|
801
|
+
name: string;
|
|
802
|
+
description: string | null;
|
|
803
|
+
category: string | null;
|
|
804
|
+
enabled: boolean;
|
|
805
|
+
active: boolean;
|
|
806
|
+
}> {
|
|
807
|
+
try {
|
|
808
|
+
const db = getDb();
|
|
809
|
+
const results = db.query(`
|
|
810
|
+
SELECT id, name, description, category, enabled, active
|
|
811
|
+
FROM tools
|
|
812
|
+
`).all() as Array<{
|
|
813
|
+
id: string;
|
|
814
|
+
name: string;
|
|
815
|
+
description: string | null;
|
|
816
|
+
category: string | null;
|
|
817
|
+
enabled: number;
|
|
818
|
+
active: number;
|
|
819
|
+
}>;
|
|
820
|
+
|
|
821
|
+
return results.map(r => ({
|
|
822
|
+
id: r.id,
|
|
823
|
+
name: r.name,
|
|
824
|
+
description: r.description,
|
|
825
|
+
category: r.category,
|
|
826
|
+
enabled: r.enabled === 1,
|
|
827
|
+
active: r.active === 1,
|
|
828
|
+
}));
|
|
829
|
+
} catch (e) {
|
|
830
|
+
log.error("⚠️ Error getting tools:", { error: (e as Error).message });
|
|
831
|
+
return [];
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
export function getAllMcpServers(): Array<{
|
|
836
|
+
id: string;
|
|
837
|
+
name: string;
|
|
838
|
+
transport: string;
|
|
839
|
+
command: string | null;
|
|
840
|
+
args: string | null;
|
|
841
|
+
url: string | null;
|
|
842
|
+
builtin: boolean;
|
|
843
|
+
enabled: boolean;
|
|
844
|
+
active: boolean;
|
|
845
|
+
}> {
|
|
846
|
+
try {
|
|
847
|
+
const db = getDb();
|
|
848
|
+
const results = db.query(`
|
|
849
|
+
SELECT id, name, transport, command, args, url, builtin, enabled, active
|
|
850
|
+
FROM mcp_servers
|
|
851
|
+
`).all() as Array<{
|
|
852
|
+
id: string;
|
|
853
|
+
name: string;
|
|
854
|
+
transport: string;
|
|
855
|
+
command: string | null;
|
|
856
|
+
args: string | null;
|
|
857
|
+
url: string | null;
|
|
858
|
+
builtin: number;
|
|
859
|
+
enabled: number;
|
|
860
|
+
active: number;
|
|
861
|
+
}>;
|
|
862
|
+
|
|
863
|
+
return results.map(r => ({
|
|
864
|
+
id: r.id,
|
|
865
|
+
name: r.name,
|
|
866
|
+
transport: r.transport,
|
|
867
|
+
command: r.command,
|
|
868
|
+
args: r.args,
|
|
869
|
+
url: r.url,
|
|
870
|
+
builtin: r.builtin === 1,
|
|
871
|
+
enabled: r.enabled === 1,
|
|
872
|
+
active: r.active === 1,
|
|
873
|
+
}));
|
|
874
|
+
} catch (e) {
|
|
875
|
+
log.error("⚠️ Error getting MCP servers:", { error: (e as Error).message });
|
|
876
|
+
return [];
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
export function getAllChannels(): Array<{
|
|
881
|
+
id: string;
|
|
882
|
+
type: string;
|
|
883
|
+
accountId: string;
|
|
884
|
+
status: string;
|
|
885
|
+
enabled: boolean;
|
|
886
|
+
active: boolean;
|
|
887
|
+
}> {
|
|
888
|
+
try {
|
|
889
|
+
const db = getDb();
|
|
890
|
+
const results = db.query(`
|
|
891
|
+
SELECT id, type, id as account_id, status, enabled, active
|
|
892
|
+
FROM channels
|
|
893
|
+
`).all() as Array<{
|
|
894
|
+
id: string;
|
|
895
|
+
type: string;
|
|
896
|
+
account_id: string;
|
|
897
|
+
status: string;
|
|
898
|
+
enabled: number;
|
|
899
|
+
active: number;
|
|
900
|
+
}>;
|
|
901
|
+
|
|
902
|
+
return results.map(r => ({
|
|
903
|
+
id: r.id,
|
|
904
|
+
type: r.type,
|
|
905
|
+
accountId: r.id,
|
|
906
|
+
status: r.status,
|
|
907
|
+
enabled: r.enabled === 1,
|
|
908
|
+
active: r.active === 1,
|
|
909
|
+
}));
|
|
910
|
+
} catch (e) {
|
|
911
|
+
log.warn("[onboarding] ⚠️ Error getting channels:", (e as Error).message);
|
|
912
|
+
return [];
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
export function getActiveTools(): Array<{
|
|
917
|
+
id: string;
|
|
918
|
+
name: string;
|
|
919
|
+
description: string | null;
|
|
920
|
+
category: string | null;
|
|
921
|
+
}> {
|
|
922
|
+
try {
|
|
923
|
+
const db = getDb();
|
|
924
|
+
const results = db.query(`
|
|
925
|
+
SELECT id, name, description, category
|
|
926
|
+
FROM tools WHERE active = 1
|
|
927
|
+
`).all() as Array<{
|
|
928
|
+
id: string;
|
|
929
|
+
name: string;
|
|
930
|
+
description: string | null;
|
|
931
|
+
category: string | null;
|
|
932
|
+
}>;
|
|
933
|
+
|
|
934
|
+
return results.map(r => ({
|
|
935
|
+
id: r.id,
|
|
936
|
+
name: r.name,
|
|
937
|
+
description: r.description,
|
|
938
|
+
category: r.category,
|
|
939
|
+
}));
|
|
940
|
+
} catch (e) {
|
|
941
|
+
log.error("⚠️ Error getting active tools:", { error: (e as Error).message });
|
|
942
|
+
return [];
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
export function getOnboardingProgress(userId: string): OnboardingSection | null {
|
|
947
|
+
try {
|
|
948
|
+
const db = getDb();
|
|
949
|
+
const result = db.query<{ step: string; data: string }, [string]>(
|
|
950
|
+
"SELECT step, data FROM onboarding_progress WHERE user_id = ? LIMIT 1"
|
|
951
|
+
).get(userId);
|
|
952
|
+
|
|
953
|
+
if (result) {
|
|
954
|
+
return {
|
|
955
|
+
step: result.step as OnboardingSection["step"],
|
|
956
|
+
userId,
|
|
957
|
+
data: JSON.parse(result.data),
|
|
958
|
+
completedAt: Date.now(),
|
|
959
|
+
};
|
|
960
|
+
}
|
|
961
|
+
return null;
|
|
962
|
+
} catch {
|
|
963
|
+
return null;
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
export function saveOnboardingProgress(section: OnboardingSection): void {
|
|
968
|
+
try {
|
|
969
|
+
const db = getDb();
|
|
970
|
+
db.query(`
|
|
971
|
+
INSERT OR REPLACE INTO onboarding_progress (id, user_id, step, data)
|
|
972
|
+
VALUES (?, ?, ?, ?)
|
|
973
|
+
`).run(section.userId, section.userId, section.step, JSON.stringify(section.data));
|
|
974
|
+
} catch (e) {
|
|
975
|
+
log.error("⚠️ Error saving progress:", { error: (e as Error).message });
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
export async function getUserProviders(userId: string): Promise<Array<{
|
|
980
|
+
id: string;
|
|
981
|
+
name: string;
|
|
982
|
+
apiKey: string | null;
|
|
983
|
+
baseUrl: string | null;
|
|
984
|
+
enabled: boolean;
|
|
985
|
+
}>> {
|
|
986
|
+
try {
|
|
987
|
+
const db = getDb();
|
|
988
|
+
const results = db.query(`
|
|
989
|
+
SELECT id, name, api_key_encrypted, api_key_iv, base_url, enabled
|
|
990
|
+
FROM providers
|
|
991
|
+
`).all() as Array<{
|
|
992
|
+
id: string;
|
|
993
|
+
name: string;
|
|
994
|
+
api_key_encrypted: string | null;
|
|
995
|
+
api_key_iv: string | null;
|
|
996
|
+
base_url: string | null;
|
|
997
|
+
enabled: number;
|
|
998
|
+
}>;
|
|
999
|
+
|
|
1000
|
+
return Promise.all(results.map(async r => ({
|
|
1001
|
+
id: r.name,
|
|
1002
|
+
name: r.name,
|
|
1003
|
+
apiKey: r.api_key_encrypted && r.api_key_iv
|
|
1004
|
+
? await decryptApiKey(r.api_key_encrypted, r.api_key_iv)
|
|
1005
|
+
: null,
|
|
1006
|
+
baseUrl: r.base_url,
|
|
1007
|
+
enabled: r.enabled === 1,
|
|
1008
|
+
})));
|
|
1009
|
+
} catch (e) {
|
|
1010
|
+
log.warn("[onboarding] ⚠️ Error getting providers:", (e as Error).message);
|
|
1011
|
+
return [];
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
export async function getUserChannels(userId: string): Promise<Array<{
|
|
1016
|
+
id: string;
|
|
1017
|
+
type: string;
|
|
1018
|
+
accountId: string;
|
|
1019
|
+
config: Record<string, unknown>;
|
|
1020
|
+
enabled: boolean;
|
|
1021
|
+
}>> {
|
|
1022
|
+
try {
|
|
1023
|
+
const db = getDb();
|
|
1024
|
+
const results = db.query<{
|
|
1025
|
+
id: string;
|
|
1026
|
+
type: string;
|
|
1027
|
+
account_id: string;
|
|
1028
|
+
config_encrypted: string | null;
|
|
1029
|
+
config_iv: string | null;
|
|
1030
|
+
enabled: number;
|
|
1031
|
+
}, [string]>(`
|
|
1032
|
+
SELECT id, type, id as account_id, config_encrypted, config_iv, enabled
|
|
1033
|
+
FROM channels WHERE user_id = ?
|
|
1034
|
+
`).all(userId);
|
|
1035
|
+
|
|
1036
|
+
return Promise.all(results.map(async r => ({
|
|
1037
|
+
id: r.type,
|
|
1038
|
+
type: r.type,
|
|
1039
|
+
accountId: r.id,
|
|
1040
|
+
config: r.config_encrypted && r.config_iv
|
|
1041
|
+
? await decryptConfig(r.config_encrypted, r.config_iv)
|
|
1042
|
+
: {},
|
|
1043
|
+
enabled: r.enabled === 1,
|
|
1044
|
+
})));
|
|
1045
|
+
} catch (e) {
|
|
1046
|
+
log.warn("[onboarding] ⚠️ Error getting channels:", (e as Error).message);
|
|
1047
|
+
return [];
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
export function getUserAgents(userId: string): Array<{
|
|
1052
|
+
id: string;
|
|
1053
|
+
name: string;
|
|
1054
|
+
providerId: string | null;
|
|
1055
|
+
modelId: string | null;
|
|
1056
|
+
tone: string;
|
|
1057
|
+
}> {
|
|
1058
|
+
try {
|
|
1059
|
+
const db = getDb();
|
|
1060
|
+
const results = db.query<{
|
|
1061
|
+
id: string;
|
|
1062
|
+
name: string;
|
|
1063
|
+
provider_id: string | null;
|
|
1064
|
+
model_id: string | null;
|
|
1065
|
+
tone: string;
|
|
1066
|
+
}, [string]>(`
|
|
1067
|
+
SELECT id, name, provider_id, model_id, tone
|
|
1068
|
+
FROM agents WHERE user_id = ?
|
|
1069
|
+
`).all(userId);
|
|
1070
|
+
|
|
1071
|
+
return results.map(r => ({
|
|
1072
|
+
id: r.id,
|
|
1073
|
+
name: r.name,
|
|
1074
|
+
providerId: r.provider_id,
|
|
1075
|
+
modelId: r.model_id,
|
|
1076
|
+
tone: r.tone || "friendly",
|
|
1077
|
+
}));
|
|
1078
|
+
} catch (e) {
|
|
1079
|
+
log.error("⚠️ Error getting agents:", { error: (e as Error).message });
|
|
1080
|
+
return [];
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
export function maskApiKey(apiKey: string): string {
|
|
1085
|
+
if (!apiKey || apiKey.length < 8) return "••••••••";
|
|
1086
|
+
return apiKey.slice(0, 4) + "••••••••" + apiKey.slice(-4);
|
|
1087
|
+
}
|