@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,443 @@
|
|
|
1
|
+
import { loadConfig, startGateway, logger, expandConfigPath, expandPath, getHiveDir } from "@johpaz/hive-core";
|
|
2
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync, unlinkSync, openSync } from "node:fs";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { spawn, ChildProcess } from "child_process";
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
const children: ChildProcess[] = [];
|
|
8
|
+
|
|
9
|
+
function cleanup() {
|
|
10
|
+
if (children.length === 0) return;
|
|
11
|
+
console.log("\n🧹 Limpiando procesos hijos...");
|
|
12
|
+
for (const child of children) {
|
|
13
|
+
if (child.pid) {
|
|
14
|
+
try {
|
|
15
|
+
// En Linux, matar el grupo de procesos si fue iniciado como detached
|
|
16
|
+
process.kill(-child.pid, "SIGTERM");
|
|
17
|
+
} catch {
|
|
18
|
+
child.kill("SIGTERM");
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Listen for termination signals
|
|
25
|
+
process.on("SIGINT", () => {
|
|
26
|
+
cleanup();
|
|
27
|
+
process.exit(0);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
process.on("SIGTERM", () => {
|
|
31
|
+
cleanup();
|
|
32
|
+
process.exit(0);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// Ensure cleanup on normal exit
|
|
36
|
+
process.on("exit", () => {
|
|
37
|
+
cleanup();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
function getDefaultPidFile(): string {
|
|
41
|
+
return path.join(getHiveDir(), "gateway.pid");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function getLogFile(): string {
|
|
45
|
+
return path.join(getHiveDir(), "logs", "gateway.log");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function getPidFile(): Promise<string> {
|
|
49
|
+
try {
|
|
50
|
+
const config = await loadConfig();
|
|
51
|
+
return expandConfigPath(config.gateway?.pidFile) ?? getDefaultPidFile();
|
|
52
|
+
} catch {
|
|
53
|
+
return getDefaultPidFile();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function ensureLogDir(): void {
|
|
58
|
+
const logDir = path.dirname(getLogFile());
|
|
59
|
+
if (!existsSync(logDir)) {
|
|
60
|
+
mkdirSync(logDir, { recursive: true });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function openBrowser(url: string): void {
|
|
65
|
+
const platform = process.platform;
|
|
66
|
+
|
|
67
|
+
// Build the shell command for the platform
|
|
68
|
+
let shellCmd: string;
|
|
69
|
+
if (platform === "win32") {
|
|
70
|
+
shellCmd = `start "" "${url}"`;
|
|
71
|
+
} else if (platform === "darwin") {
|
|
72
|
+
shellCmd = `open "${url}"`;
|
|
73
|
+
} else {
|
|
74
|
+
// Linux: try gio open first (GNOME/Wayland native, no DISPLAY needed),
|
|
75
|
+
// then xdg-open, then common browser names. '|| true' ensures exit 0.
|
|
76
|
+
shellCmd = `gio open "${url}" 2>/dev/null || xdg-open "${url}" 2>/dev/null || sensible-browser "${url}" 2>/dev/null || x-www-browser "${url}" 2>/dev/null || true`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
console.log(`🌐 Abriendo navegador en ${url}`);
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
const shell = platform === "win32" ? "cmd" : "/bin/sh";
|
|
83
|
+
const shellArg = platform === "win32" ? "/c" : "-c";
|
|
84
|
+
// Bun.spawn is the native Bun API — more reliable than child_process.spawn in Bun
|
|
85
|
+
const proc = Bun.spawn([shell, shellArg, shellCmd], {
|
|
86
|
+
stdout: "ignore",
|
|
87
|
+
stderr: "ignore",
|
|
88
|
+
stdin: "ignore",
|
|
89
|
+
});
|
|
90
|
+
proc.unref(); // Don't keep event loop alive waiting for the browser process
|
|
91
|
+
} catch {
|
|
92
|
+
console.log(`\n🌐 Abre Hive aquí: ${url}\n`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function isSetupMode(): Promise<boolean> {
|
|
97
|
+
const hiveDir = getHiveDir();
|
|
98
|
+
const dbPath = path.join(hiveDir, "data", "hive.db");
|
|
99
|
+
return !existsSync(dbPath);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function isRunning(): Promise<boolean> {
|
|
103
|
+
const pidFile = await getPidFile();
|
|
104
|
+
if (!existsSync(pidFile)) return false;
|
|
105
|
+
const pid = parseInt(readFileSync(pidFile, "utf-8").trim(), 10);
|
|
106
|
+
if (isNaN(pid)) return false;
|
|
107
|
+
try {
|
|
108
|
+
process.kill(pid, 0);
|
|
109
|
+
return true;
|
|
110
|
+
} catch {
|
|
111
|
+
try {
|
|
112
|
+
unlinkSync(pidFile);
|
|
113
|
+
} catch { }
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function waitForPort(port: number, timeout: number = 30000): Promise<boolean> {
|
|
119
|
+
const start = Date.now();
|
|
120
|
+
while (Date.now() - start < timeout) {
|
|
121
|
+
try {
|
|
122
|
+
const response = await fetch(`http://127.0.0.1:${port}/health`, {
|
|
123
|
+
method: "HEAD",
|
|
124
|
+
signal: AbortSignal.timeout(1000)
|
|
125
|
+
});
|
|
126
|
+
if (response.ok || response.status === 204) {
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
} catch {
|
|
130
|
+
// Port not ready yet
|
|
131
|
+
}
|
|
132
|
+
await Bun.sleep(500);
|
|
133
|
+
}
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async function waitForVite(port: number, timeout: number = 30000): Promise<boolean> {
|
|
138
|
+
const start = Date.now();
|
|
139
|
+
while (Date.now() - start < timeout) {
|
|
140
|
+
try {
|
|
141
|
+
// Usar 127.0.0.1 en lugar de localhost para evitar demoras de resolución DNS (IPv6 vs IPv4)
|
|
142
|
+
const response = await fetch(`http://127.0.0.1:${port}`, {
|
|
143
|
+
method: "HEAD",
|
|
144
|
+
signal: AbortSignal.timeout(200) // Timeout más agresivo para una conexión local
|
|
145
|
+
});
|
|
146
|
+
if (response.ok || response.status === 200) {
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
} catch {
|
|
150
|
+
// Puerto aún no disponible
|
|
151
|
+
}
|
|
152
|
+
await Bun.sleep(200); // Poll más frecuente
|
|
153
|
+
}
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export async function start(flags: string[]): Promise<void> {
|
|
158
|
+
const daemon = flags.includes("--daemon");
|
|
159
|
+
const skipCheck = flags.includes("--skip-check");
|
|
160
|
+
const devInternal = flags.includes("--dev-internal");
|
|
161
|
+
|
|
162
|
+
// Check if we're in dev mode and config doesn't exist
|
|
163
|
+
const isDev = process.env.HIVE_DEV === "true" || process.env.NODE_ENV === "development"
|
|
164
|
+
const hiveDir = getHiveDir();
|
|
165
|
+
const dbPath = path.join(hiveDir, "data", "hive.db")
|
|
166
|
+
|
|
167
|
+
// Skip onboarding check if running as child process in dev mode
|
|
168
|
+
const isGatewayChild = process.env.HIVE_GATEWAY_CHILD === "1";
|
|
169
|
+
|
|
170
|
+
// Database-centric onboarding check
|
|
171
|
+
if (isDev && !isGatewayChild) {
|
|
172
|
+
let hasCompletedOnboarding = false;
|
|
173
|
+
|
|
174
|
+
if (existsSync(dbPath)) {
|
|
175
|
+
try {
|
|
176
|
+
const { initializeDatabase, getDb } = await import("../../../core/src/storage/sqlite");
|
|
177
|
+
initializeDatabase();
|
|
178
|
+
const db = getDb();
|
|
179
|
+
const existingAgent = db.query("SELECT id FROM agents WHERE is_coordinator = 1 AND status = 'idle' LIMIT 1").get();
|
|
180
|
+
hasCompletedOnboarding = !!existingAgent;
|
|
181
|
+
} catch (e) {
|
|
182
|
+
hasCompletedOnboarding = false;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (!hasCompletedOnboarding) {
|
|
187
|
+
console.log("\n🚀 Sistema no inicializado. Ejecutando onboard...\n");
|
|
188
|
+
const { onboard } = await import("./onboard");
|
|
189
|
+
await onboard();
|
|
190
|
+
|
|
191
|
+
// Verify onboarding success via DB
|
|
192
|
+
try {
|
|
193
|
+
const { getDb } = await import("../../../core/src/storage/sqlite");
|
|
194
|
+
const db = getDb();
|
|
195
|
+
const existingAgent = db.query("SELECT id FROM agents WHERE is_coordinator = 1 AND status = 'idle' LIMIT 1").get();
|
|
196
|
+
|
|
197
|
+
if (!existingAgent) {
|
|
198
|
+
console.error("❌ Onboard no completó la inicialización correctamente");
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
} catch (e) {
|
|
202
|
+
console.error("❌ Error verificando inicialización:", (e as Error).message);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (!skipCheck && await isRunning()) {
|
|
209
|
+
console.log("⚠️ Hive Gateway ya está corriendo");
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const config = await loadConfig();
|
|
214
|
+
|
|
215
|
+
if (config.logging?.level) {
|
|
216
|
+
logger.setLevel(config.logging.level);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Only show banner if not running as child process (parent already shows status)
|
|
220
|
+
if (!isGatewayChild) {
|
|
221
|
+
console.log(`
|
|
222
|
+
╔═══════════════════════════════════════════╗
|
|
223
|
+
║ ║
|
|
224
|
+
║ ██╗ ██╗██╗██╗ ██╗███████╗ ║
|
|
225
|
+
║ ██║ ██║██║██║ ██║██╔════╝ ║
|
|
226
|
+
║ ███████║██║██║ ██║█████╗ ║
|
|
227
|
+
║ ██╔══██║██║╚██╗ ██╔╝██╔══╝ ║
|
|
228
|
+
║ ██║ ██║██║ ╚████╔╝ ███████╗ ║
|
|
229
|
+
║ ╚═╝ ╚═╝╚═╝ ╚═══╝ ╚══════╝ ║
|
|
230
|
+
║ ║
|
|
231
|
+
║ Personal Swarm AI Gateway — v1.0.10 ║
|
|
232
|
+
╚════════════════════════════════════════════╝
|
|
233
|
+
`);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (daemon) {
|
|
237
|
+
ensureLogDir();
|
|
238
|
+
const logFile = getLogFile();
|
|
239
|
+
const child = spawn(process.execPath, [process.argv[1] || "", "start", "--skip-check"], {
|
|
240
|
+
detached: true,
|
|
241
|
+
stdio: ["ignore", openSync(logFile, "a"), openSync(logFile, "a")],
|
|
242
|
+
});
|
|
243
|
+
child.unref();
|
|
244
|
+
writeFileSync(await getPidFile(), child.pid?.toString() || "");
|
|
245
|
+
console.log(`✅ Hive Gateway iniciado en modo daemon (PID: ${child.pid})`);
|
|
246
|
+
console.log(` Logs: ${logFile}`);
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// In dev mode, start Vite and Gateway concurrently for faster startup
|
|
251
|
+
if (isDev) {
|
|
252
|
+
// ── CHILD PROCESS: Only run Gateway server ───────────────────────────────
|
|
253
|
+
if (isGatewayChild) {
|
|
254
|
+
// Child process: skip Vite and Code Bridge, just start Gateway
|
|
255
|
+
logger.info("Starting Gateway server (child process)...");
|
|
256
|
+
await startGateway(config);
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// ── PARENT PROCESS: Start Vite, Code Bridge, and spawn child Gateway ─────
|
|
261
|
+
const hiveUiPath = path.join(process.cwd(), "packages/hive-ui");
|
|
262
|
+
const hasVite = existsSync(path.join(hiveUiPath, "package.json"));
|
|
263
|
+
|
|
264
|
+
// Start Vite in background (don't wait)
|
|
265
|
+
if (hasVite) {
|
|
266
|
+
console.log("🎨 Iniciando Vite (UI)...\n");
|
|
267
|
+
|
|
268
|
+
const viteProcess = spawn("bun", ["run", "dev"], {
|
|
269
|
+
cwd: hiveUiPath,
|
|
270
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
271
|
+
detached: true,
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
// Pipe logs to stdout/stderr with a prefix
|
|
275
|
+
viteProcess.stdout?.on("data", (data) => {
|
|
276
|
+
const lines = data.toString().split("\n");
|
|
277
|
+
for (const line of lines) {
|
|
278
|
+
if (line.trim()) console.log(`[Vite] ${line}`);
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
viteProcess.stderr?.on("data", (data) => {
|
|
282
|
+
const lines = data.toString().split("\n");
|
|
283
|
+
for (const line of lines) {
|
|
284
|
+
if (line.trim()) console.error(`[Vite] ${line}`);
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
viteProcess.on("error", (error) => {
|
|
289
|
+
console.error(`❌ Error iniciando Vite: ${error.message}`);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
if (!daemon) {
|
|
293
|
+
children.push(viteProcess);
|
|
294
|
+
} else {
|
|
295
|
+
viteProcess.unref();
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Start Code Bridge sidecar (parent process only)
|
|
300
|
+
try {
|
|
301
|
+
await import("@johpaz/hive-code-bridge");
|
|
302
|
+
} catch (error) {
|
|
303
|
+
console.warn(`⚠️ No se pudo iniciar el Code Bridge: ${(error as Error).message}`);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Start Gateway in a separate process (non-blocking)
|
|
307
|
+
const gatewayProcess = spawn(process.execPath, [process.argv[1] || "", "start", "--skip-check", "--dev-internal"], {
|
|
308
|
+
detached: true,
|
|
309
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
310
|
+
env: { ...process.env, HIVE_DEV: "true", HIVE_GATEWAY_CHILD: "1" },
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
gatewayProcess.stdout?.on("data", (data) => {
|
|
314
|
+
const lines = data.toString().split("\n");
|
|
315
|
+
for (const line of lines) {
|
|
316
|
+
if (line.trim()) console.log(`[Gateway] ${line}`);
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
gatewayProcess.stderr?.on("data", (data) => {
|
|
320
|
+
const lines = data.toString().split("\n");
|
|
321
|
+
for (const line of lines) {
|
|
322
|
+
if (line.trim()) console.error(`[Gateway] ${line}`);
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
gatewayProcess.on("error", (error) => {
|
|
327
|
+
console.error(`❌ Error iniciando Gateway: ${error.message}`);
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
if (!daemon) {
|
|
331
|
+
children.push(gatewayProcess);
|
|
332
|
+
} else {
|
|
333
|
+
gatewayProcess.unref();
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Wait for both Vite and Gateway to be ready
|
|
337
|
+
console.log("⏳ Esperando servicios...");
|
|
338
|
+
const [viteReady, gatewayReady] = await Promise.all([
|
|
339
|
+
hasVite ? waitForVite(5173, 30000) : Promise.resolve(true),
|
|
340
|
+
waitForPort(18790, 30000),
|
|
341
|
+
]);
|
|
342
|
+
|
|
343
|
+
if (!viteReady && hasVite) {
|
|
344
|
+
console.error("⚠️ Vite no respondió a tiempo");
|
|
345
|
+
}
|
|
346
|
+
if (!gatewayReady) {
|
|
347
|
+
console.error("⚠️ Gateway no respondió a tiempo");
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
console.log("✅ Servicios listos\n");
|
|
352
|
+
|
|
353
|
+
// Determine if we should open /setup or /ui
|
|
354
|
+
const setupMode = await isSetupMode();
|
|
355
|
+
const url = setupMode
|
|
356
|
+
? "http://localhost:5173/setup"
|
|
357
|
+
: "http://localhost:5173";
|
|
358
|
+
|
|
359
|
+
console.log(`
|
|
360
|
+
╔════════════════════════════════════════╗
|
|
361
|
+
║ 🐝 Hive — Modo Desarrollo ║
|
|
362
|
+
╠════════════════════════════════════════╣
|
|
363
|
+
║ UI: ${url.padEnd(24)}║
|
|
364
|
+
║ API: http://127.0.0.1:18790 ║
|
|
365
|
+
║ WebSocket: ws://127.0.0.1:18790/ws ║
|
|
366
|
+
║ Canvas: ws://127.0.0.1:18790/canvas║
|
|
367
|
+
╠════════════════════════════════════════╣
|
|
368
|
+
║ ${setupMode ? "🎉 Primer arranque — abriendo setup..." : "Administra tu Hive aquí "}║
|
|
369
|
+
╚════════════════════════════════════════╝
|
|
370
|
+
`);
|
|
371
|
+
|
|
372
|
+
openBrowser(url);
|
|
373
|
+
|
|
374
|
+
// Keep the process alive
|
|
375
|
+
if (!daemon) {
|
|
376
|
+
await new Promise(() => {}); // Infinite wait
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Production mode (not dev) - start Gateway blocking
|
|
383
|
+
await startGateway(config);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
export async function stop(): Promise<void> {
|
|
387
|
+
if (!(await isRunning())) {
|
|
388
|
+
console.log("⚠️ Hive Gateway no está corriendo");
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const pidFile = await getPidFile();
|
|
393
|
+
const pid = parseInt(readFileSync(pidFile, "utf-8").trim(), 10);
|
|
394
|
+
try {
|
|
395
|
+
process.kill(pid, "SIGTERM");
|
|
396
|
+
unlinkSync(pidFile);
|
|
397
|
+
console.log("✅ Hive Gateway detenido");
|
|
398
|
+
} catch (e) {
|
|
399
|
+
console.error("❌ Error deteniendo el Gateway:", e);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
export async function status(flags: string[]): Promise<void> {
|
|
404
|
+
const running = await isRunning();
|
|
405
|
+
const hiveDir = getHiveDir();
|
|
406
|
+
|
|
407
|
+
console.log("🐝 Hive Gateway Status\n");
|
|
408
|
+
|
|
409
|
+
const config = await loadConfig();
|
|
410
|
+
const pidFile = await getPidFile();
|
|
411
|
+
|
|
412
|
+
console.log(`Estado: ${running ? "✅ Corriendo" : "⏹️ Detenido"}`);
|
|
413
|
+
if (running) {
|
|
414
|
+
const pid = readFileSync(pidFile, "utf-8").trim();
|
|
415
|
+
console.log(`PID: ${pid}`);
|
|
416
|
+
}
|
|
417
|
+
console.log(`Puerto: ${config.gateway?.port || 18790}`);
|
|
418
|
+
console.log(`Host: ${config.gateway?.host || "127.0.0.1"}`);
|
|
419
|
+
const provider = config.models?.defaultProvider || "no configurado";
|
|
420
|
+
const model = (config.models as any)?.defaults?.[provider] || (config.models as any)?.defaults?.default || "no configurado";
|
|
421
|
+
console.log(`Modelo: ${provider} / ${model}`);
|
|
422
|
+
console.log(`Home: ${hiveDir}`);
|
|
423
|
+
console.log(`Logs: ${getLogFile()}`);
|
|
424
|
+
|
|
425
|
+
if (flags.includes("--json")) {
|
|
426
|
+
console.log("\n" + JSON.stringify({ running, pid: running ? readFileSync(pidFile, "utf-8").trim() : null, config }, null, 2));
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
export async function reload(): Promise<void> {
|
|
431
|
+
if (!(await isRunning())) {
|
|
432
|
+
console.log("⚠️ Hive Gateway no está corriendo");
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const pid = parseInt(readFileSync(await getPidFile(), "utf-8").trim(), 10);
|
|
437
|
+
try {
|
|
438
|
+
process.kill(pid, "SIGHUP");
|
|
439
|
+
console.log("✅ Configuración recargada");
|
|
440
|
+
} catch (e) {
|
|
441
|
+
console.error("❌ Error recargando configuración:", e);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import { spawn } from "child_process";
|
|
4
|
+
|
|
5
|
+
const LOG_DIR = path.join(process.env.HOME || "", ".hive", "logs");
|
|
6
|
+
const LOG_FILE = path.join(LOG_DIR, "gateway.log");
|
|
7
|
+
|
|
8
|
+
export async function logs(flags: string[]): Promise<void> {
|
|
9
|
+
if (flags.includes("--clear")) {
|
|
10
|
+
await clearLogs();
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (!fs.existsSync(LOG_FILE)) {
|
|
15
|
+
console.log("No hay logs disponibles");
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const follow = flags.includes("--follow") || flags.includes("-f");
|
|
20
|
+
const levelFlag = flags.find((f) => f.startsWith("--level"));
|
|
21
|
+
const level = levelFlag ? levelFlag.split("=")[1] || flags[flags.indexOf(levelFlag) + 1] : null;
|
|
22
|
+
const agentFlag = flags.find((f) => f.startsWith("--agent"));
|
|
23
|
+
const agent = agentFlag ? agentFlag.split("=")[1] || flags[flags.indexOf(agentFlag) + 1] : null;
|
|
24
|
+
const lines = 100;
|
|
25
|
+
|
|
26
|
+
if (follow) {
|
|
27
|
+
const tail = spawn("tail", ["-f", LOG_FILE], { stdio: "inherit" });
|
|
28
|
+
process.on("SIGINT", () => {
|
|
29
|
+
tail.kill();
|
|
30
|
+
process.exit(0);
|
|
31
|
+
});
|
|
32
|
+
} else {
|
|
33
|
+
let content = fs.readFileSync(LOG_FILE, "utf-8");
|
|
34
|
+
const allLines = content.split("\n");
|
|
35
|
+
|
|
36
|
+
let filtered = allLines;
|
|
37
|
+
if (level) {
|
|
38
|
+
const levelLower = level.toLowerCase();
|
|
39
|
+
filtered = filtered.filter((l) => l.toLowerCase().includes(levelLower));
|
|
40
|
+
}
|
|
41
|
+
if (agent) {
|
|
42
|
+
filtered = filtered.filter((l) => l.includes(agent));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const lastLines = filtered.slice(-lines);
|
|
46
|
+
console.log(lastLines.join("\n"));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function clearLogs(): Promise<void> {
|
|
51
|
+
if (fs.existsSync(LOG_FILE)) {
|
|
52
|
+
fs.writeFileSync(LOG_FILE, "");
|
|
53
|
+
console.log("✅ Logs limpiados");
|
|
54
|
+
} else {
|
|
55
|
+
console.log("No hay logs para limpiar");
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
import { spawn } from "child_process";
|
|
3
|
+
import { loadConfig } from "@johpaz/hive-core/config/loader";
|
|
4
|
+
|
|
5
|
+
interface MCPServerConfig {
|
|
6
|
+
command: string;
|
|
7
|
+
args?: string[];
|
|
8
|
+
env?: Record<string, string>;
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export async function mcp(subcommand: string | undefined, args: string[]): Promise<void> {
|
|
13
|
+
switch (subcommand) {
|
|
14
|
+
case "list":
|
|
15
|
+
await listMCP();
|
|
16
|
+
break;
|
|
17
|
+
case "add":
|
|
18
|
+
await addMCP();
|
|
19
|
+
break;
|
|
20
|
+
case "test":
|
|
21
|
+
await testMCP(args[0]);
|
|
22
|
+
break;
|
|
23
|
+
case "tools":
|
|
24
|
+
await toolsMCP(args[0]);
|
|
25
|
+
break;
|
|
26
|
+
case "remove":
|
|
27
|
+
await removeMCP(args[0]);
|
|
28
|
+
break;
|
|
29
|
+
default:
|
|
30
|
+
console.log(`
|
|
31
|
+
Usage: hive mcp <command>
|
|
32
|
+
|
|
33
|
+
Commands:
|
|
34
|
+
list Listar servidores MCP
|
|
35
|
+
add Añadir servidor MCP
|
|
36
|
+
test <nombre> Verificar servidor MCP
|
|
37
|
+
tools <nombre> Listar tools de un servidor
|
|
38
|
+
remove <nombre> Eliminar servidor MCP
|
|
39
|
+
`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function listMCP(): Promise<void> {
|
|
44
|
+
const config = loadConfig();
|
|
45
|
+
const mcpServers = (config.mcp as Record<string, MCPServerConfig>) || {};
|
|
46
|
+
|
|
47
|
+
if (Object.keys(mcpServers).length === 0) {
|
|
48
|
+
console.log("No hay servidores MCP configurados");
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
console.log("\n🔌 Servidores MCP:\n");
|
|
53
|
+
for (const [name, server] of Object.entries(mcpServers)) {
|
|
54
|
+
const status = server.disabled ? "⏸️ Deshabilitado" : "✅ Activo";
|
|
55
|
+
console.log(` ${name}`);
|
|
56
|
+
console.log(` Estado: ${status}`);
|
|
57
|
+
console.log(` Comando: ${server.command} ${(server.args || []).join(" ")}`);
|
|
58
|
+
console.log();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function addMCP(): Promise<void> {
|
|
63
|
+
const config = loadConfig();
|
|
64
|
+
|
|
65
|
+
const name = await p.text({
|
|
66
|
+
message: "Nombre del servidor MCP:",
|
|
67
|
+
placeholder: "filesystem",
|
|
68
|
+
validate: (v) => (!v ? "El nombre es requerido" : undefined),
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
if (p.isCancel(name)) {
|
|
72
|
+
p.cancel("Cancelado");
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const command = await p.text({
|
|
77
|
+
message: "Comando para ejecutar:",
|
|
78
|
+
placeholder: "npx",
|
|
79
|
+
validate: (v) => (!v ? "El comando es requerido" : undefined),
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
if (p.isCancel(command)) {
|
|
83
|
+
p.cancel("Cancelado");
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const argsInput = await p.text({
|
|
88
|
+
message: "Argumentos (separados por espacio):",
|
|
89
|
+
placeholder: "-y @modelcontextprotocol/server-filesystem /path/to/dir",
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
if (p.isCancel(argsInput)) {
|
|
93
|
+
p.cancel("Cancelado");
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const args = argsInput ? (argsInput as string).split(" ").filter(Boolean) : [];
|
|
98
|
+
|
|
99
|
+
const serverConfig: MCPServerConfig = {
|
|
100
|
+
command: command as string,
|
|
101
|
+
args,
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
console.log(`✅ Servidor MCP "${name}" añadido (usa la UI o API del gateway para persistirlo en BD)`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async function testMCP(name: string | undefined): Promise<void> {
|
|
108
|
+
if (!name) {
|
|
109
|
+
console.log("❌ Especifica el nombre del servidor: hive mcp test <nombre>");
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const config = loadConfig();
|
|
114
|
+
const mcpServers = (config.mcp as Record<string, MCPServerConfig>) || {};
|
|
115
|
+
const server = mcpServers[name];
|
|
116
|
+
|
|
117
|
+
if (!server) {
|
|
118
|
+
console.log(`❌ No existe el servidor MCP "${name}"`);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
console.log(`🔌 Probando conexión con ${name}...`);
|
|
123
|
+
|
|
124
|
+
const proc = spawn(server.command, server.args || [], {
|
|
125
|
+
env: { ...process.env, ...server.env },
|
|
126
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
proc.on("error", (err) => {
|
|
130
|
+
console.log(`❌ Error: ${err.message}`);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
setTimeout(() => {
|
|
134
|
+
proc.kill();
|
|
135
|
+
console.log(`✅ Servidor ${name} responde`);
|
|
136
|
+
}, 2000);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async function toolsMCP(name: string | undefined): Promise<void> {
|
|
140
|
+
if (!name) {
|
|
141
|
+
console.log("❌ Especifica el nombre del servidor: hive mcp tools <nombre>");
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
console.log(`📋 Tools de ${name}:`);
|
|
146
|
+
console.log(" (Esta funcionalidad requiere conexión MCP activa)");
|
|
147
|
+
console.log(" Usa 'hive mcp test <nombre>' para verificar conexión");
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async function removeMCP(name: string | undefined): Promise<void> {
|
|
151
|
+
if (!name) {
|
|
152
|
+
console.log("❌ Especifica el nombre del servidor: hive mcp remove <nombre>");
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const config = loadConfig();
|
|
157
|
+
const mcpServers = (config.mcp as Record<string, MCPServerConfig>) || {};
|
|
158
|
+
|
|
159
|
+
if (!mcpServers[name]) {
|
|
160
|
+
console.log(`❌ No existe el servidor MCP "${name}"`);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const confirm = await p.confirm({
|
|
165
|
+
message: `¿Eliminar el servidor MCP "${name}"?`,
|
|
166
|
+
initialValue: false,
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
if (p.isCancel(confirm) || !confirm) {
|
|
170
|
+
console.log("Cancelado");
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
console.log(`✅ Servidor MCP "${name}" eliminado (usa la UI o API del gateway para persistirlo en BD)`);
|
|
175
|
+
}
|