@johpaz/hive-sdk 0.1.4 → 0.1.5
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/CHANGELOG.md +97 -0
- package/README.md +78 -23
- package/bunfig.toml +4 -2
- package/docs/API-AGENTS.md +78 -27
- package/docs/API-CONTEXT-COMPILER.md +31 -34
- package/docs/API-TOOLS-SKILLS-CHANNELS.md +58 -22
- package/docs/HIVE-HARNESS.md +1 -1
- package/docs/INDEX.md +4 -4
- package/docs/TEMPLATE-HIVE-APP.md +10 -10
- package/package.json +9 -4
- package/packages/cli/package.json +2 -2
- package/packages/cli/src/commands/create-app.test.ts +36 -7
- package/packages/cli/src/commands/init.ts +3 -3
- package/packages/cli/src/commands/run.ts +1 -1
- package/packages/cli/src/commands/test.ts +37 -25
- package/packages/cli/src/commands/trace.ts +30 -28
- package/packages/cli/templates/hive-app/.env.example +10 -2
- package/packages/cli/templates/hive-app/README.md +103 -0
- package/packages/cli/templates/hive-app/hive.config.ts +9 -3
- package/packages/cli/templates/hive-app/src/agents/coordinator.ts +8 -1
- package/packages/cli/templates/hive-app/src/main.ts +12 -19
- package/packages/core/package.json +5 -4
- package/packages/core/src/agent/acceptance-checks.ts +166 -0
- package/packages/core/src/agent/agent-catalog.ts +348 -0
- package/packages/core/src/agent/agent-loop.ts +1373 -0
- package/packages/core/src/agent/capability-search.ts +186 -0
- package/packages/core/src/agent/catalog-selector.ts +103 -0
- package/packages/core/src/agent/{Compaction.ts → compaction.ts} +86 -63
- package/packages/core/src/agent/context-compiler.ts +689 -0
- package/packages/core/src/agent/conversation-store.ts +381 -0
- package/packages/core/src/agent/curator.ts +276 -0
- package/packages/core/src/agent/delegation-runtime.ts +241 -0
- package/packages/core/src/agent/goal-runner.ts +323 -0
- package/packages/core/src/agent/index.ts +17 -12
- package/packages/core/src/agent/llm-client.ts +266 -0
- package/packages/core/src/agent/llm-providers/anthropic.ts +264 -0
- package/packages/core/src/agent/llm-providers/deepseek.ts +8 -0
- package/packages/core/src/agent/{providers → llm-providers}/gemini.ts +98 -60
- package/packages/core/src/agent/llm-providers/groq.ts +5 -0
- package/packages/core/src/agent/llm-providers/hiveagents.ts +253 -0
- package/packages/core/src/agent/{providers → llm-providers}/interface.ts +73 -13
- package/packages/core/src/agent/llm-providers/kimi.ts +8 -0
- package/packages/core/src/agent/llm-providers/minimax.ts +13 -0
- package/packages/core/src/agent/llm-providers/mistral.ts +5 -0
- package/packages/core/src/agent/llm-providers/modelscope.ts +5 -0
- package/packages/core/src/agent/llm-providers/nvidia.ts +5 -0
- package/packages/core/src/agent/{providers → llm-providers}/ollama.ts +31 -5
- package/packages/core/src/agent/llm-providers/openai-compat-base.ts +418 -0
- package/packages/core/src/agent/llm-providers/openai.ts +5 -0
- package/packages/core/src/agent/llm-providers/opencode-go.ts +9 -0
- package/packages/core/src/agent/llm-providers/openrouter.ts +5 -0
- package/packages/core/src/agent/llm-providers/qwen.ts +5 -0
- package/packages/core/src/agent/llm-providers/z-ai.ts +5 -0
- package/packages/core/src/agent/minimal-loadout.ts +47 -0
- package/packages/core/src/agent/playbook-selector.ts +119 -0
- package/packages/core/src/agent/{PromptBuilder.ts → prompt-builder.ts} +21 -22
- package/packages/core/src/{harness → agent}/proof-packet.ts +16 -21
- package/packages/core/src/agent/providers/index.ts +35 -16
- package/packages/core/src/agent/reflector.ts +320 -0
- package/packages/core/src/agent/routing-intent.ts +22 -0
- package/packages/core/src/{harness → agent}/run-epoch.ts +4 -3
- package/packages/core/src/{harness → agent}/run-store.ts +142 -81
- package/packages/core/src/agent/{Service.ts → service.ts} +37 -26
- package/packages/core/src/agent/skill-selector.ts +374 -0
- package/packages/core/src/agent/stuck-loop.ts +209 -0
- package/packages/core/src/agent/{selectors/ToolSelector.ts → tool-selector.ts} +188 -178
- package/packages/core/src/{ace/Tracer.ts → agent/tracer.ts} +37 -27
- package/packages/core/src/api/createAgent.test.ts +139 -27
- package/packages/core/src/api/createAgent.ts +232 -44
- package/packages/core/src/artifacts/store.ts +162 -0
- package/packages/core/src/canvas/canvas-manager.ts +161 -0
- package/packages/core/src/canvas/canvas.test.ts +8 -4
- package/packages/core/src/canvas/emitter.ts +131 -80
- package/packages/core/src/canvas/index.ts +1 -3
- package/packages/core/src/channels/base.ts +9 -1
- package/packages/core/src/channels/discord.ts +5 -4
- package/packages/core/src/channels/manager.ts +122 -30
- package/packages/core/src/channels/slack.ts +5 -4
- package/packages/core/src/channels/telegram.ts +36 -6
- package/packages/core/src/channels/webchat.ts +11 -10
- package/packages/core/src/channels/whatsapp.ts +23 -7
- package/packages/core/src/config/index.ts +13 -2
- package/packages/core/src/config/loader.ts +71 -29
- package/packages/core/src/ethics/EthicsGuard.test.ts +90 -36
- package/packages/core/src/ethics/EthicsGuard.ts +51 -47
- package/packages/core/src/events/agent-bus.ts +44 -68
- package/packages/core/src/events/channel-narration.ts +150 -0
- package/packages/core/src/events/narration.ts +82 -0
- package/packages/core/src/events/tool-narration.ts +62 -0
- package/packages/core/src/gateway/delegation-groups.ts +258 -0
- package/packages/core/src/{harness → gateway}/durable-queue.ts +102 -42
- package/packages/core/src/{harness → gateway}/job-store.ts +85 -48
- package/packages/core/src/gateway/lane-queue.ts +173 -0
- package/packages/core/src/gateway/notification-inbox.ts +57 -0
- package/packages/core/src/gateway/server.ts +1 -1
- package/packages/core/src/harness/index.ts +46 -27
- package/packages/core/src/index.ts +33 -27
- package/packages/core/src/mcp/hot-reload.ts +32 -23
- package/packages/core/src/mcp/index.ts +6 -3
- package/packages/core/src/mcp/singleton.ts +1 -4
- package/packages/core/src/mcp/tool-sync.ts +138 -0
- package/packages/core/src/memory/Scratchpad.test.ts +39 -20
- package/packages/core/src/memory/Scratchpad.ts +27 -34
- package/packages/core/src/multimodal/vision-service.ts +44 -38
- package/packages/core/src/resilience/retry.ts +95 -0
- package/packages/core/src/scheduler/CronScheduler.ts +334 -287
- package/packages/core/src/scheduler/index.ts +9 -7
- package/packages/core/src/scheduler/integration.ts +46 -26
- package/packages/core/src/scheduler/scheduler.test.ts +9 -13
- package/packages/core/src/scheduler/types.ts +7 -2
- package/packages/core/src/security/Pairing.ts +1 -1
- package/packages/core/src/skills/bundled/a2ui/a2ui_dashboard/SKILL.md +176 -0
- package/packages/core/src/skills/bundled/a2ui/a2ui_form/SKILL.md +202 -0
- package/packages/core/src/skills/bundled/a2ui/a2ui_interactive/SKILL.md +206 -0
- package/packages/core/src/skills/bundled/agents/agent_spawner/SKILL.md +173 -0
- package/packages/core/src/skills/bundled/agents/memory_manager/SKILL.md +143 -0
- package/packages/core/src/skills/bundled/agents/research_and_remember/SKILL.md +139 -0
- package/packages/core/src/skills/bundled/agents/task_orchestrator/SKILL.md +98 -0
- package/packages/core/src/skills/bundled/api/api_client/SKILL.md +132 -0
- package/packages/core/src/skills/bundled/cli/cli_pipeline/SKILL.md +135 -0
- package/packages/core/src/skills/bundled/cli/cli_safe_exec/SKILL.md +125 -0
- package/packages/core/src/skills/bundled/cli/software_engineering/SKILL.md +23 -0
- package/packages/core/src/skills/bundled/cron_manager/SKILL.md +188 -0
- package/packages/core/src/skills/bundled/cron_reminder/SKILL.md +112 -0
- package/packages/core/src/skills/bundled/filesystem/file_manager/SKILL.md +118 -0
- package/packages/core/src/skills/bundled/filesystem/file_read_and_summarize/SKILL.md +109 -0
- package/packages/core/src/skills/bundled/filesystem/file_writer/SKILL.md +129 -0
- package/packages/core/src/skills/bundled/filesystem/workspace_file_operator/SKILL.md +22 -0
- package/packages/core/src/skills/bundled/office/office_document_manager/SKILL.md +262 -0
- package/packages/core/src/skills/bundled/search_knowledge/capability_discovery/SKILL.md +75 -0
- package/packages/core/src/skills/bundled/web/browser_automate/SKILL.md +120 -0
- package/packages/core/src/skills/bundled/web/browser_scrape/SKILL.md +109 -0
- package/packages/core/src/skills/bundled/web/web_monitor/SKILL.md +127 -0
- package/packages/core/src/skills/bundled/web/web_research/SKILL.md +119 -0
- package/packages/core/src/skills/bundled-data.generated.ts +731 -2678
- package/packages/core/src/skills/skills.test.ts +52 -11
- package/packages/core/src/{harness → storage}/boot-id.ts +5 -2
- package/packages/core/src/storage/bootstrap.ts +151 -0
- package/packages/core/src/storage/causal-events.ts +84 -0
- package/packages/core/src/storage/collections.ts +680 -0
- package/packages/core/src/storage/crypto.ts +205 -74
- package/packages/core/src/{harness/db-helpers.ts → storage/hive.ts} +63 -7
- package/packages/core/src/storage/hivedb.ts +61 -0
- package/packages/core/src/storage/index.ts +111 -18
- package/packages/core/src/storage/model-id.ts +53 -0
- package/packages/core/src/storage/onboarding.ts +540 -972
- package/packages/core/src/storage/reconcile.ts +238 -0
- package/packages/core/src/storage/seed.ts +572 -406
- package/packages/core/src/storage/usage.ts +285 -225
- package/packages/core/src/storage/user-email.ts +11 -0
- package/packages/core/src/swarm/AgentExecutor.ts +1 -1
- package/packages/core/src/swarm/EventBridge.ts +1 -1
- package/packages/core/src/swarm/index.ts +12 -9
- package/packages/core/src/tool-runtime/index.ts +146 -23
- package/packages/core/src/tool-runtime/tool-worker.ts +2 -2
- package/packages/core/src/tool-runtime/worker-tools.ts +27 -0
- package/packages/core/src/{canvas/a2ui-tools.ts → tools/a2ui/index.ts} +17 -8
- package/packages/core/src/tools/agents/get-available-models.ts +36 -54
- package/packages/core/src/tools/agents/index.ts +784 -292
- package/packages/core/src/tools/api/api-request.test.ts +164 -0
- package/packages/core/src/tools/api/api-request.ts +174 -0
- package/packages/core/src/tools/api/index.ts +16 -0
- package/packages/core/src/tools/cli/index.ts +4 -0
- package/packages/core/src/tools/core/index.ts +281 -112
- package/packages/core/src/tools/cron/index.ts +121 -124
- package/packages/core/src/tools/index.ts +63 -78
- package/packages/core/src/tools/office/office-escribir-xlsx.ts +3 -1
- package/packages/core/src/tools/types.ts +3 -1
- package/packages/core/src/tools/web/artifact-inspect.ts +23 -0
- package/packages/core/src/tools/web/browser-screenshot.ts +26 -5
- package/packages/core/src/tools/web/browser-service.ts +5 -0
- package/packages/core/src/tools/web/browser-type.ts +3 -8
- package/packages/core/src/tools/web/index.ts +4 -4
- package/packages/core/src/voice/index.ts +89 -63
- package/packages/core/src/workers/agent.worker.ts +2 -2
- package/packages/core/src/workers/workers.test.ts +3 -10
- package/scripts/bump-version.ts +248 -0
- package/scripts/generate-skill-bundle.ts +108 -0
- package/test/agent-loop-terminal-synthesis.test.ts +32 -0
- package/test/catalog-agents-stay-enabled.test.ts +117 -0
- package/test/causal-events.test.ts +117 -0
- package/test/compaction.test.ts +105 -0
- package/test/context-compiler.test.ts +269 -0
- package/test/curator.test.ts +130 -0
- package/test/durable-queue.test.ts +114 -0
- package/test/harness-barrel.test.ts +64 -0
- package/test/hive-helpers.test.ts +130 -0
- package/test/hivedb-search.test.ts +189 -0
- package/test/internal-turns.test.ts +166 -0
- package/test/job-idempotency.test.ts +68 -0
- package/test/job-retry-backoff.test.ts +184 -0
- package/test/job-store.test.ts +381 -0
- package/test/llm-retry.test.ts +97 -0
- package/test/memory-perf.test.ts +774 -0
- package/test/minimal-loadout.test.ts +78 -0
- package/test/model-catalog.test.ts +105 -0
- package/test/preload.ts +12 -0
- package/test/reflector.test.ts +320 -0
- package/test/retention-cap.test.ts +91 -0
- package/test/retired-capabilities-pruned.test.ts +192 -0
- package/test/run-store.test.ts +355 -0
- package/test/scratchpad.test.ts +74 -0
- package/test/secrets-durability.test.ts +119 -0
- package/test/seed-model-reseed.test.ts +155 -0
- package/test/setup-agent-seed.test.ts +264 -0
- package/test/tool-inventory.test.ts +65 -0
- package/test/tool-runtime.test.ts +258 -0
- package/test/toon.test.ts +429 -0
- package/tsconfig.json +2 -0
- package/packages/core/src/ace/Curator.ts +0 -158
- package/packages/core/src/ace/Reflector.ts +0 -200
- package/packages/core/src/ace/index.ts +0 -4
- package/packages/core/src/agent/AgentRunner.ts +0 -711
- package/packages/core/src/agent/ContextCompiler.ts +0 -567
- package/packages/core/src/agent/ContextGuard.ts +0 -91
- package/packages/core/src/agent/ConversationStore.ts +0 -254
- package/packages/core/src/agent/Hooks.ts +0 -166
- package/packages/core/src/agent/StuckLoop.ts +0 -133
- package/packages/core/src/agent/providers/LLMClient.ts +0 -149
- package/packages/core/src/agent/providers/anthropic.ts +0 -212
- package/packages/core/src/agent/providers/openai-compat.ts +0 -231
- package/packages/core/src/agent/selectors/PlaybookSelector.ts +0 -121
- package/packages/core/src/agent/selectors/SkillSelector.ts +0 -322
- package/packages/core/src/agent/selectors/index.ts +0 -6
- package/packages/core/src/auth/auth.ts +0 -121
- package/packages/core/src/auth/index.ts +0 -1
- package/packages/core/src/canvas/CanvasManager.ts +0 -390
- package/packages/core/src/canvas/canvas-tools.ts +0 -448
- package/packages/core/src/harness/collections.ts +0 -98
- package/packages/core/src/harness/goal-verifier.ts +0 -141
- package/packages/core/src/harness/harness.test.ts +0 -236
- package/packages/core/src/harness/reconcile.ts +0 -149
- package/packages/core/src/mcp/MCPToolAdapter.ts +0 -176
- package/packages/core/src/multimodal/VisionService.ts +0 -293
- package/packages/core/src/scheduler/dag/AgentExecutor.ts +0 -53
- package/packages/core/src/scheduler/dag/DAGScheduler.ts +0 -250
- package/packages/core/src/scheduler/dag/EventBridge.ts +0 -122
- package/packages/core/src/scheduler/dag/TaskGraph.ts +0 -192
- package/packages/core/src/scheduler/dag/TaskNode.ts +0 -97
- package/packages/core/src/scheduler/dag/TaskResult.ts +0 -22
- package/packages/core/src/scheduler/dag/errors.ts +0 -37
- package/packages/core/src/scheduler/dag/index.ts +0 -26
- package/packages/core/src/scheduler/dag/presets/ResearchPreset.ts +0 -97
- package/packages/core/src/scheduler/dag/strategies/ParallelStrategy.ts +0 -21
- package/packages/core/src/scheduler/dag/strategies/PriorityStrategy.ts +0 -46
- package/packages/core/src/storage/HiveDBStorage.ts +0 -64
- package/packages/core/src/storage/SQLiteStorage.ts +0 -414
- package/packages/core/src/storage/hiveSeed.ts +0 -308
- package/packages/core/src/storage/hiveStorage.test.ts +0 -38
- package/packages/core/src/storage/schema.ts +0 -689
- package/packages/core/src/storage/storage.test.ts +0 -37
- package/packages/core/src/swarm/AgentBus.ts +0 -460
- package/packages/core/src/swarm/EventBus.ts +0 -169
- package/packages/core/src/swarm/WorkerPool.ts +0 -236
- package/packages/core/src/tools/bridge-events.ts +0 -26
- package/packages/core/src/tools/canvas/index.ts +0 -375
- package/packages/core/src/tools/codebridge/index.ts +0 -342
- package/packages/core/src/tools/meeting/index.ts +0 -353
- package/packages/core/src/tools/projects/index.ts +0 -37
- package/packages/core/src/tools/projects/project-create.ts +0 -94
- package/packages/core/src/tools/projects/project-done.ts +0 -66
- package/packages/core/src/tools/projects/project-fail.ts +0 -66
- package/packages/core/src/tools/projects/project-list.ts +0 -96
- package/packages/core/src/tools/projects/project-update.ts +0 -72
- package/packages/core/src/tools/projects/task-create.ts +0 -68
- package/packages/core/src/tools/projects/task-evaluate.ts +0 -93
- package/packages/core/src/tools/projects/task-update.ts +0 -93
- package/packages/core/src/tools/voice/index.ts +0 -104
- package/packages/core/src/tools/web/api-request.test.ts +0 -170
- package/packages/core/src/tools/web/api-request.ts +0 -239
- package/test/setup-db.ts +0 -216
- /package/packages/core/src/agent/{NativeTools.ts → native-tools.ts} +0 -0
|
@@ -1,41 +1,105 @@
|
|
|
1
|
+
import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto"
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"
|
|
3
|
+
import * as path from "node:path"
|
|
4
|
+
import { getHiveDir } from "../config/loader.ts"
|
|
1
5
|
import { logger } from "../utils/logger"
|
|
6
|
+
import { col } from "./hive"
|
|
2
7
|
|
|
3
8
|
const log = logger.child("crypto")
|
|
4
9
|
const SERVICE = "hive"
|
|
5
10
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
11
|
+
interface SecretDoc {
|
|
12
|
+
ciphertext: string
|
|
13
|
+
iv: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// ─── Durable secret store ────────────────────────────────────────────────────
|
|
17
|
+
// The `secrets` collection is the source of truth: AES-256-GCM ciphertext in
|
|
18
|
+
// the database, keyed by <HIVE_HOME>/.master.key. It lives inside the data
|
|
19
|
+
// directory, so whatever persists the database (a Docker volume, a backup)
|
|
20
|
+
// persists the secrets with it.
|
|
21
|
+
//
|
|
22
|
+
// The OS keychain (Bun.secrets) is only a best-effort mirror. It throws on
|
|
23
|
+
// headless Linux/Docker (no libsecret/D-Bus), and on desktops it can be a
|
|
24
|
+
// session keyring that is discarded when the session ends — so a secret
|
|
25
|
+
// written *only* there does not survive a server restart. That is exactly
|
|
26
|
+
// what was wiping every provider API key, channel token and MCP header on
|
|
27
|
+
// restart in production.
|
|
10
28
|
|
|
11
29
|
const _mem = new Map<string, string>()
|
|
12
30
|
let _keychainOk: boolean | null = null // null = untested
|
|
13
31
|
|
|
14
32
|
async function _get(name: string): Promise<string | null> {
|
|
15
|
-
|
|
33
|
+
const cached = _mem.get(name)
|
|
34
|
+
if (cached !== undefined) return cached
|
|
35
|
+
|
|
36
|
+
// Durable store first — it is the one every write goes to.
|
|
37
|
+
const stored = await _readCollectionSecret(name)
|
|
38
|
+
if (stored) return stored
|
|
39
|
+
|
|
40
|
+
// Legacy/desktop installs may only have the value in the OS keychain.
|
|
41
|
+
const fromKeychain = await _keychainGet(name)
|
|
42
|
+
if (fromKeychain) _mem.set(name, fromKeychain)
|
|
43
|
+
return fromKeychain
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Persist a secret. Always writes the durable store; the keychain is
|
|
48
|
+
* mirrored on top when available. Returns false only when the value ended up
|
|
49
|
+
* nowhere but this process's memory — callers can use that to warn instead of
|
|
50
|
+
* silently accepting a secret that dies with the process.
|
|
51
|
+
*/
|
|
52
|
+
async function _set(name: string, value: string): Promise<boolean> {
|
|
53
|
+
_mem.set(name, value)
|
|
54
|
+
const durable = await persistSecretToCollection(name, value)
|
|
55
|
+
const mirrored = await _keychainSet(name, value)
|
|
56
|
+
if (!durable && !mirrored) {
|
|
57
|
+
log.error(`[secrets] ${name} could not be persisted — it will be lost on restart`)
|
|
58
|
+
}
|
|
59
|
+
return durable || mirrored
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Read a secret from the `secrets` HiveDB collection — the durable store.
|
|
64
|
+
* Decrypted values are cached in memory for the rest of the process.
|
|
65
|
+
*/
|
|
66
|
+
async function _readCollectionSecret(name: string): Promise<string | null> {
|
|
67
|
+
try {
|
|
68
|
+
const secrets = await col<SecretDoc>("secrets")
|
|
69
|
+
const entry = await secrets.get(name)
|
|
70
|
+
if (!entry) return null
|
|
71
|
+
const plain = decryptSecret(entry.doc.ciphertext, entry.doc.iv)
|
|
72
|
+
if (plain) {
|
|
73
|
+
// Cache in memory for subsequent lookups in this process
|
|
74
|
+
_mem.set(name, plain)
|
|
75
|
+
}
|
|
76
|
+
return plain || null
|
|
77
|
+
} catch {
|
|
78
|
+
return null
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function _keychainGet(name: string): Promise<string | null> {
|
|
83
|
+
if (_keychainOk === false) return null
|
|
16
84
|
try {
|
|
17
85
|
const val = await (Bun as any).secrets.get({ service: SERVICE, name })
|
|
18
86
|
_keychainOk = true
|
|
19
87
|
return val ?? null
|
|
20
88
|
} catch {
|
|
21
89
|
_keychainOk = false
|
|
22
|
-
return
|
|
90
|
+
return null
|
|
23
91
|
}
|
|
24
92
|
}
|
|
25
93
|
|
|
26
|
-
async function
|
|
27
|
-
if (_keychainOk === false)
|
|
28
|
-
log.warn(`[secrets] OS keychain unavailable — in-memory fallback (secret lost on restart): ${name}`)
|
|
29
|
-
_mem.set(name, value)
|
|
30
|
-
return
|
|
31
|
-
}
|
|
94
|
+
async function _keychainSet(name: string, value: string): Promise<boolean> {
|
|
95
|
+
if (_keychainOk === false) return false
|
|
32
96
|
try {
|
|
33
97
|
await (Bun as any).secrets.set({ service: SERVICE, name, value })
|
|
34
98
|
_keychainOk = true
|
|
99
|
+
return true
|
|
35
100
|
} catch {
|
|
36
101
|
_keychainOk = false
|
|
37
|
-
|
|
38
|
-
_mem.set(name, value)
|
|
102
|
+
return false
|
|
39
103
|
}
|
|
40
104
|
}
|
|
41
105
|
|
|
@@ -46,6 +110,28 @@ async function _del(name: string): Promise<void> {
|
|
|
46
110
|
} catch {
|
|
47
111
|
// ignore — might not exist or keychain unavailable
|
|
48
112
|
}
|
|
113
|
+
try {
|
|
114
|
+
const secrets = await col<SecretDoc>("secrets")
|
|
115
|
+
await secrets.delete(name)
|
|
116
|
+
} catch {
|
|
117
|
+
// ignore — might not exist
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Mint the master key (if this is a fresh install) and report where secrets
|
|
123
|
+
* will be stored. Called once at boot so an operator sees the answer before
|
|
124
|
+
* typing an API key, not after a restart lost it.
|
|
125
|
+
*/
|
|
126
|
+
export function ensureSecretsBackend(): { durable: boolean } {
|
|
127
|
+
const durable = getMasterKey() !== null
|
|
128
|
+
if (!durable) {
|
|
129
|
+
log.error(
|
|
130
|
+
`[secrets] No master key available — API keys and channel tokens will NOT survive a restart. ` +
|
|
131
|
+
`Set HIVE_MASTER_KEY or make ${getHiveDir()} writable.`
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
return { durable }
|
|
49
135
|
}
|
|
50
136
|
|
|
51
137
|
// ─── Primitive API ────────────────────────────────────────────────────────────
|
|
@@ -64,16 +150,22 @@ export async function deleteSecret(name: string): Promise<void> {
|
|
|
64
150
|
|
|
65
151
|
// ─── Provider secrets ────────────────────────────────────────────────────────
|
|
66
152
|
|
|
67
|
-
|
|
68
|
-
|
|
153
|
+
/**
|
|
154
|
+
* Returns true if the secret was persisted to a durable store (OS keychain
|
|
155
|
+
* or the `secrets` collection fallback). Returns false if it ended up in the
|
|
156
|
+
* per-process in-memory map only — useful so callers can avoid destructive
|
|
157
|
+
* actions that would lose data on restart.
|
|
158
|
+
*/
|
|
159
|
+
export async function storeProviderApiKey(id: string, apiKey: string): Promise<boolean> {
|
|
160
|
+
return await _set(`provider:${id}:api_key`, apiKey)
|
|
69
161
|
}
|
|
70
162
|
|
|
71
163
|
export async function loadProviderApiKey(id: string): Promise<string> {
|
|
72
164
|
return (await _get(`provider:${id}:api_key`)) ?? ""
|
|
73
165
|
}
|
|
74
166
|
|
|
75
|
-
export async function storeProviderHeaders(id: string, headers: Record<string, unknown>): Promise<
|
|
76
|
-
await _set(`provider:${id}:headers`, JSON.stringify(headers))
|
|
167
|
+
export async function storeProviderHeaders(id: string, headers: Record<string, unknown>): Promise<boolean> {
|
|
168
|
+
return await _set(`provider:${id}:headers`, JSON.stringify(headers))
|
|
77
169
|
}
|
|
78
170
|
|
|
79
171
|
export async function loadProviderHeaders(id: string): Promise<Record<string, unknown>> {
|
|
@@ -90,8 +182,8 @@ export async function deleteProviderSecrets(id: string): Promise<void> {
|
|
|
90
182
|
|
|
91
183
|
// ─── Channel secrets ─────────────────────────────────────────────────────────
|
|
92
184
|
|
|
93
|
-
export async function storeChannelConfig(id: string, config: Record<string, unknown>): Promise<
|
|
94
|
-
await _set(`channel:${id}:config`, JSON.stringify(config))
|
|
185
|
+
export async function storeChannelConfig(id: string, config: Record<string, unknown>): Promise<boolean> {
|
|
186
|
+
return await _set(`channel:${id}:config`, JSON.stringify(config))
|
|
95
187
|
}
|
|
96
188
|
|
|
97
189
|
export async function loadChannelConfig(id: string): Promise<Record<string, unknown>> {
|
|
@@ -105,8 +197,8 @@ export async function deleteChannelSecrets(id: string): Promise<void> {
|
|
|
105
197
|
|
|
106
198
|
// ─── MCP secrets ──────────────────────────────────────────────────────────────
|
|
107
199
|
|
|
108
|
-
export async function storeMcpHeaders(id: string, headers: Record<string, unknown>): Promise<
|
|
109
|
-
await _set(`mcp:${id}:headers`, JSON.stringify(headers))
|
|
200
|
+
export async function storeMcpHeaders(id: string, headers: Record<string, unknown>): Promise<boolean> {
|
|
201
|
+
return await _set(`mcp:${id}:headers`, JSON.stringify(headers))
|
|
110
202
|
}
|
|
111
203
|
|
|
112
204
|
export async function loadMcpHeaders(id: string): Promise<Record<string, unknown>> {
|
|
@@ -114,8 +206,8 @@ export async function loadMcpHeaders(id: string): Promise<Record<string, unknown
|
|
|
114
206
|
return raw ? JSON.parse(raw) : {}
|
|
115
207
|
}
|
|
116
208
|
|
|
117
|
-
export async function storeMcpEnv(id: string, env: Record<string, string>): Promise<
|
|
118
|
-
await _set(`mcp:${id}:env`, JSON.stringify(env))
|
|
209
|
+
export async function storeMcpEnv(id: string, env: Record<string, string>): Promise<boolean> {
|
|
210
|
+
return await _set(`mcp:${id}:env`, JSON.stringify(env))
|
|
119
211
|
}
|
|
120
212
|
|
|
121
213
|
export async function loadMcpEnv(id: string): Promise<Record<string, string>> {
|
|
@@ -132,8 +224,8 @@ export async function deleteMcpSecrets(id: string): Promise<void> {
|
|
|
132
224
|
|
|
133
225
|
// ─── Agent secrets ────────────────────────────────────────────────────────────
|
|
134
226
|
|
|
135
|
-
export async function storeAgentHeaders(id: string, headers: Record<string, unknown>): Promise<
|
|
136
|
-
await _set(`agent:${id}:headers`, JSON.stringify(headers))
|
|
227
|
+
export async function storeAgentHeaders(id: string, headers: Record<string, unknown>): Promise<boolean> {
|
|
228
|
+
return await _set(`agent:${id}:headers`, JSON.stringify(headers))
|
|
137
229
|
}
|
|
138
230
|
|
|
139
231
|
export async function loadAgentHeaders(id: string): Promise<Record<string, unknown>> {
|
|
@@ -164,31 +256,15 @@ export function verifyPassword(password: string, hash: string): boolean {
|
|
|
164
256
|
return hasher.digest("hex") === hash
|
|
165
257
|
}
|
|
166
258
|
|
|
167
|
-
// ───
|
|
168
|
-
// Used only by the one-shot migration in storage/migrate.ts.
|
|
169
|
-
// Safe to remove after all installs have run the migration once.
|
|
170
|
-
|
|
171
|
-
export function legacyDecryptAES(encrypted: string, iv: string): string {
|
|
172
|
-
const nodeCrypto = require("node:crypto")
|
|
173
|
-
const nodeFs = require("node:fs")
|
|
174
|
-
const nodePath = require("node:path")
|
|
175
|
-
const nodeOs = require("node:os")
|
|
176
|
-
|
|
177
|
-
let key: Buffer
|
|
178
|
-
const masterKey = process.env.HIVE_MASTER_KEY
|
|
179
|
-
if (masterKey) {
|
|
180
|
-
key = Buffer.from(masterKey.slice(0, 32).padEnd(32, "0"), "utf8")
|
|
181
|
-
} else {
|
|
182
|
-
const hiveDir = process.env.HIVE_HOME || nodePath.join(nodeOs.homedir(), ".hive")
|
|
183
|
-
const keyPath = nodePath.join(hiveDir, ".master.key")
|
|
184
|
-
if (!nodeFs.existsSync(keyPath)) return ""
|
|
185
|
-
key = Buffer.from(nodeFs.readFileSync(keyPath, "utf-8").trim(), "hex")
|
|
186
|
-
}
|
|
259
|
+
// ─── AES-256-GCM for the collection-backed secret fallback ──────────────────
|
|
187
260
|
|
|
261
|
+
export function decryptSecret(encrypted: string, iv: string): string {
|
|
262
|
+
const key = getMasterKey()
|
|
263
|
+
if (!key) return ""
|
|
188
264
|
try {
|
|
189
265
|
const ivBuf = Buffer.from(iv, "hex")
|
|
190
266
|
const [encData, authTag] = encrypted.split(":")
|
|
191
|
-
const decipher =
|
|
267
|
+
const decipher = createDecipheriv("aes-256-gcm", key, ivBuf)
|
|
192
268
|
decipher.setAuthTag(Buffer.from(authTag, "hex"))
|
|
193
269
|
return decipher.update(encData, "hex", "utf8") + decipher.final("utf8")
|
|
194
270
|
} catch {
|
|
@@ -196,38 +272,93 @@ export function legacyDecryptAES(encrypted: string, iv: string): string {
|
|
|
196
272
|
}
|
|
197
273
|
}
|
|
198
274
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
275
|
+
let _masterKey: Buffer | null = null
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* The AES key protecting the `secrets` collection.
|
|
279
|
+
*
|
|
280
|
+
* `HIVE_MASTER_KEY` wins when set (the recommended way to run in production:
|
|
281
|
+
* the key never touches the data volume). Otherwise the key is minted on
|
|
282
|
+
* first use and written 0600 to `<HIVE_HOME>/.master.key`, next to the
|
|
283
|
+
* database — a fresh install becomes durable without the user configuring
|
|
284
|
+
* anything, and restoring the data directory restores the ability to read
|
|
285
|
+
* the secrets inside it.
|
|
286
|
+
*/
|
|
287
|
+
function getMasterKey(): Buffer | null {
|
|
288
|
+
if (_masterKey) return _masterKey
|
|
289
|
+
|
|
290
|
+
const fromEnv = process.env.HIVE_MASTER_KEY
|
|
291
|
+
if (fromEnv) {
|
|
292
|
+
_masterKey = Buffer.from(fromEnv.slice(0, 32).padEnd(32, "0"), "utf8")
|
|
293
|
+
return _masterKey
|
|
294
|
+
}
|
|
214
295
|
|
|
215
|
-
|
|
216
|
-
|
|
296
|
+
const keyPath = path.join(getHiveDir(), ".master.key")
|
|
297
|
+
try {
|
|
298
|
+
if (!existsSync(keyPath)) {
|
|
299
|
+
mkdirSync(path.dirname(keyPath), { recursive: true })
|
|
300
|
+
try {
|
|
301
|
+
// "wx" so two processes starting at once can't overwrite each other's key
|
|
302
|
+
writeFileSync(keyPath, randomBytes(32).toString("hex"), { mode: 0o600, flag: "wx" })
|
|
303
|
+
log.info(`[secrets] Master key generated at ${keyPath}`)
|
|
304
|
+
} catch (err) {
|
|
305
|
+
if ((err as NodeJS.ErrnoException).code !== "EEXIST") throw err
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
const key = Buffer.from(readFileSync(keyPath, "utf8").trim(), "hex")
|
|
309
|
+
if (key.length !== 32) {
|
|
310
|
+
log.error(`[secrets] ${keyPath} is not a 32-byte hex key — secrets cannot be read or written`)
|
|
311
|
+
return null
|
|
312
|
+
}
|
|
313
|
+
_masterKey = key
|
|
314
|
+
return _masterKey
|
|
315
|
+
} catch (err) {
|
|
316
|
+
log.error(`[secrets] Could not read or create ${keyPath}: ${(err as Error).message}`)
|
|
317
|
+
return null
|
|
318
|
+
}
|
|
217
319
|
}
|
|
218
320
|
|
|
219
|
-
|
|
220
|
-
|
|
321
|
+
/**
|
|
322
|
+
* Encrypt a plaintext string with AES-256-GCM. Format:
|
|
323
|
+
* `<encDataHex>:<authTagHex>`. Used for every document in the `secrets`
|
|
324
|
+
* collection.
|
|
325
|
+
*/
|
|
326
|
+
export function encryptSecret(plain: string, ivHex: string): string {
|
|
327
|
+
const key = getMasterKey()
|
|
328
|
+
if (!key) return ""
|
|
329
|
+
try {
|
|
330
|
+
const iv = Buffer.from(ivHex, "hex")
|
|
331
|
+
const cipher = createCipheriv("aes-256-gcm", key, iv)
|
|
332
|
+
const encData = cipher.update(plain, "utf8", "hex") + cipher.final("hex")
|
|
333
|
+
const authTag = cipher.getAuthTag().toString("hex")
|
|
334
|
+
return `${encData}:${authTag}`
|
|
335
|
+
} catch {
|
|
336
|
+
return ""
|
|
337
|
+
}
|
|
221
338
|
}
|
|
222
339
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
340
|
+
/**
|
|
341
|
+
* Persist a secret to the `secrets` collection — the durable store — keyed by
|
|
342
|
+
* the canonical secret name (e.g. `provider:openai:api_key`). Returns true if
|
|
343
|
+
* the document was written.
|
|
344
|
+
*/
|
|
345
|
+
async function persistSecretToCollection(name: string, value: string): Promise<boolean> {
|
|
346
|
+
const key = getMasterKey()
|
|
347
|
+
if (!key) {
|
|
348
|
+
log.warn(`[secrets] No master key — cannot persist ${name} durably`)
|
|
349
|
+
return false
|
|
350
|
+
}
|
|
226
351
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
352
|
+
const iv = randomBytes(12).toString("hex")
|
|
353
|
+
const ciphertext = encryptSecret(value, iv)
|
|
354
|
+
if (!ciphertext) return false
|
|
230
355
|
|
|
231
|
-
|
|
232
|
-
|
|
356
|
+
try {
|
|
357
|
+
const secrets = await col<SecretDoc>("secrets")
|
|
358
|
+
await secrets.put(name, { ciphertext, iv })
|
|
359
|
+
return true
|
|
360
|
+
} catch (err) {
|
|
361
|
+
log.warn(`[secrets] Durable write failed for ${name}: ${(err as Error).message}`)
|
|
362
|
+
return false
|
|
363
|
+
}
|
|
233
364
|
}
|
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Reusable HiveDB collection helpers
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* Reusable HiveDB collection helpers.
|
|
3
|
+
*
|
|
4
|
+
* These replace the SQL patterns that don't have a direct HiveDB primitive:
|
|
5
|
+
* autoincrement ids (`nextId`), whitelisted partial UPDATEs (`updateDoc`,
|
|
6
|
+
* `updateManyByIndex`), `WHERE field IN (...)` (`findByAny`), and
|
|
7
|
+
* SUM/AVG/GROUP BY (`bumpRollup`).
|
|
6
8
|
*/
|
|
7
9
|
|
|
8
|
-
import {
|
|
10
|
+
import { getHiveDb } from "./hivedb";
|
|
9
11
|
|
|
10
12
|
const MAX_RETRIES = 5;
|
|
11
13
|
|
|
12
14
|
/** Sentinel for nullable FK-like fields used in equality indexes (`findBy`/`createIndex` reject `null`). */
|
|
13
15
|
export const NO_PARENT = "__none__";
|
|
16
|
+
/** Sentinel for a broadcast recipient (e.g. `agentBusMessages.to_worker_id`). */
|
|
17
|
+
export const BROADCAST = "*";
|
|
14
18
|
|
|
15
19
|
/** Encode a nullable FK-like value for storage in an indexed field. */
|
|
16
20
|
export function toIndexable(value: string | null | undefined): string {
|
|
@@ -23,7 +27,8 @@ export function fromIndexable(value: string): string | null {
|
|
|
23
27
|
}
|
|
24
28
|
|
|
25
29
|
export async function col<T>(name: string) {
|
|
26
|
-
|
|
30
|
+
const db = await getHiveDb();
|
|
31
|
+
return db.collection<T>(name);
|
|
27
32
|
}
|
|
28
33
|
|
|
29
34
|
/**
|
|
@@ -32,7 +37,7 @@ export async function col<T>(name: string) {
|
|
|
32
37
|
* conflicts (another writer bumped the same counter concurrently).
|
|
33
38
|
*/
|
|
34
39
|
export async function nextId(counterName: string): Promise<string> {
|
|
35
|
-
const counters = await col<{ value: number }>("
|
|
40
|
+
const counters = await col<{ value: number }>("counters");
|
|
36
41
|
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
|
|
37
42
|
const cur = await counters.get(counterName);
|
|
38
43
|
const next = (cur?.doc.value ?? 0) + 1;
|
|
@@ -71,6 +76,23 @@ export async function updateDoc<T extends object>(
|
|
|
71
76
|
throw new Error(`updateDoc: too much contention on ${collection}/${id}`);
|
|
72
77
|
}
|
|
73
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Apply the same patch to every document whose indexed `field` equals
|
|
81
|
+
* `value` — replaces `UPDATE x SET ... WHERE fk = ?`. Requires a prior
|
|
82
|
+
* `createIndex(field)` on the collection.
|
|
83
|
+
*/
|
|
84
|
+
export async function updateManyByIndex<T extends object>(
|
|
85
|
+
collection: string,
|
|
86
|
+
field: string,
|
|
87
|
+
value: string | number | boolean,
|
|
88
|
+
patch: Partial<T>
|
|
89
|
+
): Promise<number> {
|
|
90
|
+
const c = await col<T>(collection);
|
|
91
|
+
const rows = await c.findBy(field, value);
|
|
92
|
+
for (const r of rows) await updateDoc<T>(collection, r.id, patch);
|
|
93
|
+
return rows.length;
|
|
94
|
+
}
|
|
95
|
+
|
|
74
96
|
/**
|
|
75
97
|
* Fetch documents whose indexed `field` matches any of `values` — emulates
|
|
76
98
|
* `WHERE field IN (...)`. Requires a prior `createIndex(field)`.
|
|
@@ -85,3 +107,37 @@ export async function findByAny<T>(
|
|
|
85
107
|
const chunks = await Promise.all(uniq.map((v) => c.findBy(field, v)));
|
|
86
108
|
return chunks.flat();
|
|
87
109
|
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Increment numeric fields on a rollup/counter document — replaces
|
|
113
|
+
* SUM/AVG/GROUP BY. `nested` additionally bumps a per-key breakdown (e.g.
|
|
114
|
+
* `byProvider[provider]`) inside the same document. Creates the document on
|
|
115
|
+
* first use. Retries on optimistic-concurrency conflicts.
|
|
116
|
+
*/
|
|
117
|
+
export async function bumpRollup(
|
|
118
|
+
collection: string,
|
|
119
|
+
id: string,
|
|
120
|
+
delta: Record<string, number>,
|
|
121
|
+
nested?: { field: string; key: string }
|
|
122
|
+
): Promise<void> {
|
|
123
|
+
const c = await col<Record<string, any>>(collection);
|
|
124
|
+
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
|
|
125
|
+
const existing = await c.get(id);
|
|
126
|
+
const doc: Record<string, any> = existing ? { ...existing.doc } : {};
|
|
127
|
+
for (const [k, v] of Object.entries(delta)) doc[k] = (doc[k] ?? 0) + v;
|
|
128
|
+
if (nested) {
|
|
129
|
+
doc[nested.field] = { ...(doc[nested.field] ?? {}) };
|
|
130
|
+
doc[nested.field][nested.key] = { ...(doc[nested.field][nested.key] ?? {}) };
|
|
131
|
+
for (const [k, v] of Object.entries(delta)) {
|
|
132
|
+
doc[nested.field][nested.key][k] = (doc[nested.field][nested.key][k] ?? 0) + v;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
try {
|
|
136
|
+
await c.put(id, doc, { expectedVersion: existing?.version ?? 0 });
|
|
137
|
+
return;
|
|
138
|
+
} catch {
|
|
139
|
+
// Version conflict — retry with a fresh read.
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
throw new Error(`bumpRollup: too much contention on ${collection}/${id}`);
|
|
143
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HiveDB — singleton accessor
|
|
3
|
+
*
|
|
4
|
+
* HiveDB (@johpaz/hive-db) is the embedded Rust engine (redb + tantivy BM25 +
|
|
5
|
+
* hnsw_rs) that is the sole data store for Hive: capability search (tools,
|
|
6
|
+
* skills, playbook, MCP tools) via its BM25/hybrid index, and every other
|
|
7
|
+
* piece of relational-shaped data (users, agents, providers, models,
|
|
8
|
+
* conversations, cron, projects, ACE/playbook, ...) via its document
|
|
9
|
+
* collections (`db.collection<T>(name)`).
|
|
10
|
+
*
|
|
11
|
+
* The database lives at ~/.hive/data/hivedb (or ~/.hive-dev/data/hivedb in
|
|
12
|
+
* dev).
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import path from "node:path";
|
|
16
|
+
import { HiveDB } from "@johpaz/hive-db";
|
|
17
|
+
import { getHiveDir } from "../config/loader.ts";
|
|
18
|
+
import { logger } from "../utils/logger";
|
|
19
|
+
|
|
20
|
+
const log = logger.child("hivedb");
|
|
21
|
+
|
|
22
|
+
let db: HiveDB | null = null;
|
|
23
|
+
let opening: Promise<HiveDB> | null = null;
|
|
24
|
+
|
|
25
|
+
export function getHiveDbPath(): string {
|
|
26
|
+
// Tests can point the database at an ephemeral (":memory:") instance.
|
|
27
|
+
if (process.env.HIVE_DB_PATH) return process.env.HIVE_DB_PATH;
|
|
28
|
+
return path.join(getHiveDir(), "data", "hivedb");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Get the shared HiveDB instance, opening it on first use.
|
|
33
|
+
* Concurrent callers share the same open() promise.
|
|
34
|
+
*/
|
|
35
|
+
export async function getHiveDb(): Promise<HiveDB> {
|
|
36
|
+
if (db) return db;
|
|
37
|
+
if (!opening) {
|
|
38
|
+
const dbPath = getHiveDbPath();
|
|
39
|
+
opening = HiveDB.open(dbPath).then((opened) => {
|
|
40
|
+
db = opened;
|
|
41
|
+
log.info(`[hivedb] Opened at ${dbPath}`);
|
|
42
|
+
return opened;
|
|
43
|
+
});
|
|
44
|
+
opening.catch(() => {
|
|
45
|
+
opening = null;
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
return opening;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function closeHiveDb(): void {
|
|
52
|
+
if (db) {
|
|
53
|
+
try {
|
|
54
|
+
db.close();
|
|
55
|
+
} catch (err) {
|
|
56
|
+
log.warn(`[hivedb] Error closing database: ${(err as Error).message}`);
|
|
57
|
+
}
|
|
58
|
+
db = null;
|
|
59
|
+
opening = null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -1,21 +1,114 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Superficie pública de storage.
|
|
3
|
+
*
|
|
4
|
+
* HiveDB es la única fuente de verdad. La capa SQLite síncrona (`SQLiteStorage`,
|
|
5
|
+
* `schema.ts`, `hiveSeed.ts`) desapareció en 0.1.5: convivían dos backends y el
|
|
6
|
+
* seed de HiveDB salía fire-and-forget desde el de SQLite, así que cuál de los
|
|
7
|
+
* dos ganaba dependía del timing.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// ─── Conexión y bootstrap ────────────────────────────────────────────────────
|
|
11
|
+
export { getHiveDbPath, getHiveDb, closeHiveDb } from "./hivedb.ts";
|
|
12
|
+
export { ensureHiveDb, isBootstrapped } from "./bootstrap.ts";
|
|
13
|
+
|
|
14
|
+
// ─── Acceso a colecciones ────────────────────────────────────────────────────
|
|
15
|
+
export {
|
|
16
|
+
col,
|
|
17
|
+
nextId,
|
|
18
|
+
updateDoc,
|
|
19
|
+
updateManyByIndex,
|
|
20
|
+
findByAny,
|
|
21
|
+
bumpRollup,
|
|
22
|
+
toIndexable,
|
|
23
|
+
fromIndexable,
|
|
24
|
+
NO_PARENT,
|
|
25
|
+
BROADCAST,
|
|
26
|
+
} from "./hive.ts";
|
|
27
|
+
|
|
28
|
+
// ─── Shapes de documento ─────────────────────────────────────────────────────
|
|
29
|
+
export type * from "./collections.ts";
|
|
30
|
+
|
|
31
|
+
// ─── Claves del catálogo de modelos ──────────────────────────────────────────
|
|
32
|
+
// El prefijo de revendedor evita que dos providers que sirven el mismo modelo
|
|
33
|
+
// se pisen la fila entre sí — ver el JSDoc de model-id.ts.
|
|
34
|
+
export { catalogModelKey, wireModelId, isResellerProvider } from "./model-id.ts";
|
|
35
|
+
|
|
36
|
+
// ─── Seed del catálogo ───────────────────────────────────────────────────────
|
|
37
|
+
export type { SeedData } from "./seed.ts";
|
|
38
|
+
export {
|
|
39
|
+
SEED_DATA,
|
|
40
|
+
seedAllData,
|
|
41
|
+
seedToolsAndSkills,
|
|
42
|
+
activateElement,
|
|
43
|
+
deactivateElement,
|
|
44
|
+
getAllElements,
|
|
45
|
+
getActiveElements,
|
|
46
|
+
} from "./seed.ts";
|
|
47
|
+
|
|
48
|
+
// ─── Consumo y costos ────────────────────────────────────────────────────────
|
|
49
|
+
// El precio vive en la fila del modelo (`input_per_1m` / `output_per_1m`), no en
|
|
50
|
+
// un mapa hardcodeado: `MODEL_PRICING` era una segunda lista que se desfasaba
|
|
51
|
+
// del catálogo en silencio.
|
|
4
52
|
export type { UsageRecord, UsageSummary } from "./usage.ts";
|
|
53
|
+
export {
|
|
54
|
+
recordUsage,
|
|
55
|
+
getUsageStats,
|
|
56
|
+
calculateCost,
|
|
57
|
+
invalidateModelPricingCache,
|
|
58
|
+
recordToonSavings,
|
|
59
|
+
hourBucket,
|
|
60
|
+
} from "./usage.ts";
|
|
61
|
+
|
|
62
|
+
// ─── Secretos ────────────────────────────────────────────────────────────────
|
|
63
|
+
export {
|
|
64
|
+
ensureSecretsBackend,
|
|
65
|
+
storeSecret,
|
|
66
|
+
loadSecret,
|
|
67
|
+
deleteSecret,
|
|
68
|
+
storeProviderApiKey,
|
|
69
|
+
loadProviderApiKey,
|
|
70
|
+
storeProviderHeaders,
|
|
71
|
+
loadProviderHeaders,
|
|
72
|
+
deleteProviderSecrets,
|
|
73
|
+
storeChannelConfig,
|
|
74
|
+
loadChannelConfig,
|
|
75
|
+
deleteChannelSecrets,
|
|
76
|
+
storeMcpHeaders,
|
|
77
|
+
loadMcpHeaders,
|
|
78
|
+
storeMcpEnv,
|
|
79
|
+
loadMcpEnv,
|
|
80
|
+
deleteMcpSecrets,
|
|
81
|
+
storeAgentHeaders,
|
|
82
|
+
loadAgentHeaders,
|
|
83
|
+
deleteAgentSecrets,
|
|
84
|
+
maskApiKey,
|
|
85
|
+
hashPassword,
|
|
86
|
+
verifyPassword,
|
|
87
|
+
} from "./crypto.ts";
|
|
88
|
+
|
|
89
|
+
// ─── Onboarding e identidad ──────────────────────────────────────────────────
|
|
5
90
|
export type { OnboardingSection } from "./onboarding.ts";
|
|
6
|
-
export { resolveUserId, resolveAgentId, initOnboardingDb } from "./onboarding.ts";
|
|
7
|
-
export type { EncryptedData } from "./crypto.ts";
|
|
8
|
-
export { encrypt, decrypt, encryptApiKey, decryptApiKey, encryptConfig, decryptConfig } from "./crypto.ts";
|
|
9
|
-
export type { SeedData } from "./seed.ts";
|
|
10
|
-
export { SEED_DATA, seedAllData } from "./seed.ts";
|
|
11
91
|
export {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
92
|
+
resolveUserId,
|
|
93
|
+
resolveAgentId,
|
|
94
|
+
initOnboardingDb,
|
|
95
|
+
saveUserProfile,
|
|
96
|
+
saveProviderConfig,
|
|
97
|
+
saveAgentConfig,
|
|
98
|
+
activateProvider,
|
|
99
|
+
activateModel,
|
|
100
|
+
deactivateProvider,
|
|
101
|
+
deactivateModel,
|
|
102
|
+
getAllProviders,
|
|
103
|
+
getAllModels,
|
|
104
|
+
} from "./onboarding.ts";
|
|
105
|
+
export { normalizeUserEmail } from "./user-email.ts";
|
|
106
|
+
|
|
107
|
+
// ─── Durabilidad entre arranques ─────────────────────────────────────────────
|
|
108
|
+
export { getBootId, resetBootId } from "./boot-id.ts";
|
|
109
|
+
export type { ReconcileResult } from "./reconcile.ts";
|
|
110
|
+
export { reconcileOnBoot } from "./reconcile.ts";
|
|
111
|
+
|
|
112
|
+
// ─── Log causal (G9) ─────────────────────────────────────────────────────────
|
|
113
|
+
export type { CausalEvent, CausalEventPattern } from "./causal-events.ts";
|
|
114
|
+
export { watchCausalEvents, formatCausalEvent } from "./causal-events.ts";
|