hippo-memory 1.70.0 → 1.72.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/agent-memories/apply.d.ts +0 -11
- package/dist/agent-memories/apply.js +6 -21
- package/dist/agent-memories/claude-code.d.ts +4 -2
- package/dist/agent-memories/claude-code.js +15 -6
- package/dist/agent-memories/codex.js +1 -1
- package/dist/agent-memories/copilot.d.ts +1 -3
- package/dist/agent-memories/copilot.js +1 -17
- package/dist/agent-memories/files.js +23 -12
- package/dist/agent-memories/markdown.d.ts +1 -3
- package/dist/agent-memories/markdown.js +1 -3
- package/dist/agent-memories/project-folds.d.ts +10 -0
- package/dist/agent-memories/project-folds.js +34 -0
- package/dist/agent-memories/report.js +2 -1
- package/dist/agent-memories/sync.d.ts +4 -2
- package/dist/agent-memories/sync.js +18 -11
- package/dist/api/assemble.d.ts +7 -44
- package/dist/api/assemble.js +3 -23
- package/dist/api/audit.d.ts +33 -1
- package/dist/api/audit.js +48 -1
- package/dist/api/auth.d.ts +20 -26
- package/dist/api/auth.js +43 -29
- package/dist/api/auto-sleep.d.ts +5 -2
- package/dist/api/auto-sleep.js +9 -5
- package/dist/api/availability.d.ts +2 -15
- package/dist/api/availability.js +4 -18
- package/dist/api/cards.d.ts +42 -0
- package/dist/api/cards.js +52 -0
- package/dist/api/conflicts.d.ts +28 -0
- package/dist/api/conflicts.js +61 -0
- package/dist/api/context-render.js +4 -3
- package/dist/api/context-select.js +12 -15
- package/dist/api/context-types.d.ts +10 -35
- package/dist/api/context.d.ts +5 -22
- package/dist/api/context.js +38 -46
- package/dist/api/continuity.d.ts +47 -0
- package/dist/api/continuity.js +66 -0
- package/dist/{cli → api}/correction-latency.d.ts +2 -14
- package/dist/{cli → api}/correction-latency.js +2 -14
- package/dist/api/dormant.d.ts +5 -21
- package/dist/api/dormant.js +11 -28
- package/dist/api/drill-down.d.ts +7 -46
- package/dist/api/drill-down.js +6 -27
- package/dist/api/embeddings.d.ts +17 -0
- package/dist/api/embeddings.js +28 -0
- package/dist/api/error-reply.d.ts +8 -0
- package/dist/api/error-reply.js +21 -0
- package/dist/api/forget.d.ts +11 -26
- package/dist/api/forget.js +23 -35
- package/dist/api/goals.d.ts +1 -1
- package/dist/api/goals.js +5 -9
- package/dist/api/graph.d.ts +8 -0
- package/dist/api/graph.js +7 -0
- package/dist/api/index.d.ts +6 -2
- package/dist/api/index.js +6 -2
- package/dist/api/instruction-detect.js +21 -6
- package/dist/api/learn.d.ts +4 -0
- package/dist/api/learn.js +53 -27
- package/dist/api/ledger-db.d.ts +2 -5
- package/dist/api/ledger-db.js +5 -35
- package/dist/api/memories.d.ts +23 -0
- package/dist/api/memories.js +46 -0
- package/dist/api/objects.d.ts +9 -0
- package/dist/api/objects.js +11 -0
- package/dist/api/on-store.js +5 -3
- package/dist/api/outcome.js +1 -1
- package/dist/api/pilot-arm.d.ts +1 -11
- package/dist/api/pilot-arm.js +5 -45
- package/dist/api/predictions.d.ts +11 -0
- package/dist/api/predictions.js +24 -0
- package/dist/api/projects.d.ts +21 -0
- package/dist/api/projects.js +24 -0
- package/dist/api/promote.d.ts +13 -17
- package/dist/api/promote.js +12 -20
- package/dist/api/prompt-hook.d.ts +19 -6
- package/dist/api/prompt-hook.js +54 -13
- package/dist/api/provenance-coverage.d.ts +14 -0
- package/dist/{cli → api}/provenance-coverage.js +2 -6
- package/dist/api/recall-cli.d.ts +58 -0
- package/dist/api/recall-cli.js +103 -0
- package/dist/api/recall-core.js +17 -9
- package/dist/api/recall-finish.d.ts +53 -0
- package/dist/api/recall-finish.js +30 -0
- package/dist/api/recall-history.d.ts +17 -84
- package/dist/api/recall-history.js +15 -88
- package/dist/api/recall-pipeline.d.ts +2 -3
- package/dist/api/recall-pipeline.js +21 -15
- package/dist/api/recall-request.js +1 -1
- package/dist/api/recall-types.d.ts +64 -304
- package/dist/api/recall.d.ts +3 -34
- package/dist/api/recall.js +63 -89
- package/dist/api/refine.d.ts +7 -0
- package/dist/api/refine.js +10 -0
- package/dist/api/remember-local.d.ts +57 -0
- package/dist/api/remember-local.js +90 -0
- package/dist/api/remember.d.ts +20 -2
- package/dist/api/remember.js +16 -3
- package/dist/api/session-end.d.ts +22 -0
- package/dist/api/session-end.js +101 -0
- package/dist/api/sleep-run.d.ts +2 -4
- package/dist/api/sleep-run.js +28 -28
- package/dist/api/sleep.d.ts +9 -28
- package/dist/api/status.d.ts +24 -0
- package/dist/api/status.js +40 -0
- package/dist/api/store-init.d.ts +4 -0
- package/dist/api/store-init.js +9 -0
- package/dist/api/tokens.d.ts +3 -10
- package/dist/api/tokens.js +4 -11
- package/dist/api/types.d.ts +8 -29
- package/dist/api/types.js +10 -23
- package/dist/capture/caller-session.d.ts +0 -3
- package/dist/capture/caller-session.js +1 -12
- package/dist/capture/capture-error.d.ts +2 -2
- package/dist/capture/capture-error.js +23 -18
- package/dist/capture/codex-patch.js +3 -1
- package/dist/capture/command.d.ts +4 -12
- package/dist/capture/command.js +39 -114
- package/dist/capture/compact.d.ts +4 -15
- package/dist/capture/compact.js +26 -30
- package/dist/capture/compaction-items-caller.js +11 -20
- package/dist/capture/compaction-record.d.ts +7 -93
- package/dist/capture/compaction-record.js +34 -399
- package/dist/capture/compaction-spool.d.ts +5 -3
- package/dist/capture/compaction-spool.js +21 -7
- package/dist/capture/copilot-transcript.d.ts +2 -1
- package/dist/capture/copilot-transcript.js +2 -1
- package/dist/capture/extract.d.ts +5 -2
- package/dist/capture/extract.js +4 -3
- package/dist/capture/failure-caller.d.ts +1 -1
- package/dist/capture/failure-caller.js +21 -16
- package/dist/capture/failure-reading.d.ts +2 -1
- package/dist/capture/failure-reading.js +7 -9
- package/dist/capture/pre-compact-caller.js +5 -4
- package/dist/capture/session-digest.d.ts +1 -1
- package/dist/capture/session-digest.js +17 -10
- package/dist/capture/session-end-caller.js +2 -1
- package/dist/capture/session-worker.js +3 -4
- package/dist/capture/transcript.d.ts +4 -11
- package/dist/capture/transcript.js +10 -21
- package/dist/capture/working-state.d.ts +4 -2
- package/dist/capture/working-state.js +4 -2
- package/dist/cli/api-context.d.ts +3 -0
- package/dist/cli/api-context.js +6 -0
- package/dist/cli/audit-prune.d.ts +1 -59
- package/dist/cli/audit-prune.js +2 -61
- package/dist/cli/audit.d.ts +1 -1
- package/dist/cli/audit.js +42 -43
- package/dist/cli/auth.d.ts +2 -2
- package/dist/cli/auth.js +61 -44
- package/dist/cli/briefs.d.ts +1 -4
- package/dist/cli/briefs.js +42 -301
- package/dist/cli/card.d.ts +2 -2
- package/dist/cli/card.js +63 -56
- package/dist/cli/client.d.ts +12 -27
- package/dist/cli/client.js +41 -44
- package/dist/cli/compact-resume-payload.js +4 -3
- package/dist/cli/context.d.ts +2 -3
- package/dist/cli/context.js +22 -18
- package/dist/cli/continuity.d.ts +6 -6
- package/dist/cli/continuity.js +81 -86
- package/dist/cli/curate.d.ts +13 -17
- package/dist/cli/curate.js +88 -116
- package/dist/cli/dag.d.ts +4 -4
- package/dist/cli/dag.js +55 -34
- package/dist/cli/decisions.d.ts +2 -4
- package/dist/cli/decisions.js +18 -309
- package/dist/cli/eval.d.ts +1 -1
- package/dist/cli/eval.js +27 -26
- package/dist/cli/exit.d.ts +6 -0
- package/dist/cli/exit.js +11 -0
- package/dist/cli/explain.d.ts +2 -3
- package/dist/cli/explain.js +25 -27
- package/dist/cli/flag-values.d.ts +6 -1
- package/dist/cli/flag-values.js +42 -15
- package/dist/cli/flags.d.ts +0 -288
- package/dist/cli/flags.js +3 -138
- package/dist/cli/github.d.ts +14 -0
- package/dist/{connectors/github/cli-impl.js → cli/github.js} +43 -71
- package/dist/cli/goals.d.ts +2 -2
- package/dist/cli/goals.js +46 -50
- package/dist/cli/graph.d.ts +3 -0
- package/dist/cli/graph.js +83 -0
- package/dist/cli/hook-runtime.d.ts +12 -32
- package/dist/cli/hook-runtime.js +21 -64
- package/dist/cli/incidents.d.ts +3 -0
- package/dist/cli/incidents.js +112 -0
- package/dist/cli/init.d.ts +2 -2
- package/dist/cli/init.js +33 -30
- package/dist/cli/install-steps.d.ts +2 -1
- package/dist/cli/install-steps.js +13 -14
- package/dist/cli/last-sleep.d.ts +2 -1
- package/dist/cli/last-sleep.js +4 -1
- package/dist/cli/lenient-id.d.ts +2 -0
- package/dist/cli/lenient-id.js +13 -0
- package/dist/cli/maintenance.d.ts +5 -5
- package/dist/cli/maintenance.js +37 -43
- package/dist/cli/notes.d.ts +2 -0
- package/dist/cli/notes.js +41 -0
- package/dist/cli/object-verbs.js +4 -3
- package/dist/cli/open-browser.d.ts +14 -0
- package/dist/cli/open-browser.js +26 -0
- package/dist/cli/owner-validation.d.ts +5 -28
- package/dist/cli/owner-validation.js +5 -28
- package/dist/cli/playbooks.d.ts +3 -4
- package/dist/cli/playbooks.js +131 -300
- package/dist/cli/predictions.d.ts +3 -0
- package/dist/cli/predictions.js +175 -0
- package/dist/cli/print.d.ts +2 -5
- package/dist/cli/print.js +3 -5
- package/dist/cli/projects.d.ts +2 -2
- package/dist/cli/projects.js +31 -25
- package/dist/cli/quality-repair-once.d.ts +1 -1
- package/dist/cli/quality-repair-once.js +7 -6
- package/dist/cli/recall.d.ts +2 -4
- package/dist/cli/recall.js +67 -137
- package/dist/cli/refine-llm.d.ts +5 -31
- package/dist/cli/refine-llm.js +23 -50
- package/dist/cli/remember.d.ts +3 -3
- package/dist/cli/remember.js +134 -192
- package/dist/cli/scheduler.d.ts +2 -17
- package/dist/cli/scheduler.js +14 -19
- package/dist/cli/serve.js +23 -14
- package/dist/cli/session-hooks.d.ts +11 -19
- package/dist/cli/session-hooks.js +143 -214
- package/dist/cli/setup.d.ts +4 -4
- package/dist/cli/setup.js +35 -30
- package/dist/cli/shared.d.ts +7 -25
- package/dist/cli/shared.js +17 -60
- package/dist/cli/slack.d.ts +2 -2
- package/dist/cli/slack.js +19 -23
- package/dist/cli/sleep-render.js +6 -3
- package/dist/cli/sleep.d.ts +3 -2
- package/dist/cli/sleep.js +17 -49
- package/dist/cli/status.d.ts +11 -14
- package/dist/cli/status.js +74 -120
- package/dist/cli/stdin.d.ts +4 -6
- package/dist/cli/stdin.js +2 -3
- package/dist/cli/transfer.d.ts +9 -9
- package/dist/cli/transfer.js +87 -108
- package/dist/cli/usage.d.ts +0 -79
- package/dist/cli/usage.js +1 -689
- package/dist/cli/verb-row.d.ts +32 -0
- package/dist/cli/verb-row.js +6 -0
- package/dist/cli/verbs/health.d.ts +27 -0
- package/dist/cli/verbs/health.js +232 -0
- package/dist/cli/verbs/memory.d.ts +16 -0
- package/dist/cli/verbs/memory.js +244 -0
- package/dist/cli/verbs/objects.d.ts +20 -0
- package/dist/cli/verbs/objects.js +254 -0
- package/dist/cli/verbs/sessions.d.ts +31 -0
- package/dist/cli/verbs/sessions.js +312 -0
- package/dist/cli/verbs.d.ts +81 -0
- package/dist/cli/verbs.js +7 -0
- package/dist/cli/versioned-verbs.d.ts +51 -0
- package/dist/cli/versioned-verbs.js +79 -0
- package/dist/cli.d.ts +1 -654
- package/dist/cli.js +44 -510
- package/dist/connectors/dlq.d.ts +39 -3
- package/dist/connectors/dlq.js +37 -3
- package/dist/connectors/github/backfill.d.ts +5 -25
- package/dist/connectors/github/backfill.js +26 -52
- package/dist/connectors/github/deletion.d.ts +4 -18
- package/dist/connectors/github/deletion.js +2 -15
- package/dist/connectors/github/dlq.d.ts +8 -33
- package/dist/connectors/github/dlq.js +34 -93
- package/dist/connectors/github/ingest.d.ts +4 -12
- package/dist/connectors/github/ingest.js +5 -36
- package/dist/connectors/github/octokit-client.d.ts +4 -16
- package/dist/connectors/github/octokit-client.js +27 -24
- package/dist/connectors/github/ratelimit.d.ts +3 -14
- package/dist/connectors/github/ratelimit.js +3 -14
- package/dist/connectors/github/scope.d.ts +3 -5
- package/dist/connectors/github/scope.js +3 -5
- package/dist/connectors/github/signature.d.ts +4 -26
- package/dist/connectors/github/signature.js +4 -26
- package/dist/connectors/github/tenant-routing.d.ts +2 -24
- package/dist/connectors/github/tenant-routing.js +4 -26
- package/dist/connectors/github/transform.js +2 -16
- package/dist/connectors/github/types.d.ts +3 -15
- package/dist/connectors/github/types.js +2 -9
- package/dist/connectors/github/webhook.d.ts +3 -16
- package/dist/connectors/github/webhook.js +38 -23
- package/dist/connectors/ingest.d.ts +10 -0
- package/dist/connectors/ingest.js +28 -0
- package/dist/connectors/slack/backfill.d.ts +4 -17
- package/dist/connectors/slack/backfill.js +4 -13
- package/dist/connectors/slack/dlq.d.ts +2 -19
- package/dist/connectors/slack/dlq.js +51 -90
- package/dist/connectors/slack/ingest.d.ts +4 -9
- package/dist/connectors/slack/ingest.js +4 -30
- package/dist/connectors/slack/scope.d.ts +2 -8
- package/dist/connectors/slack/scope.js +2 -8
- package/dist/connectors/slack/signature.d.ts +1 -6
- package/dist/connectors/slack/tenant-routing.d.ts +2 -19
- package/dist/connectors/slack/tenant-routing.js +2 -21
- package/dist/connectors/slack/transform.d.ts +2 -18
- package/dist/connectors/slack/transform.js +2 -18
- package/dist/connectors/slack/types.d.ts +3 -9
- package/dist/connectors/slack/types.js +2 -4
- package/dist/connectors/slack/web-client.js +33 -27
- package/dist/connectors/slack/webhook.d.ts +3 -19
- package/dist/connectors/slack/webhook.js +6 -26
- package/dist/connectors/slack/workspaces.d.ts +4 -24
- package/dist/connectors/slack/workspaces.js +4 -24
- package/dist/consolidate/conflicts.js +7 -15
- package/dist/consolidate/dag-summary.d.ts +8 -0
- package/dist/consolidate/dag-summary.js +41 -0
- package/dist/consolidate/dag.d.ts +5 -28
- package/dist/consolidate/dag.js +29 -84
- package/dist/consolidate/decay.js +7 -19
- package/dist/consolidate/dedupe.d.ts +13 -52
- package/dist/consolidate/dedupe.js +18 -74
- package/dist/consolidate/flush-units.d.ts +3 -4
- package/dist/consolidate/flush-units.js +3 -3
- package/dist/consolidate/half-life-migration.d.ts +3 -26
- package/dist/consolidate/half-life-migration.js +11 -28
- package/dist/consolidate/llm-passes.js +49 -40
- package/dist/consolidate/memory-value-weights.d.ts +2 -13
- package/dist/consolidate/memory-value-weights.js +2 -13
- package/dist/consolidate/memory-value.d.ts +11 -84
- package/dist/consolidate/memory-value.js +18 -105
- package/dist/consolidate/merge.js +12 -33
- package/dist/consolidate/physics-pass.d.ts +6 -1
- package/dist/consolidate/physics-pass.js +27 -9
- package/dist/consolidate/replay.d.ts +6 -38
- package/dist/consolidate/replay.js +25 -56
- package/dist/consolidate/run.js +1 -1
- package/dist/consolidate/sleep.d.ts +1 -8
- package/dist/consolidate/sleep.js +13 -22
- package/dist/consolidate/trace.d.ts +3 -18
- package/dist/consolidate/trace.js +3 -18
- package/dist/consolidate/traces.js +9 -25
- package/dist/core/ablation.d.ts +14 -91
- package/dist/core/ablation.js +16 -91
- package/dist/core/active-scope.d.ts +7 -0
- package/dist/{sharing/scope.js → core/active-scope.js} +5 -12
- package/dist/core/ambient.d.ts +5 -8
- package/dist/core/ambient.js +40 -26
- package/dist/core/api-errors.d.ts +16 -0
- package/dist/core/api-errors.js +20 -0
- package/dist/core/capture-contract.d.ts +4 -2
- package/dist/core/capture-contract.js +4 -2
- package/dist/core/card.d.ts +4 -1
- package/dist/core/card.js +11 -1
- package/dist/core/compaction-timing.d.ts +7 -0
- package/dist/core/compaction-timing.js +9 -0
- package/dist/core/compare.d.ts +11 -67
- package/dist/core/compare.js +9 -63
- package/dist/core/config.d.ts +10 -24
- package/dist/core/config.js +20 -16
- package/dist/core/cosine.d.ts +3 -0
- package/dist/core/cosine.js +19 -0
- package/dist/core/handoff.d.ts +1 -7
- package/dist/core/handoff.js +1 -7
- package/dist/core/markdown.d.ts +6 -0
- package/dist/{store → core}/markdown.js +23 -22
- package/dist/core/memory-audit.d.ts +17 -0
- package/dist/core/memory-audit.js +54 -0
- package/dist/core/memory-quality.js +1 -1
- package/dist/core/memory.d.ts +51 -168
- package/dist/core/memory.js +76 -239
- package/dist/{consolidate → core}/merged-row.d.ts +1 -1
- package/dist/{consolidate → core}/merged-row.js +3 -2
- package/dist/core/package-root.d.ts +3 -0
- package/dist/core/package-root.js +5 -0
- package/dist/core/physics-config.d.ts +1 -4
- package/dist/core/physics-config.js +3 -8
- package/dist/core/physics.d.ts +15 -61
- package/dist/core/physics.js +65 -138
- package/dist/core/prediction.d.ts +3 -0
- package/dist/core/prediction.js +6 -0
- package/dist/core/project-identity.d.ts +20 -58
- package/dist/core/project-identity.js +27 -39
- package/dist/core/project-remote.js +13 -2
- package/dist/core/raw-append-only.d.ts +5 -0
- package/dist/core/raw-append-only.js +7 -0
- package/dist/core/recall-scope.d.ts +69 -0
- package/dist/core/recall-scope.js +138 -0
- package/dist/core/salience.d.ts +1 -7
- package/dist/core/salience.js +1 -7
- package/dist/dashboard/dashboard-actions.js +2 -2
- package/dist/dashboard/dashboard-snapshot.d.ts +2 -1
- package/dist/dashboard/dashboard-snapshot.js +11 -2
- package/dist/dashboard/dashboard-types.d.ts +2 -1
- package/dist/dashboard/dashboard.d.ts +1 -0
- package/dist/dashboard/dashboard.js +50 -4
- package/dist/db/busy.d.ts +3 -8
- package/dist/db/busy.js +37 -33
- package/dist/db/connect.js +7 -8
- package/dist/db/index.d.ts +3 -3
- package/dist/db/index.js +3 -3
- package/dist/db/migrate.js +3 -3
- package/dist/db/migrations/index.d.ts +1 -1
- package/dist/db/migrations/index.js +3 -1
- package/dist/db/migrations/types.d.ts +2 -5
- package/dist/db/migrations/v14.js +2 -3
- package/dist/db/migrations/v15.js +2 -8
- package/dist/db/migrations/v16.js +2 -3
- package/dist/db/migrations/v17.js +2 -3
- package/dist/db/migrations/v19.js +2 -5
- package/dist/db/migrations/v20.js +2 -7
- package/dist/db/migrations/v22.js +4 -17
- package/dist/db/migrations/v23.js +3 -14
- package/dist/db/migrations/v24.js +2 -4
- package/dist/db/migrations/v25.js +3 -8
- package/dist/db/migrations/v26.js +2 -13
- package/dist/db/migrations/v27.js +3 -11
- package/dist/db/migrations/v28.js +2 -14
- package/dist/db/migrations/v29.js +4 -18
- package/dist/db/migrations/v30.js +4 -16
- package/dist/db/migrations/v31.js +2 -15
- package/dist/db/migrations/v32.js +4 -22
- package/dist/db/migrations/v33.js +2 -15
- package/dist/db/migrations/v34.js +2 -11
- package/dist/db/migrations/v35.js +2 -11
- package/dist/db/migrations/v36.js +2 -11
- package/dist/db/migrations/v37.js +8 -33
- package/dist/db/migrations/v38.js +5 -38
- package/dist/db/migrations/v39.js +9 -20
- package/dist/db/migrations/v40.js +2 -9
- package/dist/db/migrations/v41.js +2 -24
- package/dist/db/migrations/v43.js +1 -1
- package/dist/db/migrations/v44.js +2 -8
- package/dist/db/migrations/v45.js +2 -7
- package/dist/db/migrations/v47.js +2 -3
- package/dist/db/migrations/v51.js +1 -1
- package/dist/db/migrations/v55.d.ts +3 -0
- package/dist/db/migrations/v55.js +17 -0
- package/dist/db/open.d.ts +2 -2
- package/dist/db/open.js +21 -9
- package/dist/db/physics-state.d.ts +6 -22
- package/dist/db/physics-state.js +28 -39
- package/dist/db/raw-archive-mirror-cleanup.d.ts +2 -17
- package/dist/db/raw-archive-mirror-cleanup.js +2 -17
- package/dist/db/tables.d.ts +2 -0
- package/dist/db/tables.js +6 -2
- package/dist/db/vector-store.d.ts +2 -0
- package/dist/db/vector-store.js +25 -5
- package/dist/db/wal-checkpointer.js +2 -1
- package/dist/doctor.js +85 -27
- package/dist/embeddings/local.d.ts +62 -0
- package/dist/embeddings/local.js +146 -0
- package/dist/embeddings/provider.d.ts +43 -0
- package/dist/{store/embeddings → embeddings}/provider.js +58 -83
- package/dist/embeddings/transformers.d.ts +26 -0
- package/dist/embeddings/transformers.js +89 -0
- package/dist/entry/session-text.d.ts +1 -1
- package/dist/entry/session-text.js +3 -2
- package/dist/eval/eval-stats.d.ts +15 -53
- package/dist/eval/eval-stats.js +12 -42
- package/dist/eval/eval-suite.d.ts +2 -13
- package/dist/eval/eval-suite.js +91 -45
- package/dist/eval/eval.d.ts +6 -24
- package/dist/eval/eval.js +8 -35
- package/dist/eval/judgment.d.ts +0 -3
- package/dist/eval/judgment.js +30 -15
- package/dist/extensions/openclaw-plugin/index.js +1 -1
- package/dist/graph/extract.d.ts +5 -27
- package/dist/graph/extract.js +10 -36
- package/dist/graph/recall.d.ts +7 -20
- package/dist/graph/recall.js +15 -77
- package/dist/graph/stream.d.ts +7 -39
- package/dist/graph/stream.js +11 -34
- package/dist/graph/view.d.ts +9 -35
- package/dist/graph/view.js +24 -44
- package/dist/graph/write.d.ts +1 -1
- package/dist/graph/write.js +9 -9
- package/dist/hooks/codex-session.js +3 -2
- package/dist/hooks/codex-wrapper.js +4 -3
- package/dist/hooks/copilot.js +2 -1
- package/dist/hooks/hook-blocks.d.ts +6 -2
- package/dist/hooks/hook-blocks.js +37 -124
- package/dist/hooks/json-hooks.d.ts +4 -36
- package/dist/hooks/json-hooks.js +22 -59
- package/dist/hooks/opencode.d.ts +0 -38
- package/dist/hooks/opencode.js +17 -81
- package/dist/hooks/shared.d.ts +6 -21
- package/dist/hooks/shared.js +31 -33
- package/dist/importers/core.d.ts +12 -37
- package/dist/importers/core.js +10 -28
- package/dist/importers/markdown-parse.d.ts +8 -22
- package/dist/importers/markdown-parse.js +8 -25
- package/dist/importers/markdown.js +3 -12
- package/dist/importers/sources.js +12 -39
- package/dist/importers/vault.d.ts +2 -8
- package/dist/importers/vault.js +31 -85
- package/dist/index.d.ts +8 -3
- package/dist/index.js +8 -3
- package/dist/learn/autolearn.d.ts +23 -42
- package/dist/learn/autolearn.js +45 -52
- package/dist/learn/churn-git.js +13 -4
- package/dist/learn/extract.d.ts +3 -2
- package/dist/learn/extract.js +8 -8
- package/dist/learn/forward-claim-detector.d.ts +6 -30
- package/dist/learn/forward-claim-detector.js +18 -68
- package/dist/learn/invalidation.d.ts +6 -18
- package/dist/learn/invalidation.js +18 -31
- package/dist/mcp/admin-tools.d.ts +2 -2
- package/dist/mcp/admin-tools.js +30 -53
- package/dist/mcp/format.js +3 -2
- package/dist/mcp/framing.d.ts +1 -0
- package/dist/mcp/framing.js +1 -1
- package/dist/mcp/memory-tools.js +7 -10
- package/dist/mcp/protocol.d.ts +15 -29
- package/dist/mcp/protocol.js +26 -16
- package/dist/mcp/recall-tools.js +14 -11
- package/dist/mcp/request.d.ts +3 -11
- package/dist/mcp/request.js +12 -27
- package/dist/mcp/server.d.ts +3 -9
- package/dist/mcp/server.js +5 -16
- package/dist/mcp/session-state.d.ts +1 -1
- package/dist/mcp/session-state.js +5 -11
- package/dist/mcp/stdio.d.ts +2 -6
- package/dist/mcp/stdio.js +68 -16
- package/dist/mcp/tool-args.js +2 -1
- package/dist/mcp/tools.js +47 -22
- package/dist/objects/customer-notes.d.ts +9 -44
- package/dist/objects/customer-notes.js +7 -48
- package/dist/objects/decisions.d.ts +14 -52
- package/dist/objects/decisions.js +15 -53
- package/dist/objects/descriptor.d.ts +1 -1
- package/dist/objects/incidents.d.ts +9 -40
- package/dist/objects/incidents.js +7 -41
- package/dist/objects/lifecycle.d.ts +1 -1
- package/dist/objects/lifecycle.js +2 -1
- package/dist/objects/policies.d.ts +13 -91
- package/dist/objects/policies.js +13 -99
- package/dist/objects/processes.d.ts +9 -54
- package/dist/objects/processes.js +7 -61
- package/dist/objects/project-briefs.d.ts +13 -57
- package/dist/objects/project-briefs.js +16 -78
- package/dist/objects/skills.d.ts +9 -44
- package/dist/objects/skills.js +8 -54
- package/dist/postinstall.js +7 -20
- package/dist/predictions/planning-fallacy.d.ts +11 -34
- package/dist/rerankers/clef.js +6 -37
- package/dist/rerankers/cross-encoder.d.ts +31 -2
- package/dist/rerankers/cross-encoder.js +65 -101
- package/dist/rerankers/index.js +5 -4
- package/dist/rerankers/jev.js +11 -10
- package/dist/rerankers/llm.d.ts +2 -11
- package/dist/rerankers/llm.js +14 -14
- package/dist/rerankers/outage-warning.d.ts +5 -2
- package/dist/rerankers/outage-warning.js +7 -4
- package/dist/rerankers/remote.d.ts +1 -0
- package/dist/rerankers/remote.js +2 -0
- package/dist/rerankers/types.d.ts +2 -14
- package/dist/search/bm25-search.js +1 -1
- package/dist/search/boosts.d.ts +0 -2
- package/dist/search/boosts.js +3 -3
- package/dist/search/embed-memories.d.ts +9 -0
- package/dist/search/embed-memories.js +377 -0
- package/dist/search/goal-boost.d.ts +23 -0
- package/dist/search/goal-boost.js +93 -0
- package/dist/search/hybrid.d.ts +11 -2
- package/dist/search/hybrid.js +9 -4
- package/dist/search/multihop.js +2 -3
- package/dist/search/path-context.d.ts +5 -18
- package/dist/search/path-context.js +5 -18
- package/dist/search/physics-search.d.ts +6 -3
- package/dist/search/physics-search.js +29 -19
- package/dist/search/rerank.js +1 -1
- package/dist/search/rrf.d.ts +7 -35
- package/dist/search/rrf.js +5 -30
- package/dist/search/vector.d.ts +13 -4
- package/dist/search/vector.js +40 -13
- package/dist/server/auth.d.ts +11 -28
- package/dist/server/auth.js +49 -32
- package/dist/server/boot.d.ts +2 -21
- package/dist/server/boot.js +69 -161
- package/dist/server/client-ip.d.ts +3 -21
- package/dist/server/client-ip.js +8 -30
- package/dist/server/deadline.d.ts +0 -2
- package/dist/server/deadline.js +1 -7
- package/dist/server/defaults.d.ts +4 -0
- package/dist/server/defaults.js +5 -0
- package/dist/server/key-check-bounds.d.ts +2 -1
- package/dist/server/lifecycle.d.ts +5 -4
- package/dist/server/lifecycle.js +19 -4
- package/dist/server/mcp-http.js +24 -53
- package/dist/server/rate-limit.d.ts +5 -18
- package/dist/server/rate-limit.js +4 -13
- package/dist/server/request.d.ts +4 -8
- package/dist/server/request.js +16 -19
- package/dist/server/route-checks.d.ts +7 -0
- package/dist/server/route-checks.js +42 -0
- package/dist/server/route-table.d.ts +9 -8
- package/dist/server/route-table.js +127 -139
- package/dist/server/routes/admin.d.ts +8 -8
- package/dist/server/routes/admin.js +35 -49
- package/dist/server/routes/customer-notes.d.ts +6 -6
- package/dist/server/routes/customer-notes.js +8 -14
- package/dist/server/routes/decisions.d.ts +6 -6
- package/dist/server/routes/decisions.js +12 -21
- package/dist/server/routes/incidents.d.ts +6 -6
- package/dist/server/routes/incidents.js +14 -25
- package/dist/server/routes/memories.d.ts +9 -9
- package/dist/server/routes/memories.js +20 -63
- package/dist/server/routes/object-routes.d.ts +6 -11
- package/dist/server/routes/object-routes.js +11 -22
- package/dist/server/routes/policies.d.ts +7 -7
- package/dist/server/routes/policies.js +12 -22
- package/dist/server/routes/predictions.d.ts +6 -6
- package/dist/server/routes/predictions.js +22 -38
- package/dist/server/routes/processes.d.ts +6 -6
- package/dist/server/routes/processes.js +17 -28
- package/dist/server/routes/project-briefs.d.ts +7 -7
- package/dist/server/routes/project-briefs.js +11 -20
- package/dist/server/routes/recall.d.ts +5 -5
- package/dist/server/routes/recall.js +16 -45
- package/dist/server/routes/skills.d.ts +7 -7
- package/dist/server/routes/skills.js +10 -22
- package/dist/server/server-detect.d.ts +15 -53
- package/dist/server/server-detect.js +55 -96
- package/dist/server/sleep-child.js +26 -7
- package/dist/server/sleep-offload.d.ts +11 -5
- package/dist/server/sleep-offload.js +21 -12
- package/dist/server/types.d.ts +32 -18
- package/dist/server/validation.d.ts +3 -2
- package/dist/server/validation.js +15 -33
- package/dist/server.d.ts +7 -6
- package/dist/server.js +8 -6
- package/dist/sharing/global-store.d.ts +19 -0
- package/dist/sharing/global-store.js +75 -0
- package/dist/sharing/global-sync.d.ts +6 -0
- package/dist/sharing/global-sync.js +73 -0
- package/dist/sharing/project-merge.d.ts +11 -19
- package/dist/sharing/project-merge.js +63 -125
- package/dist/sharing/search-both.d.ts +60 -0
- package/dist/sharing/search-both.js +138 -0
- package/dist/sharing/share.d.ts +40 -0
- package/dist/sharing/share.js +229 -0
- package/dist/store/ambient.js +9 -15
- package/dist/store/audit-event.d.ts +5 -13
- package/dist/store/audit-event.js +4 -12
- package/dist/store/audit.d.ts +2 -21
- package/dist/store/audit.js +8 -67
- package/dist/store/auth.d.ts +10 -8
- package/dist/store/auth.js +17 -12
- package/dist/store/candidates.d.ts +23 -5
- package/dist/store/candidates.js +119 -71
- package/dist/store/capture-write.d.ts +17 -0
- package/dist/store/capture-write.js +35 -0
- package/dist/store/card-detail.d.ts +2 -1
- package/dist/store/card-detail.js +2 -1
- package/dist/store/cards.d.ts +14 -9
- package/dist/store/cards.js +72 -126
- package/dist/store/compaction-caller.d.ts +18 -0
- package/dist/store/compaction-caller.js +29 -0
- package/dist/store/compactions-record.d.ts +115 -0
- package/dist/store/compactions-record.js +417 -0
- package/dist/store/compactions.js +6 -4
- package/dist/store/conflicts.d.ts +4 -21
- package/dist/store/conflicts.js +76 -152
- package/dist/store/connectors/dlq.d.ts +10 -0
- package/dist/store/connectors/dlq.js +32 -0
- package/dist/store/connectors/github.d.ts +14 -16
- package/dist/store/connectors/github.js +34 -57
- package/dist/store/connectors/slack.d.ts +2 -11
- package/dist/store/connectors/slack.js +18 -47
- package/dist/store/dashboard-reads.js +11 -14
- package/dist/store/delete-and-batch.d.ts +17 -31
- package/dist/store/delete-and-batch.js +84 -67
- package/dist/store/delivery-recorder.d.ts +15 -4
- package/dist/store/delivery-recorder.js +34 -17
- package/dist/store/descendant-page.js +6 -3
- package/dist/store/diagnostics.js +6 -13
- package/dist/store/dormant.d.ts +8 -30
- package/dist/store/dormant.js +18 -57
- package/dist/store/embeddings/index.d.ts +11 -90
- package/dist/store/embeddings/index.js +18 -523
- package/dist/store/entry-reads.d.ts +34 -61
- package/dist/store/entry-reads.js +111 -136
- package/dist/store/entry-row.d.ts +6 -35
- package/dist/store/entry-row.js +9 -37
- package/dist/store/entry-writes.d.ts +12 -18
- package/dist/store/entry-writes.js +46 -44
- package/dist/store/failure-log-at.d.ts +8 -0
- package/dist/store/failure-log-at.js +16 -0
- package/dist/store/failure-log.d.ts +2 -2
- package/dist/store/failure-log.js +5 -3
- package/dist/{trust → store}/gated-write.d.ts +3 -1
- package/dist/{trust → store}/gated-write.js +7 -5
- package/dist/store/goals.d.ts +3 -40
- package/dist/store/goals.js +41 -178
- package/dist/store/graph-queue.d.ts +6 -22
- package/dist/store/graph-queue.js +14 -45
- package/dist/store/graph-reads.d.ts +11 -35
- package/dist/store/graph-reads.js +24 -60
- package/dist/store/graph-rows.d.ts +3 -6
- package/dist/store/graph-rows.js +6 -9
- package/dist/store/graph-view-rows.d.ts +2 -1
- package/dist/store/graph-view-rows.js +2 -1
- package/dist/store/graph-writes.d.ts +10 -35
- package/dist/store/graph-writes.js +59 -106
- package/dist/store/half-life.d.ts +2 -1
- package/dist/store/half-life.js +14 -16
- package/dist/store/handoffs.d.ts +6 -12
- package/dist/store/handoffs.js +22 -40
- package/dist/store/index-and-stats.d.ts +6 -19
- package/dist/store/index-and-stats.js +36 -84
- package/dist/store/key-writes.js +1 -1
- package/dist/store/ledger-turn.d.ts +9 -0
- package/dist/store/ledger-turn.js +38 -0
- package/dist/store/mirrors.d.ts +2 -6
- package/dist/store/mirrors.js +24 -29
- package/dist/store/open.d.ts +0 -4
- package/dist/store/open.js +42 -62
- package/dist/store/pilot-arm.d.ts +12 -0
- package/dist/store/pilot-arm.js +42 -0
- package/dist/store/planning-fallacy-evidence.d.ts +2 -30
- package/dist/store/planning-fallacy-evidence.js +8 -37
- package/dist/store/port.d.ts +91 -82
- package/dist/store/port.js +8 -0
- package/dist/store/predictions.d.ts +12 -53
- package/dist/store/predictions.js +41 -125
- package/dist/store/project-tags.d.ts +62 -0
- package/dist/store/project-tags.js +67 -0
- package/dist/{cli → store}/quality-repair.js +15 -31
- package/dist/store/quarantine.js +2 -1
- package/dist/store/raw-archive.d.ts +4 -16
- package/dist/store/raw-archive.js +14 -29
- package/dist/store/recall-trace.d.ts +12 -77
- package/dist/store/recall-trace.js +37 -97
- package/dist/store/rejected-values.js +9 -20
- package/dist/store/rejection-probe.d.ts +4 -0
- package/dist/store/rejection-probe.js +19 -0
- package/dist/store/rejection.d.ts +10 -73
- package/dist/store/rejection.js +12 -84
- package/dist/store/report-reads.d.ts +6 -0
- package/dist/store/report-reads.js +29 -0
- package/dist/store/rows.d.ts +9 -19
- package/dist/store/rows.js +36 -34
- package/dist/store/rule-sql.d.ts +14 -0
- package/dist/store/rule-sql.js +56 -0
- package/dist/store/search-rows.d.ts +7 -38
- package/dist/store/search-rows.js +23 -54
- package/dist/store/sessions.d.ts +10 -47
- package/dist/store/sessions.js +21 -75
- package/dist/store/set-aside.d.ts +14 -0
- package/dist/store/set-aside.js +23 -0
- package/dist/store/sqlite/busy.d.ts +3 -0
- package/dist/store/sqlite/busy.js +8 -0
- package/dist/store/sqlite/connector-events-group.js +5 -8
- package/dist/store/sqlite/connector-writes-group.js +3 -3
- package/dist/store/sqlite/entry-writes-group.js +4 -4
- package/dist/store/sqlite/error-codec.js +8 -5
- package/dist/store/sqlite/executor.d.ts +2 -1
- package/dist/store/sqlite/executor.js +12 -5
- package/dist/store/sqlite/graph-reads-group.js +1 -1
- package/dist/store/sqlite/held-connection.d.ts +9 -0
- package/dist/store/sqlite/held-connection.js +41 -0
- package/dist/store/sqlite/local.d.ts +3 -3
- package/dist/store/sqlite/local.js +1 -0
- package/dist/store/sqlite/object-rows.d.ts +1 -1
- package/dist/store/sqlite/object-rows.js +6 -3
- package/dist/store/sqlite/objects-group.d.ts +1 -1
- package/dist/store/sqlite/objects-group.js +5 -3
- package/dist/store/sqlite/quarantine-group.js +2 -2
- package/dist/store/sqlite/store.d.ts +1 -4
- package/dist/store/sqlite/store.js +16 -15
- package/dist/store/sqlite/vector-reads-group.js +2 -2
- package/dist/store/sqlite/worker-jobs.js +1 -1
- package/dist/store/sqlite/worker-ops.d.ts +13 -2
- package/dist/store/sqlite/worker-ops.js +13 -2
- package/dist/store/sqlite/worker-store.js +5 -2
- package/dist/store/summaries.d.ts +11 -63
- package/dist/store/summaries.js +37 -148
- package/dist/store/summary-dirty.js +1 -1
- package/dist/store/tenant-lookup.d.ts +6 -1
- package/dist/store/tenant-lookup.js +7 -0
- package/dist/store/tenant.d.ts +2 -16
- package/dist/store/tenant.js +4 -19
- package/dist/store/token-ledger.d.ts +13 -50
- package/dist/store/token-ledger.js +19 -85
- package/dist/store/usage-reports.js +5 -13
- package/dist/store/vector-index.d.ts +46 -0
- package/dist/store/vector-index.js +171 -0
- package/dist/store/working-memory.d.ts +6 -25
- package/dist/store/working-memory.js +18 -52
- package/dist/support-bundle.js +9 -8
- package/dist/trust/reject-flow.js +3 -5
- package/dist/util/agent-homes.d.ts +15 -0
- package/dist/util/agent-homes.js +34 -0
- package/dist/util/anthropic-messages.d.ts +3 -0
- package/dist/util/anthropic-messages.js +35 -6
- package/dist/util/atomic-write.d.ts +2 -1
- package/dist/util/atomic-write.js +35 -4
- package/dist/util/capped-json.d.ts +6 -0
- package/dist/util/capped-json.js +46 -0
- package/dist/util/chunked.d.ts +3 -0
- package/dist/util/chunked.js +10 -0
- package/dist/util/crash-handlers.d.ts +8 -1
- package/dist/util/crash-handlers.js +52 -10
- package/dist/util/ellipsize.d.ts +3 -0
- package/dist/util/ellipsize.js +5 -0
- package/dist/util/env.d.ts +14 -6
- package/dist/util/env.js +15 -7
- package/dist/util/format.d.ts +3 -0
- package/dist/util/format.js +6 -0
- package/dist/util/home-path.d.ts +2 -1
- package/dist/util/home-path.js +4 -2
- package/dist/util/hook-payload.d.ts +8 -0
- package/dist/util/hook-payload.js +37 -0
- package/dist/util/http-retry.d.ts +9 -1
- package/dist/util/http-retry.js +67 -8
- package/dist/util/http-util.d.ts +0 -8
- package/dist/util/http-util.js +10 -21
- package/dist/util/limits.d.ts +7 -0
- package/dist/util/limits.js +14 -0
- package/dist/util/log.d.ts +7 -2
- package/dist/util/log.js +34 -9
- package/dist/util/request-scope.d.ts +5 -2
- package/dist/util/request-scope.js +13 -7
- package/dist/util/secret-detect.d.ts +5 -30
- package/dist/util/secret-detect.js +19 -40
- package/dist/util/sqlite-blocked.d.ts +2 -0
- package/dist/util/sqlite-blocked.js +5 -0
- package/dist/util/stored-json.d.ts +1 -1
- package/dist/util/stored-json.js +1 -1
- package/dist/util/stream-tee.d.ts +3 -0
- package/dist/util/stream-tee.js +35 -0
- package/dist/util/time.d.ts +4 -0
- package/dist/util/time.js +4 -0
- package/dist/util/token-text.d.ts +1 -4
- package/dist/util/token-text.js +1 -4
- package/dist/util/transcript-tail.d.ts +2 -0
- package/dist/util/transcript-tail.js +24 -23
- package/dist/util/version.d.ts +5 -20
- package/dist/util/version.js +5 -20
- package/dist/{store → util}/yaml.d.ts +1 -5
- package/dist/{store → util}/yaml.js +2 -6
- package/extensions/openclaw-plugin/index.ts +1 -1
- package/extensions/openclaw-plugin/openclaw.plugin.json +1 -1
- package/extensions/openclaw-plugin/package.json +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +2 -2
- package/dist/cli/provenance-coverage.d.ts +0 -18
- package/dist/connectors/github/cli-impl.d.ts +0 -25
- package/dist/sharing/scope.d.ts +0 -14
- package/dist/sharing/shared.d.ts +0 -187
- package/dist/sharing/shared.js +0 -563
- package/dist/store/embeddings/local.d.ts +0 -15
- package/dist/store/embeddings/local.js +0 -192
- package/dist/store/embeddings/provider.d.ts +0 -77
- package/dist/store/markdown.d.ts +0 -10
- package/dist/store/recall-scope.d.ts +0 -138
- package/dist/store/recall-scope.js +0 -216
- package/dist/store/vector-writes.d.ts +0 -13
- package/dist/store/vector-writes.js +0 -89
- /package/dist/{store → core}/object-types.d.ts +0 -0
- /package/dist/{store → core}/object-types.js +0 -0
- /package/dist/{cli → store}/quality-repair.d.ts +0 -0
- /package/dist/{capture → util}/compaction-items.d.ts +0 -0
- /package/dist/{capture → util}/compaction-items.js +0 -0
package/dist/server/auth.js
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
// Bearer and loopback auth for the HTTP server.
|
|
2
|
-
import { envRequireAuth } from '../util/env.js';
|
|
2
|
+
import { envAllowKeylessLocal, envRequireAuth } from '../util/env.js';
|
|
3
3
|
import { resolveTenantId } from '../store/tenant.js';
|
|
4
|
-
import { log } from '../util/log.js';
|
|
4
|
+
import { errorFields, errorMessage, log } from '../util/log.js';
|
|
5
5
|
import { API_KEY_PREFIX, verifyApiKeyCached } from '../store/auth.js';
|
|
6
6
|
import { ownerOrSubject } from '../api/index.js';
|
|
7
|
-
import { HttpError, isCrossSite, isHeaderString, LOOPBACK_HOST_HEADER
|
|
7
|
+
import { HttpError, isCrossSite, isHeaderString, LOOPBACK_HOST_HEADER } from '../util/http-util.js';
|
|
8
|
+
import { MAX_ID_LEN } from '../util/limits.js';
|
|
8
9
|
import { clientLimitKey } from './client-ip.js';
|
|
9
10
|
import { keyCheckBounds } from './key-check-bounds.js';
|
|
10
11
|
import { isJsonString } from '../util/json.js';
|
|
11
|
-
/**
|
|
12
|
-
* Recognise loopback remote addresses. Node reports IPv6-mapped IPv4 as
|
|
13
|
-
* '::ffff:127.0.0.1' on dual-stack sockets, so we accept that alongside
|
|
14
|
-
* the bare v4 and v6 loopbacks. Anything else is treated as remote.
|
|
15
|
-
*/
|
|
12
|
+
/** Recognises loopback remote addresses, including the IPv6-mapped '::ffff:127.0.0.1' Node reports on dual-stack sockets; anything else is remote. */
|
|
16
13
|
export function isLoopback(remoteAddress) {
|
|
17
14
|
if (!remoteAddress)
|
|
18
15
|
return false;
|
|
@@ -29,21 +26,36 @@ const PROXY_HEADERS = [
|
|
|
29
26
|
'forwarded', 'x-forwarded-for', 'x-forwarded-host', 'x-forwarded-proto', 'x-real-ip', 'cf-connecting-ip', 'true-client-ip',
|
|
30
27
|
'fly-client-ip',
|
|
31
28
|
];
|
|
32
|
-
|
|
29
|
+
function proxyHeaderOf(req) {
|
|
30
|
+
return PROXY_HEADERS.find((name) => req.headers[name] !== undefined);
|
|
31
|
+
}
|
|
32
|
+
/** A page in a browser on this machine: another site's request, or a hostile name resolved to loopback (DNS rebinding). */
|
|
33
|
+
function isForeignPage(req) {
|
|
34
|
+
const host = req.headers.host;
|
|
35
|
+
return (host !== undefined && !LOOPBACK_HOST_HEADER.test(host)) || isCrossSite(req);
|
|
36
|
+
}
|
|
37
|
+
/** True only for a request made on this machine by its own user: a proxy and a browser page are loopback too, so the socket alone proves nothing. */
|
|
38
|
+
export function isLocalCaller(req) {
|
|
39
|
+
return isLoopback(req.socket.remoteAddress) && proxyHeaderOf(req) === undefined && !isForeignPage(req);
|
|
40
|
+
}
|
|
41
|
+
// The throwing twin of isLocalCaller, for the no-key fallback: each refusal has its own status, and a proxied one is logged.
|
|
33
42
|
function assertLocalCaller(req) {
|
|
34
43
|
if (!isLoopback(req.socket.remoteAddress))
|
|
35
44
|
throw new HttpError(401, 'auth required');
|
|
36
|
-
const proxyHeader =
|
|
45
|
+
const proxyHeader = proxyHeaderOf(req);
|
|
37
46
|
if (proxyHeader !== undefined) {
|
|
38
47
|
log.warn(`proxied loopback request refused: it carries ${proxyHeader}, so the no-key local fallback does not apply. ` +
|
|
39
48
|
'Send an API key (hippo auth create, then Authorization: Bearer hk_...).');
|
|
40
49
|
throw new HttpError(401, 'auth required');
|
|
41
50
|
}
|
|
42
|
-
|
|
43
|
-
if ((host !== undefined && !LOOPBACK_HOST_HEADER.test(host)) || isCrossSite(req)) {
|
|
51
|
+
if (isForeignPage(req)) {
|
|
44
52
|
throw new HttpError(403, 'cross-site or non-local request refused; send an API key');
|
|
45
53
|
}
|
|
46
54
|
}
|
|
55
|
+
/** The 401 a keyless request from this machine gets by default; it names both fixes, since an upgrade is where most people meet it. */
|
|
56
|
+
const KEY_REQUIRED_MESSAGE = 'auth required: this server takes no request without an API key. Mint one with `hippo auth create` and send it as ' +
|
|
57
|
+
'"Authorization: Bearer <key>" (the hippo CLI reads HIPPO_API_KEY), or start the server with HIPPO_ALLOW_KEYLESS_LOCAL=1 ' +
|
|
58
|
+
'to let requests from this machine in without a key.';
|
|
47
59
|
export function readAuthHeader(req) {
|
|
48
60
|
const raw = req.headers['authorization'];
|
|
49
61
|
if (raw === undefined)
|
|
@@ -167,7 +179,7 @@ async function resolveBearer(req, token, opts) {
|
|
|
167
179
|
id.owner = key.ownerSubject;
|
|
168
180
|
return id;
|
|
169
181
|
}
|
|
170
|
-
/** The auth check without a charge: the bearer's identity, or null for the
|
|
182
|
+
/** The auth check without a charge: the bearer's identity, or null for the keyless local fallback. */
|
|
171
183
|
async function checkAuth(req, opts) {
|
|
172
184
|
const auth = readAuthHeader(req);
|
|
173
185
|
if (auth.kind === 'malformed') {
|
|
@@ -175,10 +187,14 @@ async function checkAuth(req, opts) {
|
|
|
175
187
|
}
|
|
176
188
|
if (auth.kind === 'bearer')
|
|
177
189
|
return resolveBearer(req, auth.token, opts);
|
|
178
|
-
// No Authorization header
|
|
190
|
+
// No Authorization header. HIPPO_REQUIRE_AUTH=1 is checked first, so a deployment that sets both stays closed.
|
|
179
191
|
if (envRequireAuth()) {
|
|
180
192
|
throw new HttpError(401, 'auth required');
|
|
181
193
|
}
|
|
194
|
+
if (!envAllowKeylessLocal()) {
|
|
195
|
+
// Only this machine's own user is told how to switch the key off; a proxied caller or a page learns nothing.
|
|
196
|
+
throw new HttpError(401, isLocalCaller(req) ? KEY_REQUIRED_MESSAGE : 'auth required');
|
|
197
|
+
}
|
|
182
198
|
assertLocalCaller(req);
|
|
183
199
|
return null;
|
|
184
200
|
}
|
|
@@ -224,20 +240,23 @@ function chargeCaller(tenantId, actor, opts) {
|
|
|
224
240
|
warnCallerLimited(key, tenantId, person);
|
|
225
241
|
throw new HttpError(429, 'rate limit exceeded for this caller', limiter.retryAfterSec);
|
|
226
242
|
}
|
|
227
|
-
/**
|
|
228
|
-
*
|
|
229
|
-
* address. Throws HttpError(401) for invalid / missing credentials. Reads
|
|
230
|
-
* the store only for an API-key-shaped Bearer token (or any Bearer token when no
|
|
231
|
-
* auth resolver is registered), so loopback no-auth requests stay cheap.
|
|
232
|
-
*/
|
|
243
|
+
/** Builds a per-request Context from the Authorization header and remote address; throws HttpError(401) for invalid or missing credentials.
|
|
244
|
+
* Reads the store only for an API-key-shaped Bearer (or any Bearer when no auth resolver is registered), so keyless local requests stay cheap. */
|
|
233
245
|
export async function buildContextWithAuth(req, opts) {
|
|
234
246
|
const id = await checkAuth(req, opts);
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
chargeCaller(id.tenantId, actor, opts);
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
247
|
+
const ctx = contextFor(id, opts);
|
|
248
|
+
if (id !== null)
|
|
249
|
+
chargeCaller(id.tenantId, ctx.actor, opts);
|
|
250
|
+
return ctx;
|
|
251
|
+
}
|
|
252
|
+
/** The auth check again, uncharged, for a handler that authenticated with requireAuth before a slow body read and must re-check right before it acts. */
|
|
253
|
+
export async function recheckContextWithAuth(req, opts) {
|
|
254
|
+
return contextFor(await checkAuth(req, opts), opts);
|
|
255
|
+
}
|
|
256
|
+
function contextFor(id, opts) {
|
|
257
|
+
if (id !== null)
|
|
258
|
+
return { hippoRoot: opts.hippoRoot, tenantId: id.tenantId, actor: bearerActor(id), store: opts.store };
|
|
259
|
+
// The keyless local fallback (HIPPO_ALLOW_KEYLESS_LOCAL=1) is this machine's own user, so it is host admin.
|
|
241
260
|
return {
|
|
242
261
|
hippoRoot: opts.hippoRoot,
|
|
243
262
|
tenantId: resolveTenantId({}),
|
|
@@ -245,12 +264,8 @@ export async function buildContextWithAuth(req, opts) {
|
|
|
245
264
|
store: opts.store,
|
|
246
265
|
};
|
|
247
266
|
}
|
|
248
|
-
/**
|
|
249
|
-
*
|
|
250
|
-
* which builds its own root resolution via findHippoRoot). Throws HttpError
|
|
251
|
-
* 401 the same way buildContextWithAuth does, but skips building the Context
|
|
252
|
-
* envelope. Loopback no-auth still passes.
|
|
253
|
-
*/
|
|
267
|
+
/** Auth check for routes that need no tenant Context (e.g. the MCP transport, which resolves its own root): throws HttpError 401 like buildContextWithAuth.
|
|
268
|
+
* A keyless local request passes only under HIPPO_ALLOW_KEYLESS_LOCAL=1. */
|
|
254
269
|
export async function requireAuth(req, opts) {
|
|
255
270
|
const id = await checkAuth(req, opts);
|
|
256
271
|
if (id !== null)
|
|
@@ -263,6 +278,8 @@ export async function heartbeatVerdict(req, opts) {
|
|
|
263
278
|
return 'ok';
|
|
264
279
|
}
|
|
265
280
|
catch (err) {
|
|
281
|
+
if (!(err instanceof HttpError))
|
|
282
|
+
log.error(`heartbeat auth check failed: ${errorMessage(err)}`, errorFields(err));
|
|
266
283
|
return err instanceof HttpError && err.status < 500 && err.status !== 429 ? 'revoked' : 'unavailable';
|
|
267
284
|
}
|
|
268
285
|
}
|
package/dist/server/boot.d.ts
CHANGED
|
@@ -1,24 +1,5 @@
|
|
|
1
1
|
import type { ServeOpts, ServerHandle } from './types.js';
|
|
2
|
-
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* Refuses non-loopback hosts at boot unless
|
|
6
|
-
* HIPPO_REQUIRE_AUTH=1 is set. The auth middleware (buildContextWithAuth /
|
|
7
|
-
* requireAuth) has shipped and every route checks it except GET /health
|
|
8
|
-
* (public by design for platform health checks) and the two connector
|
|
9
|
-
* webhooks in PUBLIC_ROUTES, which are HMAC-gated by their own signing
|
|
10
|
-
* secrets and 404 when those secrets are unset, and any publicJson GET path. But the loopback
|
|
11
|
-
* no-auth fallback inside buildContextWithAuth still admits unauthenticated
|
|
12
|
-
* requests from a loopback remote address (unless they carry Forwarded,
|
|
13
|
-
* X-Forwarded-For/-Host/-Proto, X-Real-IP, Cf-Connecting-Ip, True-Client-Ip or Fly-Client-Ip, which mark a same-host proxy and get
|
|
14
|
-
* a 401 like any keyless remote request), so binding to a non-loopback host
|
|
15
|
-
* is only safe once that fallback is disabled with HIPPO_REQUIRE_AUTH=1,
|
|
16
|
-
* which forces every request (loopback or not) through Bearer-token
|
|
17
|
-
* validation. Without that env var set, a non-loopback bind would expose the
|
|
18
|
-
* DB to the network with no auth, so we fail fast instead.
|
|
19
|
-
*
|
|
20
|
-
* Use port: 0 in tests to bind to an ephemeral port and read the actual
|
|
21
|
-
* port back via server.address() after listen.
|
|
22
|
-
*/
|
|
2
|
+
/** Boots the HTTP daemon on host:port and writes the pidfile under hippoRoot; refuses non-loopback hosts unless HIPPO_REQUIRE_AUTH=1.
|
|
3
|
+
* Use port: 0 in tests for an ephemeral port. See docs/ARCHITECTURE.md#srcserverbootts. */
|
|
23
4
|
export declare function serve(opts: ServeOpts): Promise<ServerHandle>;
|
|
24
5
|
//# sourceMappingURL=boot.d.ts.map
|
package/dist/server/boot.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { envPort, envRequireAuth, envV1Rps } from '../util/env.js';
|
|
2
|
-
import { existsSync } from 'node:fs';
|
|
3
2
|
import { detectServer, removePidfileIfOwned, writePidfile } from './server-detect.js';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
3
|
+
import { outsideSqliteOffLoop, runWithRequestStores, SERVER_DB_WAIT_MS, withSqliteBlocked } from '../db/index.js';
|
|
4
|
+
import { isStoreBusy } from '../store/sqlite/busy.js';
|
|
5
|
+
import { holdSqliteConnection } from '../store/sqlite/held-connection.js';
|
|
7
6
|
import { workerSqliteStore } from '../store/sqlite/worker-store.js';
|
|
8
7
|
import { markSharedStore } from '../core/config.js';
|
|
9
8
|
import { auditWriteFailureCount } from '../store/audit.js';
|
|
@@ -12,30 +11,28 @@ import { errorFields, errorMessage, log } from '../util/log.js';
|
|
|
12
11
|
import { runWithRequestId } from '../util/request-scope.js';
|
|
13
12
|
import { createRateLimiter } from './rate-limit.js';
|
|
14
13
|
import { RecallContractError } from '../api/index.js';
|
|
15
|
-
import {
|
|
16
|
-
import { handleGitHubEventsWebhook } from '../connectors/github/webhook.js';
|
|
17
|
-
import { bodyDeadlineMs, BodyTimeoutError, BodyTooLargeError, closeAfterReply, DeadlineExceededError, HttpError, JSON_HEADERS, sendJson } from '../util/http-util.js';
|
|
14
|
+
import { BodyTimeoutError, BodyTooLargeError, closeAfterReply, DeadlineExceededError, HttpError, JSON_HEADERS, sendJson } from '../util/http-util.js';
|
|
18
15
|
import { workerCounts } from '../store/sqlite/executor-counts.js';
|
|
19
|
-
import {
|
|
16
|
+
import { isLocalCaller, LIMITER_MAX_KEYS } from './auth.js';
|
|
20
17
|
import { enforceRateLimit, warnIfClientIpHeaderUnpinned } from './client-ip.js';
|
|
21
18
|
import { answerAtDeadline, handlerDeadlineCount, isAbandoned, requestDeadlineFor } from './deadline.js';
|
|
22
|
-
import { drainAndClose } from './lifecycle.js';
|
|
19
|
+
import { DEFAULT_SHUTDOWN_DRAIN_MS, drainAndClose, setKeepAliveTimeouts, shutdownBoundMs } from './lifecycle.js';
|
|
23
20
|
import { readyProbeFor } from './ready.js';
|
|
24
|
-
import {
|
|
21
|
+
import { DEFAULT_SERVER_HOST, DEFAULT_SERVER_PORT } from './defaults.js';
|
|
22
|
+
import { installCrashHandlers, installSignalHandlers } from '../util/crash-handlers.js';
|
|
25
23
|
import { handleMcpPost, handleMcpStream } from './mcp-http.js';
|
|
26
24
|
import { MCP_PROJECT_SCOPED_HEADER } from '../core/project-identity.js';
|
|
27
|
-
import { logRequestFailure, noteAccess, openRequest, parseRequest, rejectEncodedSlash, replyFor, sendError } from './request.js';
|
|
25
|
+
import { accessRouteOf, logRequestFailure, noteAccess, openRequest, parseRequest, rejectEncodedSlash, replyFor, sendError } from './request.js';
|
|
28
26
|
import { createListener, warnIfCleartext } from './tls.js';
|
|
29
|
-
import { assertAddonRoutes, assertPublicJson
|
|
27
|
+
import { assertAddonRoutes, assertPublicJson } from './route-checks.js';
|
|
28
|
+
import { dispatchAddonRoute, dispatchPublicJson, dispatchV1Route } from './route-table.js';
|
|
30
29
|
// server.address() returns AddressInfo once a TCP socket is bound; null before
|
|
31
30
|
// listening, a string only for pipe/unix-socket listeners (never used here).
|
|
32
31
|
function isAddressInfo(a) {
|
|
33
32
|
return a !== null && typeof a !== 'string';
|
|
34
33
|
}
|
|
35
|
-
// Pinned at module load
|
|
36
|
-
//
|
|
37
|
-
// would couple the daemon to its on-disk install path, which we want to
|
|
38
|
-
// avoid for tests that mkdtemp a hippoRoot.
|
|
34
|
+
// Pinned at module load, bumped with package.json on releases; reading package.json synchronously here would couple the daemon to its install path,
|
|
35
|
+
// which tests that mkdtemp a hippoRoot avoid.
|
|
39
36
|
const VERSION = PACKAGE_VERSION;
|
|
40
37
|
const LOOPBACK_HOSTS = new Set(['127.0.0.1', '::1', 'localhost']);
|
|
41
38
|
async function handleRequest(req, res, opts, options) {
|
|
@@ -61,7 +58,8 @@ async function handleRequest(req, res, opts, options) {
|
|
|
61
58
|
const routeRequest = { req, res, opts, query };
|
|
62
59
|
if (await runWithRequestStores(() => dispatchScopedRoute(routeRequest, method, path), { busyWaitMs: SERVER_DB_WAIT_MS }))
|
|
63
60
|
return;
|
|
64
|
-
// A scope of its own, which the heartbeat timer keeps after it closes, so the key check and every
|
|
61
|
+
// A scope of its own, which the heartbeat timer keeps after it closes, so the key check and every
|
|
62
|
+
// heartbeat wait the server's lock wait. Store-ready: the stream only authenticates, through the port.
|
|
65
63
|
if (method === 'GET' && path === '/mcp/stream') {
|
|
66
64
|
noteAccess(req, { route: path });
|
|
67
65
|
await runWithRequestStores(() => handleMcpStream(req, res, opts, streamSlots), { busyWaitMs: SERVER_DB_WAIT_MS });
|
|
@@ -70,13 +68,7 @@ async function handleRequest(req, res, opts, options) {
|
|
|
70
68
|
res.writeHead(404, JSON_HEADERS);
|
|
71
69
|
res.end(JSON.stringify({ error: 'not found' }));
|
|
72
70
|
}
|
|
73
|
-
|
|
74
|
-
/** A store missing a group a delivery writes through refuses the webhook up front, so no delivery is acknowledged and then half stored. */
|
|
75
|
-
function requireWebhookGroups(store) {
|
|
76
|
-
for (const group of WEBHOOK_GROUPS)
|
|
77
|
-
requireGroup(store, group);
|
|
78
|
-
}
|
|
79
|
-
/** Every route that runs inside a request scope, so it opens each store once: the /v1 table, public JSON, add-on routes, the webhooks and POST /mcp. */
|
|
71
|
+
/** Every route that runs inside a request scope, so it opens each store once: the /v1 table with the webhooks, public JSON, add-on routes and POST /mcp. */
|
|
80
72
|
async function dispatchScopedRoute(r, method, path) {
|
|
81
73
|
if (await dispatchV1Route(r, method, path))
|
|
82
74
|
return true;
|
|
@@ -85,26 +77,6 @@ async function dispatchScopedRoute(r, method, path) {
|
|
|
85
77
|
if (await dispatchAddonRoute(r, method, path))
|
|
86
78
|
return true;
|
|
87
79
|
const { req, res, opts } = r;
|
|
88
|
-
if (method === 'POST' && path === '/v1/connectors/slack/events') {
|
|
89
|
-
noteAccess(req, { route: path });
|
|
90
|
-
// Bearer auth deliberately skipped: this route is in PUBLIC_ROUTES and authenticates via the Slack HMAC signature.
|
|
91
|
-
if (!isPublicRoute(method, path)) {
|
|
92
|
-
// Defensive: PUBLIC_ROUTES drift would land here. Fail closed.
|
|
93
|
-
throw new HttpError(401, 'auth required');
|
|
94
|
-
}
|
|
95
|
-
requireWebhookGroups(opts.store);
|
|
96
|
-
await handleSlackEventsWebhook({ req, res, opts }, opts.store);
|
|
97
|
-
return true;
|
|
98
|
-
}
|
|
99
|
-
if (method === 'POST' && path === '/v1/connectors/github/events') {
|
|
100
|
-
noteAccess(req, { route: path });
|
|
101
|
-
if (!isPublicRoute(method, path)) {
|
|
102
|
-
throw new HttpError(401, 'auth required');
|
|
103
|
-
}
|
|
104
|
-
requireWebhookGroups(opts.store);
|
|
105
|
-
await handleGitHubEventsWebhook({ req, res, opts }, opts.store);
|
|
106
|
-
return true;
|
|
107
|
-
}
|
|
108
80
|
// Store-ready: under another store the MCP layer lists and runs only the tools ported to the port.
|
|
109
81
|
if (method === 'POST' && path === '/mcp') {
|
|
110
82
|
noteAccess(req, { route: path });
|
|
@@ -114,11 +86,9 @@ async function dispatchScopedRoute(r, method, path) {
|
|
|
114
86
|
return false;
|
|
115
87
|
}
|
|
116
88
|
function sendHealth(req, res, startedAt) {
|
|
117
|
-
//
|
|
118
|
-
//
|
|
119
|
-
|
|
120
|
-
// the pid is noise. Platform health checks only need the 200.
|
|
121
|
-
if (isLoopback(req.socket.remoteAddress)) {
|
|
89
|
+
// A caller on this machine (detectServer's probe reads version and pid) gets the full body; anyone else, even a proxied caller or a browser page on loopback,
|
|
90
|
+
// gets liveness only: the version would fingerprint the build and the pid is noise. Platform health checks only need the 200.
|
|
91
|
+
if (isLocalCaller(req)) {
|
|
122
92
|
sendJson(res, 200, {
|
|
123
93
|
ok: true,
|
|
124
94
|
version: VERSION,
|
|
@@ -135,7 +105,8 @@ function sendHealth(req, res, startedAt) {
|
|
|
135
105
|
sendJson(res, 200, { ok: true });
|
|
136
106
|
}
|
|
137
107
|
}
|
|
138
|
-
/** Readiness: one cheap read on the served store under the server's lock wait. /health stays
|
|
108
|
+
/** Readiness: one cheap read on the served store under the server's lock wait. /health stays
|
|
109
|
+
* liveness only, so a probe can tell a store that does not answer from a dead process. */
|
|
139
110
|
async function sendReady(res, store) {
|
|
140
111
|
const { readiness } = store;
|
|
141
112
|
if (readiness === undefined) {
|
|
@@ -156,10 +127,8 @@ function assertBindable(host) {
|
|
|
156
127
|
}
|
|
157
128
|
}
|
|
158
129
|
async function assertNoLiveServer(hippoRoot) {
|
|
159
|
-
// Refuse to start if a live hippo server already serves this hippoRoot
|
|
160
|
-
//
|
|
161
|
-
// ignored, but a live peer means a concurrent `hippo serve` would race for
|
|
162
|
-
// the port and clobber the pidfile.
|
|
130
|
+
// Refuse to start if a live hippo server already serves this hippoRoot (detectServer probes the recorded /health and unlinks a stale pidfile):
|
|
131
|
+
// a concurrent `hippo serve` would race for the port and clobber the pidfile.
|
|
163
132
|
const existing = await detectServer(hippoRoot);
|
|
164
133
|
if (existing) {
|
|
165
134
|
throw new Error(`hippo serve: already running on port ${existing.port} (pid ${existing.pid}). ` +
|
|
@@ -175,10 +144,8 @@ function bootRateLimiter(perAddress) {
|
|
|
175
144
|
return undefined;
|
|
176
145
|
if (perAddress !== undefined)
|
|
177
146
|
return limiterFor(perAddress);
|
|
178
|
-
// Per-IP rate limiter for /v1/* and /mcp
|
|
179
|
-
//
|
|
180
|
-
// set the rate before serve(). A non-positive or non-finite value disables
|
|
181
|
-
// limiting (the opt-out knob).
|
|
147
|
+
// Per-IP rate limiter for /v1/* and /mcp*, built here (not at module scope) so HIPPO_V1_RPS is read at boot and a test can set it before serve().
|
|
148
|
+
// A non-positive or non-finite value disables limiting (the opt-out knob).
|
|
182
149
|
const v1Rps = Number(envV1Rps() ?? 20);
|
|
183
150
|
return Number.isFinite(v1Rps) && v1Rps > 0 ? limiterFor({ ratePerSec: v1Rps, burst: v1Rps * 2 }) : undefined;
|
|
184
151
|
}
|
|
@@ -209,39 +176,7 @@ function createStoreHolder(hippoRoot, store) {
|
|
|
209
176
|
log.info(`serve: the '${store.kind}' store serves ${hippoRoot}, so no hippo.db connection is held`);
|
|
210
177
|
return { hold: () => { }, afterResponse: () => { }, release: async () => { } };
|
|
211
178
|
}
|
|
212
|
-
|
|
213
|
-
// which checkpoints and deletes the WAL. It opens only once the store exists, so serving never creates one.
|
|
214
|
-
let heldDb;
|
|
215
|
-
let checkpointer;
|
|
216
|
-
let stopHolding = false;
|
|
217
|
-
const hold = () => {
|
|
218
|
-
if (heldDb || stopHolding || !existsSync(getHippoDbPath(hippoRoot)))
|
|
219
|
-
return;
|
|
220
|
-
try {
|
|
221
|
-
// The 'finish' listener can fire inside a request scope, which would close this connection with the request,
|
|
222
|
-
// and inside the block of a `loop: 'off'` route, which would refuse the open.
|
|
223
|
-
// The server's lock wait: this open runs on the event loop, where SQLite's 5 s default and the 30 s journal-mode retry would stall every request.
|
|
224
|
-
heldDb = outsideSqliteOffLoop(() => outsideRequestStores(() => openHippoDb(hippoRoot, { busyWaitMs: SERVER_DB_WAIT_MS })));
|
|
225
|
-
checkpointer = startWalCheckpointer(getHippoDbPath(hippoRoot));
|
|
226
|
-
}
|
|
227
|
-
catch (err) {
|
|
228
|
-
stopHolding = true;
|
|
229
|
-
log.warn(`serve: could not hold a store connection; requests still work, only slower: ${errorMessage(err)}`);
|
|
230
|
-
}
|
|
231
|
-
};
|
|
232
|
-
const afterResponse = () => {
|
|
233
|
-
hold();
|
|
234
|
-
checkpointer?.noteResponse();
|
|
235
|
-
};
|
|
236
|
-
const release = async () => {
|
|
237
|
-
stopHolding = true;
|
|
238
|
-
// The worker's connection closes first, so the held one is SQLite's last and its close checkpoints and deletes the WAL.
|
|
239
|
-
await checkpointer?.stop();
|
|
240
|
-
if (heldDb)
|
|
241
|
-
closeHippoDb(heldDb);
|
|
242
|
-
heldDb = undefined;
|
|
243
|
-
};
|
|
244
|
-
return { hold, afterResponse, release };
|
|
179
|
+
return holdSqliteConnection(hippoRoot);
|
|
245
180
|
}
|
|
246
181
|
// A plugin's resolver may read hippo.db through this package's synchronous functions, so it runs outside the block of a `loop: 'off'` route.
|
|
247
182
|
function outsideRouteBlock(resolver) {
|
|
@@ -281,9 +216,23 @@ function replyWithFailure(req, res, err, requestId) {
|
|
|
281
216
|
sendError(res, mapped.status, mapped.message);
|
|
282
217
|
}
|
|
283
218
|
const HANDLER_LATE = 'the request did not finish by its deadline and was abandoned; a write it started may or may not be saved';
|
|
284
|
-
// The caller already has its 504, so
|
|
285
|
-
function logLateFailure(req, err) {
|
|
286
|
-
|
|
219
|
+
// The caller already has its 504, so this line is the only trace of the failure. The path and the error text can hold caller data, so it carries neither.
|
|
220
|
+
function logLateFailure(req, err, requestId) {
|
|
221
|
+
const fields = { requestId, method: req.method ?? 'GET', route: accessRouteOf(req), failureStatus: replyFor(err).status };
|
|
222
|
+
log.warn('request failed after its deadline reply', { ...fields, errorClass: errorFields(err).errorClass });
|
|
223
|
+
}
|
|
224
|
+
// A handler that ends after the 504 finds the response finished, so sending its own reply throws this: its work ran to the end.
|
|
225
|
+
function isDroppedLateReply(err) {
|
|
226
|
+
return err instanceof Error && 'code' in err && err.code === 'ERR_HTTP_HEADERS_SENT';
|
|
227
|
+
}
|
|
228
|
+
/** A GET only reads; every other method this server routes can write. */
|
|
229
|
+
const isRead = (method) => method === 'GET';
|
|
230
|
+
// A read that ends late changed nothing. A write that ends late ran to its end after its caller was told it may not have been saved.
|
|
231
|
+
function logLateFinish(req, requestId) {
|
|
232
|
+
const method = req.method ?? 'GET';
|
|
233
|
+
if (isRead(method))
|
|
234
|
+
return;
|
|
235
|
+
log.info('request finished after its deadline reply; its own reply was dropped', { requestId, method, route: accessRouteOf(req) });
|
|
287
236
|
}
|
|
288
237
|
function replyOrClose(req, res, err, requestId) {
|
|
289
238
|
try {
|
|
@@ -291,7 +240,7 @@ function replyOrClose(req, res, err, requestId) {
|
|
|
291
240
|
}
|
|
292
241
|
catch (replyErr) {
|
|
293
242
|
// A throw here would be an unhandled rejection, which stops the daemon for every caller.
|
|
294
|
-
log.error(`serve: failure reply not sent, socket closed: ${errorMessage(replyErr)}
|
|
243
|
+
log.error(`serve: failure reply not sent, socket closed: ${errorMessage(replyErr)}`, errorFields(replyErr));
|
|
295
244
|
res.destroy();
|
|
296
245
|
}
|
|
297
246
|
}
|
|
@@ -303,24 +252,19 @@ function answerRequest(req, res, handle, slowWarnMs) {
|
|
|
303
252
|
runWithRequestId(requestId, () => {
|
|
304
253
|
if (deadline)
|
|
305
254
|
answerAtDeadline(res, deadline, () => replyOrClose(req, res, new DeadlineExceededError(HANDLER_LATE), requestId));
|
|
306
|
-
handle().
|
|
255
|
+
handle().then(() => {
|
|
307
256
|
if (isAbandoned(res))
|
|
308
|
-
|
|
309
|
-
|
|
257
|
+
logLateFinish(req, requestId);
|
|
258
|
+
}, (err) => {
|
|
259
|
+
if (!isAbandoned(res))
|
|
310
260
|
replyOrClose(req, res, err, requestId);
|
|
261
|
+
else if (isDroppedLateReply(err))
|
|
262
|
+
logLateFinish(req, requestId);
|
|
263
|
+
else
|
|
264
|
+
logLateFailure(req, err, requestId);
|
|
311
265
|
});
|
|
312
266
|
}, deadline);
|
|
313
267
|
}
|
|
314
|
-
// Node's own default, named so the three socket deadlines read together. It times the request arriving, never the handler, so a 10 minute sleep is not cut.
|
|
315
|
-
const REQUEST_RECEIVE_TIMEOUT_MS = 300_000;
|
|
316
|
-
function setKeepAliveTimeouts(server) {
|
|
317
|
-
// The default 5s keepAliveTimeout closes idle sockets just as clients reuse them (ECONNRESET).
|
|
318
|
-
// headersTimeout must stay ABOVE keepAliveTimeout + keepAliveTimeoutBuffer (1s), or it closes idle reused sockets itself.
|
|
319
|
-
server.keepAliveTimeout = 65_000;
|
|
320
|
-
server.headersTimeout = 70_000;
|
|
321
|
-
// Never below what a raised body deadline allows, or Node's bare 408 would come before the route's own.
|
|
322
|
-
server.requestTimeout = Math.max(REQUEST_RECEIVE_TIMEOUT_MS, server.headersTimeout + bodyDeadlineMs());
|
|
323
|
-
}
|
|
324
268
|
function listenOn(server, port, host) {
|
|
325
269
|
return new Promise((resolve, reject) => {
|
|
326
270
|
const onError = (err) => {
|
|
@@ -338,57 +282,23 @@ function listenOn(server, port, host) {
|
|
|
338
282
|
server.listen(port, host);
|
|
339
283
|
});
|
|
340
284
|
}
|
|
341
|
-
function
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
return;
|
|
346
|
-
shuttingDown = true;
|
|
347
|
-
log.warn(`received ${signal}, shutting down`);
|
|
348
|
-
try {
|
|
349
|
-
await stop();
|
|
350
|
-
process.exit(0);
|
|
351
|
-
}
|
|
352
|
-
catch (err) {
|
|
353
|
-
log.error(`error during stop: ${errorMessage(err)}`, errorFields(err));
|
|
354
|
-
process.exit(1);
|
|
355
|
-
}
|
|
356
|
-
};
|
|
357
|
-
process.once('SIGTERM', () => { void gracefulShutdown('SIGTERM'); });
|
|
358
|
-
process.once('SIGINT', () => { void gracefulShutdown('SIGINT'); });
|
|
285
|
+
function exitOnSignalOrCrash(stop, drainMs) {
|
|
286
|
+
const shutdown = { run: stop, boundMs: shutdownBoundMs(drainMs) };
|
|
287
|
+
installSignalHandlers('serve', shutdown);
|
|
288
|
+
installCrashHandlers('serve', shutdown);
|
|
359
289
|
}
|
|
360
|
-
/**
|
|
361
|
-
*
|
|
362
|
-
*
|
|
363
|
-
* Refuses non-loopback hosts at boot unless
|
|
364
|
-
* HIPPO_REQUIRE_AUTH=1 is set. The auth middleware (buildContextWithAuth /
|
|
365
|
-
* requireAuth) has shipped and every route checks it except GET /health
|
|
366
|
-
* (public by design for platform health checks) and the two connector
|
|
367
|
-
* webhooks in PUBLIC_ROUTES, which are HMAC-gated by their own signing
|
|
368
|
-
* secrets and 404 when those secrets are unset, and any publicJson GET path. But the loopback
|
|
369
|
-
* no-auth fallback inside buildContextWithAuth still admits unauthenticated
|
|
370
|
-
* requests from a loopback remote address (unless they carry Forwarded,
|
|
371
|
-
* X-Forwarded-For/-Host/-Proto, X-Real-IP, Cf-Connecting-Ip, True-Client-Ip or Fly-Client-Ip, which mark a same-host proxy and get
|
|
372
|
-
* a 401 like any keyless remote request), so binding to a non-loopback host
|
|
373
|
-
* is only safe once that fallback is disabled with HIPPO_REQUIRE_AUTH=1,
|
|
374
|
-
* which forces every request (loopback or not) through Bearer-token
|
|
375
|
-
* validation. Without that env var set, a non-loopback bind would expose the
|
|
376
|
-
* DB to the network with no auth, so we fail fast instead.
|
|
377
|
-
*
|
|
378
|
-
* Use port: 0 in tests to bind to an ephemeral port and read the actual
|
|
379
|
-
* port back via server.address() after listen.
|
|
380
|
-
*/
|
|
290
|
+
/** Boots the HTTP daemon on host:port and writes the pidfile under hippoRoot; refuses non-loopback hosts unless HIPPO_REQUIRE_AUTH=1.
|
|
291
|
+
* Use port: 0 in tests for an ephemeral port. See docs/ARCHITECTURE.md#srcserverbootts. */
|
|
381
292
|
export async function serve(opts) {
|
|
382
|
-
const host = opts.host ??
|
|
383
|
-
const requestedPort = opts.port ?? Number(envPort() ??
|
|
293
|
+
const host = opts.host ?? DEFAULT_SERVER_HOST;
|
|
294
|
+
const requestedPort = opts.port ?? Number(envPort() ?? DEFAULT_SERVER_PORT);
|
|
384
295
|
const routes = frozenAddonRoutes(opts.routes);
|
|
385
296
|
const publicJsonBodies = assertPublicJson(opts.publicJson ?? {});
|
|
386
297
|
const limiters = bootLimiters(opts.rateLimits);
|
|
387
298
|
assertBindable(host);
|
|
388
299
|
await assertNoLiveServer(opts.hippoRoot);
|
|
389
|
-
//
|
|
390
|
-
//
|
|
391
|
-
// can match the two and prove a pid-reusing impostor is not the real server.
|
|
300
|
+
// Single source of truth for the start time: returned by GET /health and written into the pidfile, so detectServer can prove a pid-reusing impostor is not
|
|
301
|
+
// this server.
|
|
392
302
|
const startedAt = new Date().toISOString();
|
|
393
303
|
// Open /mcp/stream count per client key, so the cap is per server rather than per process.
|
|
394
304
|
const streamSlots = new Map();
|
|
@@ -414,15 +324,15 @@ export async function serve(opts) {
|
|
|
414
324
|
await served.store.close();
|
|
415
325
|
await holder.release();
|
|
416
326
|
};
|
|
417
|
-
if (opts.handleSignals)
|
|
418
|
-
|
|
419
|
-
installCrashHandlers('serve', stop);
|
|
420
|
-
}
|
|
327
|
+
if (opts.handleSignals)
|
|
328
|
+
exitOnSignalOrCrash(stop, opts.shutdownDrainMs ?? DEFAULT_SHUTDOWN_DRAIN_MS);
|
|
421
329
|
return { port: actualPort, url, stop, server };
|
|
422
330
|
}
|
|
423
331
|
function frozenAddonRoutes(addonRoutes) {
|
|
424
332
|
// A frozen copy, so a route the caller adds or renames after boot never skips the check below.
|
|
425
|
-
const routes = Object.freeze((addonRoutes ?? []).map(({ path, handler, storeReady }) => Object.freeze(storeReady === undefined
|
|
333
|
+
const routes = Object.freeze((addonRoutes ?? []).map(({ path, handler, storeReady }) => Object.freeze(storeReady === undefined
|
|
334
|
+
? { path, handler }
|
|
335
|
+
: { path, handler, storeReady })));
|
|
426
336
|
assertAddonRoutes(routes);
|
|
427
337
|
return routes;
|
|
428
338
|
}
|
|
@@ -456,10 +366,8 @@ async function listenAndDescribe(server, port, host, tls) {
|
|
|
456
366
|
return { port: actualPort, url };
|
|
457
367
|
}
|
|
458
368
|
async function stopListening(opts, server, inflight, startedAt) {
|
|
459
|
-
// Remove the pidfile only if it still names this server
|
|
460
|
-
// may have started on this hippoRoot and rewritten the pidfile; an
|
|
461
|
-
// unconditional unlink here would orphan it.
|
|
369
|
+
// Remove the pidfile only if it still names this server: a newer server may have rewritten it, and an unconditional unlink would orphan it.
|
|
462
370
|
removePidfileIfOwned(opts.hippoRoot, { pid: process.pid, startedAt });
|
|
463
|
-
await drainAndClose(server, inflight, opts.shutdownDrainMs ??
|
|
371
|
+
await drainAndClose(server, inflight, opts.shutdownDrainMs ?? DEFAULT_SHUTDOWN_DRAIN_MS);
|
|
464
372
|
}
|
|
465
373
|
//# sourceMappingURL=boot.js.map
|
|
@@ -1,30 +1,12 @@
|
|
|
1
1
|
import type { IncomingMessage } from 'node:http';
|
|
2
2
|
import type { RateLimiter } from './rate-limit.js';
|
|
3
3
|
/**
|
|
4
|
-
* Rate-limit key
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* carries the proxy's address, so per-IP buckets collapse into one global
|
|
8
|
-
* bucket that unauthenticated traffic can drain before auth runs. Set
|
|
9
|
-
* HIPPO_CLIENT_IP_HEADER to the header the proxy stamps with the real
|
|
10
|
-
* client address (fly-client-ip on Fly, which the edge always overwrites)
|
|
11
|
-
* to key buckets per client instead.
|
|
12
|
-
*
|
|
13
|
-
* Only set this when a trusted proxy fronts EVERY request: a directly
|
|
14
|
-
* reachable server honoring the header would let clients mint a fresh
|
|
15
|
-
* bucket per request and bypass the limiter entirely.
|
|
16
|
-
*
|
|
17
|
-
* HIPPO_TRUSTED_PROXIES (comma-separated IPs or CIDRs) pins which peers count
|
|
18
|
-
* as that proxy: the header is read only when the socket peer is listed, and
|
|
19
|
-
* listed hops are skipped. In a comma-joined chain (X-Forwarded-For) the key is
|
|
20
|
-
* the rightmost hop no trusted proxy added; entries to its left are whatever
|
|
21
|
-
* the client sent, so they never pick the bucket.
|
|
22
|
-
*/
|
|
4
|
+
* Rate-limit key: the socket's remote address, which behind a TLS proxy collapses into one bucket unauthenticated traffic can drain.
|
|
5
|
+
* Set HIPPO_CLIENT_IP_HEADER (fly-client-ip on Fly) only when a trusted proxy fronts EVERY request, or clients could mint a fresh bucket per request.
|
|
6
|
+
* HIPPO_TRUSTED_PROXIES (IPs or CIDRs) pins which peers count as that proxy; in a comma-joined chain the key is the rightmost hop no trusted proxy added. */
|
|
23
7
|
export declare function clientIpForRateLimit(req: IncomingMessage): string;
|
|
24
8
|
/** Said once at boot: with no proxy list, a caller that reaches the port without the proxy picks its own bucket by sending the header. */
|
|
25
9
|
export declare function warnIfClientIpHeaderUnpinned(): void;
|
|
26
|
-
/** The key for one subscriber: an IPv6 address's /64, since one host may hold the whole /64; any other key as is. */
|
|
27
|
-
export declare function subscriberKey(ip: string): string;
|
|
28
10
|
/** The one key every per-address limit and slot count uses, so rotating inside a /64 never buys a fresh bucket. */
|
|
29
11
|
export declare function clientLimitKey(req: IncomingMessage): string;
|
|
30
12
|
export declare function enforceRateLimit(req: IncomingMessage, path: string, limiter?: RateLimiter): void;
|